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