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