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