50f44c07064fdb2db7c4b25e126f7667eb5c0851
[anneal.git] / src / edu / berkeley / qfat / Main.java
1 package edu.berkeley.qfat;
2 import java.awt.*;
3 import java.awt.event.*;
4 import javax.swing.*;
5 import javax.media.opengl.*;
6 import javax.media.opengl.glu.*;
7 import java.util.*;
8 import edu.berkeley.qfat.geom.*;
9 import edu.berkeley.qfat.geom.Point;
10 import edu.berkeley.qfat.geom.Polygon;
11
12 // TO DO:
13 // - real anneal
14 // - solve self-intersection problem
15 // - get a better test model?
16 // - symmetry constraints withing the tile
17 // - rotation matrices
18 // - overbinding results in forced equational constraints on the leader
19 // - shatter in invertd-triforce pattern brian mentioned
20 // - aspect ratio?  non-uniform deformation?
21 // - rotational alignment
22
23 // - movie-style user interface like
24 //      http://www.coleran.com/markcoleranreell.html ?
25
26 // - consider recasting the Shewchuk predicates in Java?
27 //    http://www.cs.cmu.edu/afs/cs/project/quake/public/code/predicates.c
28
29 /*
30   blender keys
31   - middle mouse = option+click
32   - right mouse = command+click
33
34   3,7,1 = view along axes (control for opp direction)
35   4, 8, 7, 2 = rotate in discrete increments (+control to translate)
36   middle trag: rotate space
37   shift+middle drag: translate space
38   wheel: zoom
39   home: home view: take current angle, zoom to whole scnee
40   5 = ortho vs non-ortho
41
42 */
43
44 /*
45
46
47  */
48
49
50 // FIXME: re-orient goal (how?)
51
52 public class Main extends MeshViewer {
53
54     public static int verts = 1;
55
56     public static final Random random = new Random();
57     
58     /** magnification factor */
59     private static final float MAG = 1;
60     public static final float MATCHING_EPSILON = 0.001f;
61
62     private static boolean small(float f) { return Math.abs(f) < 0.001; }
63     public void generateTile(Matrix[] matrices, Mesh mesh) {
64         mesh.coalesce = true;
65         HashSet<HalfSpace> halfSpaces = new HashSet<HalfSpace>();
66         HashSet<Polygon> polygons = new HashSet<Polygon>();
67         for(Matrix m : matrices) {
68             Vec v = m.getTranslationalComponent();
69             if (v.mag() < 0.0001) continue;
70             v = v.times(0.5f);
71             Point p = Point.ORIGIN.plus(v);
72             Vec v0 = v;
73             if (v.dot(Point.ORIGIN.minus(p)) < 0) v = v.times(-1);
74
75             System.out.println(v);
76             HalfSpace hs = new HalfSpace(p, v.norm());
77             halfSpaces.add(hs);
78             polygons.add(new Polygon(hs));
79         }
80         for(Polygon p : polygons) {
81             System.out.println(p.plane.norm + " " + p.plane.dvalue);
82             for(HalfSpace hs : halfSpaces) {
83                 if (p.plane==hs) continue;
84                 p = p.intersect(hs);
85             }
86             p.tesselate(mesh);
87         }
88     }
89
90     private void quad(Mesh mesh, Matrix m, Point p1_, Point p2_, Point p3_, Point p4_) {
91         Point p1 = m.times(p1_);
92         Point p2 = m.times(p2_);
93         Point p3 = m.times(p3_);
94         Point p4 = m.times(p4_);
95         Point c  = new Point((p1.x+p2.x+p3.x+p4.x)/4,
96                              (p1.y+p2.y+p3.y+p4.y)/4,
97                              (p1.z+p2.z+p3.z+p4.z)/4);
98         mesh.newT(p1, p2, c, null, 0);
99         mesh.newT(p2, p3, c, null, 0);
100         mesh.newT(p3, p4, c, null, 0);
101         mesh.newT(p4, p1, c, null, 0);
102     }
103
104     public void loadGoal(String file) {
105         try {
106             StlFile stlf = new StlFile();
107             stlf.load(file);
108             goal = new Mesh(false);
109             for(int i=0; i<stlf.coordArray.length; i+=3) {
110                 Point p0 = new Point(stlf.coordArray[i+0].x * MAG, stlf.coordArray[i+0].y * MAG, stlf.coordArray[i+0].z * MAG);
111                 Point p1 = new Point(stlf.coordArray[i+1].x * MAG, stlf.coordArray[i+1].y * MAG, stlf.coordArray[i+1].z * MAG);
112                 Point p2 = new Point(stlf.coordArray[i+2].x * MAG, stlf.coordArray[i+2].y * MAG, stlf.coordArray[i+2].z * MAG);
113                 Vec n  = new Vec(stlf.normArray[i/3].x * MAG, stlf.normArray[i/3].y  * MAG, stlf.normArray[i/3].z * MAG);
114                 Mesh.T t  = goal.newT(p0, p1, p2, n, 0);
115             }
116             float goal_width  = goal.diagonal().dot(new Vec(1, 0, 0));
117             float goal_height = goal.diagonal().dot(new Vec(0, 1, 0));
118             float goal_depth  = goal.diagonal().dot(new Vec(0, 0, 1));
119             
120         } catch (Exception e) { throw new RuntimeException(e);}
121     }
122     public void fixupGoal() {
123         // translate to match centroid
124         goal.transform(Matrix.translate(tile.centroid().minus(goal.centroid())));
125         goal.makeVerticesImmutable();
126         tile.error_against = goal;
127         goal.error_against = tile;
128     }
129
130     public Main(JFrame f) { super(f); }
131
132     public void fixupTile() { 
133         synchronized(safeTriangles) {
134         for(Matrix m1 : transforms) {
135             for(Matrix m2 : transforms) {
136                 if (m1==m2) continue;
137                 for(Mesh.T t1 : tile) {
138                     for(Mesh.T t2 : tile) {
139
140                         Matrix m = m1.inverse().times(m2);
141                         if ((t1.v1().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
142                             (t1.v2().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON) &&
143                             (t1.v3().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON)) {
144                             t2.e3().bindEdge(t1.e1(), m);
145                             t2.e2().bindEdge(t1.e2(), m);
146                             t2.e1().bindEdge(t1.e3(), m);
147                         }
148                         if ((t1.v2().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
149                             (t1.v3().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON) &&
150                             (t1.v1().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON)) {
151                             t2.e3().bindEdge(t1.e2(), m);
152                             t2.e2().bindEdge(t1.e3(), m);
153                             t2.e1().bindEdge(t1.e1(), m);
154                         }
155                         if ((t1.v3().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
156                             (t1.v1().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON) &&
157                             (t1.v2().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON)) {
158                             t2.e3().bindEdge(t1.e3(), m);
159                             t2.e2().bindEdge(t1.e1(), m);
160                             t2.e1().bindEdge(t1.e2(), m);
161                         }
162
163                         if ((t1.v1().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
164                             (t1.v2().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON) &&
165                             (t1.v3().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON)) {
166                             t2.e1().bindEdge(t1.e1().pair, m);
167                             t2.e2().bindEdge(t1.e2().pair, m);
168                             t2.e3().bindEdge(t1.e3().pair, m);
169                         }
170                         if ((t1.v2().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
171                             (t1.v3().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON) &&
172                             (t1.v1().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON)) {
173                             t2.e2().bindEdge(t1.e1().pair, m);
174                             t2.e3().bindEdge(t1.e2().pair, m);
175                             t2.e1().bindEdge(t1.e3().pair, m);
176                         }
177                         if ((t1.v3().p.times(m).distance(t2.v1().p) < MATCHING_EPSILON) &&
178                             (t1.v1().p.times(m).distance(t2.v2().p) < MATCHING_EPSILON) &&
179                             (t1.v2().p.times(m).distance(t2.v3().p) < MATCHING_EPSILON)) {
180                             t2.e3().bindEdge(t1.e1().pair, m);
181                             t2.e1().bindEdge(t1.e2().pair, m);
182                             t2.e2().bindEdge(t1.e3().pair, m);
183                         }
184
185                     }
186                 }
187             }
188         }
189         tile.rebindPoints();
190         System.out.println("tile volume: " + tile.volume());
191         System.out.println("goal volume: " + goal.volume());
192         tile.error_against = goal;
193         goal.error_against = tile;
194         fixupGoal();
195         }
196     }
197
198     public void breakit() {
199         int oldverts = verts;
200         System.out.println("doubling vertices.");
201         PriorityQueue<Mesh.E> es = new PriorityQueue<Mesh.E>();
202         for(Mesh.T t : tile) {
203             es.add(t.e1());
204             es.add(t.e2());
205             es.add(t.e3());
206             Thread.yield();
207             repaint();
208         }
209         for(int i=0; i<Math.min(oldverts,50); i++) {
210             Mesh.E e = es.poll();
211             verts++;
212             e.shatter();
213             Thread.yield();
214             repaint();
215         }
216         tile.rebindPoints();
217     }
218
219     public boolean rand(double temp, Mesh.Vertex p) {
220
221         p.reComputeErrorAround();
222         double tile_error = tile.error();
223         double goal_error = goal.error();
224
225         float max = p.averageEdgeLength()/5;
226         Vec v = new Vec(random.nextFloat(), random.nextFloat(), random.nextFloat());
227         v = v.norm().times((random.nextFloat() - 0.5f) * max);
228         Matrix m = Matrix.translate(v);
229
230         boolean good = p.move(m, false);
231         if (!good) { return false; }
232
233         double new_tile_error = tile.error();
234         double new_goal_error = goal.error();
235         double tile_delta = (new_tile_error - tile_error) / tile_error;
236         double goal_delta = (new_goal_error - goal_error) / goal_error;
237         double delta = tile_delta + goal_delta;
238         double swapProbability = Math.exp((-1 * delta) / temp);
239
240         //boolean doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
241         //boolean doSwap = good && (tile_delta + goal_delta <= 0);
242         //if (temp < 0.000001) doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
243         boolean doSwap = good && (Math.random() < swapProbability);
244         if (doSwap) {
245             tile_error = new_tile_error;
246             goal_error = new_goal_error;
247             hits++;
248             p.goodp = p.p;
249         } else {
250             p.move(Matrix.translate(v.times(-1)), true);
251             misses++;
252         }
253         p.reComputeErrorAround();
254         return true;
255     }
256
257     float hits = 0;
258     float misses = 0;
259     public void anneal() throws Exception {
260         double hightemp = 1;
261         temp = hightemp;
262         double last = 10;
263         boolean seek_upward = false;
264         double acceptance = 1;
265         while(true) {
266             synchronized(safeTriangles) {
267                 safeTriangles.clear();
268                 for(Mesh.T t : tile) 
269                     if (t.shouldBeDrawn())
270                         safeTriangles.add(t);
271             }
272             if (!anneal) { repaint(); Thread.sleep(10); continue; }
273
274             double ratio = (hits+misses==0) ? 1 : (hits / (hits+misses));
275             hits = 0;
276             misses = 0;
277             double gamma = 1;
278             acceptance = (ratio+acceptance)/2;
279             accepts = (int)(Math.ceil(ratio*100));
280             temps = (int)(Math.ceil(temp*1000));
281             vertss = tile.size();
282             if (breaks > 0) {
283                 while (breaks>0) {
284                     breaks--;
285                     breakit();
286                 }
287                 seek_upward = true;
288                 continue;
289             } else if (acceptance > 0.96) gamma = 0.1f;
290             else if (acceptance > 0.9)    gamma = 0.2f;
291             else if (acceptance > 0.8)    gamma = 0.3f;
292             else if (acceptance > 0.6)    gamma = 0.4f;
293             else if (acceptance > 0.3)    gamma = 0.8f;
294             else if (acceptance > 0.15)   gamma = 0.94f;
295             else if (acceptance > 0.10)   gamma = 0.98f;
296             else { breaks++; }
297
298             if (seek_upward) {
299                 if (acceptance > 0.25) seek_upward = false;
300                 else gamma = 2-gamma;
301             }
302
303             if (anneal)
304                 temp = temp * gamma;
305
306
307             HashSet<Mesh.Vertex> hs = new HashSet<Mesh.Vertex>();
308             for(Mesh.Vertex p : tile.vertices()) hs.add(p);
309             Mesh.Vertex[] pts = (Mesh.Vertex[])hs.toArray(new Mesh.Vertex[0]);
310
311             int count = 0;
312             long then = System.currentTimeMillis();
313             for(int i=0; i<300; i++) {
314                 if (anneal) {
315                     Mesh.Vertex v = pts[Math.abs(random.nextInt()) % pts.length];
316                     if (breaks>0) break;
317                     if (!rand(temp,v)) { i--; continue; }
318                     v.recomputeFundamentalQuadricIfStale();
319                     v.recomputeFundamentalQuadricIfNeighborChanged();
320                     count++;
321                 }
322                 Thread.yield();
323                 repaint();
324             }
325
326             System.out.println("temp="+temp + " ratio="+(Math.ceil(acceptance*100)) + " " +
327                                "points_per_second=" +
328                                (count*1000)/((double)(System.currentTimeMillis()-then)));
329             for(Mesh.Vertex p : goal.vertices()) {
330                 p.quadricStale = true;
331                 p.recomputeFundamentalQuadricIfNeighborChanged();
332             }
333         }
334     }
335
336
337     public static void main(String[] s) throws Exception {
338         JFrame f = new JFrame();
339         Main main = new Main(f);
340         f.setJMenuBar(main.new MyMenuBar());
341         f.pack();
342         f.show();
343         f.setSize(900, 900);
344         f.doLayout();
345         main.anneal();
346     }
347
348     public class MyMenuItem extends JMenuItem implements ActionListener {
349         public MyMenuItem(String s) {
350             super(s);
351             this.addActionListener(this);
352         }
353         public void actionPerformed(ActionEvent event) {
354             synchronized(Main.this) {
355                 synchronized(safeTriangles) {
356                     hit();
357                 }
358             }
359         }
360         public void hit() {}
361     }
362
363     public void hexBrick(boolean offset, boolean rotated) {
364                 tile = new Mesh(false);
365                 float width  = (float)0.8;
366                 float depth  = (float)0.08;
367                 float height = (float)0.4;
368                 float rshift =   width/2;
369                 float lshift = -(width/2);
370                 float halfup = 0;
371                 Point ltf = new Point(lshift,  (depth/2),  (height/2));
372                 Point mtf = new Point( 0.0,    (depth/2),  (height/2));
373                 Point rtf = new Point(rshift,  (depth/2),  (height/2));
374                 Point lbf = new Point(lshift, -(depth/2),  (height/2));
375                 Point mbf = new Point( 0.0,   -(depth/2),  (height/2));
376                 Point rbf = new Point(rshift, -(depth/2),  (height/2));
377
378                 Point ltc = new Point(lshift,  (depth/2), 0);
379                 Point mtc = new Point( 0.0,    (depth/2), 0);
380                 Point rtc = new Point(rshift,  (depth/2), 0);
381                 Point lbc = new Point(lshift, -(depth/2), 0);
382                 Point mbc = new Point( 0.0,   -(depth/2), 0);
383                 Point rbc = new Point(rshift, -(depth/2), 0);
384
385                 Point ltn = new Point(lshift,  (depth/2), -(height/2));
386                 Point mtn = new Point( 0.0,    (depth/2), -(height/2));
387                 Point rtn = new Point(rshift,  (depth/2), -(height/2));
388                 Point lbn = new Point(lshift, -(depth/2), -(height/2));
389                 Point mbn = new Point( 0.0,   -(depth/2), -(height/2));
390                 Point rbn = new Point(rshift, -(depth/2), -(height/2));
391
392         
393                 Point[] points = new Point[] {
394                     ltf,
395                     mtf,
396                     rtf,
397                     lbf,
398                     mbf,
399                     rbf,
400
401                     ltc,
402                     mtc,
403                     rtc,
404                     lbc,
405                     mbc,
406                     rbc,
407
408                     ltn,
409                     mtn,
410                     rtn,
411                     lbn,
412                     mbn,
413                     rbn
414                 };
415
416
417                 // top
418                 tile.newT(ltf, mtf, mtc, null, 1);
419                 tile.newT(mtc, ltc, ltf, null, 1);
420                 tile.newT(mtf, rtf, rtc, null, 1);
421                 tile.newT(rtc, mtc, mtf, null, 1);
422
423                 tile.newT(ltc, mtc, mtn, null, 1);
424                 tile.newT(mtn, ltn, ltc, null, 1);
425                 tile.newT(mtc, rtc, rtn, null, 1);
426                 tile.newT(rtn, mtn, mtc, null, 1);
427
428                 // bottom (swap normals)
429                 tile.newT(mbf, lbf, mbc, null, 2);
430                 tile.newT(lbc, mbc, lbf, null, 2);
431                 tile.newT(rbf, mbf, rbc, null, 2);
432                 tile.newT(mbc, rbc, mbf, null, 2);
433
434                 tile.newT(mbc, lbc, mbn, null, 2);
435                 tile.newT(lbn, mbn, lbc, null, 2);
436
437                 tile.newT(rbc, mbc, rbn, null, 2);
438                 tile.newT(mbn, rbn, mbc, null, 2);
439
440
441                 // left
442                 tile.newT(ltf, ltc, lbc, null, 3);
443                 tile.newT(lbc, lbf, ltf, null, 3);
444                 tile.newT(ltc, ltn, lbn, null, 3);
445                 tile.newT(lbn, lbc, ltc, null, 3);
446
447                 // right (swap normals)
448                 tile.newT(rtc, rtf, rbc, null, 4);
449                 tile.newT(rbf, rbc, rtf, null, 4);
450                 tile.newT(rtn, rtc, rbn, null, 4);
451                 tile.newT(rbc, rbn, rtc, null, 4);
452
453                 // front
454                 tile.newT(ltn, mtn, mbn, null, 5);
455                 tile.newT(ltn, mbn, lbn, null, 5);
456                 tile.newT(mtn, rtn, rbn, null, 5);
457                 tile.newT(mtn, rbn, mbn, null, 5);
458
459                 // back
460                 tile.newT(mtf, ltf, mbf, null, 6);
461                 tile.newT(mbf, ltf, lbf, null, 6);
462                 tile.newT(rtf, mtf, rbf, null, 6);
463                 tile.newT(rbf, mtf, mbf, null, 6);
464
465                 if (offset) {
466                   transforms = new Matrix[] {
467                       Matrix.translate(new Vec(lshift,      0,    height)),
468                       Matrix.translate(new Vec(lshift,      0,   -height)),
469                       Matrix.translate(new Vec(rshift,      0,    height)),
470                       Matrix.translate(new Vec(rshift,      0,   -height)),
471                       Matrix.translate(new Vec( width,      0,         0)),
472                       Matrix.translate(new Vec(-width,      0,         0)),
473                       Matrix.translate(new Vec(lshift,  depth, 0)),
474                       Matrix.translate(new Vec(lshift, -depth, 0)),
475                       Matrix.translate(new Vec(rshift,  depth, 0)),
476                       Matrix.translate(new Vec(rshift, -depth, 0)),
477                       Matrix.ONE,
478                   };
479                 } else if (rotated) {
480                     HashSet<Mesh.E> es = new HashSet<Mesh.E>();
481                     for(Mesh.T t : tile) {
482                         es.add(t.e1());
483                         es.add(t.e2());
484                         es.add(t.e3());
485                     }
486                     for(Mesh.E e : es) {
487                         if (e.p1.p.x == e.p2.p.x && e.p1.p.y == e.p2.p.y) continue;
488                         if (e.p1.p.z == e.p2.p.z && e.p1.p.y == e.p2.p.y) continue;
489                         if (e.p1.p.x == e.p2.p.x && e.p1.p.z == e.p2.p.z) continue;
490                         e.shatter();
491                     }
492                     transforms = new Matrix[] {
493                         Matrix.translate(new Vec(0,   0,    height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
494                         Matrix.translate(new Vec(0,   0,   -height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
495                         Matrix.translate(new Vec(0,  depth, 0)),
496                         Matrix.translate(new Vec(0, -depth, 0)),
497                         Matrix.translate(new Vec( width,  0,    0)),
498                         Matrix.translate(new Vec(-width,  0,    0)),
499                       Matrix.ONE,
500                   };
501                 } else {
502                   transforms = new Matrix[] {
503                       Matrix.translate(new Vec(lshift,      0,    height)),
504                       Matrix.translate(new Vec(lshift,      0,   -height)),
505                       Matrix.translate(new Vec(rshift,      0,    height)),
506                       Matrix.translate(new Vec(rshift,      0,   -height)),
507                       Matrix.translate(new Vec( width,      0,         0)),
508                       Matrix.translate(new Vec(-width,      0,         0)),
509                       Matrix.translate(new Vec(0,  depth, 0)),
510                       Matrix.translate(new Vec(0, -depth, 0)),
511                       Matrix.ONE,
512                   };
513                 }
514                 
515                 fixupTile();
516     }
517
518     public class MyMenuBar extends JMenuBar {
519
520         public MyMenuBar() {
521
522             JMenu tileMenu = new JMenu("Tile");
523             JMenu goalMenu = new JMenu("Goal");
524             JMenu hideMenu = new JMenu("Actions");
525
526             hideMenu.add(new MyMenuItem("Start Anneal") { public void hit() { anneal = true; }});
527             hideMenu.add(new MyMenuItem("Stop Anneal") { public void hit() { anneal = false; }});
528             hideMenu.add(new MyMenuItem("Reset to high temperature") { public void hit() { temp = 1; }});
529             hideMenu.add(new MyMenuItem("Subdivide surface") { public void hit() { breaks++; }});
530             hideMenu.add(new MyMenuItem("Show Goal") { public void hit() { goalon = true; }});
531             hideMenu.add(new MyMenuItem("Hide Goal") { public void hit() { goalon = false; }});
532             hideMenu.add(new MyMenuItem("Show All Neighbors") { public void hit() { neighbors = true; }});
533             hideMenu.add(new MyMenuItem("Show One Neighbor Wireframe") { public void hit() { neighborsWireOne = true; }});
534             hideMenu.add(new MyMenuItem("Show All Neighbors Wireframe") { public void hit() { neighborsWire = true; neighborsWireOne = false;}});
535             hideMenu.add(new MyMenuItem("Hide Neighbors") { public void hit() { neighborsWire = false; neighborsWireOne = false; neighbors = false; }});
536
537             goalMenu.add(new MyMenuItem("Fish") { public void hit() {
538                 loadGoal("fish.stl");
539                 //goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
540                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
541                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
542                 goal.transform(Matrix.scale(factor));
543                 fixupGoal();
544             }});
545             goalMenu.add(new MyMenuItem("Vertical Fish") { public void hit() {
546                 loadGoal("fish.stl");
547                 //goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
548                 //goal.transform(Matrix.rotate(new Vec(1, 0, 0), (float)(Math.PI/2)));
549                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
550                 goal.transform(Matrix.scale(factor/1.6f));
551                 fixupGoal();
552             }});
553             goalMenu.add(new MyMenuItem("Torus") { public void hit() {
554                 loadGoal("torus.stl");
555                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
556                 goal.transform(Matrix.scale(factor/3.3f));
557                 goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
558                 fixupGoal();
559             }});
560             tileMenu.add(new MyMenuItem("Hex Brick") { public void hit() {
561                 hexBrick(false, false);
562             }});
563             tileMenu.add(new MyMenuItem("Hex Brick, offset planes") { public void hit() {
564                 hexBrick(true, false);
565             }});
566             tileMenu.add(new MyMenuItem("Hex Brick, rotated") { public void hit() {
567                 hexBrick(false, true);
568             }});
569             tileMenu.add(new MyMenuItem("Temp (do not use)") { public void hit() {
570                 tile = new Mesh(false);
571                 float width  = (float)0.8;
572                 float depth  = (float)0.08;
573                 float height = (float)0.4;
574
575                 float rshift =   width/2;
576                 float lshift = -(width/2);
577                 float halfup = 0;
578                 //float shift = height/2;
579                 //width = (width*2)/3;
580                 float shift = 0;
581                 transforms = new Matrix[] {
582
583                     Matrix.translate(new Vec(lshift/2,  depth,    -shift)),
584                     Matrix.translate(new Vec(rshift/2,  depth,    -shift)),
585                     Matrix.translate(new Vec(lshift/2, -depth,    -shift)),
586                     Matrix.translate(new Vec(rshift/2, -depth,    -shift)),
587
588                     Matrix.translate(new Vec(lshift,  depth/2,    -shift)),
589                     Matrix.translate(new Vec(rshift,  depth/2,    -shift)),
590                     Matrix.translate(new Vec(lshift, -depth/2,    -shift)),
591                     Matrix.translate(new Vec(rshift, -depth/2,    -shift)),
592
593
594                     /*
595                       Matrix.translate(new Vec(lshift,  depth,    -shift)),
596                       Matrix.translate(new Vec(rshift,  depth,    -shift)),
597                       Matrix.translate(new Vec(lshift, -depth,    -shift)),
598                       Matrix.translate(new Vec(rshift, -depth,    -shift)),
599                     */
600                     /*
601                       Matrix.translate(new Vec(lshift,  depth,    shift)),
602                       Matrix.translate(new Vec(rshift,  depth,    shift)),
603                       Matrix.translate(new Vec(lshift, -depth,    shift)),
604                       Matrix.translate(new Vec(rshift, -depth,    shift)),
605                     */
606                     //Matrix.translate(new Vec(0,  depth,    0)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
607                     //Matrix.translate(new Vec(0, -depth,    0)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
608                     //Matrix.translate(new Vec(0,   0,    height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
609                     //Matrix.translate(new Vec(0,   0,   -height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
610
611                     //Matrix.translate(new Vec(0,  depth, 0)),
612                     //Matrix.translate(new Vec(0, -depth, 0)),
613                     Matrix.translate(new Vec(0,      0,  height)),
614                     Matrix.translate(new Vec(0,      0, -height)),
615
616                     //Matrix.translate(new Vec(lshift,        depth,  height/2)),
617                     //Matrix.translate(new Vec(lshift,        depth, -height/2)),
618                     //Matrix.translate(new Vec(rshift,       -depth,  height/2)),
619                     //Matrix.translate(new Vec(rshift,       -depth, -height/2)),
620                     //Matrix.translate(new Vec(rshift,       0,  height)),
621                     //Matrix.translate(new Vec(rshift,       0, -height)),
622
623                     Matrix.translate(new Vec( width,           0,    0)),
624                     Matrix.translate(new Vec(-width,           0,    0)),
625
626                     Matrix.ONE
627                 };
628                 fixupTile();
629             } });
630             tileMenu.add(new MyMenuItem("Dense Packing (Hex)") { public void hit() {
631                 tile = new Mesh(false);
632                 float width  = (float)3.2;
633                 float depth  = (float)0.32;
634                 float height = (float)1.6;
635                 float unit = 0.4f;
636                 float r = unit/2;
637                 float sin = (float)(unit * Math.sin(Math.PI/3));
638                 float cos = (float)(unit * Math.cos(Math.PI/3));
639                 float x = (float)(r*Math.tan(Math.PI/6));
640                 float z = (float)(r/Math.cos(Math.PI/6));
641                 height = 2*r*(float)Math.sqrt(2f/3f);
642
643                 /*
644                 r *= 0.3f;
645                 cos *= 0.3f;
646                 unit *= 0.3f;
647                 */
648
649                 /*
650                   sin *= 0.3f;
651                   x *= 0.3f;
652                   z *= 0.3f;
653                 */
654                 transforms = new Matrix[] {
655                     Matrix.translate(new Vec(-unit, 0, 0)),
656                     Matrix.translate(new Vec( unit, 0, 0)),
657                     Matrix.translate(new Vec(-cos,  0,  sin)),
658                     Matrix.translate(new Vec( cos,  0,  sin)),
659                     Matrix.translate(new Vec(-cos,  0, -sin)),
660                     Matrix.translate(new Vec( cos,  0, -sin)),
661                     Matrix.translate(new Vec( 0,  height, z)),
662                     Matrix.translate(new Vec(-r,  height, -x)),
663                     Matrix.translate(new Vec( r,  height, -x)),
664                     Matrix.translate(new Vec( 0, -height, -z)),
665                     Matrix.translate(new Vec(-r, -height, x)),
666                     Matrix.translate(new Vec( r, -height, x)),
667                     Matrix.ONE,
668                 };
669                 generateTile(transforms, tile);
670                 fixupTile();
671             } });
672             tileMenu.add(new MyMenuItem("Slim Dense Packing (Hex)") { public void hit() {
673                 tile = new Mesh(false);
674                 float width  = (float)3.2;
675                 float depth  = (float)0.32;
676                 float height = (float)1.6;
677                 float unit = 0.4f;
678                 float r = unit/2;
679                 float sin = (float)(unit * Math.sin(Math.PI/3));
680                 float cos = (float)(unit * Math.cos(Math.PI/3));
681                 float x = (float)(r*Math.tan(Math.PI/6));
682                 float z = (float)(r/Math.cos(Math.PI/6));
683                 height = 2*r*(float)Math.sqrt(2f/3f);
684
685
686                 r *= 0.3f;
687                 cos *= 0.3f;
688                 unit *= 0.3f;
689                 height *= 1.5f;
690
691                 /*
692                   sin *= 0.3f;
693                   x *= 0.3f;
694                   z *= 0.3f;
695                 */
696                 transforms = new Matrix[] {
697                     Matrix.translate(new Vec(-unit, 0, 0)),
698                     Matrix.translate(new Vec( unit, 0, 0)),
699                     Matrix.translate(new Vec(-cos,  0,  sin)),
700                     Matrix.translate(new Vec( cos,  0,  sin)),
701                     Matrix.translate(new Vec(-cos,  0, -sin)),
702                     Matrix.translate(new Vec( cos,  0, -sin)),
703                     Matrix.translate(new Vec( 0,  height, z)),
704                     Matrix.translate(new Vec(-r,  height, -x)),
705                     Matrix.translate(new Vec( r,  height, -x)),
706                     Matrix.translate(new Vec( 0, -height, -z)),
707                     Matrix.translate(new Vec(-r, -height, x)),
708                     Matrix.translate(new Vec( r, -height, x)),
709                     Matrix.ONE,
710                 };
711                 generateTile(transforms, tile);
712                 fixupTile();
713             } });
714             tileMenu.add(new MyMenuItem("Genus-1") { public void hit() {
715                 synchronized(this) {
716                     tile = new Mesh(false);
717                     float height = 4;
718                     float width  = 4;
719                     float depth  = 1;
720                     Matrix mm = Matrix.scale(0.1f);
721                     // top
722                     quad(tile, mm, 
723                          new Point( 2,  2,  0),
724                          new Point( 1,  1, -1),
725                          new Point(-1,  1, -1),
726                          new Point(-2,  2,  0));
727                     quad(tile, mm, 
728                          new Point(-2,  2,  0),
729                          new Point(-1,  1,  1),
730                          new Point( 1,  1,  1),
731                          new Point( 2,  2,  0));
732                     quad(tile, mm, 
733                          new Point( 1,  1, -1),
734                          new Point( 1,  1,  1),
735                          new Point(-1,  1,  1),
736                          new Point(-1,  1, -1));
737
738                     // bottom
739                     quad(tile, mm, 
740                          new Point(-2, -2,  0),
741                          new Point(-1, -1, -1),
742                          new Point( 1, -1, -1),
743                          new Point( 2, -2,  0));
744                     quad(tile, mm, 
745                          new Point( 2, -2,  0),
746                          new Point( 1, -1,  1),
747                          new Point(-1, -1,  1),
748                          new Point(-2, -2,  0));
749                     quad(tile, mm, 
750                          new Point(-1, -1, -1),
751                          new Point(-1, -1,  1),
752                          new Point( 1, -1,  1),
753                          new Point( 1, -1, -1));
754
755                     // left
756                     quad(tile, mm, 
757                          new Point( 2, -2,  0),
758                          new Point( 1, -1, -1),
759                          new Point( 1,  1, -1),
760                          new Point( 2,  2,  0));
761                     quad(tile, mm, 
762                          new Point( 2,  2,  0),
763                          new Point( 1,  1,  1),
764                          new Point( 1, -1,  1),
765                          new Point( 2, -2,  0));
766                     quad(tile, mm, 
767                          new Point( 1, -1, -1),
768                          new Point( 1, -1,  1),
769                          new Point( 1,  1,  1),
770                          new Point( 1,  1, -1));
771
772                     // bottom
773                     quad(tile, mm, 
774                          new Point(-2,  2,  0),
775                          new Point(-1,  1, -1),
776                          new Point(-1, -1, -1),
777                          new Point(-2, -2,  0));
778                     quad(tile, mm, 
779                          new Point(-2, -2,  0),
780                          new Point(-1, -1,  1),
781                          new Point(-1,  1,  1),
782                          new Point(-2,  2,  0));
783                     quad(tile, mm, 
784                          new Point(-1,  1, -1),
785                          new Point(-1,  1,  1),
786                          new Point(-1, -1,  1),
787                          new Point(-1, -1, -1));
788
789                     height = 4;
790                     width  = 4;
791                     depth  = 1;
792
793
794                     transforms = new Matrix[] {
795                         Matrix.translate(new Vec(0, 0.2f,0))
796                         .times(Matrix.rotate(new Vec(0,1,0), (float)(1*Math.PI/2))),
797
798                         Matrix.translate(new Vec(0,-0.2f,0))
799                         .times(Matrix.rotate(new Vec(0,1,0), (float)(-1*Math.PI/2))),
800
801                         Matrix.translate(new Vec( 0.2f,0,0))
802                         .times(Matrix.rotate(new Vec(1,0,0), (float)(1*Math.PI/2))),
803
804                         Matrix.translate(new Vec(-0.2f,0,0))
805                         .times(Matrix.rotate(new Vec(1,0,0), (float)(-1*Math.PI/2))),
806
807                         //Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
808
809                         /*
810                           Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
811
812                           Matrix.rotate(new Vec(0,0,1), (float)(3*Math.PI/2)),
813                           Matrix.rotate(new Vec(1,0,0), (float)(2*Math.PI/2)),
814                         */
815
816                         //Matrix.rotate(new Vec(0,0,1), (float)(2*Math.PI/2)),
817                         //Matrix.scale(1,-1,1),
818
819                         //Matrix.translate(new Vec( 0.2f, 0,0))
820                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2)))
821                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
822
823                         //Matrix.translate(new Vec(-0.2f, 0,0))
824                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 3*Math.PI/2)))
825                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
826             
827                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
828                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
829                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
830                         //Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2))
831                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
832                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
833
834                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
835                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
836                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
837                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
838                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
839                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
840
841                         Matrix.ONE,
842                     };
843                     fixupTile();
844                 }}});
845
846             // Finally, add all the menus to the menu bar.
847             add(tileMenu);
848             add(goalMenu);
849             add(hideMenu);
850         }
851
852     }
853
854 }