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