1 /*-
2 # X-BASED SKEWB
3 #
4 #  Skewb2d.c
5 #
6 ###
7 #
8 #  Copyright (c) 1994 - 99	David Albert Bagley, bagleyd@tux.org
9 #
10 #                   All Rights Reserved
11 #
12 #  Permission to use, copy, modify, and distribute this software and
13 #  its documentation for any purpose and without fee is hereby granted,
14 #  provided that the above copyright notice appear in all copies and
15 #  that both that copyright notice and this permission notice appear in
16 #  supporting documentation, and that the name of the author not be
17 #  used in advertising or publicity pertaining to distribution of the
18 #  software without specific, written prior permission.
19 #
20 #  This program is distributed in the hope that it will be "playable",
21 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
22 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 #
24 */
25 
26 /* Methods file for Skewb2d */
27 
28 #include <stdio.h>
29 #include <X11/IntrinsicP.h>
30 #include <X11/Intrinsic.h>
31 #include <X11/StringDefs.h>
32 #include <X11/CoreP.h>
33 #include "SkewbP.h"
34 #include "Skewb2dP.h"
35 
36 static void InitializeSkewb2D(Widget request, Widget renew);
37 static void ResizeSkewb2D(Skewb2DWidget w);
38 static void ExposeSkewb2D(Widget renew, XEvent * event, Region region);
39 static Boolean SetValuesSkewb2D(Widget current, Widget request, Widget renew);
40 static void MoveSkewb2DTl(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
41 static void MoveSkewb2DTop(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
42 static void MoveSkewb2DTr(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
43 static void MoveSkewb2DLeft(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
44 static void MoveSkewb2dRight(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
45 static void MoveSkewb2DBl(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
46 static void MoveSkewb2DBottom(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
47 static void MoveSkewb2DBr(Skewb2DWidget w, XEvent * event, char **args, int nArgs);
48 static void ResizePolyhedrons(Skewb2DWidget w);
49 static void DrawFrame(Skewb2DWidget w, GC gc);
50 static void DrawOrientLine(Skewb2DWidget w, int cube, int orient, int dx, int dy, GC borderGC);
51 
52 static char defaultTranslationsSkewb2D[] =
53 "<KeyPress>q: Quit()\n\
54    Ctrl<KeyPress>C: Quit()\n\
55    <KeyPress>KP_Divide: MoveCcw()\n\
56    <KeyPress>Home: MoveTl()\n\
57    <KeyPress>KP_7: MoveTl()\n\
58    <KeyPress>R7: MoveTl()\n\
59    <KeyPress>Up: MoveTop()\n\
60    <KeyPress>KP_8: MoveTop()\n\
61    <KeyPress>R8: MoveTop()\n\
62    <KeyPress>Prior: MoveTr()\n\
63    <KeyPress>KP_9: MoveTr()\n\
64    <KeyPress>R9: MoveTr()\n\
65    <KeyPress>Left: MoveLeft()\n\
66    <KeyPress>KP_4: MoveLeft()\n\
67    <KeyPress>R10: MoveLeft()\n\
68    <KeyPress>Begin: MoveCw()\n\
69    <KeyPress>KP_5: MoveCw()\n\
70    <KeyPress>R11: MoveCw()\n\
71    <KeyPress>Right: MoveRight()\n\
72    <KeyPress>KP_6: MoveRight()\n\
73    <KeyPress>R12: MoveRight()\n\
74    <KeyPress>End: MoveBl()\n\
75    <KeyPress>KP_1: MoveBl()\n\
76    <KeyPress>R1: MoveBl()\n\
77    <KeyPress>Down: MoveBottom()\n\
78    <KeyPress>KP_2: MoveBottom()\n\
79    <KeyPress>R14: MoveBottom()\n\
80    <KeyPress>Next: MoveBr()\n\
81    <KeyPress>KP_3: MoveBr()\n\
82    <KeyPress>R3: MoveBr()\n\
83    <Btn1Down>: Select()\n\
84    <Btn1Up>: Release()\n\
85    <KeyPress>p: Practice()\n\
86    <Btn2Down>(2+): Practice()\n\
87    <Btn2Down>: PracticeMaybe()\n\
88    <KeyPress>r: Randomize()\n\
89    <Btn3Down>(2+): Randomize()\n\
90    <Btn3Down>: RandomizeMaybe()\n\
91    <KeyPress>g: Get()\n\
92    <KeyPress>w: Write()\n\
93    <KeyPress>u: Undo()\n\
94    <KeyPress>s: Solve()\n\
95    <KeyPress>o: Orientize()";
96 
97 static XtActionsRec actionsListSkewb2D[] =
98 {
99 	{"Quit", (XtActionProc) QuitSkewb},
100 	{"MoveCcw", (XtActionProc) MoveSkewbCcw},
101 	{"MoveTl", (XtActionProc) MoveSkewb2DTl},
102 	{"MoveTop", (XtActionProc) MoveSkewb2DTop},
103 	{"MoveTr", (XtActionProc) MoveSkewb2DTr},
104 	{"MoveLeft", (XtActionProc) MoveSkewb2DLeft},
105 	{"MoveCw", (XtActionProc) MoveSkewbCw},
106 	{"MoveRight", (XtActionProc) MoveSkewb2dRight},
107 	{"MoveBl", (XtActionProc) MoveSkewb2DBl},
108 	{"MoveBottom", (XtActionProc) MoveSkewb2DBottom},
109 	{"MoveBr", (XtActionProc) MoveSkewb2DBr},
110 	{"Select", (XtActionProc) SelectSkewb},
111 	{"Release", (XtActionProc) ReleaseSkewb},
112 	{"Practice", (XtActionProc) PracticeSkewb},
113 	{"PracticeMaybe", (XtActionProc) PracticeSkewbMaybe},
114 	{"Randomize", (XtActionProc) RandomizeSkewb},
115 	{"RandomizeMaybe", (XtActionProc) RandomizeSkewbMaybe},
116 	{"Get", (XtActionProc) GetSkewb},
117 	{"Write", (XtActionProc) WriteSkewb},
118 	{"Undo", (XtActionProc) UndoSkewb},
119 	{"Solve", (XtActionProc) SolveSkewb},
120 	{"Orientize", (XtActionProc) OrientizeSkewb}
121 };
122 
123 static XtResource resourcesSkewb2D[] =
124 {
125 	{XtNuserName, XtCUserName, XtRString, sizeof (String),
126 	 XtOffset(SkewbWidget, skewb.username), XtRString, "nobody"},
127 	{XtNfaceColor0, XtCLabel, XtRString, sizeof (String),
128 	 XtOffset(SkewbWidget, skewb.faceName[0]), XtRString, "Red"},
129 	{XtNfaceColor1, XtCLabel, XtRString, sizeof (String),
130 	 XtOffset(SkewbWidget, skewb.faceName[1]), XtRString, "Blue"},
131 	{XtNfaceColor2, XtCLabel, XtRString, sizeof (String),
132 	 XtOffset(SkewbWidget, skewb.faceName[2]), XtRString, "White"},
133 	{XtNfaceColor3, XtCLabel, XtRString, sizeof (String),
134 	 XtOffset(SkewbWidget, skewb.faceName[3]), XtRString, "Green"},
135 	{XtNfaceColor4, XtCLabel, XtRString, sizeof (String),
136 	 XtOffset(SkewbWidget, skewb.faceName[4]), XtRString, "Pink"},
137 	{XtNfaceColor5, XtCLabel, XtRString, sizeof (String),
138 	 XtOffset(SkewbWidget, skewb.faceName[5]), XtRString, "Yellow"},
139 	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
140     XtOffset(SkewbWidget, skewb.foreground), XtRString, XtDefaultForeground},
141 	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
142    XtOffset(SkewbWidget, skewb.borderColor), XtRString, XtDefaultForeground},
143 	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
144 	 XtOffset(SkewbWidget, core.width), XtRString, "300"},
145 	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
146 	 XtOffset(SkewbWidget, core.height), XtRString, "400"},
147 	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
148 	 XtOffset(SkewbWidget, skewb.orient), XtRString, "FALSE"},	/* DEFAULTORIENT */
149 	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
150 	 XtOffset(SkewbWidget, skewb.mono), XtRString, "FALSE"},
151 	{XtNreverse, XtCReverse, XtRBoolean, sizeof (Boolean),
152 	 XtOffset(SkewbWidget, skewb.reverse), XtRString, "FALSE"},
153 	{XtNface, XtCFace, XtRInt, sizeof (int),
154 	 XtOffset(SkewbWidget, skewb.currentFace), XtRString, "-1"},
155 	{XtNpos, XtCPos, XtRInt, sizeof (int),
156 	 XtOffset(SkewbWidget, skewb.currentPosition), XtRString, "-1"},
157 	{XtNdirection, XtCDirection, XtRInt, sizeof (int),
158 	 XtOffset(SkewbWidget, skewb.currentDirection), XtRString, "-1"},
159 	{XtNpractice, XtCBoolean, XtRBoolean, sizeof (Boolean),
160 	 XtOffset(SkewbWidget, skewb.practice), XtRString, "FALSE"},
161 	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
162 	 XtOffset(SkewbWidget, skewb.started), XtRString, "FALSE"},
163 	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
164 	 XtOffset(SkewbWidget, skewb.select), XtRCallback, NULL}
165 };
166 
167 Skewb2DClassRec skewb2dClassRec =
168 {
169 	{
170 		(WidgetClass) & skewbClassRec,	/* superclass */
171 		"Skewb2D",	/* class name */
172 		sizeof (Skewb2DRec),	/* widget size */
173 		NULL,		/* class initialize */
174 		NULL,		/* class part initialize */
175 		FALSE,		/* class inited */
176 		(XtInitProc) InitializeSkewb2D,		/* initialize */
177 		NULL,		/* initialize hook */
178 		XtInheritRealize,	/* realize */
179 		actionsListSkewb2D,	/* actions */
180 		XtNumber(actionsListSkewb2D),	/* num actions */
181 		resourcesSkewb2D,	/* resources */
182 		XtNumber(resourcesSkewb2D),	/* num resources */
183 		NULLQUARK,	/* xrm class */
184 		TRUE,		/* compress motion */
185 		TRUE,		/* compress exposure */
186 		TRUE,		/* compress enterleave */
187 		TRUE,		/* visible interest */
188 		NULL,		/* destroy */
189 		(XtWidgetProc) ResizeSkewb2D,	/* resize */
190 		(XtExposeProc) ExposeSkewb2D,	/* expose */
191 		(XtSetValuesFunc) SetValuesSkewb2D,	/* set values */
192 		NULL,		/* set values hook */
193 		XtInheritSetValuesAlmost,	/* set values almost */
194 		NULL,		/* get values hook */
195 		XtInheritAcceptFocus,	/* accept focus */
196 		XtVersion,	/* version */
197 		NULL,		/* callback private */
198 		defaultTranslationsSkewb2D,	/* tm table */
199 		NULL,		/* query geometry */
200 		NULL,		/* display accelerator */
201 		NULL		/* extension */
202 	},
203 	{
204 		0		/* ignore */
205 	},
206 	{
207 		0		/* ignore */
208 	}
209 };
210 
211 WidgetClass skewb2dWidgetClass = (WidgetClass) & skewb2dClassRec;
212 
213 static int  planeToCube[MAXRECT] =
214 {6, 0, 6, 1, 2, 3, 6, 4, 6, 6, 5, 6};
215 static int  cubeToPlane[MAXFACES] =
216 {1, 3, 4, 5, 7, 10};
217 static XPoint diamondUnit[MAXORIENT + 1] =
218 {
219 	{0, 1},
220 	{1, -1},
221 	{1, 1},
222 	{-1, 1},
223 	{-1, -1}
224 };
225 static XPoint triangleUnit[MAXORIENT][4] =
226 {
227 	{
228 		{2, 0},
229 		{1, 0},
230 		{0, 1},
231 		{-1, -1}
232 	},
233 	{
234 		{3, 2},
235 		{0, 1},
236 		{-1, 0},
237 		{1, -1}
238 	},
239 	{
240 		{1, 3},
241 		{-1, 0},
242 		{0, -1},
243 		{1, 1}
244 	},
245 	{
246 		{0, 1},
247 		{0, -1},
248 		{1, 0},
249 		{-1, 1}
250 	}
251 };
252 static XPoint letterUnit[MAXCUBES] =
253 {
254 	{2, 0},
255 	{2, 2},
256 	{0, 2},
257 	{0, 0},
258 	{1, 1}
259 };
260 static XPoint diamondList[MAXORIENT + 1], triangleList[MAXORIENT][4], letterList[MAXCUBES],
261             offsetList[MAXCUBES];
262 
263 static void
InitializeSkewb2D(Widget request,Widget renew)264 InitializeSkewb2D(Widget request, Widget renew)
265 {
266 	Skewb2DWidget w = (Skewb2DWidget) renew;
267 
268 	w->skewb.dim = 2;
269 	ResizeSkewb2D(w);
270 }
271 
272 static void
ResizeSkewb2D(Skewb2DWidget w)273 ResizeSkewb2D(Skewb2DWidget w)
274 {
275 	w->skewb.delta = 3;
276 	w->skewb.vertical = (w->core.height >= w->core.width);
277 	if (w->skewb.vertical)
278 		w->skewb2d.faceLength = MIN(w->core.height / MAXY, w->core.width / MAXX);
279 	else
280 		w->skewb2d.faceLength = MIN(w->core.height / MAXX, w->core.width / MAXY);
281 	w->skewb2d.faceLength = MAX(w->skewb2d.faceLength - w->skewb.delta - 1, 0);
282 	w->skewb2d.diamondLength = w->skewb2d.faceLength - w->skewb.delta;
283 	w->skewb2d.viewLength = w->skewb2d.faceLength + w->skewb.delta;
284 	if (w->skewb.vertical) {
285 		w->skewb.puzzleSize.x = MAXX * (w->skewb2d.viewLength - 1) +
286 			w->skewb.delta;
287 		w->skewb.puzzleSize.y = MAXY * (w->skewb2d.viewLength - 1) +
288 			w->skewb.delta;
289 	} else {
290 		w->skewb.puzzleSize.x = MAXY * (w->skewb2d.viewLength - 1) +
291 			w->skewb.delta;
292 		w->skewb.puzzleSize.y = MAXX * (w->skewb2d.viewLength - 1) +
293 			w->skewb.delta;
294 	}
295 	w->skewb.puzzleOffset.x = ((int) w->core.width - w->skewb.puzzleSize.x)
296 		/ 2;
297 	w->skewb.puzzleOffset.y = ((int) w->core.height - w->skewb.puzzleSize.y)
298 		/ 2;
299 	ResizePolyhedrons(w);
300 }
301 
302 static void
ExposeSkewb2D(Widget renew,XEvent * event,Region region)303 ExposeSkewb2D(Widget renew, XEvent * event, Region region)
304 {
305 	Skewb2DWidget w = (Skewb2DWidget) renew;
306 
307 	if (w->core.visible) {
308 		if (w->skewb.reverse)
309 			XFillRectangle(XtDisplay(w), XtWindow(w),
310 				       w->skewb.inverseGC, 0, 0, w->core.width, w->core.height);
311 		DrawFrame(w, w->skewb.puzzleGC);
312 		DrawAllPolyhedrons((SkewbWidget) w);
313 	}
314 }
315 
316 static      Boolean
SetValuesSkewb2D(Widget current,Widget request,Widget renew)317 SetValuesSkewb2D(Widget current, Widget request, Widget renew)
318 {
319 	Skewb2DWidget c = (Skewb2DWidget) current, w = (Skewb2DWidget) renew;
320 	Boolean     redraw = False;
321 
322 	if (w->skewb2d.diamondLength != c->skewb2d.diamondLength) {
323 		ResizeSkewb2D(w);
324 		redraw = True;
325 	}
326 	return (redraw);
327 }
328 
329 static void
MoveSkewb2DTl(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)330 MoveSkewb2DTl(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
331 {
332 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, TL,
333 		       (int) (event->xkey.state & ControlMask), FALSE);
334 }
335 
336 static void
MoveSkewb2DTop(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)337 MoveSkewb2DTop(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
338 {
339 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, TOP,
340 		       (int) (event->xkey.state & ControlMask),
341 		       (int) (event->xkey.state &
342 		    (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
343 }
344 
345 static void
MoveSkewb2DTr(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)346 MoveSkewb2DTr(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
347 {
348 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, TR,
349 		       (int) (event->xkey.state & ControlMask), FALSE);
350 }
351 
352 static void
MoveSkewb2DLeft(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)353 MoveSkewb2DLeft(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
354 {
355 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, LEFT,
356 		       (int) (event->xkey.state & ControlMask),
357 		       (int) (event->xkey.state &
358 		    (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
359 }
360 
361 static void
MoveSkewb2dRight(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)362 MoveSkewb2dRight(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
363 {
364 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, RIGHT,
365 		       (int) (event->xkey.state & ControlMask),
366 		       (int) (event->xkey.state &
367 		    (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
368 }
369 
370 static void
MoveSkewb2DBl(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)371 MoveSkewb2DBl(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
372 {
373 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, BL,
374 		       (int) (event->xkey.state & ControlMask), FALSE);
375 }
376 
377 static void
MoveSkewb2DBottom(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)378 MoveSkewb2DBottom(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
379 {
380 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, BOTTOM,
381 		       (int) (event->xkey.state & ControlMask),
382 		       (int) (event->xkey.state &
383 		    (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)));
384 }
385 
386 static void
MoveSkewb2DBr(Skewb2DWidget w,XEvent * event,char ** args,int nArgs)387 MoveSkewb2DBr(Skewb2DWidget w, XEvent * event, char **args, int nArgs)
388 {
389 	MoveSkewbInput((SkewbWidget) w, event->xbutton.x, event->xbutton.y, BR,
390 		       (int) (event->xkey.state & ControlMask), FALSE);
391 }
392 
393 static void
ResizePolyhedrons(Skewb2DWidget w)394 ResizePolyhedrons(Skewb2DWidget w)
395 {
396 	int         i, j;
397 
398 	w->skewb.orientLineLength = w->skewb2d.diamondLength / 8;
399 	w->skewb.letterOffset.x = -2;
400 	w->skewb.letterOffset.y = 4;
401 	for (i = 0; i <= MAXORIENT; i++) {
402 		diamondList[i].x = diamondUnit[i].x * (w->skewb2d.diamondLength / 2 -
403 						       w->skewb.delta);
404 		diamondList[i].y = diamondUnit[i].y * (w->skewb2d.diamondLength / 2 -
405 						       w->skewb.delta);
406 	}
407 	for (i = 0; i < MAXORIENT; i++) {
408 		for (j = 0; j <= 3; j++) {
409 			triangleList[i][j].x = triangleUnit[i][j].x *
410 				(w->skewb2d.diamondLength / 2 - 3 * w->skewb.delta);
411 			triangleList[i][j].y = triangleUnit[i][j].y *
412 				(w->skewb2d.diamondLength / 2 - 3 * w->skewb.delta);
413 		}
414 	}
415 
416 	for (i = 0; i < MAXORIENT; i++) {
417 		if (letterUnit[i].x == 0)
418 			letterList[i].x = w->skewb2d.diamondLength / 8 +
419 				w->skewb.letterOffset.x;
420 		else if (letterUnit[i].x == 2)
421 			letterList[i].x = 7 * w->skewb2d.diamondLength / 8 - 2 +
422 				w->skewb.letterOffset.x;
423 		if (letterUnit[i].y == 0)
424 			letterList[i].y = w->skewb2d.diamondLength / 8 + 2 +
425 				w->skewb.letterOffset.y;
426 		else if (letterUnit[i].y == 2)
427 			letterList[i].y = 7 * w->skewb2d.diamondLength / 8 - 3 +
428 				w->skewb.letterOffset.y;
429 
430 		if (triangleUnit[i][0].x == 0)
431 			offsetList[i].x = w->skewb.delta - 1;
432 		else if (triangleUnit[i][0].x == 1)
433 			offsetList[i].x = w->skewb2d.diamondLength / 2 -
434 				2 * w->skewb.delta - 1;
435 		else if (triangleUnit[i][0].x == 2)
436 			offsetList[i].x = w->skewb2d.diamondLength / 2 +
437 				2 * w->skewb.delta;
438 		else if (triangleUnit[i][0].x == 3)
439 			offsetList[i].x = w->skewb2d.diamondLength - w->skewb.delta - 1;
440 		if (triangleUnit[i][0].y == 0)
441 			offsetList[i].y = w->skewb.delta - 1;
442 		else if (triangleUnit[i][0].y == 1)
443 			offsetList[i].y = w->skewb2d.diamondLength / 2 -
444 				2 * w->skewb.delta - 1;
445 		else if (triangleUnit[i][0].y == 2)
446 			offsetList[i].y = w->skewb2d.diamondLength / 2 +
447 				2 * w->skewb.delta - 1;
448 		else if (triangleUnit[i][0].y == 3)
449 			offsetList[i].y = w->skewb2d.diamondLength - w->skewb.delta - 2;
450 	}
451 	if (diamondUnit[0].x == 0)
452 		offsetList[MAXORIENT].x = w->skewb.delta - 2;
453 	else if (diamondUnit[0].x == 1)
454 		offsetList[MAXORIENT].x = w->skewb2d.diamondLength / 2 - 1;
455 	if (diamondUnit[0].y == 0)
456 		offsetList[MAXORIENT].y = w->skewb.delta - 2;
457 	else if (diamondUnit[0].y == 1)
458 		offsetList[MAXORIENT].y = w->skewb2d.diamondLength / 2 - 2;
459 	if (letterUnit[MAXORIENT].x == 1)
460 		letterList[MAXORIENT].x = w->skewb2d.diamondLength / 2 - 2 +
461 			w->skewb.letterOffset.x;
462 	if (letterUnit[MAXORIENT].y == 1)
463 		letterList[MAXORIENT].y = w->skewb2d.diamondLength / 2 - 2 +
464 			w->skewb.letterOffset.y;
465 }
466 
467 Boolean
SelectPolyhedrons2D(Skewb2DWidget w,int x,int y,int * face,int * position)468 SelectPolyhedrons2D(Skewb2DWidget w, int x, int y, int *face, int *position)
469 {
470 	int         faceX, faceY, i, j;
471 
472 	x -= w->skewb.puzzleOffset.x;
473 	y -= w->skewb.puzzleOffset.y;
474 	faceX = x / w->skewb2d.viewLength;
475 	faceY = y / w->skewb2d.viewLength;
476 	i = x - faceX * w->skewb2d.viewLength;
477 	j = y - faceY * w->skewb2d.viewLength;
478 	if (i - j > w->skewb2d.viewLength / 2 - 3)
479 		*position = TR;
480 	else if (i + j > 3 * w->skewb2d.viewLength / 2)
481 		*position = BR;
482 	else if (j - i > w->skewb2d.viewLength / 2 - 2)
483 		*position = BL;
484 	else if (i + j < w->skewb2d.viewLength / 2 + 7)
485 		*position = TL;
486 	else
487 		*position = MAXORIENT;
488 	if ((faceX != 1 && faceY != 1) ||
489 	    (faceX >= 3 && w->skewb.vertical) ||
490 	    (faceY >= 3 && !w->skewb.vertical))
491 		return False;
492 	*face = planeToCube[faceX + faceY * MAXX];
493 	if (faceX == 3) {
494 		*face = MAXFACES - 1;
495 		if (*position != MAXORIENT)
496 			*position = (*position + HALF) % MAXORIENT;
497 	}
498 	return True;
499 }
500 
501 Boolean
NarrowSelection2D(Skewb2DWidget w,int * face,int * position,int * direction)502 NarrowSelection2D(Skewb2DWidget w, int *face, int *position, int *direction)
503 {
504 	if (*face == MAXFACES - 1 && *direction < MAXORIENT && !w->skewb.vertical) {
505 		if (*direction < MAXORIENT)
506 			*direction = (*direction + HALF) % MAXORIENT;
507 		else if (*direction >= 2 * MAXORIENT)
508 			*direction = 2 * MAXORIENT + (*direction + HALF) % MAXORIENT;
509 	}
510 	if (*position != MAXORIENT) {
511 		if (*direction == CW)
512 			*direction = (*position + 1) % MAXORIENT;
513 		else if (*direction == CCW)
514 			*direction = (*position + 3) % MAXORIENT;
515 		else if (*direction < MAXORIENT && !((*direction + *position) % 2))
516 			return False;
517 	}
518 	return True;
519 }
520 
521 static void
DrawFrame(Skewb2DWidget w,GC gc)522 DrawFrame(Skewb2DWidget w, GC gc)
523 {
524 	int         i;
525 	XPoint      pos[MAXXY + 1], letters;
526 
527 	for (i = 0; i <= MAXXY; i++) {
528 		pos[i].x = i * w->skewb2d.viewLength + w->skewb.puzzleOffset.x;
529 		pos[i].y = i * w->skewb2d.viewLength + w->skewb.puzzleOffset.y;
530 	}
531 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
532 		  pos[1].x, pos[0].y, pos[2].x, pos[0].y);
533 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
534 		  pos[3].x, pos[1].y, pos[3].x, pos[2].y);
535 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
536 		  pos[1].x, pos[3].y, pos[2].x, pos[3].y);
537 	XDrawLine(XtDisplay(w), XtWindow(w), gc,
538 		  pos[0].x, pos[1].y, pos[0].x, pos[2].y);
539 	letters.x = pos[0].x + w->skewb2d.viewLength / 2 - w->skewb.delta;
540 	letters.y = pos[0].y + w->skewb2d.viewLength / 2;
541 	XDrawString(XtDisplay(w), XtWindow(w), gc,
542 		    (int) (letters.x + 5 * w->skewb.letterOffset.x),
543 		    (int) (letters.y + w->skewb.letterOffset.y), "Front", 5);
544 	letters.x = pos[2].x + w->skewb2d.viewLength / 2 - w->skewb.delta;
545 	letters.y = pos[2].y + w->skewb2d.viewLength / 2;
546 	XDrawString(XtDisplay(w), XtWindow(w), gc,
547 		    (int) (letters.x + 4 * w->skewb.letterOffset.x),
548 		    (int) (letters.y + w->skewb.letterOffset.y), "Back", 4);
549 	if (w->skewb.vertical) {
550 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
551 			  pos[1].x, pos[0].y, pos[1].x, pos[4].y);
552 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
553 			  pos[2].x, pos[0].y, pos[2].x, pos[4].y);
554 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
555 			  pos[0].x, pos[1].y, pos[3].x, pos[1].y);
556 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
557 			  pos[0].x, pos[2].y, pos[3].x, pos[2].y);
558 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
559 			  pos[1].x, pos[4].y, pos[2].x, pos[4].y);
560 	} else {
561 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
562 			  pos[0].x, pos[1].y, pos[4].x, pos[1].y);
563 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
564 			  pos[0].x, pos[2].y, pos[4].x, pos[2].y);
565 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
566 			  pos[1].x, pos[0].y, pos[1].x, pos[3].y);
567 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
568 			  pos[2].x, pos[0].y, pos[2].x, pos[3].y);
569 		XDrawLine(XtDisplay(w), XtWindow(w), gc,
570 			  pos[4].x, pos[1].y, pos[4].x, pos[2].y);
571 	}
572 }
573 
574 void
DrawDiamond2D(Skewb2DWidget w,int face,int offset)575 DrawDiamond2D(Skewb2DWidget w, int face, int offset)
576 {
577 	GC          faceGC, borderGC;
578 	int         dx, dy, orient;
579 
580 	orient = w->skewb.cubeLoc[face][MAXORIENT].rotation;
581 	if (w->skewb.vertical || face != MAXFACES - 1) {
582 		dx = (cubeToPlane[face] % MAXX) * w->skewb2d.viewLength +
583 			w->skewb.delta;
584 		dy = (cubeToPlane[face] / MAXX) * w->skewb2d.viewLength +
585 			w->skewb.delta;
586 	} else {
587 		dx = (cubeToPlane[face] / MAXX) * w->skewb2d.viewLength +
588 			w->skewb.delta;
589 		dy = (cubeToPlane[face] % MAXX) * w->skewb2d.viewLength +
590 			w->skewb.delta;
591 		orient = (orient + HALF) % STRT;
592 	}
593 	dx += w->skewb.puzzleOffset.x + w->skewb.delta;
594 	dy += w->skewb.puzzleOffset.y + w->skewb.delta;
595 	diamondList[0].x = offsetList[MAXORIENT].x + dx;
596 	diamondList[0].y = offsetList[MAXORIENT].y + dy;
597 	if (offset) {
598 		borderGC = w->skewb.faceGC[(int) w->skewb.cubeLoc[face][MAXORIENT].face];
599 		if (w->skewb.depth < 2 || w->skewb.mono) {
600 			faceGC = w->skewb.inverseGC;
601 		} else {
602 			faceGC = w->skewb.borderGC;
603 		}
604 	} else {
605 		faceGC = w->skewb.faceGC[(int) w->skewb.cubeLoc[face][MAXORIENT].face];
606 		borderGC = w->skewb.borderGC;
607 	}
608 	XFillPolygon(XtDisplay(w), XtWindow(w),
609 		     faceGC, diamondList, 4, Convex, CoordModePrevious);
610 	XDrawLines(XtDisplay(w), XtWindow(w),
611 		   borderGC, diamondList, 5, CoordModePrevious);
612 	if (w->skewb.depth < 2 || w->skewb.mono) {
613 		int         letterX, letterY;
614 		char        buf[2];
615 
616 		(void) sprintf(buf, "%c",
617 		w->skewb.faceName[w->skewb.cubeLoc[face][MAXORIENT].face][0]);
618 		letterX = dx + letterList[MAXORIENT].x;
619 		letterY = dy + letterList[MAXORIENT].y;
620 		if (offset) {
621 			borderGC = w->skewb.borderGC;
622 		} else {
623 			borderGC = w->skewb.inverseGC;
624 		}
625 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
626 			    letterX, letterY, buf, 1);
627 	}
628 	if (w->skewb.orient)
629 		DrawOrientLine(w, MAXORIENT, orient, dx, dy, borderGC);
630 }
631 
632 void
DrawTriangle2D(Skewb2DWidget w,int face,int position,int offset)633 DrawTriangle2D(Skewb2DWidget w, int face, int position, int offset)
634 {
635 	GC          faceGC, borderGC;
636 	int         dx, dy, letterX, letterY, orient, newCorner;
637 
638 	orient = w->skewb.cubeLoc[face][position].rotation;
639 	if (w->skewb.vertical || face != MAXFACES - 1) {
640 		dx = (cubeToPlane[face] % MAXX) * w->skewb2d.viewLength +
641 			w->skewb.delta - 1;
642 		dy = (cubeToPlane[face] / MAXX) * w->skewb2d.viewLength +
643 			w->skewb.delta - 1;
644 		newCorner = position;
645 	} else {
646 		dx = (cubeToPlane[face] / MAXX) * w->skewb2d.viewLength +
647 			w->skewb.delta - 1;
648 		dy = (cubeToPlane[face] % MAXX) * w->skewb2d.viewLength +
649 			w->skewb.delta - 1;
650 		newCorner = (position + HALF) % STRT;
651 		orient = (orient + HALF) % STRT;
652 	}
653 	dx += w->skewb.puzzleOffset.x + w->skewb.delta;
654 	dy += w->skewb.puzzleOffset.y + w->skewb.delta;
655 	letterX = dx + letterList[newCorner].x;
656 	letterY = dy + letterList[newCorner].y;
657 	triangleList[newCorner][0].x = offsetList[newCorner].x + dx;
658 	triangleList[newCorner][0].y = offsetList[newCorner].y + dy;
659 	if (offset) {
660 		borderGC = w->skewb.faceGC[(int) w->skewb.cubeLoc[face][position].face];
661 		if (w->skewb.depth < 2 || w->skewb.mono) {
662 			faceGC = w->skewb.inverseGC;
663 		} else {
664 			faceGC = w->skewb.borderGC;
665 		}
666 	} else {
667 		faceGC = w->skewb.faceGC[(int) w->skewb.cubeLoc[face][position].face];
668 		borderGC = w->skewb.borderGC;
669 	}
670 	XFillPolygon(XtDisplay(w), XtWindow(w), faceGC,
671 		     triangleList[newCorner], 3, Convex, CoordModePrevious);
672 	XDrawLines(XtDisplay(w), XtWindow(w), borderGC,
673 		   triangleList[newCorner], 4, CoordModePrevious);
674 	if (w->skewb.depth < 2 || w->skewb.mono) {
675 		char        buf[2];
676 
677 		(void) sprintf(buf, "%c",
678 		w->skewb.faceName[w->skewb.cubeLoc[face][position].face][0]);
679 		if (offset) {
680 			borderGC = w->skewb.borderGC;
681 		} else {
682 			borderGC = w->skewb.inverseGC;
683 		}
684 		XDrawString(XtDisplay(w), XtWindow(w), borderGC,
685 			    letterX, letterY, buf, 1);
686 	}
687 	if (w->skewb.orient)
688 		DrawOrientLine(w, newCorner, orient,
689 			       letterX - w->skewb.letterOffset.x, letterY - w->skewb.letterOffset.y,
690 			       borderGC);
691 }
692 
693 static void
DrawOrientLine(Skewb2DWidget w,int cube,int orient,int dx,int dy,GC borderGC)694 DrawOrientLine(Skewb2DWidget w, int cube, int orient, int dx, int dy, GC borderGC)
695 {
696 	if (cube == MAXORIENT)
697 		switch (orient) {
698 			case TR:
699 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
700 				       dx + w->skewb2d.diamondLength / 2 - 2,
701 					  dy + w->skewb.delta - 2,
702 				       dx + w->skewb2d.diamondLength / 2 - 2,
703 					  dy + w->skewb.delta + w->skewb.orientLineLength);
704 				return;
705 			case BR:
706 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
707 					  dx + w->skewb2d.diamondLength - 7,
708 				       dy + w->skewb2d.diamondLength / 2 - 2,
709 					  dx + w->skewb2d.diamondLength -
710 					  w->skewb.orientLineLength - 7,
711 				      dy + w->skewb2d.diamondLength / 2 - 2);
712 				return;
713 			case BL:
714 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
715 				       dx + w->skewb2d.diamondLength / 2 - 2,
716 					  dy + w->skewb2d.diamondLength - 7,
717 				       dx + w->skewb2d.diamondLength / 2 - 2,
718 					  dy + w->skewb2d.diamondLength -
719 					  w->skewb.orientLineLength - 7);
720 				return;
721 			case TL:
722 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
723 					  dx + w->skewb.delta - 2,
724 				       dy + w->skewb2d.diamondLength / 2 - 2,
725 					  dx + w->skewb.delta + w->skewb.orientLineLength,
726 				      dy + w->skewb2d.diamondLength / 2 - 2);
727 				return;
728 			default:
729 				(void) printf("DrawOrientLine: orient %d\n", orient);
730 	} else			/* cube != MAXORIENT */
731 		switch (orient) {
732 			case TR:
733 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
734 					  dx,
735 					  dy - w->skewb.delta,
736 					  dx,
737 					  dy - w->skewb.delta - w->skewb.orientLineLength / 2);
738 				return;
739 			case BR:
740 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
741 					  dx + w->skewb.delta,
742 					  dy,
743 					  dx + w->skewb.delta + w->skewb.orientLineLength / 2,
744 					  dy);
745 				return;
746 			case BL:
747 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
748 					  dx,
749 					  dy + w->skewb.delta,
750 					  dx,
751 					  dy + w->skewb.delta + w->skewb.orientLineLength / 2);
752 				return;
753 			case TL:
754 				XDrawLine(XtDisplay(w), XtWindow(w), borderGC,
755 					  dx - w->skewb.delta,
756 					  dy,
757 					  dx - w->skewb.delta - w->skewb.orientLineLength / 2,
758 					  dy);
759 				return;
760 			default:
761 				(void) printf("DrawOrientLine: orient %d\n", orient);
762 		}
763 }
764