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