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