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