checkpoint
[anneal.git] / src / edu / berkeley / qfat / Main.java
1 package edu.berkeley.qfat;
2 import java.awt.*;
3 import java.io.*;
4 import java.awt.event.*;
5 import javax.swing.*;
6 import javax.media.opengl.*;
7 import javax.media.opengl.glu.*;
8 import java.util.*;
9 import edu.berkeley.qfat.bind.*;
10 import edu.berkeley.qfat.geom.*;
11 import edu.berkeley.qfat.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.rotate(new Vec(1,0,0), (float)Math.PI),
594                     //Matrix.reflect(new Vec(1,0,0)),
595
596                     Matrix.translate(new Vec(-unit, 0, 0)),
597                     Matrix.translate(new Vec( unit, 0, 0)),
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                     Matrix.translate(new Vec( cos,  0, -sin)),
602
603                     Matrix.translate(new Vec( 0,  height, -z)),
604                     Matrix.translate(new Vec(-r,  height,  x)),
605                     Matrix.translate(new Vec( r,  height,  x)),
606                     Matrix.translate(new Vec( 0, -height,  z)),
607                     Matrix.translate(new Vec(-r, -height, -x)),
608                     Matrix.translate(new Vec( r, -height, -x)),
609
610                     /*
611                     Matrix.translate(new Vec( 0,  height, -z)).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( r,  height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
614                     Matrix.translate(new Vec( 0, -height, -z)).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                     Matrix.translate(new Vec( r, -height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
617                     */
618
619                     /*
620                     Matrix.translate(new Vec( 0,  height, -z)).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( r,  height,  x)).times(Matrix.scale(-1,1,-1)),
623                     Matrix.translate(new Vec( 0, -height, -z)).times(Matrix.scale(-1,1,-1)),
624                     Matrix.translate(new Vec(-r, -height,  x)).times(Matrix.scale(-1,1,-1)),
625                     Matrix.translate(new Vec( r, -height,  x)).times(Matrix.scale(-1,1,-1)),
626                     */
627                     Matrix.ONE
628                 };
629                 generateTile(transforms, tile);
630
631
632                 transforms = new Matrix[] {
633                     Matrix.reflect(new Vec( 0,  height, -z).norm()),
634                     //Matrix.reflect(new Vec( 0, -height,  z)),
635                     Matrix.ONE
636                 };
637
638                 //for(int i=0; i<transforms.length; i++) transforms[i] = Matrix.translate(m.times(vecs[i]));
639                 /*
640                 Matrix m = Matrix.scale(1.9f, 1f ,1);
641                 //Matrix m = Matrix.scale(1f, 2.1f, 1f);
642                 tile.transform(m);
643                 for(int i=0; i<transforms.length; i++)
644                     transforms[i] = preMultiplyTranslationalComponentBy(transforms[i], m);
645                 */
646
647                 fixupTile();
648
649             } });
650             tileMenu.add(new MyMenuItem("Genus-1") { public void hit() {
651                 synchronized(this) {
652                     setTile(new Mesh(false));
653                     Matrix mm = Matrix.scale(0.1f);
654                     float height = 4;
655                     float width  = 4;
656                     float depth  = 1;
657                     // top
658                     quad(tile, mm, 
659                          new Point( 2,  2,  0),
660                          new Point( 1,  1, -1),
661                          new Point(-1,  1, -1),
662                          new Point(-2,  2,  0));
663                     quad(tile, mm, 
664                          new Point(-2,  2,  0),
665                          new Point(-1,  1,  1),
666                          new Point( 1,  1,  1),
667                          new Point( 2,  2,  0));
668                     quad(tile, mm, 
669                          new Point( 1,  1, -1),
670                          new Point( 1,  1,  1),
671                          new Point(-1,  1,  1),
672                          new Point(-1,  1, -1));
673
674                     // bottom
675                     quad(tile, mm, 
676                          new Point(-2, -2,  0),
677                          new Point(-1, -1, -1),
678                          new Point( 1, -1, -1),
679                          new Point( 2, -2,  0));
680                     quad(tile, mm, 
681                          new Point( 2, -2,  0),
682                          new Point( 1, -1,  1),
683                          new Point(-1, -1,  1),
684                          new Point(-2, -2,  0));
685                     quad(tile, mm, 
686                          new Point(-1, -1, -1),
687                          new Point(-1, -1,  1),
688                          new Point( 1, -1,  1),
689                          new Point( 1, -1, -1));
690
691                     // left
692                     quad(tile, mm, 
693                          new Point( 2, -2,  0),
694                          new Point( 1, -1, -1),
695                          new Point( 1,  1, -1),
696                          new Point( 2,  2,  0));
697                     quad(tile, mm, 
698                          new Point( 2,  2,  0),
699                          new Point( 1,  1,  1),
700                          new Point( 1, -1,  1),
701                          new Point( 2, -2,  0));
702                     quad(tile, mm, 
703                          new Point( 1, -1, -1),
704                          new Point( 1, -1,  1),
705                          new Point( 1,  1,  1),
706                          new Point( 1,  1, -1));
707
708                     // bottom
709                     quad(tile, mm, 
710                          new Point(-2,  2,  0),
711                          new Point(-1,  1, -1),
712                          new Point(-1, -1, -1),
713                          new Point(-2, -2,  0));
714                     quad(tile, mm, 
715                          new Point(-2, -2,  0),
716                          new Point(-1, -1,  1),
717                          new Point(-1,  1,  1),
718                          new Point(-2,  2,  0));
719                     quad(tile, mm, 
720                          new Point(-1,  1, -1),
721                          new Point(-1,  1,  1),
722                          new Point(-1, -1,  1),
723                          new Point(-1, -1, -1));
724
725                     height = 4;
726                     width  = 4;
727                     depth  = 1;
728
729
730                     transforms = new Matrix[] {
731                         Matrix.translate(new Vec(0, 0.2f,0))
732                         .times(Matrix.rotate(new Vec(0,1,0), (float)(1*Math.PI/2))),
733
734                         Matrix.translate(new Vec(0,-0.2f,0))
735                         .times(Matrix.rotate(new Vec(0,1,0), (float)(-1*Math.PI/2))),
736
737                         Matrix.translate(new Vec( 0.2f,0,0))
738                         .times(Matrix.rotate(new Vec(1,0,0), (float)(1*Math.PI/2))),
739
740                         Matrix.translate(new Vec(-0.2f,0,0))
741                         .times(Matrix.rotate(new Vec(1,0,0), (float)(-1*Math.PI/2))),
742
743
744                         //Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
745
746                         /*
747                           Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
748
749                           Matrix.rotate(new Vec(0,0,1), (float)(3*Math.PI/2)),
750                           Matrix.rotate(new Vec(1,0,0), (float)(2*Math.PI/2)),
751                         */
752
753                         //Matrix.rotate(new Vec(0,0,1), (float)(2*Math.PI/2)),
754                         //Matrix.scale(1,-1,1),
755
756                         //Matrix.translate(new Vec( 0.2f, 0,0))
757                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2)))
758                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
759
760                         //Matrix.translate(new Vec(-0.2f, 0,0))
761                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 3*Math.PI/2)))
762                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
763             
764                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
765                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
766                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
767                         //Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2))
768                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
769                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
770
771                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
772                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
773                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
774                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
775                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
776                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
777
778                         Matrix.ONE,
779                     };
780                     fixupTile();
781                 }}});
782             tileMenu.add(new MyMenuItem("Hammerhead") { public void hit() {
783                 synchronized(this) {
784                     setTile(new Mesh(false));
785                     Matrix mm = Matrix.ONE;
786                     float height1 = .1f;
787                     float height2 = .1f;
788                     float width  = .4f;
789                     float depth  = .1f;
790                     // top
791                     Point a1 = new Point( -width/2,         height2/2, -depth/2);
792                     Point b1 = new Point(        0,         height2/2, -depth/2);
793                     Point c1 = new Point(        0, height1+height2/2, -depth/2);
794                     Point d1 = new Point(  width/2, height1+height2/2, -depth/2);
795                     Point e1 = new Point(  width/2,         height2/2, -depth/2);
796                     Point f1 = new Point(  width/2,        -height2/2, -depth/2);
797                     Point g1 = new Point(  width/2,-height1-height2/2, -depth/2);
798                     Point h1 = new Point(        0,-height1-height2/2, -depth/2);
799                     Point i1 = new Point(        0,        -height2/2, -depth/2);
800                     Point j1 = new Point( -width/2,        -height2/2, -depth/2);
801                     Point a2 = new Point( -width/2,         height2/2,  depth/2);
802                     Point b2 = new Point(        0,         height2/2,  depth/2);
803                     Point c2 = new Point(        0, height1+height2/2,  depth/2);
804                     Point d2 = new Point(  width/2, height1+height2/2,  depth/2);
805                     Point e2 = new Point(  width/2,         height2/2,  depth/2);
806                     Point f2 = new Point(  width/2,        -height2/2,  depth/2);
807                     Point g2 = new Point(  width/2,-height1-height2/2,  depth/2);
808                     Point h2 = new Point(        0,-height1-height2/2,  depth/2);
809                     Point i2 = new Point(        0,        -height2/2,  depth/2);
810                     Point j2 = new Point( -width/2,        -height2/2,  depth/2);
811
812                     quad(tile, mm, a1, b1, i1, j1);
813                     quad(tile, mm, c1, d1, e1, b1);
814                     quad(tile, mm, b1, e1, f1, i1);
815                     quad(tile, mm, i1, f1, g1, h1);
816
817                     quad(tile, mm, j2, i2, b2, a2);
818                     quad(tile, mm, b2, e2, d2, c2);
819                     quad(tile, mm, i2, f2, e2, b2);
820                     quad(tile, mm, h2, g2, f2, i2);
821
822                     quad(tile, mm, d1, d2, e2, e1);
823                     quad(tile, mm, e1, e2, f2, f1);
824                     quad(tile, mm, f1, f2, g2, g1);
825                     quad(tile, mm, h1, g1, g2, h2);
826                     quad(tile, mm, i2, i1, h1, h2);
827                     quad(tile, mm, j1, i1, i2, j2);
828                     quad(tile, mm, a2, a1, j1, j2);
829                     quad(tile, mm, a1, a2, b2, b1);
830                     quad(tile, mm, c2, c1, b1, b2);
831                     quad(tile, mm, c1, c2, d2, d1);
832
833                     transforms = new Matrix[] {
834
835                         mm.times(Matrix.translate(new Vec(   width,     0, 0))),
836                         mm.times(Matrix.translate(new Vec(  -width,     0, 0))),
837                         mm.times(Matrix.translate(new Vec(-width/2, height1+height2, 0))),
838                         mm.times(Matrix.translate(new Vec( width/2, height1+height2, 0))),
839                         mm.times(Matrix.translate(new Vec(-width/2,-height1-height2, 0))),
840                         mm.times(Matrix.translate(new Vec( width/2,-height1-height2, 0))),
841
842                         mm.times(Matrix.translate(new Vec(   width/2,     0,  depth))),
843                         mm.times(Matrix.translate(new Vec(  -width/2,     0,  depth))),
844                         mm.times(Matrix.translate(new Vec(         0, height1+height2,  depth))),
845                         mm.times(Matrix.translate(new Vec(         0,-height1-height2,  depth))),
846
847                         mm.times(Matrix.translate(new Vec(   width/2,     0, -depth))),
848                         mm.times(Matrix.translate(new Vec(  -width/2,     0, -depth))),
849                         mm.times(Matrix.translate(new Vec(         0, height1+height2, -depth))),
850                         mm.times(Matrix.translate(new Vec(         0,-height1-height2, -depth))),
851
852                         Matrix.ONE
853                     };
854                     fixupTile();
855                 }}});
856
857             // Finally, add all the menus to the menu bar.
858             add(tileMenu);
859             add(goalMenu);
860             add(hideMenu);
861         }
862
863     }
864
865     private static Matrix preMultiplyTranslationalComponentBy(Matrix mthis, Matrix mm) {
866         Vec v = mm.times(mthis.getTranslationalComponent());
867         return new Matrix(mthis.a, mthis.b, mthis.c, v.x,
868                           mthis.e, mthis.f, mthis.g, v.y,
869                           mthis.i, mthis.j, mthis.k, v.z,
870                           mthis.m, mthis.n, mthis.o, 1);
871     }
872
873 //////////////////////////////////////////////////////////////////////////////
874     public void breakit() {
875         int oldverts = verts;
876         if (verts > 2000 && !force) return;
877         force = false;
878         System.out.println("doubling vertices.");
879         PriorityQueue<Mesh.E> es = new PriorityQueue<Mesh.E>();
880         for(Mesh.T t : tile) {
881             es.add(t.e1());
882             es.add(t.e2());
883             es.add(t.e3());
884             Thread.yield();
885             repaint();
886         }
887         for(int i=0; i<Math.min(oldverts,50); i++) {
888             Mesh.E e = es.poll();
889             verts++;
890             e.shatter();
891             Thread.yield();
892             repaint();
893         }
894         System.out.println("now have " + verts + " vertices; max is 2000");
895         tile.rebindPoints();
896     }
897
898     public boolean rand(double temp, Mesh.Vertex p) {
899
900         p.reComputeErrorAround();
901         double tile_error = tile.error();
902         double goal_error = goal.error();
903
904         float max = p.averageEdgeLength()/5;
905         Vec v = new Vec(random.nextFloat(), random.nextFloat(), random.nextFloat());
906         v = v.norm().times((random.nextFloat() - 0.5f) * max);
907         Matrix m = Matrix.translate(v);
908         boolean good = p.move(v, false);
909         if (!good) { return false; }
910
911         double new_tile_error = tile.error();
912         double new_goal_error = goal.error();
913         double tile_delta = (new_tile_error - tile_error) / tile_error;
914         double goal_delta = (new_goal_error - goal_error) / goal_error;
915         double delta = tile_delta + goal_delta;
916         double swapProbability = Math.exp((-1 * delta) / temp);
917
918         //boolean doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
919         //boolean doSwap = good && (tile_delta + goal_delta <= 0);
920         //if (temp < 0.000001) doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
921         boolean doSwap = good && (Math.random() < swapProbability);
922
923         // always move uphill if possible -- potential idea
924         if (tile_delta <= 0 && goal_delta <= 0 && good) doSwap = true;
925         if (hillclimb)
926             doSwap = tile_delta <= 0 && goal_delta <= 0 && good;
927
928         if (doSwap) {
929             tile_error = new_tile_error;
930             goal_error = new_goal_error;
931             hits++;
932             p.goodp = p.p;
933         } else {
934             p.move(v.times(-1), true);
935             misses++;
936         }
937         p.reComputeErrorAround();
938         return true;
939     }
940
941     float hits = 0;
942     float misses = 0;
943     public void anneal() throws Exception {
944         double hightemp = 1;
945         temp = hightemp;
946         double last = 10;
947         boolean seek_upward = false;
948         double acceptance = 1;
949         while(true) {
950             synchronized(this) {
951                 if (!anneal) { repaint(); Thread.sleep(10); continue; }
952
953             double ratio = (hits+misses==0) ? 1 : (hits / (hits+misses));
954             hits = 0;
955             misses = 0;
956             double gamma = 1;
957             acceptance = (ratio+acceptance)/2;
958             accepts = (int)(Math.ceil(ratio*100));
959             temps = (int)(Math.ceil(temp*1000));
960             vertss = tile.size();
961             if (breaks > 0) {
962                 while (breaks>0) {
963                     breaks--;
964                     breakit();
965                 }
966                 seek_upward = true;
967                 continue;
968             } else if (acceptance > 0.96) gamma = 0.1f;
969             else if (acceptance > 0.9)    gamma = 0.2f;
970             else if (acceptance > 0.8)    gamma = 0.3f;
971             else if (acceptance > 0.6)    gamma = 0.4f;
972             else if (acceptance > 0.3)    gamma = 0.8f;
973             else if (acceptance > 0.15)   gamma = 0.94f;
974             else if (acceptance > 0.10)   gamma = 0.98f;
975
976             else if (acceptance < 0.01)   breaks++;
977
978             if (seek_upward) {
979                 if (acceptance > 0.25) seek_upward = false;
980                 else gamma = 2-gamma;
981             }
982
983             if (anneal)
984                 temp = temp * gamma;
985
986
987             HashSet<Mesh.Vertex> hs = new HashSet<Mesh.Vertex>();
988             for(Mesh.Vertex p : tile.vertices()) hs.add(p);
989             Mesh.Vertex[] pts = (Mesh.Vertex[])hs.toArray(new Mesh.Vertex[0]);
990
991             int count = 0;
992             long then = System.currentTimeMillis();
993             for(int i=0; i<300; i++) {
994                 if (anneal) {
995                     Mesh.Vertex v = pts[Math.abs(random.nextInt()) % pts.length];
996                     if (breaks>0) break;
997                     if (!rand(temp,v)) { i--; continue; }
998                     v.recomputeFundamentalQuadricIfStale();
999                     v.recomputeFundamentalQuadricIfNeighborChanged();
1000                     count++;
1001                 }
1002                 Thread.yield();
1003                 repaint();
1004             }
1005
1006             System.out.println("temp="+temp + " ratio="+(Math.ceil(acceptance*100)) + " " +
1007                                "points_per_second=" +
1008                                (count*1000)/((double)(System.currentTimeMillis()-then)));
1009             for(Mesh.Vertex p : goal.vertices()) {
1010                 p.quadricStale = true;
1011                 p.recomputeFundamentalQuadricIfNeighborChanged();
1012             }
1013             }
1014         }
1015     }
1016
1017
1018     public static void main(String[] s) throws Exception {
1019         JFrame f = new JFrame();
1020         f.setLayout(new BorderLayout());
1021         Main main = new Main(f);
1022         f.add(main, BorderLayout.CENTER);
1023         f.setJMenuBar(main.new MyMenuBar());
1024         f.pack();
1025         f.show();
1026         f.setSize(900, 900);
1027         f.doLayout();
1028         main.anneal();
1029     }
1030
1031
1032 }