-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPuzzle.java
4264 lines (3790 loc) · 210 KB
/
Puzzle.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Puzzle.java: Implementing class for MC3D (Applet) and MC3DApp (application)
* (c)2005, 2006 David Vanderschel - [email protected] or [email protected]
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 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. See the
* GNU General Public License for more details.
* To view the GNU General Public License, visit the gnu.org Web site
* at http://www.gnu.org/licenses/gpl.html. (If something goes wrong
* with that link, check around at fsf.org and gnu.org. If even that
* fails, to obtain a printed copy, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.)
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.filechooser.*;
import java.lang.Math;
import java.util.*;
import java.text.*;
import java.beans.*;
import java.io.*;
public class Puzzle {
Puzzle( String s, String args[] ) {
startMethod = s;
puzzleArgs = args;
makeFrame();
}
String puzzleArgs[];
String startMethod;
/******** Graphics Pane Setup - Mostly about accepting input. ********/
MainPane pane;
InputMap iMap;
class MainPane extends JPanel implements MouseListener, MouseMotionListener {
MainPane() {
super();
addMouseListener(this);
addMouseMotionListener(this);
keyBoardInSetup( getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW ), getActionMap() );
}
public void paintComponent( Graphics g1 ) {
super.paintComponent(g1);
g = (Graphics2D)g1; // Save graphics context as a global for everybody.
paintPane( getSize().width, getSize().height );
}
public void mouseClicked ( MouseEvent e ) { }
public void mouseEntered ( MouseEvent e ) { }
public void mouseExited ( MouseEvent e ) { exitMouse(e); }
public void mousePressed ( MouseEvent e ) { downMouse(e); }
public void mouseReleased( MouseEvent e ) { upMouse(e); }
public void mouseDragged ( MouseEvent e ) { dragMouse(e); }
public void mouseMoved ( MouseEvent e ) { moveMouse(e); }
}
int modsIE[] = new int[16]; // Array of all 16 possible combinations of below mask bits.
/* Modifier bits from InputEvent */
int javaMod[] = { InputEvent.CTRL_DOWN_MASK, InputEvent.ALT_DOWN_MASK,
InputEvent.META_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK };
/* BFUDLR KeyEvent codes */
char bEvent[] = { KeyEvent.VK_B, KeyEvent.VK_F, KeyEvent.VK_U,
KeyEvent.VK_D, KeyEvent.VK_L, KeyEvent.VK_R };
/* Arrow keys for Rover events. */
char rEvent[] = { KeyEvent.VK_KP_LEFT, KeyEvent.VK_KP_RIGHT, KeyEvent.VK_KP_UP, KeyEvent.VK_KP_DOWN,
KeyEvent. VK_LEFT, KeyEvent. VK_RIGHT, KeyEvent. VK_UP, KeyEvent. VK_DOWN };
String arrowDir[] = {"left", "right", "up", "down"}; // Defines indices for the arrow directions.
/* Request receipt of the various desired key combinations. Also used in FlatPane setup. */
void keyBoardInSetup( InputMap iMap, ActionMap actionMap ) {
int i, j, k, bits;
for(i=0; i< bEvent.length; i++) {
for(j=0; j<16; j++) { // There must be a better way than explicitly doing every combination of mods.
int mods = 0; // Convert j's bit pattern to corresponding pattern of InputEvent modifier bits.
for( k=0, bits=j; k<4; k++, bits = bits>>1 ) if( bits%2 != 0 ) modsIE[j] |= javaMod[k];
iMap.put( KeyStroke.getKeyStroke( bEvent[i], modsIE[j] ), "twist" );
}
}
for(i=0; i<rEvent.length; i++) {
iMap.put( KeyStroke.getKeyStroke( rEvent[i], InputEvent. ALT_DOWN_MASK ), arrowDir[i%4] );
iMap.put( KeyStroke.getKeyStroke( rEvent[i], InputEvent.CTRL_DOWN_MASK ), arrowDir[i%4] );
iMap.put( KeyStroke.getKeyStroke( rEvent[i], InputEvent.META_DOWN_MASK ), arrowDir[i%4] );
}
for(i=0; i<command.length-1; i++) {
iMap.put( KeyStroke.getKeyStroke( command[i].accKey, InputEvent.CTRL_DOWN_MASK ), command[i].name );
actionMap.put( command[i].name, command[i].action );
}
String actionName[] = { "left", "right", "up", "down", "twist" };
AbstractAction action[] = { left, right, up, down, twist };
for(i=0; i<action.length; i++) actionMap.put( actionName[i], action[i] );
}
/* I don't know why I need the separate four actions below. The problem is that getActionCommand on the
passed ActionEvent returns null, so I can't tell which arrow was typed without the separate actions. */
AbstractAction left = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { roverKey( 0, e.getModifiers() ); } };
AbstractAction right = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { roverKey( 1, e.getModifiers() ); } };
AbstractAction up = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { roverKey( 2, e.getModifiers() ); } };
AbstractAction down = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { roverKey( 3, e.getModifiers() ); } };
AbstractAction twist = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { twist(e); } };
AbstractAction init = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
int i;
curStack.push( new Permute() );
for(i=0; i<54; i++) place[i].colorI = i/9;
paintPanes();
}
};
AbstractAction scramble = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
int i;
boolean reflect = reflect1;
curStack.push( new Permute() );
drawing = false;
for(i=0; i<100; i++) {
dirControl = ( random.nextInt(2) == 0 );
sliceGo = ( random.nextInt(2) == 0 ) ? posCase : negCase;
twistAxis = random.nextInt(3);
if( reflect ) reflect2 = ( random.nextInt(3) == 0 );
twist();
}
reflect1 = false;
drawing = true;
paintPanes();
}
};
AbstractAction undo = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
if( animating && twistQueue.n < Q_LEN ) twistQueue.push( new Undo() );
if( animating || undoStack.n == 0 ) return;
curStack = redoStack;
undoStack.pop().doIt();
curStack = undoStack;
paintPanes();
}
};
AbstractAction redo = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
if( animating && twistQueue.n < Q_LEN ) twistQueue.push( new Redo() );
if( animating || redoStack.n == 0 ) return;
redoStack.pop().doIt();
paintPanes();
}
};
AbstractAction cancel = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
curStack = trashStack;
redo.actionPerformed( dummyEvent );
curStack = undoStack;
}
};
AbstractAction exit = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
aniTimer.stop();
roverTimer.stop();
driveTimer.stop();
if( frame != null ) frame.dispose();
if( flatFrame != null ) flatFrame.dispose();
frame = null;
flatFrame = null;
}
};
/* Action for changes to Check Boxes on View and Toggles Menus. */
AbstractAction cbChange = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
String s = e.getActionCommand();
if( s.equals( roverUndoableBox.name ) ) whatsChanging = NOTHING;
if( s.equals( viewUndoableBox.name ) ) whatsChanging = NOTHING;
if( s.equals( autoScaleBox.name ) ) scaleDone = false;
if( s.equals( autoPosBox.name ) && autoPosBox.isSelected() ) positionCenters();
if( s.equals( layeredBox.name ) ) {
if( layeredBox.isSelected() ) {
if( flatFrame != null ) flatFrame.setVisible(true); else makeFlatFrame();
} else {
if( flatFrame != null ) flatFrame.setVisible(false);
}
}
paintPanes();
}
};
AbstractAction mirror = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { reflect1 = true; } };
AbstractAction colorAdjust = new AbstractAction() {
public void actionPerformed( ActionEvent ae ) {
int i=0, j;
String title;
String s = JOptionPane.showInputDialog( frame,
"Enter character for color to change.\nbfudlr: for sticker colors.\n"+
"BFUDLR: for label colors.\ng/G: for backGround/foreGround\ne/E: for edge colors - 2D/1D",
"Adjusting a color.", JOptionPane.QUESTION_MESSAGE );
if( s==null || s.length()==0 ) return;
char c = s.charAt(0);
boolean uc = (c<='Z'); // true when character is Upper Case
if(!uc) c -= 32; // Transform lower case to upper case.
if(c=='G') {
title = uc ? "Specify new foreground color." : "Specify new background color.";
colorChooser.setColor( uc ? fgColor : bgColor );
} else if(c=='E') {
title = uc ? "Specify new 1D separator color." : "Specify new 2D edge color." ;
colorChooser.setColor( uc ? edge1Color : edge2Color );
} else {
for(i=0; i<6; i++) if( fChar[i] == c ) break;
if(i==6) return;
title = uc ? "Specify new color for \""+c+"\" label." : "Specify new color for "+c+" face sticker.";
colorChooser.setColor( uc ? labelColor[i] : faceColor[i] );
}
JColorChooser.createDialog( frame, title, true, colorChooser, newColor, cancelColor ).setVisible(true);
if( colorChosen == null ) return;
if(c=='G') {
if(uc) fgColor = colorChosen;
else bgColor = colorChosen;
} else if(c=='E') {
if(uc) edge1Color = colorChosen;
else edge2Color = colorChosen;
} else if(uc) {
labelColor[i] = colorChosen;
} else {
float f[] = colorChosen.getRGBColorComponents(null);
for(j=0; j<3; j++) colorComponents[i][j] = (double)f[j];
}
createColors( faceColor, 1.f );
createColors( dimColor1D, .85f );
lastInFaceBright = 0.f;
paintPanes();
}
};
/* The extra fooling around with the createDialog method of JColorChooser, as opposed to the simpler
showDialog method, is to achieve persistence of the "recent" colors in the dialogue. */
JColorChooser colorChooser = new JColorChooser();
Color colorChosen;
AbstractAction newColor = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { colorChosen = colorChooser.getColor(); } };
AbstractAction cancelColor = new AbstractAction() {
public void actionPerformed( ActionEvent e ) { colorChosen = null; } };
/* Save State. */
AbstractAction save = new AbstractAction() {
public void actionPerformed( ActionEvent ae ) {
int i, j;
if( !startMethod.equals("application") ) return;
JFileChooser fc = new JFileChooser(".");
fc.setFileFilter( new Filter() );
if( fc.showSaveDialog(frame) != JFileChooser.APPROVE_OPTION ) return;
File f = fc.getSelectedFile();
String filePath = fix(f);
f = new File( filePath );
if( f.canRead() ) {
if( JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
pane, "File "+filePath+" already exists.\nDo you want to CANCEL this operation?",
"Save-State Confirmation", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ) ) {
JOptionPane.showMessageDialog( pane, "Save aborted.", "State Saving Attempt",
JOptionPane.WARNING_MESSAGE );
return;
}
}
try {
PrintStream ps = new PrintStream( new FileOutputStream(f) );
ps.println("Current_Parameter: " + curParam);
for(i=0; i<parButsSub.length; i++) {
ps.print( parButsSub[i].mc3ID + " " ); // parameter-group name
for(j=0; j<parButsSub[i].paramA.length; j++) ps.print( parButsSub[i].paramA[j].val + " " );
ps.println();
}
ps.print("Frame_Size: ");
ps.println( frame.getWidth() + " " + frame.getHeight() );
ps.print("Center_Locations: ");
ps.println( in_Center.x + " " + in_Center.y + " " +
outCenter.x + " " + outCenter.y );
ps.print("View_Checkboxes: ");
for(i=0; i<vCheckBox.length; i++) ps.print( vCheckBox[i].isSelected() ? 'Y' : 'N' );
ps.println();
ps.print("Toggle_Checkboxes: ");
for(i=0; i<tCheckBox.length; i++) ps.print( tCheckBox[i].isSelected() ? 'Y' : 'N' );
ps.println();
ps.print("Sticker_Colors: ");
for(i=0; i<54; i++) ps.print( fChar[ place[i].colorI ] );
ps.println();
for(i=0; i<6; i++) ps.println( bChar[i] + " " + colToString( faceColor[ bFace[i] ] ) +
" " + colToString( labelColor[ bFace[i] ] ) );
ps.println( "G " + colToString( bgColor ) + " " + colToString( fgColor ) );
ps.println( "E " + colToString( edge2Color ) + " " + colToString( edge1Color ) );
ps.close();
} catch( IOException ee ) { System.err.println("Caught IOException: " + ee.getMessage() ); }
}
};
String colToString( Color c ) { // Return a string representation of the 3 components.
int i;
float f[] = c.getRGBColorComponents(null);
String s = "";
for(i=0; i<3; i++) s = s.concat( Float.toString( f[i] ) + " " );
return s;
}
class Filter extends javax.swing.filechooser.FileFilter { // Require .mc3 extension.
public String getDescription() { return ".mc3 files"; }
public boolean accept( File f ) {
if( f.isDirectory() ) return true;
String s = f.getName();
int i = s.lastIndexOf('.');
return "mc3".equals( ( i>0 && i<s.length()-1 ) ? s.substring(i+1).toLowerCase() : null );
}
}
String fix( File f ) { // Add the ".mc3" extension to file path if not already present, and return path.
String fn = f.getPath();
return ( fn.length() < 4 || !".mc3".equals( fn.substring( fn.length() - 4 ) ) ) ? fn.concat(".mc3") : fn;
}
/* Recall State. */
AbstractAction recall = new AbstractAction() {
public void actionPerformed( ActionEvent ae ) {
if( !startMethod.equals("application") ) return;
JFileChooser fc = new JFileChooser(".");
fc.setFileFilter( new Filter() );
if( fc.showOpenDialog(frame) != JFileChooser.APPROVE_OPTION ) return;
if( !readStateFile( fix( fc.getSelectedFile() ) ) )
JOptionPane.showMessageDialog( pane, "Sorry. That failed for some reason.\n" +
"(The name of the file on disk must end in \".mc3\".)",
"State Recall Attempt", JOptionPane.WARNING_MESSAGE );
}
};
/* Set up a disk file as the input stream for readState(). Return true if successful, false otherwise. */
boolean readStateFile( String filePath ) {
try {
return readState( new BufferedReader( new FileReader( new File( filePath ) ) ) );
} catch( FileNotFoundException e ) { return false; }
}
/* Restore a saved state from an input stream. Return true if successful, false otherwise. */
boolean readState( BufferedReader in ) {
int i, j, k, nLines=0;
int diff; // missing-parameter count for reverse compatibility when new items are introduced
String line;
String[] s;
float f[] = new float[6];
try {
/* Read the lines of saved-state file and break them into tokens. Line limit is arbitrary. */
while( nLines<50 && ( ( line = in.readLine() ) != null ) ) {
s = line.split(" +");
if( s.length < 2 ) continue;
/* Parse the tokens and modify state. */
if( s[0].equals("Current_Parameter:") ) {
curParam = Integer.parseInt( s[1] );
param[ curParam ].setSelected( true ); // Value-display will occur when modifyParam() is called below.
}
/* Look for parameter-group IDs. */
for(i=0; i<parButsSub.length; i++) {
if( s[0].equals( parButsSub[i].mc3ID ) ) {
diff = parButsSub[i].paramA.length - ( s.length - 1 );
for(k=0, j=1; j < s.length && k < parButsSub[i].paramA.length; k++) {
Param p = parButsSub[i].paramA[k];
if( p.skipOrder > 0 && p.skipOrder <= diff ) continue; // Skip this one.
modifyParam( p, Float.parseFloat( s[j++] ) );
}
}
}
/* Applets add an extra 20 pixels of height, which would reduce pane height if not compensated. */
if( s[0].equals("Frame_Size:") ) {
if( s.length < 3 ) continue;
int height = Integer.parseInt( s[2] ) + ( startMethod.equals("application") ? 0 : 20 );
frame.setSize( Integer.parseInt( s[1] ), height );
}
if( s[0].equals("Center_Locations:") ) {
if( s.length < 4 ) continue;
in_Center.set( Integer.parseInt( s[1] ), Integer.parseInt( s[2] ) );
outCenter.set( Integer.parseInt( s[3] ), Integer.parseInt( s[4] ) );
}
if( s[0].equals("View_Checkboxes:") ) {
diff = vCheckBox.length - s[1].length();
for(i=0, j=0; i < vCheckBox.length && j < s[1].length(); i++) {
CheckBox cb = vCheckBox[i];
if( cb.skipOrder > 0 && cb.skipOrder <= diff ) continue; // Skip this one.
cb.setSelected( s[1].charAt(j++) == 'Y' );
}
}
if( s[0].equals("Toggle_Checkboxes:") ) {
diff = tCheckBox.length - s[1].length();
for(i=0, j=0; i < tCheckBox.length && j < s[1].length(); i++) {
CheckBox cb = tCheckBox[i];
if( cb.skipOrder > 0 && cb.skipOrder <= diff ) continue; // Skip this one.
cb.setSelected( s[1].charAt(j++) == 'Y' );
}
}
if( s[0].equals("Sticker_Colors:") ) {
if( s[1].length() != 54 ) continue;
for(i=0; i<54; i++) if( "LRFBDU".indexOf( s[1].charAt(i) ) < 0 ) break;
if(i<54) continue; // Some nonsense in there. Ignore line.
for(i=0; i<54; i++) place[i].colorI = "LRFBDU".indexOf( s[1].charAt(i) );
}
/* Now looking for color-pair names. */
for(i=0; i<8; i++) if( s[0].equals( "LRFBDUGE".substring(i,i+1) ) ) break;
if(i==8) continue; // Did not recognize label on line.
if( s.length < 7 ) continue;
for(k=0; k<6; k++) f[k] = Float.parseFloat( s[k+1] );
if(i==6) { // Is a 'G'. Background and foreground colors.
bgColor = new Color( f[0], f[1], f[2] );
fgColor = new Color( f[3], f[4], f[5] );
} else if(i==7) { // Is a 'E'. 2D sticker edge color & 1D separator color.
edge2Color = new Color( f[0], f[1], f[2] );
edge1Color = new Color( f[3], f[4], f[5] );
} else { // Face color and label color.
for(k=0; k<3; k++) colorComponents[i][k] = (double)f[k];
labelColor[i] = new Color( f[3], f[4], f[5] );
}
}
in.close();
} catch( IOException e ) { System.err.println("Caught IOException: " + e.getMessage() ); return false; }
createColors( faceColor, 1.f );
createColors( dimColor1D, .85f );
lastInFaceBright = 0.f;
param[ curParam ].actionPerformed( dummyEvent );
width = 0; // Assure update at beginning of paintPane.
cbChange.actionPerformed( new ActionEvent( this, 0, layeredBox.name ) );
highlightsOff();
frame.dispatchEvent( new ComponentEvent( frame, ComponentEvent.COMPONENT_RESIZED ) );
stateRecover = true;
paintPanes();
return true;
}
/* A place to put test code. */
boolean testOn = false; // If true, a menu item for invoking test action below will show up.
//boolean testOn = true; // If true, a menu item for invoking test action below will show up.
AbstractAction test = new AbstractAction() {
public void actionPerformed( ActionEvent e ) {
System.out.println( (new Undo()).getClass().getName() );
}
};
/******** Help-Related Stuff, including all the strings. ********/
/* Class HelpItem for describing and implementing items on Help menu. */
class HelpItem extends JMenuItem implements ActionListener {
String name;
String content;
HelpItem( String name, String content, int key ) {
super( name );
addActionListener(this);
setMnemonic( key );
this.name = name;
this.content = content;
}
public void actionPerformed( ActionEvent e ) {
JOptionPane.showMessageDialog( null, content, name, JOptionPane.PLAIN_MESSAGE, null );
}
}
/* Help strings */
String helpHelpS = new String (
"The Help Menu consists of a number of specific help items, with emphasis on\n" +
"briefly documenting the less obvious features of the program - like the\n" +
"significance of the Shift, Control, and Alt modifier keys on the keyboard. As\n" +
"it turns out, there are plenty of nonobvious features.\n\n" +
"The ConfigSelect Menu allows you to choose among some different configurations\n" +
"for the program. These are worth checking out to get familiar with what the\n" +
"program can do. \n\n" +
"The check boxes on the View Menu control what aspects of the display appear or\n" +
"not, while those on the Toggles Menu enable or disable certain behaviours of the\n" +
"program.\n\n" +
"The function of most of the various menu items should be obvious. When not, it\n" +
"should usually be easy to learn by means of experimentation. If that fails, it\n" +
"probably does not matter.\n\n" +
"In the help for mouse-based operations, the buttons are referred to as button 1\n" +
"and button 2. However, what is called \"button 2\" may actually be any button\n" +
"other than 1. Thus the buttons can usually be the left and right buttons\n" +
"respectively. If no button number is mentioned, any button can be used.\n\n" +
"For detailed documentation read the pages on the Web site.\n\n" +
"The performance of the program depends almost exclusively on how large you make\n" +
"the image.");
String twistHelpS = new String (
"Click anywhere in 2D display. Which button determines direction. If click is\n" +
"on a sticker, corresponding slice is turned. A click that misses all stickers\n" +
"is resolved to closest face center, except that around the outside clicks only\n" +
"refer to in-facing faces.\n\n" +
"Click on a BFUDLR character under either tattle.\n\n" +
"Type a BFUDLR character.\n\n" +
"Use Shift modifier to reverse direction.\n" +
"Use Control modifier to turn a center slice.\n" +
"Use Alt modifier to turn whole cube.\n" +
"Use Alt and Control modifiers together to turn the external slices.\n\n" +
"For mouse, last method above works only for clicks NOT ON stickers; for\n" +
"otherwise mouse-down is treated as the beginning of a drag for center moving.\n\n" +
"A click in either 1D projection area initiates a twist of the face whose face\n" +
"sticker is closest in the projection. Unless you disable the Drive Mode feature\n" +
"(Toggles Menu), only button 1 is available for twisting in the rover area (as\n" +
"button 2 is for Drive Mode), but this is intended primarily for the purpose of\n" +
"taking over the animation with the mouse, in which case the direction does not\n" +
"matter. (If you want a clockwise turn, you can still click on the appropriate\n" +
"BFUDLR character under one of the tattles.)\n\n" +
"If you command twists at a rate faster than the animation can perform them, the\n" +
"not-yet-done twists are queued and performed in FIFO order. In particular, a\n" +
"180 degree twist can be commanded with a double click for which the second click\n" +
"occurs while animation for the first twist is still in progress.\n\n" +
"If you precede any twist command (or Scramble) with the Mirror Prefix command \n" +
"(Control-Q), mirroring transformations will be performed. If you click on a \n" +
"sticker, that sticker will be stationary in the reflection process.");
String undoHelpS = new String (
"The results of twists and drags, modification of the 3D viewing\n" +
"transformation, modification of rover parameters, scrambling, and\n" +
"initializing can be undone and redone. The stacks for both undoing\n" +
"and redoing hold 100 operations each.\n\n" +
"If you use the mouse-control-of-animation feature to produce a 180\n" +
"degree twist, it will be treated as two 90 degree twists for the \n" +
"purpose of undoing and redoing.\n\n" +
"Undone operations remain on the redo stack even when new operations are\n" +
"performed. Thus if you forgot something 2 steps back, you can go\n" +
"undo, undo, what-you-missed, redo, redo. This does not apply exactly\n" +
"for anything but twists, as redoing a non-twist puts state back to \n" +
"the state that existed before the undo of a previous operation, as\n" +
"opposed to performing some operation relative to the current state.\n\n" +
"The Cancel Undo command is like the Redo command except that whatever\n" +
"it (re)does is not itself undoable. (Useful for removing from the\n" +
"undo/redo stacks actions like reorientation or center moving that you\n" +
"do not really want undone.)\n\n" +
"Changes to Rover parameters or the 3D viewing transformation are\n" +
"optionally undoable; and there are checkboxes on the Toggles Menu\n" +
"which you can use to control this. By default, view changes are\n" +
"undoable and rover changes are not.\n\n" +
"If you do Undo or Redo commands at a rate faster than the animation \n" +
"can execute them, the pending commands are queued and performed in \n" +
"FIFO order.");
String paramHelpS = new String (
"The Parameters Menu consists of a set of radio buttons.\n" +
"Which one you select determines which parameter is displayed and is \n" +
"modifiable via the slider and numeric display field at the bottom.\n\n" +
"The four parameters in the first group control the viewing trans-\n" +
"formation for the 2D display. Except for the viewing distance, \n" +
"these parameters can also be changed by dragging with the mouse.\n" +
"Similarly the third group for roving eye state can also be modified\n" +
"through graphical interaction. In cases where a parameter is being\n" +
"modified by means other than the numeric input field or the slider,\n" +
"you can continuously monitor the effect of the interactive modifi-\n" +
"cation on the displayed numeric value of the selected parameter.\n\n" +
"There is a help item, Appearance Parameters, which offers some \n" +
"explanations for the second to last group of parameters.\n\n" +
"Use of the Scale Limit is a bit tricky and that also has its own\n" +
"help item.");
String appearHelpS = new String (
"The second to last group of parameters on the Parameters Menu affect the way the\n" +
"program behaves while drawing stickers. Considerations are mostly aesthetic.\n\n" +
"Sticker-Edge Line-Width controls how heavy a black line the program draws for\n" +
"the boundary of a sticker. These lines actually obscure the polygons for the\n" +
"stickers themselves somewhat. This effect is most obvious when a face of the\n" +
"puzzle is seen nearly edge-on. Drawing the edges is not particularly important\n" +
"when sticker size is any significant amount less than 1. Some may consider it\n" +
"to matter when the drawing of an out-facing sticker overlaps that of an\n" +
"in-facing one, as it displays the edge explicitly. However, lack of the edge is\n" +
"really only a problem if the stickers are the same color, and, even then, a\n" +
"fairly minor one. A setting of this parameter which is less than 0.4 turns the\n" +
"edge lines off completely. (They look ugly if the program permits any smaller\n" +
"value.) Configuration #5 shows an example of using a very heavy line for\n" +
"sticker edges.\n\n" +
"The separator width for stickers in the 1D projections is similar; but more \n" +
"important. They are turned off for values below 0.1. (They still look \n" +
"better with it off, but this does create ambiguities for same-colored stickers.)\n\n" +
"Brightness of In-Faces can be used to reduce the apparent brightness of faces\n" +
"which are seen from the inside. An example of a situation in which this is\n" +
"useful occurs when sticker edges are not being drawn at all, as it will still\n" +
"provide some contrast for an out-facing sticker which overlaps an in-facing one\n" +
"of the same color. Configuration #7 is an example in which the edges are not\n" +
"being drawn and the in-facing stickers are dimmed somewhat.\n\n" +
"Out-Face Alpha controls transparency of outward facing stickers. This is\n" +
"illustrated in Configuration #6. This feature turns out to be not very useful,\n" +
"but it makes some interesting looking pictures. (Try scrambling and tumbling.)");
String scaleHelpS = new String (
"After projecting the 3D model of the puzzle onto the projection plane, the\n" +
"coordinates still need to be scaled for graphic presentation. The program\n" +
"maintains a scale value by which it multiplies coordinate values in MCS units to\n" +
"obtain coordinates in pixels. (Roughly speaking, the length of the edge of a\n" +
"cubie is 1 in MCS units, but this can be distorted by the perspective\n" +
"transformation.) The scale value used by the program will never exceed the\n" +
"Scale Limit parameter you specify.\n\n" +
"The program always computes a maximum scale, max-scale, which, depending on\n" +
"current window size and center positions assures no sticker will plot outside\n" +
"the provided drawing area. The auto-Scaling feature refers to automatic update\n" +
"of the Scale Limit. When it is enabled and you change the window size or modify\n" +
"a parameter that can affect max-Scale, the Scale Limit parameter is updated to\n" +
"the max-scale value. The idea is that the automatic setting makes the picture\n" +
"as big as possible. Resetting of the Scale Limit will also occur when you\n" +
"toggle the auto-Scale option to an enabled state. As long as your Scale Limit\n" +
"is less than the program's max-scale, it IS the scale.\n\n" +
"If you wish to observe the effect of changing things like the face and sticker\n" +
"sizes or the 3D viewing distance, it is preferable to think of that in terms of\n" +
"model coordinates which are not being rescaled dynamically as you change the\n" +
"parameters. To assure this, you can reduce the Scale Limit parameter or disable\n" +
"auto-Scaling and make the window bigger.");
String dragHelpS = new String (
"There are five reasons for dragging: to MOVE A CENTER, to CONTROL\n" +
"THE ANIMATION, to ROTATE the 2D image, to TUMBLE the 3D effect,\n" +
"and to make adjustments to ROVER parameters. (\"Tumbling\" refers \n" +
"to moving the eyepoint around on the sphere whose radius is \n" +
"the 3D viewing distance so you observe cube from arbitrary points of\n" +
"view.) There are separate help items to tell you how to get into \n" +
"and use each corresponding state.\n\n" +
"Once you have initiated a drag by any means, the number of and which\n" +
"buttons you are holding down ceases to make any difference.\n" +
"The drag ends when the last button comes up.\n\n" +
"To enable single-button dragging for rotating and tumbling,\n" +
"reduce the Drag Threshold parameter to a small number like 4.0." );
String moveHelpS = new String (
"With Control and Alt both depressed, press a mouse button down with the\n"+
"cursor over a sticker associated with the center you wish to move.\n\n"+
"You can release the modifier keys once the drag is started." );
String animateHelpS = new String (
"Depress a mouse button while timer-based animation is in progress and\n" +
"start moving the mouse immediately. While the mouse button remains\n" +
"down, the angle of the twist will depend on mouse cursor position. If\n" +
"there is only one face which is out-facing and you do a twist about\n" +
"that face's axis, the angle for the animation depends on angular\n" +
"position of the mouse cursor relative to the center. Otherwise and\n" +
"for other twist axes, the angle depends on cursor motion perpendicular\n" +
"to the twist axis.\n\n" +
"You can also depress a mouse button in the rover 1D projection area,\n" +
"in which case only horizontal motion of the mouse affects the current\n" +
"twist angle. (Note that you can also initiate a twist by clicking\n" +
"with button 1 in the rover 1D projection area. This is intended\n" +
"primarily for taking over control of the resulting twist animation\n" +
"with the mouse.)\n\n" +
"It is possible to enter twist commands faster than than they can be\n" +
"animated, in which case there can be pending twists in addition to\n" +
"that of the animation in progress. In such case, you cannot take over\n" +
"the animation with the mouse - at least not until the last pending\n" +
"twist starts. \n\n" +
"If you try to command a new twist by clicking with the mouse while a\n" +
"twist animation is in progress, it is important that the click be\n" +
"perceived as a click and not as drag, or else you will be taking over\n" +
"the animation. Once there is a pending twist besides the one in\n" +
"progress, this ceases to be a problem, since taking over the animation\n" +
"is no longer permitted.");
String tumbleHelpS = new String (
"Drag radially from near one of the two virtual puzzle centers with two\n" +
"mouse buttons depressed.\n\n" +
"Tumbling is achieved by moving the eyepoint around on the sphere\n" +
"centered on the origin with a radius equal to the 3D viewing distance.\n" +
"It appears that the cube is rotating around an axis which lies in the\n" +
"projection plane. That axis is perpendicular to the line segment from\n" +
"where your drag started to the current mouse position. The amount of \n" +
"of turn is proportional to the length of that line.\n\n" +
"To view the orientation of the axis for the tumble, you can enable\n" +
"display of a representation of it via the View Menu. The program \n" +
"always draws a little \"+\" where the drag started. It does not \n" +
"correspond to the cube center which is indicated on the axis line.");
String rotateHelpS = new String (
"Drag circumferentially from periphery with two mouse buttons depressed.\n" +
"Rotation angle depends on the angular position of cursor relative to the\n" +
"center.\n" );
String dragRovHelpS = new String (
"The parameters for the roving eye can be adjusted by dragging in the rover 1D\n" +
"projection area at the bottom of the display. You can use a single button (any)\n" +
"and indicate the type of drag with modifier keys, or you can use a two-button\n" +
"method to indicate the drag type without using modifier keys. Initiating the\n" +
"drag with the two-button method requires first pressing two buttons and then\n" +
"releasing one. Furthermore, which button you press first matters:\n\n" +
"Sideways Slide: 1 click 2 or Control-drag\n" +
" If button 1 remains down and you started with button 1, the drag is for\n" +
" sliding the eyepoint sideways relative to the rover heading.\n\n" +
"Forwards/Backwards: Roll 2 to 1 or Control-Shift-drag\n" +
" If button 1 remains down and you started with button 2, the drag is for\n" +
" forwards and backwards movement along the direction of the rover heading.\n\n" +
"Heading Adjustment: 2 click 1 or Alt-drag\n" +
" If button 2 remains down and you started with button 2, the drag is for\n" +
" adjusting the rover heading.\n\n" +
"View Angle Adjustment: Roll 1 to 2 or Alt-Shift-drag\n" +
" If button 2 remains down and you started with button 1, the drag is for\n" +
" adjusting the view angle.\n\n" +
"Only horizontal movement of the mouse matters in the rover's 1D projection area.\n" +
"The intent for the directional sense in the sideways slide and heading\n" +
"adjustments is that it 'feel like' you are dragging the position of the image on\n" +
"the screen, as with tumbling the 2D image. But the calibration of sensitivity\n" +
"for this movement for heading adjustment varies with the distance from what you\n" +
"are looking at and for eye movement with the view angle. For the other two,\n" +
"dragging to the right will enlarge the image, while dragging to the left will\n" +
"shrink it.");
String roveGenHelpS = new String (
"The roving eye allows the perspective view generated in the Rover 1D projection area\n" +
"to be taken from any point of view in the 2D drawing area. The roving eye has four\n" +
"parameters which determine its state. Two determine its plot position. In the GUI,\n" +
"they are fractional positions relative to the width and height of the window. The\n" +
"other two determine the rover's Heading and its View Angle. Adjusting these second\n" +
"two parameters is akin to adjusting the pan and zoom for the view. \"Heading\" makes\n" +
"more sense in this context than \"pan\", because it also determines what direction is\n" +
"\"forwards\" when moving the rover by interactive means. For the zoom, the most\n" +
"convenient units for specifying it would seem to be the view angle, in which case\n" +
"\"View Angle\" seems a more appropriate term for the parameter. What is drawn to\n" +
"indicate the rover's position is just the boundary of its field of view as determined\n" +
"by heading and view angle.\n\n" +
"Adjustment of rover parameters via the slider or numeric field is not so convenient\n" +
"(though the effect of interactive adjustment can be monitored with them). The\n" +
"program provides a number of distinct alternative ways in which to modify them. For\n" +
"moving the rover, the modes of motion are a sideways slide (left and right) or\n" +
"forwards and backwards motion along its heading direction.\n\n" +
"One method for adjusting heading and position is called \"Drive Mode\". It is akin to\n" +
"driving a vehicle. See the separate help item for it.\n\n" +
"The remaining rover adjustment methods involve dragging in the Rover 1D projection\n" +
"area or use of arrow keys (either group) with the Control and Alt modifiers. When\n" +
"you use the arrow keys, continuous change of a parameter is initiated and proceeds on\n" +
"a timed basis. Similar adjustments can also be made by dragging with the mouse. In\n" +
"general, the Control modifier is associated with position-moving operations, while\n" +
"the Alt modifier is associated with heading and view angle changes. If you think of\n" +
"the Control and Alt keys as being oriented left and right, there is also a related\n" +
"left/right association that can be made with the two-button methods for initiating\n" +
"the drag based on which button remains down.");
String roveArrowHelpS = new String (
"To move the roving eye use arrow keys with Control modifier.\n" +
"To change heading or view angle use arrow keys with Alt modifier.\n" +
"Speed triples with each repeat of corresponding arrow key. It stops \n" +
"when you hit any other arrow key, after which you can restart.\n\n" +
"It is possible to get the view field indicator stuck in a corner\n" +
"so that you can no longer see where it is. If this happens to you,\n" +
"you can use Alt-left several times to get the heading swinging \n" +
"around rapidly so the view field indicator will become visible again.\n\n" +
"Adjusting the rover by dragging the mouse is compatible with \n" +
"simultaneous adjustment by the arrow keys. Some interesting\n" +
"possibilities exist.");
String driveHelpS = new String (
"In addition to dragging with the mouse, there is another method for controlling\n" +
"the position and heading of the rover with the mouse. This method is called\n" +
"\"Drive Mode\", as it is more nearly analogous to driving a vehicle.\n\n" +
"Mouse button 1 is the brake. Button 2 or button 1 with the Shift modifier\n" +
"depressed is the accelerator. To start, click the accelerator when the mouse\n" +
"cursor is in the rover 1D projection area. The speed of the rover doubles each\n" +
"time you click the accelerator.\n\n" +
"Steering is controlled by the position of the mouse cursor relative to the\n" +
"horizontal center of the display. (There is a little mark there on the\n" +
"horizontal line which marks the top of the rover 1D projection area.) Think of\n" +
"the cursor as being attached to the top of a steering wheel (or a flight control\n" +
"stick).\n\n" +
"Drive Mode is enabled by default, but there is a check box on the Toggles Menu\n" +
"which can be used to disable the feature. It can be nuisance if you try to use\n" +
"a button 2 click in the rover projection area to start a twist. (If you tend to\n" +
"make this mistake, you might want to select the check box which makes rover\n" +
"changes undoable rather than disable the Drive Mode feature.)\n\n" +
"The rover will stop moving if hits a boundary of the 2D area. It will also stop\n" +
"if you move the mouse cursor out of the rover projection area.\n\n" +
"There is no reverse direction mode for driving the rover. However, if you do\n" +
"get it stuck up against a boundary, you can still use one of the\n" +
"forwards/backwards adjustment methods to back off. You can also use heading\n" +
"adjustment to get the rover pointed back inwards\n\n" +
"While driving the rover, you can still use keyboard commands to change the Rover\n" +
"View Angle. (Actually, you can do almost anything with the keyboard while the\n" +
"rover is moving - e.g., scramble, twist.)");
String saveRecallHelpS = new String (
"The current state of the program can be saved using the \"Save State\" command.\n" +
"A saved state includes the settings of all the parameters, the states of the\n" +
"checkboxes on the View and Toggles menus, the colors, the size of the frame, the\n" +
"positions of the centers, and the current permutation of the stickers.\n\n" +
"Saved states are represented by disk files with extension .mc3. By default they\n" +
"are expected in and placed in the directory in which the executable for the\n" +
"program resides. Any such saved state can be recovered using the Recall State\n" +
"command. When specifying a file name, you do not need to specify the .mc3\n" +
"extension. If your file name lacks such an extension, one will be appended.\n\n" +
"If the directory from which the program executes contains a saved state file\n" +
"with name \"default.mc3\", then that state will be set when the program first\n" +
"starts. After you have found a configuration for the program which you like,\n" +
"you will probably want to save that as your default. For your default, you\n" +
"probably do not want to save state when the cube is in a scrambled state. You\n" +
"can also specify a file argument on the command line which will supercede\n" +
"\"default.mc3\" as the state-file for the initial settings. (\".mc3\" still\n" +
"optional on command line argument, but the filename itself must end in\n" +
"\".mc3\".)");
String saveFileHelpS = new String (
"The .mc3 state-saving files are plain text. Each line in a save file is\n" +
"identified by a token starting in the first column. You can omit lines from a\n" +
"save file if you do not want the corresponding aspects of state to be recalled.\n" +
"Except for the colors (See below.), it is not recommended that you edit the\n" +
"values in a save file, as the program does not take any significant measures to\n" +
"protect itself from bad data.\n\n" +
"During a recall, the program ignores lines for which it does not recognize the\n" +
"token starting in the first character position. It also ignores lines which\n" +
"make no sense for other reasons - like not having enough parameters. The order\n" +
"of the lines in a save file does not matter.\n\n" +
"The colors are written in the last 8 lines of the file in a simple format\n" +
"specfying fractional RGB components. On these lines, the identifying token is a\n" +
"single character which determines which colors are being specified. If it is a\n" +
"BFUDLR character, the first three decimal numbers are for the corresponding\n" +
"sticker color; and the second group of three numbers are for the color used for\n" +
"a character (most often the one identifying the line) which is drawn as a label\n" +
"on top of a sticker with the associated sticker color. If the first character\n" +
"is a 'G' the two colors are the background and foreground colors respectively.\n" +
"If the first character is a 'E' the colors are for 2D sticker edges and 1D \n" +
"separators. Knowing this, you can also specify new colors by editing a save \n" +
"file if this is more convenient. (Some folks may already have their favorite \n" +
"set of colors in RGB format.)");
String colorsHelpS = new String (
"The default colors for the program follow the most obvious\n" +
"conceivable pattern. R, G, and B are matched up with the positive\n" +
"x, y, and z directions, while C, M, and Y are matched with the\n" +
"negative directions. The RGB and CMY triples of colors should be\n" +
"familiar to most folks. (In case \"CMY\" is not, those letters\n" +
"stand for cyan, magenta, and yellow - the colors complementary to\n" +
"red, green, and blue, respectively. The CMY colors are appropriate\n" +
"for mixing pigments, while the RGB colors are best suited to the\n" +
"mixing of light.)\n\n" +
"Logical though they may be, some may regard the default colors as\n" +
"unfamiliar or aesthetically unpleasing. (There is a slight problem\n" +
"in that the human eye does not distinguish blue and cyan all that\n" +
"well, cyan appearing somewhat like an unsaturated blue. However, as\n" +
"can be seen by examining a save file, the default colors have been\n" +
"tweaked slightly to improve discrimination.) The program allows you\n" +
"to specify any colors you like using the Adjust Color command. This\n" +
"is hardly worth the trouble with the Applet version of the program.\n" +
"However, the colors are saved with a saved state in the application\n" +
"version; so, if you are running the application, you can save your\n" +
"color modifying efforts.\n\n" +
"If you change sticker colors, the defaults for face labels may\n" +
"become inappropriate, so you can also adjust the label colors. You\n" +
"may also change the background and foreground colors and the colors\n" +
"used to draw sticker edges/separators in the 2D/1D areas.");
String labelsHelpS = new String (
"In the 2D display, face labels are the characters which (by default) are shown on\n" +
"each face sticker. They are intended to identify the faces to which those face\n" +
"stickers belong. There is a checkbox on the View Menu which can be used to turn\n" +
"these indicators off. By default the labels are upper case and specify the name of\n" +
"the color of the face sticker on which they are plotted. This also happens to be\n" +
"the name of the associated face and the associated slice. Thus this method\n" +
"corresponds to the Face Coordinate System.\n\n" +
"There is a checkbox on the Toggles Menu which can be used to switch the labels so\n" +
"that they indicate MCS directions, in which case they appear as lower case since\n" +
"they specify direction vectors as opposed to names of things. In the MCS case, the\n" +
"indicated direction is the MCS direction in which the face currently faces. Note\n" +
"that, if such a direction indicator is on a face sticker in a center slice which is\n" +
"turning during animation, the labels are not displayed, as the faces are not facing\n" +
"along any particular axis until the animation ends. There is no equivalent problem\n" +
"for the FCS labels as the FCS turns with a turning center slice.\n\n" +
"There is no particular significance to the color of a label in the 2D display or in\n" +
"the Colored Tiles display of the Layered-View Window. They are written in the label\n" +
"color associated with the color of the stickers on which they appear. By default,\n" +
"that is the complementary color for contrast's sake. It is not intended that\n" +
"significance be attached to the color of the label in these cases.\n\n" +
"When face labels are enabled, labels also appear in the 1D projection areas. These\n" +
"are always FCS labels (face names) and they are displayed in the colors associated\n" +
"with the faces they identify. The Colored Tiles display in the Layered-View Window\n" +
"always uses FCS labels, as the MCS orientation of that display never changes and is\n" +
"easy to remember.");
String layeredHelpS = new String (
"If you select the Layered-View Window checkbox on the View Menu, the\n" +
"program will produce a different kind of representation of the state\n" +
"of the cube. It may be thought of as layered or flat. The\n" +
"presentation in the main window is focussed on stickers. That in the\n" +
"layered presentation is focussed on cubies. There are two different\n" +
"presentations. The one on the left is referred to as the \"Colored\n" +
"Tiles\" presentation, while that on the right is referred to as the\n" +
"\"Text Display\". They both present the same information. Each little\n" +
"square corresponds to a cubie. Each 3x3 'board' corresponds to the\n" +
"cubies in one of the 3 slices perpendicular to the z-axis. The\n" +
"U-slice is at the top.\n\n" +
"You can do twists by clicking in the layered view window. Clicks are\n" +
"resolved to the nearest face cubie. The turning in the layered window\n" +
"is always in terms of the 3D turning direction. I.e., defined as if\n" +
"you were looking face-on at the turned face from the outside of the\n" +
"cube. This is most surprising for the D face.\n\n" +
"All keyboard commands (pure ones, as opposed to those that work via\n" +
"the menus) are valid in the Layered-View Window. So, BFUDLR\n" +
"characters command twists, and the control keys work for the commands\n" +
"on the Commands Menu.");
String tiledHelpS = new String (
"In the Colored Tiles presentation, the colored areas of each square\n" +
"are arranged to indicate in which directions the corresponding cubie's\n" +
"stickers are facing. For a corner cubie, the color for a sticker\n" +
"which faces up or down is shown in the small square near the tile for\n" +
"the face cubie in the slice. For up or down facing stickers on edge\n" +
"cubies in the U or D slice, the color is shown on the inside half of\n" +
"the tile. For all other stickers, the corresponding color will extend\n" +
"to the edge of the tile which faces in the corresponding direction.\n" +
"No 3D interpretation of a tile's surface is intended.\n\n" +
"Compare to Configuration #2. The three rings of laterally facing (not\n" +
"up or down) stickers surrounding the D face match up with the outsides\n" +
"of the 3 3x3 arrays of cubie squares. Try clicking on the U and D\n" +
"faces, with and without the Control modifier.");
String textHelpS = new String (
"In the Text Display, each cubie has a name in upper case,\n" +
"corresponding to the names of the colors of its stickers. The\n" +
"relative position in which the sticker color name is displayed\n" +
"corresponds to the axis (xyz order) which is perpendicular to sticker\n" +
"after initialization. Blank columns correspond to axes for which the\n" +
"coordinate value of the cubie's location is zero - i.e., no sticker\n" +
"for that axis.\n\n" +
"Under each sticker name is a lower case letter which indicates in\n" +
"which direction the corresponding sticker currently faces. These\n" +
"lower case characters are called \"Direction Indicators\". By default\n" +
"they use the Face Coordinate System; so reorienting the whole cube\n" +
"does not change them. However, if you do reorient the cube, you have\n" +
"to interpret them relative to the current orientation of the faces.\n" +
"There is a toggle which will switch the Direction Indicators to use\n" +
"the MCS. As long as you leave the cube in its default orientation,\n" +
"there is no difference.\n\n" +
"In the text display, all the information is in the text. The color\n" +
"coding of the character backgrounds is just a bonus. However, what is\n" +
"relevant, color-wise, is the color of the background, not the color of\n" +
"the label which appears there. Think of the background for the\n" +
"character as the sticker. (With the default color scheme, the color\n" +
"of a label for a sticker is the color of the opposite face. This is\n" +
"just to achieve good contrast. Beware of attaching any directional\n" +
"significance to the color of the label itself.");
String centersHelpS = new String (
"The program draws nothing but stickers. The side of a sticker which\n" +
"is visible from the eyepoint may be either that which faces outwards\n" +
"from the center of the puzzle or that which faces inwards. Stickers\n" +
"are called in- or out-facing depending on which side you are looking\n" +
"at. Because the out-facing stickers can obscure your view of\n" +
"in-facing ones, the program offers the option of separating the two\n" +
"groups of stickers. This is done by maintaining two virtual cube\n" +
"centers, the in-center and the out-center, relative to which are \n" +
"plotted the corresponding stickers.\n\n" +
"The auto-Position toggle controls whether or not the program will\n" +
"automatically decide where to put the centers when you change the\n" +
"window size or enable auto-Position. If the window size is such that\n" +
"the 2D drawing area is roughly square, the centers will be made to\n" +
"coincide, in which case you can get some fairly normal looking\n" +
"cube simulators. If the area is oblong, the program will separate\n" +
"the centers so that no out-facing stickers plot atop in-facing ones.\n" +
"The displacement can be vertical or horizontal depending on the \n" +
"aspect ratio of the window.");