/*
 * @(#)Bevel3d.c
 *
 * Copyright 2023  David A. Bagley, bagleyd AT verizon.net
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * This program is distributed in the hope that it will be "useful",
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

/* Methods file for Bevel3d */

#include "BevelP.h"
#include "Bevel3dP.h"

#ifndef WINVER
static Boolean setValuesPuzzle3D(Widget current, Widget request, Widget renew);
static void resizePuzzle3D(Bevel3DWidget w);
static void initializePuzzle3D(Widget request, Widget renew);
static void exposePuzzle3D(Widget renew, XEvent *event, Region region);
static void movePuzzle3DTl(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DTop(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DTr(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DLeft(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DRight(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DBl(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DBottom(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzle3DBr(Bevel3DWidget w,
	XEvent *event, char **args, int nArgs);

static char translations3D[] =
"<KeyPress>q: Quit()\n\
 Ctrl<KeyPress>C: Quit()\n\
 <KeyPress>osfCancel: Hide()\n\
 <KeyPress>Escape: Hide()\n\
 <KeyPress>osfEscape: Hide()\n\
 Ctrl<KeyPress>[: Hide()\n\
 <KeyPress>0x1B: Hide()\n\
 <KeyPress>0x2E: Speed()\n\
 <KeyPress>0x3E: Speed()\n\
 <KeyPress>0x3C: Slow()\n\
 <KeyPress>0x2C: Slow()\n\
 Shift<KeyPress>2: Sound()\n\
 <KeyPress>F11: MoveCcw()\n\
 <KeyPress>KP_Divide: MoveCcw()\n\
 <KeyPress>R5: MoveCcw()\n\
 <KeyPress>Home: MoveTl()\n\
 <KeyPress>KP_7: MoveTl()\n\
 <KeyPress>R7: MoveTl()\n\
 <KeyPress>Up: MoveTop()\n\
 <KeyPress>osfUp: MoveTop()\n\
 <KeyPress>KP_Up: MoveTop()\n\
 <KeyPress>KP_8: MoveTop()\n\
 <KeyPress>R8: MoveTop()\n\
 <KeyPress>Prior: MoveTr()\n\
 <KeyPress>KP_9: MoveTr()\n\
 <KeyPress>R9: MoveTr()\n\
 <KeyPress>Left: MoveLeft()\n\
 <KeyPress>osfLeft: MoveLeft()\n\
 <KeyPress>KP_Left: MoveLeft()\n\
 <KeyPress>KP_4: MoveLeft()\n\
 <KeyPress>R10: MoveLeft()\n\
 <KeyPress>F12: MoveCw()\n\
 <KeyPress>Begin: MoveCw()\n\
 <KeyPress>KP_5: MoveCw()\n\
 <KeyPress>R11: MoveCw()\n\
 <KeyPress>Right: MoveRight()\n\
 <KeyPress>osfRight: MoveRight()\n\
 <KeyPress>KP_Right: MoveRight()\n\
 <KeyPress>KP_6: MoveRight()\n\
 <KeyPress>R12: MoveRight()\n\
 <KeyPress>End: MoveBl()\n\
 <KeyPress>KP_1: MoveBl()\n\
 <KeyPress>R13: MoveBl()\n\
 <KeyPress>Down: MoveBottom()\n\
 <KeyPress>osfDown: MoveBottom()\n\
 <KeyPress>KP_Down: MoveBottom()\n\
 <KeyPress>KP_2: MoveBottom()\n\
 <KeyPress>R14: MoveBottom()\n\
 <KeyPress>Next: MoveBr()\n\
 <KeyPress>KP_3: MoveBr()\n\
 <KeyPress>R15: MoveBr()\n\
 <Btn1Down>: Select()\n\
 <Btn1Up>: Release()\n\
 <Btn2Down>: PracticeMaybe()\n\
 <Btn2Down>(2+): Practice2()\n\
 <Btn3Down>: RandomizeMaybe()\n\
 <Btn3Down>(2+): Randomize2()\n\
 <Btn4Down>: MoveTop()\n\
 <Btn5Down>: MoveBottom()\n\
 <KeyPress>g: Get()\n\
 <KeyPress>w: Write()\n\
 <KeyPress>u: Undo()\n\
 <KeyPress>r: Redo()\n\
 <KeyPress>c: Clear()\n\
 <KeyPress>z: Randomize()\n\
 <KeyPress>s: Solve()\n\
 <KeyPress>f: Find()\n\
 <KeyPress>p: Practice()\n\
 <KeyPress>o: Orientize()\n\
 <KeyPress>v: View()\n\
 <EnterWindow>: Enter()\n\
 <LeaveWindow>: Leave()";

static XtActionsRec actionsList3D[] =
{
	{(char *) "Quit", (XtActionProc) quitPuzzle},
	{(char *) "Hide", (XtActionProc) hidePuzzle},
	{(char *) "MoveCcw", (XtActionProc) movePuzzleCcw},
	{(char *) "MoveTl", (XtActionProc) movePuzzle3DTl},
	{(char *) "MoveTop", (XtActionProc) movePuzzle3DTop},
	{(char *) "MoveTr", (XtActionProc) movePuzzle3DTr},
	{(char *) "MoveLeft", (XtActionProc) movePuzzle3DLeft},
	{(char *) "MoveCw", (XtActionProc) movePuzzleCw},
	{(char *) "MoveRight", (XtActionProc) movePuzzle3DRight},
	{(char *) "MoveBl", (XtActionProc) movePuzzle3DBl},
	{(char *) "MoveBottom", (XtActionProc) movePuzzle3DBottom},
	{(char *) "MoveBr", (XtActionProc) movePuzzle3DBr},
	{(char *) "Select", (XtActionProc) selectPuzzle},
	{(char *) "Release", (XtActionProc) releasePuzzle},
	{(char *) "PracticeMaybe", (XtActionProc) practicePuzzleWithQuery},
	{(char *) "Practice2", (XtActionProc) practicePuzzleWithDoubleClick},
	{(char *) "RandomizeMaybe", (XtActionProc) randomizePuzzleWithQuery},
	{(char *) "Randomize2", (XtActionProc) randomizePuzzleWithDoubleClick},
	{(char *) "Get", (XtActionProc) getPuzzle},
	{(char *) "Write", (XtActionProc) writePuzzle},
	{(char *) "Undo", (XtActionProc) undoPuzzle},
	{(char *) "Redo", (XtActionProc) redoPuzzle},
	{(char *) "Clear", (XtActionProc) clearPuzzle},
	{(char *) "Randomize", (XtActionProc) randomizePuzzle},
	{(char *) "Solve", (XtActionProc) solvePuzzle},
	{(char *) "Find", (XtActionProc) findPuzzle},
	{(char *) "Practice", (XtActionProc) practicePuzzle},
	{(char *) "Orientize", (XtActionProc) orientizePuzzle},
	{(char *) "View", (XtActionProc) viewPuzzle},
	{(char *) "Speed", (XtActionProc) speedUpPuzzle},
	{(char *) "Slow", (XtActionProc) slowDownPuzzle},
	{(char *) "Sound", (XtActionProc) toggleSoundPuzzle},
	{(char *) "Enter", (XtActionProc) enterPuzzle},
	{(char *) "Leave", (XtActionProc) leavePuzzle}
};

static XtResource resources3D[] =
{
	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
	 XtOffset(BevelWidget, core.width),
	 XtRString, (caddr_t) "300"},
	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
	 XtOffset(BevelWidget, core.height),
	 XtRString, (caddr_t) "400"},
	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.mono),
	 XtRString, (caddr_t) "FALSE"},
	{XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.reverse),
	 XtRString, (caddr_t) "FALSE"},
	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.foreground),
	 XtRString, (caddr_t) XtDefaultForeground},
	{XtNbackground, XtCBackground, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.background),
	 XtRString, (caddr_t) "#AEB2C3" /*XtDefaultBackground*/},
	{XtNframeColor, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.frameColor),
	 XtRString, (caddr_t) "Cyan" /*XtDefaultForeground*/},
	{XtNfaceColor0, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[0]),
	 XtRString, (caddr_t) "Red"},
	{XtNfaceColor1, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[1]),
	 XtRString, (caddr_t) "Yellow"},
	{XtNfaceColor2, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[2]),
	 XtRString, (caddr_t) "Blue"},
	{XtNfaceColor3, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[3]),
	 XtRString, (caddr_t) "Orange"},
	{XtNfaceColor4, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[4]),
	 XtRString, (caddr_t) "White"},
	{XtNfaceColor5, XtCLabel, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.faceName[5]),
	 XtRString, (caddr_t) "Green"},
	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(BevelWidget, bevel.borderColor),
	 XtRString, (caddr_t) "gray25" /*XtDefaultForeground*/},
	{XtNdelay, XtCDelay, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.delay),
	 XtRString, (caddr_t) "10"},
	{XtNsound, XtCSound, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.sound),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmoveSound, XtCMoveSound, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.moveSound),
	 XtRString, (caddr_t) MOVESOUND},
	{XtNfont, XtCFont, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.font),
	 XtRString, (caddr_t) "9x15bold"},
	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.orient),
	 XtRString, (caddr_t) "FALSE"},	/* DEFAULT_ORIENT */
	{XtNpractice, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.practice),
	 XtRString, (caddr_t) "TRUE"}, /* DEFAULT_PRACTICE */
	{XtNuserName, XtCUserName, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.userName),
	 XtRString, (caddr_t) ""},
	{XtNscoreFile, XtCScoreFile, XtRString, sizeof (String),
	 XtOffset(BevelWidget, bevel.scoreFile),
	 XtRString, (caddr_t) ""},
	{XtNscoreOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.scoreOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNversionOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.versionOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmenu, XtCMenu, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.menu),
	 XtRString, (caddr_t) "999"}, /* ACTION_IGNORE */
	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.started),
	 XtRString, (caddr_t) "FALSE"},
	{XtNcheat, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(BevelWidget, bevel.cheat),
	 XtRString, (caddr_t) "FALSE"},
	{XtNface, XtCFace, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentFace),
	 XtRString, (caddr_t) "-1"},
	{XtNpos, XtCPos, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentPosition),
	 XtRString, (caddr_t) "-1"},
	{XtNdirection, XtCDirection, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentDirection),
	 XtRString, (caddr_t) "-1"},
	{XtNcontrol, XtCControl, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentControl),
	 XtRString, (caddr_t) "0"},
	{XtNfast, XtCFast, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.currentFast),
	 XtRString, (caddr_t) "1"},
	{XtNpixmapSize, XtCPixmapSize, XtRInt, sizeof (int),
	 XtOffset(BevelWidget, bevel.pixmapSize),
	 XtRString, (caddr_t) "64"},
	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
	 XtOffset(BevelWidget, bevel.select),
	 XtRCallback, (caddr_t) NULL}
};

Bevel3DClassRec bevel3dClassRec =
{
	{
		(WidgetClass) & bevelClassRec,	/* superclass */
		(char *) "Bevel3D",	/* class name */
		sizeof (Bevel3DRec),	/* widget size */
		NULL,		/* class initialize */
		NULL,		/* class part initialize */
		FALSE,		/* class inited */
		(XtInitProc) initializePuzzle3D,	/* initialize */
		NULL,		/* initialize hook */
		XtInheritRealize,	/* realize */
		actionsList3D,	/* actions */
		XtNumber(actionsList3D),	/* num actions */
		resources3D,	/* resources */
		XtNumber(resources3D),	/* num resources */
		NULLQUARK,	/* xrm class */
		TRUE,		/* compress motion */
		TRUE,		/* compress exposure */
		TRUE,		/* compress enterleave */
		TRUE,		/* visible interest */
		NULL,		/* destroy */
		(XtWidgetProc) resizePuzzle3D,	/* resize */
		(XtExposeProc) exposePuzzle3D,	/* expose */
		(XtSetValuesFunc) setValuesPuzzle3D,	/* set values */
		NULL,		/* set values hook */
		XtInheritSetValuesAlmost,	/* set values almost */
		NULL,		/* get values hook */
		XtInheritAcceptFocus,	/* accept focus */
		XtVersion,	/* version */
		NULL,		/* callback private */
		translations3D,	/* tm table */
		NULL,		/* query geometry */
		NULL,		/* display accelerator */
		NULL		/* extension */
	},
	{
		0		/* ignore */
	},
	{
		0		/* ignore */
	}
};

WidgetClass bevel3dWidgetClass = (WidgetClass) & bevel3dClassRec;
#endif

static Point faceLoc3D[MAX_FACES][MAX_ORIENT];
static Point cubeLoc3D[MAX_FACES][MAX_ORIENT];
static Point diamondLoc3D[MAX_FACES][MAX_ORIENT + 1];
static Point innerTriangleLoc3D[MAX_FACES][MAX_ORIENT][4];
static Point outerTriangleLoc3D[MAX_FACES][MAX_ORIENT][4];
static Point letter3DList[MAX_FACES][MAX_CUBES];
static Point orientDiamond[MAX_FACES][MAX_ORIENT][2];
static Point orientTriangle[MAX_FACES][MAX_ORIENT][2];

static Pixmap dr = 0; /* dummy for future double buffering */

static void
diamondOffset3D(Bevel3DWidget w, int face, int *dx, int *dy)
{
	char *buf;

	if (w->bevel.vertical) {
		switch (face) {
		case 0:
			*dx = w->bevel3d.viewMiddle.x + w->bevel.delta - 1;
			*dy = w->bevel3d.viewMiddle.y - w->bevel.delta - 2;
			break;
		case 1:
			*dx = w->bevel3d.viewMiddle.x - 2 * w->bevel.delta;
			*dy = w->bevel3d.viewMiddle.y;
			break;
		case 2:
			*dx = w->bevel3d.viewMiddle.x + w->bevel.delta - 1;
			*dy = w->bevel3d.viewMiddle.y + 2 * w->bevel.delta - 1;
			break;
		case 3:
			*dx = w->bevel3d.viewMiddle.x + 2 * w->bevel.delta;
			*dy = w->bevel3d.viewSize.y + w->bevel3d.viewMiddle.y +
				w->bevel.delta - 1;
			break;
		case 4:
			*dx = w->bevel3d.viewMiddle.x - w->bevel.delta + 1;
			*dy = w->bevel3d.viewSize.y + w->bevel3d.viewMiddle.y -
				w->bevel.delta + 1;
			break;
		case 5:
			*dx = w->bevel3d.viewMiddle.x - 2;
			*dy = w->bevel3d.viewSize.y + w->bevel3d.viewMiddle.y +
				2 * w->bevel.delta + 2;
			break;
		default:
			intCat(&buf, "diamondOffset3D: face ", face);
			DISPLAY_WARNING(buf);
			free(buf);
		}
	} else {
		switch (face) {
		case 0:
			*dx = w->bevel3d.viewMiddle.x;
			*dy = w->bevel3d.viewMiddle.y - 2 * w->bevel.delta + 1;
			break;
		case 1:
			*dx = w->bevel3d.viewMiddle.x - w->bevel.delta - 2;
			*dy = w->bevel3d.viewMiddle.y + w->bevel.delta;
			break;
		case 2:
			*dx = w->bevel3d.viewMiddle.x + 2 * w->bevel.delta - 1;
			*dy = w->bevel3d.viewMiddle.y + w->bevel.delta;
			break;
		case 3:
			*dx = w->bevel3d.viewSize.x + w->bevel3d.viewMiddle.x -
				w->bevel.delta + 1;
			*dy = w->bevel3d.viewMiddle.y - w->bevel.delta;
			break;
		case 4:
			*dx = w->bevel3d.viewSize.x + w->bevel3d.viewMiddle.x +
				w->bevel.delta - 1;
			*dy = w->bevel3d.viewMiddle.y + 2 * w->bevel.delta;
			break;
		case 5:
			*dx = w->bevel3d.viewSize.x + w->bevel3d.viewMiddle.x +
				2 * w->bevel.delta + 2;
			*dy = w->bevel3d.viewMiddle.y - w->bevel.delta;
			break;
		default:
			intCat(&buf, "diamondOffset3D: face ", face);
			DISPLAY_WARNING(buf);
			free(buf);
		}
	}
}

static void
cubeOffset3D(Bevel3DWidget w, int face, int corner, int *dx, int *dy)
{
	int side;

	diamondOffset3D(w, face, dx, dy);
	for (side = 1; side <= corner; side++) {
		*dx += cubeLoc3D[face][side].x;
		*dy += cubeLoc3D[face][side].y;
	}
}

static void
mapTo3D(Bevel3DWidget w, int face, int side, int *corner)
{
	switch (face) {
	case 0:
		*corner = (side + 2) % MAX_ORIENT;
		break;
	case 1:
	case 5:
		*corner = side;
		break;
	case 2:
		*corner = (side + 1) % MAX_ORIENT;
		break;
	case 3:
	case 4:
		*corner = (side + 3) % MAX_ORIENT;
		break;
	default:
		{
			char *buf;

			intCat(&buf, "mapTo3D: face ", face);
			DISPLAY_WARNING(buf);
			free(buf);
		}
	}
}

static void
mapFrom3D(Bevel3DWidget w, int face, int corner, int *side)
{
	switch (face) {
	case 0:
		*side = (corner + 2) % MAX_ORIENT;
		break;
	case 1:
	case 5:
		*side = corner;
		break;
	case 2:
		*side = (corner + 3) % MAX_ORIENT;
		break;
	case 3:
	case 4:
		*side = (corner + 1) % MAX_ORIENT;
		break;
	default:
		{
			char *buf;

			intCat(&buf, "mapFrom3D: face ", face);
			DISPLAY_WARNING(buf);
			free(buf);
		}
	}
}

void
drawInnerTriangle3D(Bevel3DWidget w, int face, int position, int offset)
{
	GC faceGC, borderGC;
	int side = 0, dx, dy, dx2, dy2, rotation, letterX, letterY;

	mapTo3D(w, face, position, &side);
	cubeOffset3D(w, face, side, &dx, &dy);
	diamondOffset3D(w, face, &dx2, &dy2);
	rotation = w->bevel.cubeLoc[face][position + MAX_ORIENT].rotation;
	dx2 += (cubeLoc3D[face][1].x  + cubeLoc3D[face][2].x) / 2;
	dy2 += (cubeLoc3D[face][1].y  + cubeLoc3D[face][2].y) / 2;
	dx = (dx + dx2 -
		(2 * innerTriangleLoc3D[face][side][1].x + innerTriangleLoc3D[face][side][2].x)) / 2;
	dy = (dy + dy2 -
		(2 * innerTriangleLoc3D[face][side][1].y + innerTriangleLoc3D[face][side][2].y)) / 2;
	innerTriangleLoc3D[face][side][0].x = dx;
	innerTriangleLoc3D[face][side][0].y = dy;
	letterX = dx + letter3DList[face][side + MAX_ORIENT].x;
	letterY = dy + letter3DList[face][side + MAX_ORIENT].y;
	if (offset) {
		borderGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position + MAX_ORIENT].face];
		if (w->bevel.mono) {
			faceGC = w->bevel.inverseGC;
		} else {
			faceGC = w->bevel.borderGC;
		}
	} else {
		faceGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position + MAX_ORIENT].face];
		borderGC = w->bevel.borderGC;
	}
	POLYGON((BevelWidget) w, dr, faceGC, borderGC,
		innerTriangleLoc3D[face][side], 3, True, False);
	if (w->bevel.mono) {
		char buf[2];

		buf[0] =
#ifdef WINVER
			w->bevel.faceChar[w->bevel.cubeLoc
				[face][position + MAX_ORIENT].face];
#else
			w->bevel.faceName[w->bevel.cubeLoc
				[face][position + MAX_ORIENT].face][0];
#endif
		buf[1] = '\0';
		if (offset) {
			borderGC = w->bevel.borderGC;
		} else {
			borderGC = w->bevel.inverseGC;
		}
		DRAWTEXT(w, dr, borderGC,
			letterX + w->bevel.letterOffset.x,
			letterY + w->bevel.letterOffset.y,
			buf, 1);
	}
	if (w->bevel.orient) {
		DRAWLINE(w, dr, borderGC,
			letterX +
			orientTriangle[face][rotation][0].x,
			letterY +
			orientTriangle[face][rotation][0].y,
			letterX +
			orientTriangle[face][rotation][1].x,
			letterY +
			orientTriangle[face][rotation][1].y);
	}
}

void
drawOuterTriangle3D(Bevel3DWidget w, int face, int position, int offset)
{
	GC faceGC, borderGC;
	int side = 0, dx, dy, rotation, letterX, letterY;

	mapTo3D(w, face, position, &side);
	cubeOffset3D(w, face, side, &dx, &dy);
	rotation = w->bevel.cubeLoc[face][position].rotation;
	letterX = dx + letter3DList[face][side].x;
	letterY = dy + letter3DList[face][side].y;
	outerTriangleLoc3D[face][side][0].x = dx;
	outerTriangleLoc3D[face][side][0].y = dy;
	if (offset) {
		borderGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position].face];
		if (w->bevel.mono) {
			faceGC = w->bevel.inverseGC;
		} else {
			faceGC = w->bevel.borderGC;
		}
	} else {
		faceGC = w->bevel.faceGC[(int) w->bevel.cubeLoc[face][position].face];
		borderGC = w->bevel.borderGC;
	}
	POLYGON((BevelWidget) w, dr, faceGC, borderGC,
		outerTriangleLoc3D[face][side], 3, True, False);
	if (w->bevel.mono) {
		char buf[2];

		buf[0] =
#ifdef WINVER
			w->bevel.faceChar[w->bevel.cubeLoc
				[face][position].face];
#else
			w->bevel.faceName[w->bevel.cubeLoc
				[face][position].face][0];
#endif
		buf[1] = '\0';
		if (offset) {
			borderGC = w->bevel.borderGC;
		} else {
			borderGC = w->bevel.inverseGC;
		}
		DRAWTEXT(w, dr, borderGC,
			letterX + w->bevel.letterOffset.x,
			letterY + w->bevel.letterOffset.y,
			buf, 1);
	}
	if (w->bevel.orient) {
		DRAWLINE(w, dr, borderGC,
			letterX +
			orientTriangle[face][rotation][0].x,
			letterY +
			orientTriangle[face][rotation][0].y,
			letterX +
			orientTriangle[face][rotation][1].x,
			letterY +
			orientTriangle[face][rotation][1].y);
	}
}

void
drawFrame3D(Bevel3DWidget w, Boolean focus)
{
	int face, dx, dy;
	GC gc = (focus) ? w->bevel.frameGC : w->bevel.borderGC;

	dx = w->bevel3d.viewSize.x + w->bevel.puzzleOffset.x;
	dy = w->bevel3d.viewSize.y + w->bevel.puzzleOffset.y;
	if (w->bevel.vertical) {
		DRAWLINE(w, dr, w->bevel.frameGC,
			0, dy, dx + w->bevel.puzzleOffset.x + 1, dy);
		DRAWTEXT(w, dr, w->bevel.borderGC,
			(int) (2 * w->bevel.delta),
			(int) (3 * w->bevel.delta + w->bevel.letterOffset.y),
			"Front", 5);
		DRAWTEXT(w, dr, w->bevel.borderGC, (int)
			(-4 * w->bevel.delta + 2 * 6 * w->bevel.letterOffset.x + w->core.width),
			(int) (-w->bevel.delta - 2 * w->bevel.letterOffset.y + w->core.height),
			"Back", 4);
	} else {
		DRAWLINE(w, dr, w->bevel.frameGC,
			dx, 0, dx, dy + w->bevel.puzzleOffset.y + 1);
		DRAWTEXT(w, dr, w->bevel.borderGC,
			(int) (2 * w->bevel.delta),
			(int) (3 * w->bevel.delta + w->bevel.letterOffset.y),
			"Front", 5);
		DRAWTEXT(w, dr, w->bevel.borderGC, (int)
			(-4 * w->bevel.delta + 2 * 6 * w->bevel.letterOffset.x + w->core.width),
			(int) (-w->bevel.delta - 2 * w->bevel.letterOffset.y + w->core.height),
			"Back", 4);
	}
	for (face = 0; face < MAX_FACES; face++) {
		POLYLINE((BevelWidget) w, dr, gc,
			faceLoc3D[face], MAX_ORIENT, False);
	}
}

static void
resizePieces(Bevel3DWidget w)
{
	int face, orient, side, corner;
	Point subcubeLoc3D[MAX_FACES][MAX_ORIENT];
	Point orientCubeLoc3D[2][MAX_FACES][MAX_ORIENT];
	Point subdiamondLoc3D[MAX_FACES][MAX_ORIENT];
	Point orientDiamondLoc3D[2][MAX_FACES][MAX_ORIENT];

	w->bevel.letterOffset.x = -2;
	w->bevel.letterOffset.y = 3;
	w->bevel3d.viewMiddle.x = w->bevel3d.faceSize.x +
		w->bevel.puzzleOffset.x;
	w->bevel3d.viewMiddle.y = w->bevel3d.faceSize.y +
		w->bevel.puzzleOffset.y;
	for (face = 0; face < MAX_FACES; face++) {
		faceLoc3D[face][0].x = w->bevel3d.viewMiddle.x;
		faceLoc3D[face][0].y = w->bevel3d.viewMiddle.y;
		for (orient = 1; orient < MAX_ORIENT; orient++) {
			faceLoc3D[face][orient].x = w->bevel3d.faceSize.x;
			faceLoc3D[face][orient].y = w->bevel3d.faceSize.y;
		}
	}
	if (w->bevel.vertical) {
		faceLoc3D[0][1].x /= -2;
		faceLoc3D[0][1].y /= -1;
		faceLoc3D[0][2].y = 0;
		faceLoc3D[0][3].x /= 2;

		faceLoc3D[1][1].x /= -2;
		faceLoc3D[1][2].x /= -2;
		faceLoc3D[1][2].y /= -1;
		faceLoc3D[1][3].x /= 2;
		faceLoc3D[1][3].y /= -1;

		faceLoc3D[2][1].y = 0;
		faceLoc3D[2][2].x /= -2;
		faceLoc3D[2][3].x /= -1;
		faceLoc3D[2][3].y = 0;

		for (face = MAX_FACES / 2; face < MAX_FACES; face++)
			faceLoc3D[face][0].y += w->bevel3d.viewSize.y + 3;

		faceLoc3D[3][1].x /= 2;
		faceLoc3D[3][1].y /= -1;
		faceLoc3D[3][2].x /= 2;
		faceLoc3D[3][3].x /= -2;

		faceLoc3D[4][1].x /= -1;
		faceLoc3D[4][1].y = 0;
		faceLoc3D[4][2].x /= 2;
		faceLoc3D[4][2].y /= -1;
		faceLoc3D[4][3].y = 0;

		faceLoc3D[5][1].x /= 2;
		faceLoc3D[5][2].x /= -1;
		faceLoc3D[5][2].y = 0;
		faceLoc3D[5][3].x /= -2;
		faceLoc3D[5][3].y /= -1;
	} else {
		faceLoc3D[0][1].x /= -1;
		faceLoc3D[0][1].y /= -2;
		faceLoc3D[0][2].y /= -2;
		faceLoc3D[0][3].y /= 2;

		faceLoc3D[1][1].x = 0;
		faceLoc3D[1][2].x /= -1;
		faceLoc3D[1][2].y /= -2;
		faceLoc3D[1][3].x = 0;
		faceLoc3D[1][3].y /= -1;

		faceLoc3D[2][1].y /= -2;
		faceLoc3D[2][2].x = 0;
		faceLoc3D[2][3].y /= 2;
		faceLoc3D[2][3].x /= -1;

		for (face = MAX_FACES / 2; face < MAX_FACES; face++)
			faceLoc3D[face][0].x += w->bevel3d.viewSize.x + 3;

		faceLoc3D[3][1].x /= -1;
		faceLoc3D[3][1].y /= 2;
		faceLoc3D[3][2].x = 0;
		faceLoc3D[3][2].y /= -1;
		faceLoc3D[3][3].y /= -2;

		faceLoc3D[4][1].y /= 2;
		faceLoc3D[4][2].x /= -1;
		faceLoc3D[4][2].y /= 2;
		faceLoc3D[4][3].x /= -1;
		faceLoc3D[4][3].y /= -2;

		faceLoc3D[5][1].x = 0;
		faceLoc3D[5][1].y /= -1;
		faceLoc3D[5][2].y /= 2;
		faceLoc3D[5][3].x = 0;
	}

	for (face = 0; face < MAX_FACES; face++) {
		int tempX, tempY;
		cubeLoc3D[face][0].x = faceLoc3D[face][0].x;
		cubeLoc3D[face][0].y = faceLoc3D[face][0].y;
		subcubeLoc3D[face][0].x = faceLoc3D[face][0].x;
		subcubeLoc3D[face][0].y = faceLoc3D[face][0].y;
		orientCubeLoc3D[0][face][0].x = orientCubeLoc3D[1][face][0].x = 0;
		orientCubeLoc3D[0][face][0].y = orientCubeLoc3D[1][face][0].y = 0;
		for (orient = 1; orient < MAX_ORIENT; orient++) {
			cubeLoc3D[face][orient].x = faceLoc3D[face][orient].x - 3 *
				w->bevel.delta * faceLoc3D[face][orient].x /
				w->bevel3d.faceSize.x;
			cubeLoc3D[face][orient].y = faceLoc3D[face][orient].y - 3 *
				w->bevel.delta * faceLoc3D[face][orient].y /
				w->bevel3d.faceSize.y;
			subcubeLoc3D[face][orient].x = (faceLoc3D[face][orient].x -
				5 * faceLoc3D[face][orient].x * w->bevel.delta /
				w->bevel3d.faceSize.x) / 2;
			subcubeLoc3D[face][orient].y = (faceLoc3D[face][orient].y -
				5 * faceLoc3D[face][orient].y * w->bevel.delta /
				w->bevel3d.faceSize.y) / 2;
			orientCubeLoc3D[0][face][orient].x = (faceLoc3D[face][orient].x -
				5 * faceLoc3D[face][orient].x * w->bevel.delta /
				w->bevel3d.faceSize.x) / 4;
			orientCubeLoc3D[0][face][orient].y = (faceLoc3D[face][orient].y -
				5 * faceLoc3D[face][orient].y * w->bevel.delta /
				w->bevel3d.faceSize.y) / 4;
			orientCubeLoc3D[1][face][orient].x = (faceLoc3D[face][orient].x -
				7 * faceLoc3D[face][orient].x * w->bevel.delta /
				w->bevel3d.faceSize.x) / 6;
			orientCubeLoc3D[1][face][orient].y = (faceLoc3D[face][orient].y -
				7 * faceLoc3D[face][orient].y * w->bevel.delta /
				w->bevel3d.faceSize.y) / 6;
		}
		tempX =  (subcubeLoc3D[face][1].x + subcubeLoc3D[face][3].x) / 2 -
				subcubeLoc3D[face][2].x;
		tempY =  (subcubeLoc3D[face][1].y + subcubeLoc3D[face][3].y) / 2 -
				subcubeLoc3D[face][2].y;
		innerTriangleLoc3D[face][0][1].x = subcubeLoc3D[face][3].x;
		innerTriangleLoc3D[face][0][2].x = tempX - subcubeLoc3D[face][3].x;
		innerTriangleLoc3D[face][0][1].y = subcubeLoc3D[face][3].y;
		innerTriangleLoc3D[face][0][2].y = tempY - subcubeLoc3D[face][3].y;
		innerTriangleLoc3D[face][1][1].x = tempX;
		innerTriangleLoc3D[face][1][2].x = -subcubeLoc3D[face][3].x - tempX;
		innerTriangleLoc3D[face][1][1].y = tempY;
		innerTriangleLoc3D[face][1][2].y = -subcubeLoc3D[face][3].y - tempY;
		innerTriangleLoc3D[face][2][1].x = -subcubeLoc3D[face][3].x;
		innerTriangleLoc3D[face][2][2].x = subcubeLoc3D[face][3].x - tempX;
		innerTriangleLoc3D[face][2][1].y = -subcubeLoc3D[face][3].y;
		innerTriangleLoc3D[face][2][2].y = subcubeLoc3D[face][3].y - tempY;
		innerTriangleLoc3D[face][3][1].x = -tempX;
		innerTriangleLoc3D[face][3][2].x = subcubeLoc3D[face][3].x + tempX;
		innerTriangleLoc3D[face][3][1].y = -tempY;
		innerTriangleLoc3D[face][3][2].y = subcubeLoc3D[face][3].y + tempY;

		outerTriangleLoc3D[face][0][1].x = subcubeLoc3D[face][1].x;
		outerTriangleLoc3D[face][0][2].x = subcubeLoc3D[face][2].x -
			subcubeLoc3D[face][1].x;
		outerTriangleLoc3D[face][0][1].y = subcubeLoc3D[face][1].y;
		outerTriangleLoc3D[face][0][2].y = subcubeLoc3D[face][2].y -
			subcubeLoc3D[face][1].y;
		outerTriangleLoc3D[face][1][1].x = subcubeLoc3D[face][2].x;
		outerTriangleLoc3D[face][1][2].x = -subcubeLoc3D[face][1].x -
			subcubeLoc3D[face][2].x;
		outerTriangleLoc3D[face][1][1].y = subcubeLoc3D[face][2].y;
		outerTriangleLoc3D[face][1][2].y = -subcubeLoc3D[face][1].y -
			subcubeLoc3D[face][2].y;
		outerTriangleLoc3D[face][2][1].x = -subcubeLoc3D[face][1].x;
		outerTriangleLoc3D[face][2][2].x = subcubeLoc3D[face][1].x -
			subcubeLoc3D[face][2].x;
		outerTriangleLoc3D[face][2][1].y = -subcubeLoc3D[face][1].y;
		outerTriangleLoc3D[face][2][2].y = subcubeLoc3D[face][1].y -
			subcubeLoc3D[face][2].y;
		outerTriangleLoc3D[face][3][1].x = -subcubeLoc3D[face][2].x;
		outerTriangleLoc3D[face][3][2].x = subcubeLoc3D[face][1].x +
			subcubeLoc3D[face][2].x;
		outerTriangleLoc3D[face][3][1].y = -subcubeLoc3D[face][2].y;
		outerTriangleLoc3D[face][3][2].y = subcubeLoc3D[face][1].y +
			subcubeLoc3D[face][2].y;
		for (orient = 0; orient < MAX_ORIENT; orient++) {
			letter3DList[face][orient].x =
				(2 * outerTriangleLoc3D[face][orient][1].x +
				outerTriangleLoc3D[face][orient][2].x) / 3;
			letter3DList[face][orient].y =
				(2 * outerTriangleLoc3D[face][orient][1].y +
				outerTriangleLoc3D[face][orient][2].y) / 3;
			letter3DList[face][orient + MAX_ORIENT].x =
				(2 * innerTriangleLoc3D[face][orient][1].x +
				innerTriangleLoc3D[face][orient][2].x) / 3;
			letter3DList[face][orient + MAX_ORIENT].y =
				(2 * innerTriangleLoc3D[face][orient][1].y +
				innerTriangleLoc3D[face][orient][2].y) / 3;
			innerTriangleLoc3D[face][orient][3].x =
				-innerTriangleLoc3D[face][orient][1].x - innerTriangleLoc3D[face][orient][2].x;
			innerTriangleLoc3D[face][orient][3].y =
				-innerTriangleLoc3D[face][orient][1].y - innerTriangleLoc3D[face][orient][2].y;
			outerTriangleLoc3D[face][orient][3].x =
				-outerTriangleLoc3D[face][orient][1].x - outerTriangleLoc3D[face][orient][2].x;
			outerTriangleLoc3D[face][orient][3].y =
				-outerTriangleLoc3D[face][orient][1].y - outerTriangleLoc3D[face][orient][2].y;
		}
	}
	w->bevel3d.cubeSize.x = w->bevel3d.faceSize.x - 2 * w->bevel.delta;
	w->bevel3d.cubeSize.y = w->bevel3d.faceSize.y - 2 * w->bevel.delta;
	w->bevel3d.cubeDiagonal = w->bevel3d.faceDiagonal - 2 * w->bevel.delta;
	w->bevel3d.cubeDiag = w->bevel3d.faceDiagonal + 2 * w->bevel.delta;
	for (face = 0; face < MAX_FACES; face++) {
		for (orient = 0; orient < MAX_ORIENT - 1; orient++) {
			diamondLoc3D[face][orient].x = (cubeLoc3D[face][orient].x +
				cubeLoc3D[face][orient + 1].x) / 2;
			diamondLoc3D[face][orient].y = (cubeLoc3D[face][orient].y +
				cubeLoc3D[face][orient + 1].y) / 2;
			subdiamondLoc3D[face][orient].x = (subcubeLoc3D[face][orient].x +
				subcubeLoc3D[face][orient + 1].x) / 2;
			subdiamondLoc3D[face][orient].y = (subcubeLoc3D[face][orient].y +
				subcubeLoc3D[face][orient + 1].y) / 2;
			orientDiamondLoc3D[0][face][orient].x =
				(orientCubeLoc3D[0][face][orient].x +
				orientCubeLoc3D[0][face][orient + 1].x) / 2;
			orientDiamondLoc3D[0][face][orient].y =
				(orientCubeLoc3D[0][face][orient].y +
				orientCubeLoc3D[0][face][orient + 1].y) / 2;
			orientDiamondLoc3D[1][face][orient].x =
				(orientCubeLoc3D[1][face][orient].x +
				orientCubeLoc3D[1][face][orient + 1].x) / 2;
			orientDiamondLoc3D[1][face][orient].y =
				(orientCubeLoc3D[1][face][orient].y +
				orientCubeLoc3D[1][face][orient + 1].y) / 2;
		}
		/* Its a parallelagram so take advantage of that */
		diamondLoc3D[face][orient].x = (cubeLoc3D[face][MAX_ORIENT - 1].x -
			cubeLoc3D[face][MAX_ORIENT / 2].x) / 2;
		diamondLoc3D[face][orient].y = (cubeLoc3D[face][MAX_ORIENT - 1].y -
			cubeLoc3D[face][MAX_ORIENT / 2].y) / 2;
		diamondLoc3D[face][MAX_ORIENT].x = -diamondLoc3D[face][1].x -
			diamondLoc3D[face][2].x - diamondLoc3D[face][3].x;
		diamondLoc3D[face][MAX_ORIENT].y = -diamondLoc3D[face][1].y -
			diamondLoc3D[face][2].y - diamondLoc3D[face][3].y;

		subdiamondLoc3D[face][orient].x = (subcubeLoc3D[face][MAX_ORIENT - 1].x -
			subcubeLoc3D[face][MAX_ORIENT / 2].x) / 2;
		subdiamondLoc3D[face][orient].y = (subcubeLoc3D[face][MAX_ORIENT - 1].y -
			subcubeLoc3D[face][MAX_ORIENT / 2].y) / 2;
		orientDiamondLoc3D[0][face][orient].x =
			(orientCubeLoc3D[0][face][MAX_ORIENT - 1].x -
			orientCubeLoc3D[0][face][MAX_ORIENT / 2].x) / 2;
		orientDiamondLoc3D[0][face][orient].y =
			(orientCubeLoc3D[0][face][MAX_ORIENT - 1].y -
			orientCubeLoc3D[0][face][MAX_ORIENT / 2].y) / 2;
		orientDiamondLoc3D[1][face][orient].x =
			(orientCubeLoc3D[1][face][MAX_ORIENT - 1].x -
			orientCubeLoc3D[1][face][MAX_ORIENT / 2].x) / 2;
		orientDiamondLoc3D[1][face][orient].y =
			(orientCubeLoc3D[1][face][MAX_ORIENT - 1].y -
			orientCubeLoc3D[1][face][MAX_ORIENT / 2].y) / 2;

		mapFrom3D(w, face, 1, &corner);
		orientDiamond[face][corner][0].x = cubeLoc3D[face][1].x / 2;
		orientDiamond[face][corner][0].y = cubeLoc3D[face][1].y / 2;
		orientDiamond[face][corner][1].x = orientDiamond[face][corner][0].x +
			(cubeLoc3D[face][2].x - subcubeLoc3D[face][2].x) / 2;
		orientDiamond[face][corner][1].y = orientDiamond[face][corner][0].y +
			(cubeLoc3D[face][2].y - subcubeLoc3D[face][2].y) / 2;
		orientTriangle[face][corner][0].x = -orientCubeLoc3D[0][face][2].x / 2;
		orientTriangle[face][corner][0].y = -orientCubeLoc3D[0][face][2].y / 2;
		orientTriangle[face][corner][1].x = -orientCubeLoc3D[1][face][2].x / 2;
		orientTriangle[face][corner][1].y = -orientCubeLoc3D[1][face][2].y / 2;
		for (orient = 1; orient < MAX_ORIENT; orient++) {
			side = corner;
			mapFrom3D(w, face, (orient + 1) % MAX_ORIENT, &corner);
			orientDiamond[face][corner][0].x =
				orientDiamond[face][side][0].x +
				diamondLoc3D[face][orient].x;
			orientDiamond[face][corner][0].y =
				orientDiamond[face][side][0].y +
				diamondLoc3D[face][orient].y;
			orientDiamond[face][corner][1].x =
				orientDiamond[face][side][1].x +
				subdiamondLoc3D[face][orient].x;
			orientDiamond[face][corner][1].y =
				orientDiamond[face][side][1].y +
				subdiamondLoc3D[face][orient].y;
			orientTriangle[face][corner][0].x =
				orientTriangle[face][side][0].x +
				orientDiamondLoc3D[0][face][orient].x;
			orientTriangle[face][corner][0].y =
				orientTriangle[face][side][0].y +
				orientDiamondLoc3D[0][face][orient].y;
			orientTriangle[face][corner][1].x =
				orientTriangle[face][side][1].x +
				orientDiamondLoc3D[1][face][orient].x;
			orientTriangle[face][corner][1].y =
				orientTriangle[face][side][1].y +
				orientDiamondLoc3D[1][face][orient].y;
		}
	}
}

Boolean
selectPieces3D(Bevel3DWidget w, int x, int y, int *face, int *position)
{
	int u, v, i = 0, j = 0;
	int front, tl, ur, ul;
	int tempx = x, tempy = y;

	if (w->bevel.vertical) {
		tempx -= w->bevel3d.viewMiddle.x;
		front = (y < w->bevel3d.viewSize.y + w->bevel.puzzleOffset.y);
		if (!front)
			tempy -= (w->bevel3d.viewSize.y);
		tl = (tempy < w->bevel3d.viewMiddle.y);
		tempy -= w->bevel3d.viewMiddle.y;
		u = -w->bevel3d.faceSize.y * tempx + w->bevel3d.faceDiagonal * tempy;
		v = w->bevel3d.faceSize.y * tempx + w->bevel3d.faceDiagonal * tempy;
		ur = (u < 0);
		ul = (v < 0);
		if (front) {
			if (tl)
				*face = (ur) ? 0 : 1;
			else
				*face = (ul) ? 1 : 2;
		} else {
			if (tl)
				*face = (ul) ? 4 : 3;
			else
				*face = (ur) ? 3 : 5;
		}
	} else {
		tempy -= w->bevel3d.viewMiddle.y;
		front = (tempx < w->bevel3d.viewSize.x + w->bevel.puzzleOffset.x);
		if (!front)
			tempx -= (w->bevel3d.viewSize.x);
		tl = (tempx < w->bevel3d.viewMiddle.x);
		tempx -= w->bevel3d.viewMiddle.x;
		u = -w->bevel3d.faceSize.x * tempy + w->bevel3d.faceDiagonal * tempx;
		v = w->bevel3d.faceSize.x * tempy + w->bevel3d.faceDiagonal * tempx;
		ur = (u < 0);
		ul = (v < 0);
		if (front) {
			if (tl)
				*face = (ur) ? 1 : 0;
			else
				*face = (ul) ? 0 : 2;
		} else {
			if (tl)
				*face = (ul) ? 3 : 4;
			else
				*face = (ur) ? 4 : 5;
		}
	}
	/*found = 0;
	diamondOffset3D(w, *face, &dx, &dy);
	for (side = 0; side < MAX_ORIENT; side++) {
		tempx = dx + orientDiamond[*face][side][0].x;
		tempy = dy + orientDiamond[*face][side][0].y;
		x_2 = dx + orientDiamond[*face][(side + 1) % MAX_ORIENT][0].x;
		y_2 = dy + orientDiamond[*face][(side + 1) % MAX_ORIENT][0].y;
		if ((x_2 - tempx) * (y - tempy) <= (y_2 - tempy) * (x - tempx)) {
			*position = side;
			found++;
		}
	}
	if (found == 0)
		*position = MAX_ORIENT;
	else if (found > 1)
		return False;
	return True;*/
	if (w->bevel.vertical)
		switch (*face) {
		case 0:
			i = (tempx - 2 -
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(w->bevel3d.cubeSize.x / 2 + w->bevel.delta);
			j = (tempy + 2) / (w->bevel3d.cubeSize.y / 2 + w->bevel.delta) +
				1;
			break;
		case 1:
			i = (tempx + 4 +
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			j = (-tempx - 6 +
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta));
			break;
		case 2:
			i = (tempx - 4 +
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(w->bevel3d.cubeSize.x / 2 + w->bevel.delta);
			j = (tempy - 4) / (w->bevel3d.cubeSize.y / 2 + w->bevel.delta);
			break;
		case 3:
			i = (-tempx + 5 +
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			j = (-tempx + 7 -
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			break;
		case 4:
			i = (tempx +
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(w->bevel3d.cubeSize.x / 2 + w->bevel.delta) + 1;
			j = (tempy - 2) / (w->bevel3d.cubeSize.y / 2 + w->bevel.delta) +
				1;
			break;
		case 5:
			i = (tempx + 2 -
				(tempy * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.y)) /
				(w->bevel3d.cubeSize.x / 2 + w->bevel.delta) + 1;
			j = (tempy - 6) / (w->bevel3d.cubeSize.y / 2 + w->bevel.delta);
			break;
		default:
			return False;
		}
	else
		switch (*face) {
		case 0:
			i = (-tempy - 3 +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta));
			j = (tempy + 1 +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			break;
		case 1:
			i = (tempx + 2) / (w->bevel3d.cubeSize.x / 2 + w->bevel.delta) +
				1;
			j = (tempy - 3 -
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(w->bevel3d.cubeSize.y / 2 + w->bevel.delta);
			break;
		case 2:
			i = (tempx - 4) / (w->bevel3d.cubeSize.x / 2 + w->bevel.delta);
			j = (tempy - 6 +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(w->bevel3d.cubeSize.y / 2 + w->bevel.delta);
			break;
		case 3:
			i = tempx / (w->bevel3d.cubeSize.x / 2 + w->bevel.delta) +
				1;
			j = (tempy +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(w->bevel3d.cubeSize.y / 2 + w->bevel.delta) + 1;
			break;
		case 4:
			i = (-tempy + 9 -
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			j = (-tempy + 7 +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(2 * (w->bevel3d.cubeDiagonal / 2 + w->bevel.delta)) + 1;
			break;
		case 5:
			i = (-tempx + 7) / (w->bevel3d.cubeSize.x / 2 + w->bevel.delta) +
				1;
			j = (-tempy - 4 +
				(tempx * w->bevel3d.faceDiagonal / w->bevel3d.faceSize.x)) /
				(w->bevel3d.cubeSize.y / 2 + w->bevel.delta);
			break;
		default:
			return False;
		}
	if (i < 0 || j < 0 || i >= 2 || j >= 2)
		return False;
	if (i >= 1 && j < 1)
		*position = 0;
	else if (i >= 1 && j >= 1)
		*position = 1;
	else if (i < 1 && j >= 1)
		*position = 2;
	else if (i < 1 && j < 1)
		*position = 3;
	return True;
}

Boolean
narrowSelection3D(Bevel3DWidget w, int *face, int *direction)
{
	switch (*direction) {
	case TOP:
	case RIGHT:
	case BOTTOM:
	case LEFT:
		if (w->bevel.vertical) {
			if (*face == 1)
				*direction = (*direction + 3) % MAX_ORIENT +
					2 * MAX_ORIENT;
			else if (*face == 3)
				*direction = (*direction + 2) % MAX_ORIENT +
					2 * MAX_ORIENT;
		} else {
			if (*face == 0)
				*direction = *direction + 2 * MAX_ORIENT;
			else if (*face == 4)
				*direction = (*direction + 1) % MAX_ORIENT +
					2 * MAX_ORIENT;
			else if (*face == 5)
				*direction = (*direction + 2) % MAX_ORIENT;
		}
		break;
	case CCW:
	case CW:
		break;
	case TR:
	case BR:
	case BL:
	case TL:
		if (w->bevel.vertical) {
			/*if (*face == 0 || * face == 5) {
				if (*direction == BR)
					*direction = BOTTOM;
				else if (*direction == TL)
					*direction = TOP;
			} else if (*face == 2 || * face == 4) {
				if (*direction == BL)
					*direction = BOTTOM;
				else if (*direction == TR)
					*direction = TOP;
			} else*/
			if (*face == 1)
				*direction = *direction;
			else if (*face == 3)
				*direction = (*direction + 3) % MAX_ORIENT;
		} else {
			/*if (*face == 1) {
				if (*direction == BR)
					*direction = RIGHT;
				else if (*direction == TL)
					*direction = LEFT;
			} else if (*face == 2 || * face == 3) {
				if (*direction == TR)
					*direction = RIGHT;
				else if (*direction == BL)
					*direction = LEFT;
			} else*/
			if (*face == 0)
				*direction = (*direction + 3) % MAX_ORIENT;
			else if (*face == 4)
				*direction = (*direction + 2) % MAX_ORIENT;
			else if (*face == 5) {
				/*if (*direction == BR)
					*direction = LEFT;
				else if (*direction == TL)
					*direction = RIGHT;
				else*/
				*direction = (*direction + 2) % MAX_ORIENT +
					2 * MAX_ORIENT;
			}
		}
		break;
	default:
		return False;
	}
	return True;
}

#ifndef WINVER
static Boolean
setValuesPuzzle3D(Widget current, Widget request, Widget renew)
{
	Bevel3DWidget c = (Bevel3DWidget) current, w = (Bevel3DWidget) renew;
	Boolean redraw = False;

	if (w->bevel3d.cubeSize.x != c->bevel3d.cubeSize.x) {
		resizePuzzle3D(w);
		redraw = True;
	}
	return (redraw);
}
#endif

#ifndef WINVER
static
#endif
void
resizePuzzle3D(Bevel3DWidget w)
{
	Point tempSize;
#ifdef WINVER
	RECT rect;

	/* Determine size of client area */
	(void) GetClientRect(w->core.hWnd, &rect);
	w->core.width = rect.right;
	w->core.height = rect.bottom;
#endif

	w->bevel.delta = 4;
	w->bevel.vertical = (w->core.height >= w->core.width);
	if (w->bevel.vertical) {
		tempSize.y = w->core.height / MAX_VIEWS;
		tempSize.x = w->core.width;
		if (tempSize.x >= DIVIDE(tempSize.y)) {
			w->bevel3d.cubeSize.y = MAX((tempSize.y - 3 * w->bevel.delta) / 2 -
				w->bevel.delta - 2, 0);
			w->bevel3d.cubeSize.x = DIVIDE(w->bevel3d.cubeSize.y);
		} else {
			w->bevel3d.cubeSize.x = MAX((tempSize.x - 2 * w->bevel.delta - 7) / 2 -
				w->bevel.delta, 0);
			w->bevel3d.cubeSize.y = MULTIPLY(w->bevel3d.cubeSize.x);
		}
		w->bevel3d.cubeDiagonal = w->bevel3d.cubeSize.x / 2;
		w->bevel3d.faceSize.x = w->bevel3d.cubeSize.x + 2 * w->bevel.delta + 1;
		w->bevel3d.faceSize.y = w->bevel3d.cubeSize.y + 2 * w->bevel.delta + 1;
		w->bevel3d.faceDiagonal = w->bevel3d.faceSize.x / 2;
		w->bevel3d.viewSize.x = 2 * w->bevel3d.faceSize.x + 3;
		w->bevel3d.viewSize.y = 2 * w->bevel3d.faceSize.y + 3;
		w->bevel.puzzleSize.x = w->bevel3d.viewSize.x + 1;
		w->bevel.puzzleSize.y = MAX_VIEWS * w->bevel3d.viewSize.y + 1;
	} else {
		tempSize.x = w->core.width / MAX_VIEWS;
		tempSize.y = w->core.height;
		if (tempSize.y >= DIVIDE(tempSize.x)) {
			w->bevel3d.cubeSize.x = MAX((tempSize.x - 3 * w->bevel.delta) / 2 -
				w->bevel.delta - 2, 0);
			w->bevel3d.cubeSize.y = DIVIDE(w->bevel3d.cubeSize.x);
		} else {
			w->bevel3d.cubeSize.y = MAX((tempSize.y - 2 * w->bevel.delta - 7) / 2 -
				w->bevel.delta, 0);
			w->bevel3d.cubeSize.x = MULTIPLY(w->bevel3d.cubeSize.y);
		}
		w->bevel3d.cubeDiagonal = w->bevel3d.cubeSize.y / 2;
		w->bevel3d.faceSize.y = w->bevel3d.cubeSize.y + 2 * w->bevel.delta + 1;
		w->bevel3d.faceSize.x = w->bevel3d.cubeSize.x + 2 * w->bevel.delta + 1;
		w->bevel3d.faceDiagonal = w->bevel3d.faceSize.y / 2;
		w->bevel3d.viewSize.y = 2 * w->bevel3d.faceSize.y + 3;
		w->bevel3d.viewSize.x = 2 * w->bevel3d.faceSize.x + 3;
		w->bevel.puzzleSize.y = w->bevel3d.viewSize.y + 1;
		w->bevel.puzzleSize.x = MAX_VIEWS * w->bevel3d.viewSize.x + 1;
	}
	w->bevel.puzzleOffset.x = ((int) w->core.width - w->bevel.puzzleSize.x) / 2;
	w->bevel.puzzleOffset.y = ((int) w->core.height - w->bevel.puzzleSize.y) /
		2;
	resizePieces(w);
}

#ifndef WINVER
static
#endif
void
initializePuzzle3D(
#ifdef WINVER
Bevel3DWidget w
#else
Widget request, Widget renew
#endif
)
{
#ifndef WINVER
	Bevel3DWidget w = (Bevel3DWidget) renew;

	setAllColors((BevelWidget) w);
#endif
	w->bevel.dim = 3;
	resizePuzzle3D(w);
}

#ifndef WINVER
static
#endif
void
exposePuzzle3D(
#ifdef WINVER
Bevel3DWidget w
#else
Widget renew, XEvent *event, Region region
#endif
)
{
#ifndef WINVER
	Bevel3DWidget w = (Bevel3DWidget) renew;

	if (!w->core.visible)
		return;
#endif
	FILLRECTANGLE(w, dr, w->bevel.inverseGC,
		0, 0, w->core.width, w->core.height);
	drawFrame3D(w, w->bevel.focus);
	drawAllPieces((BevelWidget) w);
}

#ifndef WINVER
static void
movePuzzle3DTl(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TL,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DTop(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TOP,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DTr(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		TR,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DLeft(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		LEFT,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DRight(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		RIGHT,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DBl(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BL,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DBottom(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BOTTOM,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}

static void
movePuzzle3DBr(Bevel3DWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((BevelWidget) w, event->xbutton.x, event->xbutton.y,
		BR,
		(int) (event->xkey.state & ControlMask),
		(int) (event->xkey.state & (Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
}
#endif
