5dbba74446abbc31ac32baa032c19344fddf3150
[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.setJMenuBar(main.new MyMenuBar());
354         f.pack();
355         f.show();
356         f.setSize(900, 900);
357         f.doLayout();
358         main.anneal();
359     }
360
361     public class MyMenuItem extends JMenuItem implements ActionListener {
362         public MyMenuItem(String s) {
363             super(s);
364             this.addActionListener(this);
365         }
366         public void actionPerformed(ActionEvent event) {
367             synchronized(Main.this) {
368                     hit();
369             }
370         }
371         public void hit() {}
372     }
373
374     public void hexBrick(boolean offset, boolean rotated) {
375         setTile(new Mesh(false));
376                 float width  = (float)0.8;
377                 float depth  = (float)0.08;
378                 float height = (float)0.4;
379                 float rshift =   width/2;
380                 float lshift = -(width/2);
381                 float halfup = 0;
382                 Point ltf = new Point(lshift,  (depth/2),  (height/2));
383                 Point mtf = new Point( 0.0,    (depth/2),  (height/2));
384                 Point rtf = new Point(rshift,  (depth/2),  (height/2));
385                 Point lbf = new Point(lshift, -(depth/2),  (height/2));
386                 Point mbf = new Point( 0.0,   -(depth/2),  (height/2));
387                 Point rbf = new Point(rshift, -(depth/2),  (height/2));
388
389                 Point ltc = new Point(lshift,  (depth/2), 0);
390                 Point mtc = new Point( 0.0,    (depth/2), 0);
391                 Point rtc = new Point(rshift,  (depth/2), 0);
392                 Point lbc = new Point(lshift, -(depth/2), 0);
393                 Point mbc = new Point( 0.0,   -(depth/2), 0);
394                 Point rbc = new Point(rshift, -(depth/2), 0);
395
396                 Point ltn = new Point(lshift,  (depth/2), -(height/2));
397                 Point mtn = new Point( 0.0,    (depth/2), -(height/2));
398                 Point rtn = new Point(rshift,  (depth/2), -(height/2));
399                 Point lbn = new Point(lshift, -(depth/2), -(height/2));
400                 Point mbn = new Point( 0.0,   -(depth/2), -(height/2));
401                 Point rbn = new Point(rshift, -(depth/2), -(height/2));
402
403         
404                 Point[] points = new Point[] {
405                     ltf,
406                     mtf,
407                     rtf,
408                     lbf,
409                     mbf,
410                     rbf,
411
412                     ltc,
413                     mtc,
414                     rtc,
415                     lbc,
416                     mbc,
417                     rbc,
418
419                     ltn,
420                     mtn,
421                     rtn,
422                     lbn,
423                     mbn,
424                     rbn
425                 };
426
427
428                 // top
429                 tile.newT(ltf, mtf, mtc, null, 1);
430                 tile.newT(mtc, ltc, ltf, null, 1);
431                 tile.newT(mtf, rtf, rtc, null, 1);
432                 tile.newT(rtc, mtc, mtf, null, 1);
433
434                 tile.newT(ltc, mtc, mtn, null, 1);
435                 tile.newT(mtn, ltn, ltc, null, 1);
436                 tile.newT(mtc, rtc, rtn, null, 1);
437                 tile.newT(rtn, mtn, mtc, null, 1);
438
439                 // bottom (swap normals)
440                 tile.newT(mbf, lbf, mbc, null, 2);
441                 tile.newT(lbc, mbc, lbf, null, 2);
442                 tile.newT(rbf, mbf, rbc, null, 2);
443                 tile.newT(mbc, rbc, mbf, null, 2);
444
445                 tile.newT(mbc, lbc, mbn, null, 2);
446                 tile.newT(lbn, mbn, lbc, null, 2);
447
448                 tile.newT(rbc, mbc, rbn, null, 2);
449                 tile.newT(mbn, rbn, mbc, null, 2);
450
451
452                 // left
453                 tile.newT(ltf, ltc, lbc, null, 3);
454                 tile.newT(lbc, lbf, ltf, null, 3);
455                 tile.newT(ltc, ltn, lbn, null, 3);
456                 tile.newT(lbn, lbc, ltc, null, 3);
457
458                 // right (swap normals)
459                 tile.newT(rtc, rtf, rbc, null, 4);
460                 tile.newT(rbf, rbc, rtf, null, 4);
461                 tile.newT(rtn, rtc, rbn, null, 4);
462                 tile.newT(rbc, rbn, rtc, null, 4);
463
464                 // front
465                 tile.newT(ltn, mtn, mbn, null, 5);
466                 tile.newT(ltn, mbn, lbn, null, 5);
467                 tile.newT(mtn, rtn, rbn, null, 5);
468                 tile.newT(mtn, rbn, mbn, null, 5);
469
470                 // back
471                 tile.newT(mtf, ltf, mbf, null, 6);
472                 tile.newT(mbf, ltf, lbf, null, 6);
473                 tile.newT(rtf, mtf, rbf, null, 6);
474                 tile.newT(rbf, mtf, mbf, null, 6);
475
476                 if (offset) {
477                   transforms = new Matrix[] {
478                       Matrix.translate(new Vec(lshift,      0,    height)),
479                       Matrix.translate(new Vec(lshift,      0,   -height)),
480                       Matrix.translate(new Vec(rshift,      0,    height)),
481                       Matrix.translate(new Vec(rshift,      0,   -height)),
482                       Matrix.translate(new Vec( width,      0,         0)),
483                       Matrix.translate(new Vec(-width,      0,         0)),
484                       Matrix.translate(new Vec(lshift,  depth, 0)),
485                       Matrix.translate(new Vec(lshift, -depth, 0)),
486                       Matrix.translate(new Vec(rshift,  depth, 0)),
487                       Matrix.translate(new Vec(rshift, -depth, 0)),
488                       Matrix.ONE,
489                   };
490                 } else if (rotated) {
491                     HashSet<Mesh.E> es = new HashSet<Mesh.E>();
492                     for(Mesh.T t : tile) {
493                         es.add(t.e1());
494                         es.add(t.e2());
495                         es.add(t.e3());
496                     }
497                     for(Mesh.E e : es) {
498                         if (e.p1.p.x == e.p2.p.x && e.p1.p.y == e.p2.p.y) continue;
499                         if (e.p1.p.z == e.p2.p.z && e.p1.p.y == e.p2.p.y) continue;
500                         if (e.p1.p.x == e.p2.p.x && e.p1.p.z == e.p2.p.z) continue;
501                         e.shatter();
502                     }
503                     transforms = new Matrix[] {
504                         Matrix.translate(new Vec(0,   0,    height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
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,  depth, 0)),
507                         Matrix.translate(new Vec(0, -depth, 0)),
508                         Matrix.translate(new Vec( width,  0,    0)),
509                         Matrix.translate(new Vec(-width,  0,    0)),
510                       Matrix.ONE,
511                   };
512                 } else {
513                   transforms = new Matrix[] {
514                       Matrix.translate(new Vec(lshift,      0,    height)),
515                       Matrix.translate(new Vec(lshift,      0,   -height)),
516                       Matrix.translate(new Vec(rshift,      0,    height)),
517                       Matrix.translate(new Vec(rshift,      0,   -height)),
518                       Matrix.translate(new Vec( width,      0,         0)),
519                       Matrix.translate(new Vec(-width,      0,         0)),
520                       Matrix.translate(new Vec(0,  depth, 0)),
521                       Matrix.translate(new Vec(0, -depth, 0)),
522                       Matrix.ONE,
523                   };
524                 }
525                 
526                 fixupTile();
527     }
528
529     public class MyMenuBar extends JMenuBar {
530
531         public MyMenuBar() {
532
533             JMenu tileMenu = new JMenu("Tile");
534             JMenu goalMenu = new JMenu("Goal");
535             JMenu hideMenu = new JMenu("Actions");
536
537             hideMenu.add(new MyMenuItem("Start Anneal") { public void hit() { anneal = true; }});
538             hideMenu.add(new MyMenuItem("Stop Anneal") { public void hit() { anneal = false; }});
539             hideMenu.add(new MyMenuItem("Reset to high temperature") { public void hit() { temp = 1; }});
540             hideMenu.add(new MyMenuItem("Subdivide surface") { public void hit() { breaks++; }});
541             hideMenu.add(new MyMenuItem("Show Goal") { public void hit() { goalon = true; }});
542             hideMenu.add(new MyMenuItem("Hide Goal") { public void hit() { goalon = false; }});
543             hideMenu.add(new MyMenuItem("Show All Neighbors") { public void hit() { neighbors = true; }});
544             hideMenu.add(new MyMenuItem("Show One Neighbor Wireframe") { public void hit() { neighborsWireOne = true; }});
545             hideMenu.add(new MyMenuItem("Show All Neighbors Wireframe") { public void hit() { neighborsWire = true; neighborsWireOne = false;}});
546             hideMenu.add(new MyMenuItem("Hide Neighbors") { public void hit() { neighborsWire = false; neighborsWireOne = false; neighbors = false; }});
547
548             goalMenu.add(new MyMenuItem("Fish with face") { public void hit() {
549                 loadGoal("face.stl");
550                 //goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
551                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
552                 //goal.transform(Matrix.scale(1, 2.2f, 1));
553                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
554                 //factor = factor * 0.8f;
555                 goal.transform(Matrix.scale(0.3f));
556                 //goal.transform(Matrix.rotate(new Vec(0,1,0), (float)(Math.PI*1/3)));
557                 goal.transform(Matrix.rotate(new Vec(1,0,0), (float)(Math.PI*1/3)));
558                 fixupGoal(true, false);
559                 //goal.transform(Matrix.translate(new Vec(0.145f, 0, 0)));
560                 fixupGoal(false, true);
561             }});
562             goalMenu.add(new MyMenuItem("Fish") { public void hit() {
563                 loadGoal("fish.stl");
564                 //goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
565                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
566                 goal.transform(Matrix.scale(1, 2.2f, 1));
567                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
568                 factor = factor * 0.8f;
569                 goal.transform(Matrix.scale(factor));
570                 fixupGoal(true, false);
571                 //goal.transform(Matrix.translate(new Vec(0.145f, 0, 0)));
572                 fixupGoal(false, true);
573             }});
574             goalMenu.add(new MyMenuItem("Hammerhead Fish") { public void hit() {
575                 loadGoal("fish.stl");
576                 goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
577                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(3*Math.PI/2)));
578                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
579                 factor *= 0.75f;
580                 goal.transform(Matrix.scale(factor));
581                 fixupGoal();
582             }});
583             goalMenu.add(new MyMenuItem("Vertical Fish") { public void hit() {
584                 loadGoal("fish.stl");
585                 //goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
586                 //goal.transform(Matrix.rotate(new Vec(1, 0, 0), (float)(Math.PI/2)));
587                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
588                 goal.transform(Matrix.scale(factor/1.6f));
589                 fixupGoal();
590             }});
591             goalMenu.add(new MyMenuItem("Torus") { public void hit() {
592                 loadGoal("torus.stl");
593                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
594                 goal.transform(Matrix.scale(factor/2.5f));
595                 goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
596                 fixupGoal();
597             }});
598             tileMenu.add(new MyMenuItem("Hex Brick") { public void hit() {
599                 hexBrick(false, false);
600             }});
601             tileMenu.add(new MyMenuItem("Hex Brick, offset planes") { public void hit() {
602                 hexBrick(true, false);
603             }});
604             tileMenu.add(new MyMenuItem("Hex Brick, rotated") { public void hit() {
605                 hexBrick(false, true);
606             }});
607             tileMenu.add(new MyMenuItem("Temp (do not use)") { public void hit() {
608                 setTile(new Mesh(false));
609                 float width  = (float)0.8;
610                 float depth  = (float)0.08;
611                 float height = (float)0.4;
612
613                 float rshift =   width/2;
614                 float lshift = -(width/2);
615                 float halfup = 0;
616                 //float shift = height/2;
617                 //width = (width*2)/3;
618                 float shift = 0;
619                 transforms = new Matrix[] {
620
621                     Matrix.translate(new Vec(lshift/2,  depth,    -shift)),
622                     Matrix.translate(new Vec(rshift/2,  depth,    -shift)),
623                     Matrix.translate(new Vec(lshift/2, -depth,    -shift)),
624                     Matrix.translate(new Vec(rshift/2, -depth,    -shift)),
625
626                     Matrix.translate(new Vec(lshift,  depth/2,    -shift)),
627                     Matrix.translate(new Vec(rshift,  depth/2,    -shift)),
628                     Matrix.translate(new Vec(lshift, -depth/2,    -shift)),
629                     Matrix.translate(new Vec(rshift, -depth/2,    -shift)),
630
631
632                     /*
633                       Matrix.translate(new Vec(lshift,  depth,    -shift)),
634                       Matrix.translate(new Vec(rshift,  depth,    -shift)),
635                       Matrix.translate(new Vec(lshift, -depth,    -shift)),
636                       Matrix.translate(new Vec(rshift, -depth,    -shift)),
637                     */
638                     /*
639                       Matrix.translate(new Vec(lshift,  depth,    shift)),
640                       Matrix.translate(new Vec(rshift,  depth,    shift)),
641                       Matrix.translate(new Vec(lshift, -depth,    shift)),
642                       Matrix.translate(new Vec(rshift, -depth,    shift)),
643                     */
644                     //Matrix.translate(new Vec(0,  depth,    0)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
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,   0,    height)).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
649                     //Matrix.translate(new Vec(0,  depth, 0)),
650                     //Matrix.translate(new Vec(0, -depth, 0)),
651                     Matrix.translate(new Vec(0,      0,  height)),
652                     Matrix.translate(new Vec(0,      0, -height)),
653
654                     //Matrix.translate(new Vec(lshift,        depth,  height/2)),
655                     //Matrix.translate(new Vec(lshift,        depth, -height/2)),
656                     //Matrix.translate(new Vec(rshift,       -depth,  height/2)),
657                     //Matrix.translate(new Vec(rshift,       -depth, -height/2)),
658                     //Matrix.translate(new Vec(rshift,       0,  height)),
659                     //Matrix.translate(new Vec(rshift,       0, -height)),
660
661                     Matrix.translate(new Vec( width,           0,    0)),
662                     Matrix.translate(new Vec(-width,           0,    0)),
663
664                     Matrix.ONE
665                 };
666                 fixupTile();
667             } });
668             tileMenu.add(new MyMenuItem("Dense Packing (hex)") { public void hit() {
669                 setTile(new Mesh(false));
670                 float width  = (float)3.2;
671                 float depth  = (float)0.32;
672                 float height = (float)1.6;
673                 float unit = 0.4f;
674                 float r = unit/2;
675                 float sin = (float)(unit * Math.sin(Math.PI/3));
676                 float cos = (float)(unit * Math.cos(Math.PI/3));
677                 float x = (float)(r*Math.tan(Math.PI/6));
678                 float z = (float)(r/Math.cos(Math.PI/6));
679                 height = 2*r*(float)Math.sqrt(2f/3f);
680
681                 /*
682                 r *= 0.3f;
683                 cos *= 0.3f;
684                 unit *= 0.3f;
685                 */
686
687                 /*
688                   sin *= 0.3f;
689                   x *= 0.3f;
690                   z *= 0.3f;
691                 */
692                 transforms = new Matrix[] {
693                     Matrix.translate(new Vec(-unit, 0, 0)),
694                     Matrix.translate(new Vec( unit, 0, 0)),
695                     Matrix.translate(new Vec(-cos,  0,  sin)),
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( 0,  height, z)),
700                     Matrix.translate(new Vec(-r,  height, -x)),
701                     Matrix.translate(new Vec( r,  height, -x)),
702                     Matrix.translate(new Vec( 0, -height, -z)),
703                     Matrix.translate(new Vec(-r, -height, x)),
704                     Matrix.translate(new Vec( r, -height, x)),
705                     Matrix.ONE,
706                 };
707                 generateTile(transforms, tile);
708                 fixupTile();
709             } });
710             tileMenu.add(new MyMenuItem("Slim Dense Packing (Cubic)") { public void hit() {
711                 setTile(new Mesh(false));
712                 float unit = 0.4f;
713                 float r = unit/2;
714                 float sin = (float)(unit * Math.sin(Math.PI/3));
715                 float cos = (float)(unit * Math.cos(Math.PI/3));
716                 float x = (float)(r*Math.tan(Math.PI/6));
717                 float z = (float)(r/Math.cos(Math.PI/6));
718                 float height = 2*r*(float)Math.sqrt(2f/3f);
719
720                 transforms = new Matrix[] {
721                     Matrix.translate(new Vec(-unit, 0, 0)),
722                     Matrix.translate(new Vec( unit, 0, 0)),
723                     Matrix.translate(new Vec(-cos,  0,  sin)),
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
728                     /*
729                     Matrix.translate(new Vec( 0,  height, -z)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
730                     Matrix.translate(new Vec(-r,  height,  x)).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( 0, -height, -z)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
733                     Matrix.translate(new Vec(-r, -height,  x)).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                     */
736
737                     Matrix.translate(new Vec( 0,  height, -z)),
738                     Matrix.translate(new Vec(-r,  height,  x)),
739                     Matrix.translate(new Vec( r,  height,  x)),
740                     Matrix.translate(new Vec( 0, -height,  z)),
741                     Matrix.translate(new Vec(-r, -height, -x)),
742                     Matrix.translate(new Vec( r, -height, -x)),
743                     //Matrix.rotate(new Vec(0,0,1), (float)Math.PI),
744
745                     /*
746                     Matrix.translate(new Vec( 0,  height, -z)).times(Matrix.scale(-1,1,-1)),
747                     Matrix.translate(new Vec(-r,  height,  x)).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( 0, -height, -z)).times(Matrix.scale(-1,1,-1)),
750                     Matrix.translate(new Vec(-r, -height,  x)).times(Matrix.scale(-1,1,-1)),
751                     Matrix.translate(new Vec( r, -height,  x)).times(Matrix.scale(-1,1,-1)),
752                     */
753                     Matrix.ONE
754                 };
755                 generateTile(transforms, tile);
756                 //for(int i=0; i<transforms.length; i++) transforms[i] = Matrix.translate(m.times(vecs[i]));
757                 Matrix m = Matrix.scale(1.9f, 1f ,1);
758                 //Matrix m = Matrix.scale(1f, 2.1f, 1f);
759                 tile.transform(m);
760                 for(int i=0; i<transforms.length; i++)
761                     transforms[i] = transforms[i].preMultiplyTranslationalComponentBy(m);
762                 fixupTile();
763
764             } });
765             tileMenu.add(new MyMenuItem("Genus-1") { public void hit() {
766                 synchronized(this) {
767                     setTile(new Mesh(false));
768                     Matrix mm = Matrix.scale(0.1f);
769                     float height = 4;
770                     float width  = 4;
771                     float depth  = 1;
772                     // top
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                     // bottom
790                     quad(tile, mm, 
791                          new Point(-2, -2,  0),
792                          new Point(-1, -1, -1),
793                          new Point( 1, -1, -1),
794                          new Point( 2, -2,  0));
795                     quad(tile, mm, 
796                          new Point( 2, -2,  0),
797                          new Point( 1, -1,  1),
798                          new Point(-1, -1,  1),
799                          new Point(-2, -2,  0));
800                     quad(tile, mm, 
801                          new Point(-1, -1, -1),
802                          new Point(-1, -1,  1),
803                          new Point( 1, -1,  1),
804                          new Point( 1, -1, -1));
805
806                     // left
807                     quad(tile, mm, 
808                          new Point( 2, -2,  0),
809                          new Point( 1, -1, -1),
810                          new Point( 1,  1, -1),
811                          new Point( 2,  2,  0));
812                     quad(tile, mm, 
813                          new Point( 2,  2,  0),
814                          new Point( 1,  1,  1),
815                          new Point( 1, -1,  1),
816                          new Point( 2, -2,  0));
817                     quad(tile, mm, 
818                          new Point( 1, -1, -1),
819                          new Point( 1, -1,  1),
820                          new Point( 1,  1,  1),
821                          new Point( 1,  1, -1));
822
823                     // bottom
824                     quad(tile, mm, 
825                          new Point(-2,  2,  0),
826                          new Point(-1,  1, -1),
827                          new Point(-1, -1, -1),
828                          new Point(-2, -2,  0));
829                     quad(tile, mm, 
830                          new Point(-2, -2,  0),
831                          new Point(-1, -1,  1),
832                          new Point(-1,  1,  1),
833                          new Point(-2,  2,  0));
834                     quad(tile, mm, 
835                          new Point(-1,  1, -1),
836                          new Point(-1,  1,  1),
837                          new Point(-1, -1,  1),
838                          new Point(-1, -1, -1));
839
840                     height = 4;
841                     width  = 4;
842                     depth  = 1;
843
844
845                     transforms = new Matrix[] {
846                         Matrix.translate(new Vec(0, 0.2f,0))
847                         .times(Matrix.rotate(new Vec(0,1,0), (float)(1*Math.PI/2))),
848
849                         Matrix.translate(new Vec(0,-0.2f,0))
850                         .times(Matrix.rotate(new Vec(0,1,0), (float)(-1*Math.PI/2))),
851
852                         Matrix.translate(new Vec( 0.2f,0,0))
853                         .times(Matrix.rotate(new Vec(1,0,0), (float)(1*Math.PI/2))),
854
855                         Matrix.translate(new Vec(-0.2f,0,0))
856                         .times(Matrix.rotate(new Vec(1,0,0), (float)(-1*Math.PI/2))),
857
858
859                         //Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
860
861                         /*
862                           Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
863
864                           Matrix.rotate(new Vec(0,0,1), (float)(3*Math.PI/2)),
865                           Matrix.rotate(new Vec(1,0,0), (float)(2*Math.PI/2)),
866                         */
867
868                         //Matrix.rotate(new Vec(0,0,1), (float)(2*Math.PI/2)),
869                         //Matrix.scale(1,-1,1),
870
871                         //Matrix.translate(new Vec( 0.2f, 0,0))
872                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2)))
873                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
874
875                         //Matrix.translate(new Vec(-0.2f, 0,0))
876                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 3*Math.PI/2)))
877                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
878             
879                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
880                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
881                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
882                         //Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2))
883                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
884                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
885
886                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
887                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
888                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
889                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
890                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
891                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
892
893                         Matrix.ONE,
894                     };
895                     fixupTile();
896                 }}});
897             tileMenu.add(new MyMenuItem("Hammerhead") { public void hit() {
898                 synchronized(this) {
899                     setTile(new Mesh(false));
900                     Matrix mm = Matrix.ONE;
901                     float height1 = .1f;
902                     float height2 = .1f;
903                     float width  = .4f;
904                     float depth  = .1f;
905                     // top
906                     Point a1 = new Point( -width/2,         height2/2, -depth/2);
907                     Point b1 = new Point(        0,         height2/2, -depth/2);
908                     Point c1 = new Point(        0, height1+height2/2, -depth/2);
909                     Point d1 = new Point(  width/2, height1+height2/2, -depth/2);
910                     Point e1 = new Point(  width/2,         height2/2, -depth/2);
911                     Point f1 = new Point(  width/2,        -height2/2, -depth/2);
912                     Point g1 = new Point(  width/2,-height1-height2/2, -depth/2);
913                     Point h1 = new Point(        0,-height1-height2/2, -depth/2);
914                     Point i1 = new Point(        0,        -height2/2, -depth/2);
915                     Point j1 = new Point( -width/2,        -height2/2, -depth/2);
916                     Point a2 = new Point( -width/2,         height2/2,  depth/2);
917                     Point b2 = new Point(        0,         height2/2,  depth/2);
918                     Point c2 = new Point(        0, height1+height2/2,  depth/2);
919                     Point d2 = new Point(  width/2, height1+height2/2,  depth/2);
920                     Point e2 = new Point(  width/2,         height2/2,  depth/2);
921                     Point f2 = new Point(  width/2,        -height2/2,  depth/2);
922                     Point g2 = new Point(  width/2,-height1-height2/2,  depth/2);
923                     Point h2 = new Point(        0,-height1-height2/2,  depth/2);
924                     Point i2 = new Point(        0,        -height2/2,  depth/2);
925                     Point j2 = new Point( -width/2,        -height2/2,  depth/2);
926
927                     quad(tile, mm, a1, b1, i1, j1);
928                     quad(tile, mm, c1, d1, e1, b1);
929                     quad(tile, mm, b1, e1, f1, i1);
930                     quad(tile, mm, i1, f1, g1, h1);
931
932                     quad(tile, mm, j2, i2, b2, a2);
933                     quad(tile, mm, b2, e2, d2, c2);
934                     quad(tile, mm, i2, f2, e2, b2);
935                     quad(tile, mm, h2, g2, f2, i2);
936
937                     quad(tile, mm, d1, d2, e2, e1);
938                     quad(tile, mm, e1, e2, f2, f1);
939                     quad(tile, mm, f1, f2, g2, g1);
940                     quad(tile, mm, h1, g1, g2, h2);
941                     quad(tile, mm, i2, i1, h1, h2);
942                     quad(tile, mm, j1, i1, i2, j2);
943                     quad(tile, mm, a2, a1, j1, j2);
944                     quad(tile, mm, a1, a2, b2, b1);
945                     quad(tile, mm, c2, c1, b1, b2);
946                     quad(tile, mm, c1, c2, d2, d1);
947
948                     transforms = new Matrix[] {
949
950                         mm.times(Matrix.translate(new Vec(   width,     0, 0))),
951                         mm.times(Matrix.translate(new Vec(  -width,     0, 0))),
952                         mm.times(Matrix.translate(new Vec(-width/2, height1+height2, 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
957                         mm.times(Matrix.translate(new Vec(   width/2,     0,  depth))),
958                         mm.times(Matrix.translate(new Vec(  -width/2,     0,  depth))),
959                         mm.times(Matrix.translate(new Vec(         0, height1+height2,  depth))),
960                         mm.times(Matrix.translate(new Vec(         0,-height1-height2,  depth))),
961
962                         mm.times(Matrix.translate(new Vec(   width/2,     0, -depth))),
963                         mm.times(Matrix.translate(new Vec(  -width/2,     0, -depth))),
964                         mm.times(Matrix.translate(new Vec(         0, height1+height2, -depth))),
965                         mm.times(Matrix.translate(new Vec(         0,-height1-height2, -depth))),
966
967                         Matrix.ONE
968                     };
969                     fixupTile();
970                 }}});
971
972             // Finally, add all the menus to the menu bar.
973             add(tileMenu);
974             add(goalMenu);
975             add(hideMenu);
976         }
977
978     }
979
980 }