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