b7df3d4a848e231fa1ac7f04f7ffa9543faecfdb
[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                         if (t1v1.distance(t2.v1().p) < MATCHING_EPSILON &&
174                             t1v2.distance(t2.v3().p) < MATCHING_EPSILON &&
175                             t1v3.distance(t2.v2().p) < MATCHING_EPSILON) {
176                             t2.e3().bindEdge(t1.e1(), m);
177                             t2.e2().bindEdge(t1.e2(), m);
178                             t2.e1().bindEdge(t1.e3(), m);
179                         }
180                         if (t1v2.distance(t2.v1().p) < MATCHING_EPSILON &&
181                             t1v3.distance(t2.v3().p) < MATCHING_EPSILON &&
182                             t1v1.distance(t2.v2().p) < MATCHING_EPSILON) {
183                             t2.e3().bindEdge(t1.e2(), m);
184                             t2.e2().bindEdge(t1.e3(), m);
185                             t2.e1().bindEdge(t1.e1(), m);
186                         }
187                         if (t1v3.distance(t2.v1().p) < MATCHING_EPSILON &&
188                             t1v1.distance(t2.v3().p) < MATCHING_EPSILON &&
189                             t1v2.distance(t2.v2().p) < MATCHING_EPSILON) {
190                             t2.e3().bindEdge(t1.e3(), m);
191                             t2.e2().bindEdge(t1.e1(), m);
192                             t2.e1().bindEdge(t1.e2(), m);
193                         }
194                         if (t1v1.distance(t2.v1().p) < MATCHING_EPSILON &&
195                             t1v2.distance(t2.v2().p) < MATCHING_EPSILON &&
196                             t1v3.distance(t2.v3().p) < MATCHING_EPSILON) {
197                             t2.e1().bindEdge(t1.e1().pair, m);
198                             t2.e2().bindEdge(t1.e2().pair, m);
199                             t2.e3().bindEdge(t1.e3().pair, m);
200                         }
201                         if (t1v2.distance(t2.v1().p) < MATCHING_EPSILON &&
202                             t1v3.distance(t2.v2().p) < MATCHING_EPSILON &&
203                             t1v1.distance(t2.v3().p) < MATCHING_EPSILON) {
204                             t2.e2().bindEdge(t1.e1().pair, m);
205                             t2.e3().bindEdge(t1.e2().pair, m);
206                             t2.e1().bindEdge(t1.e3().pair, m);
207                         }
208                         if (t1v3.distance(t2.v1().p) < MATCHING_EPSILON &&
209                             t1v1.distance(t2.v2().p) < MATCHING_EPSILON &&
210                             t1v2.distance(t2.v3().p) < MATCHING_EPSILON) {
211                             t2.e3().bindEdge(t1.e1().pair, m);
212                             t2.e1().bindEdge(t1.e2().pair, m);
213                             t2.e2().bindEdge(t1.e3().pair, m);
214                         }
215                     }
216                 }
217             }
218         }
219         tile.rebindPoints();
220         System.out.println("tile volume: " + tile.volume());
221         System.out.println("goal volume: " + goal.volume());
222         tile.error_against = goal;
223         goal.error_against = tile;
224         fixupGoal();
225     }
226
227
228     public class MyMenuItem extends JMenuItem implements ActionListener {
229         public MyMenuItem(String s) {
230             super(s);
231             this.addActionListener(this);
232         }
233         public void actionPerformed(ActionEvent event) {
234             synchronized(Main.this) {
235                     hit();
236             }
237         }
238         public void hit() {}
239     }
240
241     public void hexBrick(boolean offset, boolean rotated) {
242         setTile(new Mesh(false));
243                 float width  = (float)0.8;
244                 float depth  = (float)0.08;
245                 float height = (float)0.4;
246                 float rshift =   width/2;
247                 float lshift = -(width/2);
248                 float halfup = 0;
249                 Point ltf = new Point(lshift,  (depth/2),  (height/2));
250                 Point mtf = new Point( 0.0,    (depth/2),  (height/2));
251                 Point rtf = new Point(rshift,  (depth/2),  (height/2));
252                 Point lbf = new Point(lshift, -(depth/2),  (height/2));
253                 Point mbf = new Point( 0.0,   -(depth/2),  (height/2));
254                 Point rbf = new Point(rshift, -(depth/2),  (height/2));
255
256                 Point ltc = new Point(lshift,  (depth/2), 0);
257                 Point mtc = new Point( 0.0,    (depth/2), 0);
258                 Point rtc = new Point(rshift,  (depth/2), 0);
259                 Point lbc = new Point(lshift, -(depth/2), 0);
260                 Point mbc = new Point( 0.0,   -(depth/2), 0);
261                 Point rbc = new Point(rshift, -(depth/2), 0);
262
263                 Point ltn = new Point(lshift,  (depth/2), -(height/2));
264                 Point mtn = new Point( 0.0,    (depth/2), -(height/2));
265                 Point rtn = new Point(rshift,  (depth/2), -(height/2));
266                 Point lbn = new Point(lshift, -(depth/2), -(height/2));
267                 Point mbn = new Point( 0.0,   -(depth/2), -(height/2));
268                 Point rbn = new Point(rshift, -(depth/2), -(height/2));
269
270         
271                 Point[] points = new Point[] {
272                     ltf,
273                     mtf,
274                     rtf,
275                     lbf,
276                     mbf,
277                     rbf,
278
279                     ltc,
280                     mtc,
281                     rtc,
282                     lbc,
283                     mbc,
284                     rbc,
285
286                     ltn,
287                     mtn,
288                     rtn,
289                     lbn,
290                     mbn,
291                     rbn
292                 };
293
294
295                 // top
296                 tile.newT(ltf, mtf, mtc, null, 1);
297                 tile.newT(mtc, ltc, ltf, null, 1);
298                 tile.newT(mtf, rtf, rtc, null, 1);
299                 tile.newT(rtc, mtc, mtf, null, 1);
300
301                 tile.newT(ltc, mtc, mtn, null, 1);
302                 tile.newT(mtn, ltn, ltc, null, 1);
303                 tile.newT(mtc, rtc, rtn, null, 1);
304                 tile.newT(rtn, mtn, mtc, null, 1);
305
306                 // bottom (swap normals)
307                 tile.newT(mbf, lbf, mbc, null, 2);
308                 tile.newT(lbc, mbc, lbf, null, 2);
309                 tile.newT(rbf, mbf, rbc, null, 2);
310                 tile.newT(mbc, rbc, mbf, null, 2);
311
312                 tile.newT(mbc, lbc, mbn, null, 2);
313                 tile.newT(lbn, mbn, lbc, null, 2);
314
315                 tile.newT(rbc, mbc, rbn, null, 2);
316                 tile.newT(mbn, rbn, mbc, null, 2);
317
318
319                 // left
320                 tile.newT(ltf, ltc, lbc, null, 3);
321                 tile.newT(lbc, lbf, ltf, null, 3);
322                 tile.newT(ltc, ltn, lbn, null, 3);
323                 tile.newT(lbn, lbc, ltc, null, 3);
324
325                 // right (swap normals)
326                 tile.newT(rtc, rtf, rbc, null, 4);
327                 tile.newT(rbf, rbc, rtf, null, 4);
328                 tile.newT(rtn, rtc, rbn, null, 4);
329                 tile.newT(rbc, rbn, rtc, null, 4);
330
331                 // front
332                 tile.newT(ltn, mtn, mbn, null, 5);
333                 tile.newT(ltn, mbn, lbn, null, 5);
334                 tile.newT(mtn, rtn, rbn, null, 5);
335                 tile.newT(mtn, rbn, mbn, null, 5);
336
337                 // back
338                 tile.newT(mtf, ltf, mbf, null, 6);
339                 tile.newT(mbf, ltf, lbf, null, 6);
340                 tile.newT(rtf, mtf, rbf, null, 6);
341                 tile.newT(rbf, mtf, mbf, null, 6);
342
343                 if (offset) {
344                   transforms = new Matrix[] {
345                       Matrix.translate(new Vec(lshift,      0,    height)),
346                       Matrix.translate(new Vec(lshift,      0,   -height)),
347                       Matrix.translate(new Vec(rshift,      0,    height)),
348                       Matrix.translate(new Vec(rshift,      0,   -height)),
349                       Matrix.translate(new Vec( width,      0,         0)),
350                       Matrix.translate(new Vec(-width,      0,         0)),
351                       Matrix.translate(new Vec(lshift,  depth, 0)),
352                       Matrix.translate(new Vec(lshift, -depth, 0)),
353                       Matrix.translate(new Vec(rshift,  depth, 0)),
354                       Matrix.translate(new Vec(rshift, -depth, 0)),
355                       Matrix.ONE,
356                   };
357                 } else if (rotated) {
358                     HashSet<Mesh.E> es = new HashSet<Mesh.E>();
359                     for(Mesh.T t : tile) {
360                         es.add(t.e1());
361                         es.add(t.e2());
362                         es.add(t.e3());
363                     }
364                     for(Mesh.E e : es) {
365                         if (e.v1.p.x == e.v2.p.x && e.v1.p.y == e.v2.p.y) continue;
366                         if (e.v1.p.z == e.v2.p.z && e.v1.p.y == e.v2.p.y) continue;
367                         if (e.v1.p.x == e.v2.p.x && e.v1.p.z == e.v2.p.z) continue;
368                         e.shatter();
369                     }
370                     transforms = new Matrix[] {
371                         Matrix.translate(new Vec(0,   0,    height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
372                         Matrix.translate(new Vec(0,   0,   -height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
373                         Matrix.translate(new Vec(0,  depth, 0)),
374                         Matrix.translate(new Vec(0, -depth, 0)),
375                         Matrix.translate(new Vec( width,  0,    0)),
376                         Matrix.translate(new Vec(-width,  0,    0)),
377                       Matrix.ONE,
378                   };
379                 } else {
380                   transforms = new Matrix[] {
381                       Matrix.translate(new Vec(lshift,      0,    height)),
382                       Matrix.translate(new Vec(lshift,      0,   -height)),
383                       Matrix.translate(new Vec(rshift,      0,    height)),
384                       Matrix.translate(new Vec(rshift,      0,   -height)),
385                       Matrix.translate(new Vec( width,      0,         0)),
386                       Matrix.translate(new Vec(-width,      0,         0)),
387                       Matrix.translate(new Vec(0,  depth, 0)),
388                       Matrix.translate(new Vec(0, -depth, 0)),
389                       Matrix.ONE,
390                   };
391                 }
392                 
393                 fixupTile();
394     }
395
396     public class MyMenuBar extends JMenuBar {
397
398         public MyMenuBar() {
399
400             JMenu tileMenu = new JMenu("Tile");
401             JMenu goalMenu = new JMenu("Goal");
402             JMenu hideMenu = new JMenu("Actions");
403
404             hideMenu.add(new MyMenuItem("Start Anneal") { public void hit() { anneal = true; }});
405             hideMenu.add(new MyMenuItem("Stop Anneal") { public void hit() { anneal = false; }});
406             hideMenu.add(new MyMenuItem("Reset to high temperature") { public void hit() { temp = 1; }});
407             hideMenu.add(new MyMenuItem("Subdivide surface") { public void hit() { breaks++; }});
408             hideMenu.add(new MyMenuItem("Show Goal") { public void hit() { goalon = true; }});
409             hideMenu.add(new MyMenuItem("Hide Goal") { public void hit() { goalon = false; }});
410             hideMenu.add(new MyMenuItem("Show All Neighbors") { public void hit() { neighbors = true; }});
411             hideMenu.add(new MyMenuItem("Show One Neighbor Wireframe") { public void hit() { neighborsWireOne = true; }});
412             hideMenu.add(new MyMenuItem("Show All Neighbors Wireframe") { public void hit() { neighborsWire = true; neighborsWireOne = false;}});
413             hideMenu.add(new MyMenuItem("Hide Neighbors") { public void hit() { neighborsWire = false; neighborsWireOne = false; neighbors = false; }});
414
415             goalMenu.add(new MyMenuItem("Fish with face") { public void hit() {
416                 loadGoal("face.stl");
417                 //goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
418                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
419                 //goal.transform(Matrix.scale(1, 2.2f, 1));
420                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
421                 //factor = factor * 0.8f;
422                 goal.transform(Matrix.scale(0.3f));
423                 //goal.transform(Matrix.rotate(new Vec(0,1,0), (float)(Math.PI*1/3)));
424                 goal.transform(Matrix.rotate(new Vec(1,0,0), (float)(Math.PI*1/3)));
425                 fixupGoal(true, false);
426                 //goal.transform(Matrix.translate(new Vec(0.145f, 0, 0)));
427                 fixupGoal(false, true);
428             }});
429             goalMenu.add(new MyMenuItem("Fish") { public void hit() {
430                 loadGoal("fish.stl");
431                 //goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
432                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
433                 goal.transform(Matrix.scale(1, 2.2f, 1));
434                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
435                 factor = factor * 0.8f;
436                 goal.transform(Matrix.scale(factor));
437                 fixupGoal(true, false);
438                 //goal.transform(Matrix.translate(new Vec(0.145f, 0, 0)));
439                 fixupGoal(false, true);
440             }});
441             goalMenu.add(new MyMenuItem("Hammerhead Fish") { public void hit() {
442                 loadGoal("fish.stl");
443                 goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
444                 goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(3*Math.PI/2)));
445                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
446                 factor *= 0.75f;
447                 goal.transform(Matrix.scale(factor));
448                 fixupGoal();
449             }});
450             goalMenu.add(new MyMenuItem("Vertical Fish") { public void hit() {
451                 loadGoal("fish.stl");
452                 //goal.transform(Matrix.rotate(new Vec(0, 0, 1), (float)(Math.PI/2)));
453                 //goal.transform(Matrix.rotate(new Vec(1, 0, 0), (float)(Math.PI/2)));
454                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
455                 goal.transform(Matrix.scale(factor/1.6f));
456                 fixupGoal();
457             }});
458             goalMenu.add(new MyMenuItem("Torus") { public void hit() {
459                 loadGoal("torus.stl");
460                 float factor = (float)Math.pow(tile.volume() / goal.volume(), 1.0/3.0);
461                 goal.transform(Matrix.scale(factor/2.5f));
462                 goal.transform(Matrix.rotate(new Vec(0, 1, 0), (float)(Math.PI/2)));
463                 fixupGoal();
464             }});
465             tileMenu.add(new MyMenuItem("Hex Brick") { public void hit() {
466                 hexBrick(false, false);
467             }});
468             tileMenu.add(new MyMenuItem("Hex Brick, offset planes") { public void hit() {
469                 hexBrick(true, false);
470             }});
471             tileMenu.add(new MyMenuItem("Hex Brick, rotated") { public void hit() {
472                 hexBrick(false, true);
473             }});
474             tileMenu.add(new MyMenuItem("Temp (do not use)") { public void hit() {
475                 setTile(new Mesh(false));
476                 float width  = (float)0.8;
477                 float depth  = (float)0.08;
478                 float height = (float)0.4;
479
480                 float rshift =   width/2;
481                 float lshift = -(width/2);
482                 float halfup = 0;
483                 //float shift = height/2;
484                 //width = (width*2)/3;
485                 float shift = 0;
486                 transforms = new Matrix[] {
487
488                     Matrix.translate(new Vec(lshift/2,  depth,    -shift)),
489                     Matrix.translate(new Vec(rshift/2,  depth,    -shift)),
490                     Matrix.translate(new Vec(lshift/2, -depth,    -shift)),
491                     Matrix.translate(new Vec(rshift/2, -depth,    -shift)),
492
493                     Matrix.translate(new Vec(lshift,  depth/2,    -shift)),
494                     Matrix.translate(new Vec(rshift,  depth/2,    -shift)),
495                     Matrix.translate(new Vec(lshift, -depth/2,    -shift)),
496                     Matrix.translate(new Vec(rshift, -depth/2,    -shift)),
497
498
499                     /*
500                       Matrix.translate(new Vec(lshift,  depth,    -shift)),
501                       Matrix.translate(new Vec(rshift,  depth,    -shift)),
502                       Matrix.translate(new Vec(lshift, -depth,    -shift)),
503                       Matrix.translate(new Vec(rshift, -depth,    -shift)),
504                     */
505                     /*
506                       Matrix.translate(new Vec(lshift,  depth,    shift)),
507                       Matrix.translate(new Vec(rshift,  depth,    shift)),
508                       Matrix.translate(new Vec(lshift, -depth,    shift)),
509                       Matrix.translate(new Vec(rshift, -depth,    shift)),
510                     */
511                     //Matrix.translate(new Vec(0,  depth,    0)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
512                     //Matrix.translate(new Vec(0, -depth,    0)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
513                     //Matrix.translate(new Vec(0,   0,    height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
514                     //Matrix.translate(new Vec(0,   0,   -height)).times(Matrix.rotate(new Vec(0, 0, 1), (float)Math.PI)),
515
516                     //Matrix.translate(new Vec(0,  depth, 0)),
517                     //Matrix.translate(new Vec(0, -depth, 0)),
518                     Matrix.translate(new Vec(0,      0,  height)),
519                     Matrix.translate(new Vec(0,      0, -height)),
520
521                     //Matrix.translate(new Vec(lshift,        depth,  height/2)),
522                     //Matrix.translate(new Vec(lshift,        depth, -height/2)),
523                     //Matrix.translate(new Vec(rshift,       -depth,  height/2)),
524                     //Matrix.translate(new Vec(rshift,       -depth, -height/2)),
525                     //Matrix.translate(new Vec(rshift,       0,  height)),
526                     //Matrix.translate(new Vec(rshift,       0, -height)),
527
528                     Matrix.translate(new Vec( width,           0,    0)),
529                     Matrix.translate(new Vec(-width,           0,    0)),
530
531                     Matrix.ONE
532                 };
533                 fixupTile();
534             } });
535             tileMenu.add(new MyMenuItem("Dense Packing (hex)") { public void hit() {
536                 setTile(new Mesh(false));
537                 float width  = (float)3.2;
538                 float depth  = (float)0.32;
539                 float height = (float)1.6;
540                 float unit = 0.4f;
541                 float r = unit/2;
542                 float sin = (float)(unit * Math.sin(Math.PI/3));
543                 float cos = (float)(unit * Math.cos(Math.PI/3));
544                 float x = (float)(r*Math.tan(Math.PI/6));
545                 float z = (float)(r/Math.cos(Math.PI/6));
546                 height = 2*r*(float)Math.sqrt(2f/3f);
547
548                 /*
549                 r *= 0.3f;
550                 cos *= 0.3f;
551                 unit *= 0.3f;
552                 */
553
554                 /*
555                   sin *= 0.3f;
556                   x *= 0.3f;
557                   z *= 0.3f;
558                 */
559                 transforms = new Matrix[] {
560                     Matrix.translate(new Vec(-unit, 0, 0)),
561                     Matrix.translate(new Vec( unit, 0, 0)),
562                     Matrix.translate(new Vec(-cos,  0,  sin)),
563                     Matrix.translate(new Vec( cos,  0,  sin)),
564                     Matrix.translate(new Vec(-cos,  0, -sin)),
565                     Matrix.translate(new Vec( cos,  0, -sin)),
566                     Matrix.translate(new Vec( 0,  height, z)),
567                     Matrix.translate(new Vec(-r,  height, -x)),
568                     Matrix.translate(new Vec( r,  height, -x)),
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.ONE,
573                 };
574                 generateTile(transforms, tile);
575                 fixupTile();
576             } });
577             tileMenu.add(new MyMenuItem("Slim Dense Packing (Cubic)") { public void hit() {
578                 setTile(new Mesh(false));
579                 float unit = 0.4f;
580                 float r = unit/2;
581                 float sin = (float)(unit * Math.sin(Math.PI/3));
582                 float cos = (float)(unit * Math.cos(Math.PI/3));
583                 float x = (float)(r*Math.tan(Math.PI/6));
584                 float z = (float)(r/Math.cos(Math.PI/6));
585                 float height = 2*r*(float)Math.sqrt(2f/3f);
586
587                 transforms = new Matrix[] {
588                     Matrix.translate(new Vec(-unit, 0, 0)),
589                     Matrix.translate(new Vec( unit, 0, 0)),
590                     Matrix.translate(new Vec(-cos,  0,  sin)),
591                     Matrix.translate(new Vec( cos,  0,  sin)),
592                     Matrix.translate(new Vec(-cos,  0, -sin)),
593                     Matrix.translate(new Vec( cos,  0, -sin)),
594
595                     /*
596                     Matrix.translate(new Vec( 0,  height, -z)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
597                     Matrix.translate(new Vec(-r,  height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
598                     Matrix.translate(new Vec( r,  height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
599                     Matrix.translate(new Vec( 0, -height, -z)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
600                     Matrix.translate(new Vec(-r, -height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
601                     Matrix.translate(new Vec( r, -height,  x)).times(Matrix.rotate(new Vec(0,1,0), (float)Math.PI)),
602                     */
603
604                     Matrix.translate(new Vec( 0,  height, -z)),
605                     Matrix.translate(new Vec(-r,  height,  x)),
606                     Matrix.translate(new Vec( r,  height,  x)),
607                     Matrix.translate(new Vec( 0, -height,  z)),
608                     Matrix.translate(new Vec(-r, -height, -x)),
609                     Matrix.translate(new Vec( r, -height, -x)),
610                     //Matrix.rotate(new Vec(0,0,1), (float)Math.PI),
611
612                     /*
613                     Matrix.translate(new Vec( 0,  height, -z)).times(Matrix.scale(-1,1,-1)),
614                     Matrix.translate(new Vec(-r,  height,  x)).times(Matrix.scale(-1,1,-1)),
615                     Matrix.translate(new Vec( r,  height,  x)).times(Matrix.scale(-1,1,-1)),
616                     Matrix.translate(new Vec( 0, -height, -z)).times(Matrix.scale(-1,1,-1)),
617                     Matrix.translate(new Vec(-r, -height,  x)).times(Matrix.scale(-1,1,-1)),
618                     Matrix.translate(new Vec( r, -height,  x)).times(Matrix.scale(-1,1,-1)),
619                     */
620                     Matrix.ONE
621                 };
622                 generateTile(transforms, tile);
623                 //for(int i=0; i<transforms.length; i++) transforms[i] = Matrix.translate(m.times(vecs[i]));
624                 Matrix m = Matrix.scale(1.9f, 1f ,1);
625                 //Matrix m = Matrix.scale(1f, 2.1f, 1f);
626                 tile.transform(m);
627                 for(int i=0; i<transforms.length; i++)
628                     transforms[i] = preMultiplyTranslationalComponentBy(transforms[i], m);
629                 fixupTile();
630
631             } });
632             tileMenu.add(new MyMenuItem("Genus-1") { public void hit() {
633                 synchronized(this) {
634                     setTile(new Mesh(false));
635                     Matrix mm = Matrix.scale(0.1f);
636                     float height = 4;
637                     float width  = 4;
638                     float depth  = 1;
639                     // top
640                     quad(tile, mm, 
641                          new Point( 2,  2,  0),
642                          new Point( 1,  1, -1),
643                          new Point(-1,  1, -1),
644                          new Point(-2,  2,  0));
645                     quad(tile, mm, 
646                          new Point(-2,  2,  0),
647                          new Point(-1,  1,  1),
648                          new Point( 1,  1,  1),
649                          new Point( 2,  2,  0));
650                     quad(tile, mm, 
651                          new Point( 1,  1, -1),
652                          new Point( 1,  1,  1),
653                          new Point(-1,  1,  1),
654                          new Point(-1,  1, -1));
655
656                     // bottom
657                     quad(tile, mm, 
658                          new Point(-2, -2,  0),
659                          new Point(-1, -1, -1),
660                          new Point( 1, -1, -1),
661                          new Point( 2, -2,  0));
662                     quad(tile, mm, 
663                          new Point( 2, -2,  0),
664                          new Point( 1, -1,  1),
665                          new Point(-1, -1,  1),
666                          new Point(-2, -2,  0));
667                     quad(tile, mm, 
668                          new Point(-1, -1, -1),
669                          new Point(-1, -1,  1),
670                          new Point( 1, -1,  1),
671                          new Point( 1, -1, -1));
672
673                     // left
674                     quad(tile, mm, 
675                          new Point( 2, -2,  0),
676                          new Point( 1, -1, -1),
677                          new Point( 1,  1, -1),
678                          new Point( 2,  2,  0));
679                     quad(tile, mm, 
680                          new Point( 2,  2,  0),
681                          new Point( 1,  1,  1),
682                          new Point( 1, -1,  1),
683                          new Point( 2, -2,  0));
684                     quad(tile, mm, 
685                          new Point( 1, -1, -1),
686                          new Point( 1, -1,  1),
687                          new Point( 1,  1,  1),
688                          new Point( 1,  1, -1));
689
690                     // bottom
691                     quad(tile, mm, 
692                          new Point(-2,  2,  0),
693                          new Point(-1,  1, -1),
694                          new Point(-1, -1, -1),
695                          new Point(-2, -2,  0));
696                     quad(tile, mm, 
697                          new Point(-2, -2,  0),
698                          new Point(-1, -1,  1),
699                          new Point(-1,  1,  1),
700                          new Point(-2,  2,  0));
701                     quad(tile, mm, 
702                          new Point(-1,  1, -1),
703                          new Point(-1,  1,  1),
704                          new Point(-1, -1,  1),
705                          new Point(-1, -1, -1));
706
707                     height = 4;
708                     width  = 4;
709                     depth  = 1;
710
711
712                     transforms = new Matrix[] {
713                         Matrix.translate(new Vec(0, 0.2f,0))
714                         .times(Matrix.rotate(new Vec(0,1,0), (float)(1*Math.PI/2))),
715
716                         Matrix.translate(new Vec(0,-0.2f,0))
717                         .times(Matrix.rotate(new Vec(0,1,0), (float)(-1*Math.PI/2))),
718
719                         Matrix.translate(new Vec( 0.2f,0,0))
720                         .times(Matrix.rotate(new Vec(1,0,0), (float)(1*Math.PI/2))),
721
722                         Matrix.translate(new Vec(-0.2f,0,0))
723                         .times(Matrix.rotate(new Vec(1,0,0), (float)(-1*Math.PI/2))),
724
725
726                         //Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
727
728                         /*
729                           Matrix.rotate(new Vec(0,0,1), (float)(1*Math.PI/2)),
730
731                           Matrix.rotate(new Vec(0,0,1), (float)(3*Math.PI/2)),
732                           Matrix.rotate(new Vec(1,0,0), (float)(2*Math.PI/2)),
733                         */
734
735                         //Matrix.rotate(new Vec(0,0,1), (float)(2*Math.PI/2)),
736                         //Matrix.scale(1,-1,1),
737
738                         //Matrix.translate(new Vec( 0.2f, 0,0))
739                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2)))
740                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
741
742                         //Matrix.translate(new Vec(-0.2f, 0,0))
743                         //.times(Matrix.rotate(new Vec(0,0,1), (float)( 3*Math.PI/2)))
744                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 3*Math.PI/2))),
745             
746                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
747                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
748                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
749                         //Matrix.rotate(new Vec(0,0,1), (float)( 1*Math.PI/2))
750                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
751                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
752
753                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
754                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
755                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
756                         //Matrix.rotate(new Vec(0,0,1), (float)( 0*Math.PI/2))
757                         //.times(Matrix.translate(new Vec(0, -0.2f, 0)))
758                         //.times(Matrix.rotate(new Vec(0,1,0), (float)( 1*Math.PI/2))),
759
760                         Matrix.ONE,
761                     };
762                     fixupTile();
763                 }}});
764             tileMenu.add(new MyMenuItem("Hammerhead") { public void hit() {
765                 synchronized(this) {
766                     setTile(new Mesh(false));
767                     Matrix mm = Matrix.ONE;
768                     float height1 = .1f;
769                     float height2 = .1f;
770                     float width  = .4f;
771                     float depth  = .1f;
772                     // top
773                     Point a1 = new Point( -width/2,         height2/2, -depth/2);
774                     Point b1 = new Point(        0,         height2/2, -depth/2);
775                     Point c1 = new Point(        0, height1+height2/2, -depth/2);
776                     Point d1 = new Point(  width/2, height1+height2/2, -depth/2);
777                     Point e1 = new Point(  width/2,         height2/2, -depth/2);
778                     Point f1 = new Point(  width/2,        -height2/2, -depth/2);
779                     Point g1 = new Point(  width/2,-height1-height2/2, -depth/2);
780                     Point h1 = new Point(        0,-height1-height2/2, -depth/2);
781                     Point i1 = new Point(        0,        -height2/2, -depth/2);
782                     Point j1 = new Point( -width/2,        -height2/2, -depth/2);
783                     Point a2 = new Point( -width/2,         height2/2,  depth/2);
784                     Point b2 = new Point(        0,         height2/2,  depth/2);
785                     Point c2 = new Point(        0, height1+height2/2,  depth/2);
786                     Point d2 = new Point(  width/2, height1+height2/2,  depth/2);
787                     Point e2 = new Point(  width/2,         height2/2,  depth/2);
788                     Point f2 = new Point(  width/2,        -height2/2,  depth/2);
789                     Point g2 = new Point(  width/2,-height1-height2/2,  depth/2);
790                     Point h2 = new Point(        0,-height1-height2/2,  depth/2);
791                     Point i2 = new Point(        0,        -height2/2,  depth/2);
792                     Point j2 = new Point( -width/2,        -height2/2,  depth/2);
793
794                     quad(tile, mm, a1, b1, i1, j1);
795                     quad(tile, mm, c1, d1, e1, b1);
796                     quad(tile, mm, b1, e1, f1, i1);
797                     quad(tile, mm, i1, f1, g1, h1);
798
799                     quad(tile, mm, j2, i2, b2, a2);
800                     quad(tile, mm, b2, e2, d2, c2);
801                     quad(tile, mm, i2, f2, e2, b2);
802                     quad(tile, mm, h2, g2, f2, i2);
803
804                     quad(tile, mm, d1, d2, e2, e1);
805                     quad(tile, mm, e1, e2, f2, f1);
806                     quad(tile, mm, f1, f2, g2, g1);
807                     quad(tile, mm, h1, g1, g2, h2);
808                     quad(tile, mm, i2, i1, h1, h2);
809                     quad(tile, mm, j1, i1, i2, j2);
810                     quad(tile, mm, a2, a1, j1, j2);
811                     quad(tile, mm, a1, a2, b2, b1);
812                     quad(tile, mm, c2, c1, b1, b2);
813                     quad(tile, mm, c1, c2, d2, d1);
814
815                     transforms = new Matrix[] {
816
817                         mm.times(Matrix.translate(new Vec(   width,     0, 0))),
818                         mm.times(Matrix.translate(new Vec(  -width,     0, 0))),
819                         mm.times(Matrix.translate(new Vec(-width/2, height1+height2, 0))),
820                         mm.times(Matrix.translate(new Vec( width/2, height1+height2, 0))),
821                         mm.times(Matrix.translate(new Vec(-width/2,-height1-height2, 0))),
822                         mm.times(Matrix.translate(new Vec( width/2,-height1-height2, 0))),
823
824                         mm.times(Matrix.translate(new Vec(   width/2,     0,  depth))),
825                         mm.times(Matrix.translate(new Vec(  -width/2,     0,  depth))),
826                         mm.times(Matrix.translate(new Vec(         0, height1+height2,  depth))),
827                         mm.times(Matrix.translate(new Vec(         0,-height1-height2,  depth))),
828
829                         mm.times(Matrix.translate(new Vec(   width/2,     0, -depth))),
830                         mm.times(Matrix.translate(new Vec(  -width/2,     0, -depth))),
831                         mm.times(Matrix.translate(new Vec(         0, height1+height2, -depth))),
832                         mm.times(Matrix.translate(new Vec(         0,-height1-height2, -depth))),
833
834                         Matrix.ONE
835                     };
836                     fixupTile();
837                 }}});
838
839             // Finally, add all the menus to the menu bar.
840             add(tileMenu);
841             add(goalMenu);
842             add(hideMenu);
843         }
844
845     }
846
847     private static Matrix preMultiplyTranslationalComponentBy(Matrix mthis, Matrix mm) {
848         Vec v = mm.times(mthis.getTranslationalComponent());
849         return new Matrix(mthis.a, mthis.b, mthis.c, v.x,
850                           mthis.e, mthis.f, mthis.g, v.y,
851                           mthis.i, mthis.j, mthis.k, v.z,
852                           mthis.m, mthis.n, mthis.o, 1);
853     }
854
855 //////////////////////////////////////////////////////////////////////////////
856     public void breakit() {
857         int oldverts = verts;
858         if (verts > 2000 && !force) return;
859         force = false;
860         System.out.println("doubling vertices.");
861         PriorityQueue<Mesh.E> es = new PriorityQueue<Mesh.E>();
862         for(Mesh.T t : tile) {
863             es.add(t.e1());
864             es.add(t.e2());
865             es.add(t.e3());
866             Thread.yield();
867             repaint();
868         }
869         for(int i=0; i<Math.min(oldverts,50); i++) {
870             Mesh.E e = es.poll();
871             verts++;
872             e.shatter();
873             Thread.yield();
874             repaint();
875         }
876         System.out.println("now have " + verts + " vertices; max is 2000");
877         tile.rebindPoints();
878     }
879
880     public boolean rand(double temp, Mesh.Vertex p) {
881
882         p.reComputeErrorAround();
883         double tile_error = tile.error();
884         double goal_error = goal.error();
885
886         float max = p.averageEdgeLength()/5;
887         Vec v = new Vec(random.nextFloat(), random.nextFloat(), random.nextFloat());
888         v = v.norm().times((random.nextFloat() - 0.5f) * max);
889         Matrix m = Matrix.translate(v);
890         boolean good = p.move(v, false);
891         if (!good) { return false; }
892
893         double new_tile_error = tile.error();
894         double new_goal_error = goal.error();
895         double tile_delta = (new_tile_error - tile_error) / tile_error;
896         double goal_delta = (new_goal_error - goal_error) / goal_error;
897         double delta = tile_delta + goal_delta;
898         double swapProbability = Math.exp((-1 * delta) / temp);
899
900         //boolean doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
901         //boolean doSwap = good && (tile_delta + goal_delta <= 0);
902         //if (temp < 0.000001) doSwap = good && (tile_delta <= 0 && goal_delta <= 0);
903         boolean doSwap = good && (Math.random() < swapProbability);
904
905         // always move uphill if possible -- potential idea
906         if (tile_delta <= 0 && goal_delta <= 0 && good) doSwap = true;
907         if (hillclimb)
908             doSwap = tile_delta <= 0 && goal_delta <= 0 && good;
909
910         if (doSwap) {
911             tile_error = new_tile_error;
912             goal_error = new_goal_error;
913             hits++;
914             p.goodp = p.p;
915         } else {
916             p.move(v.times(-1), true);
917             misses++;
918         }
919         p.reComputeErrorAround();
920         return true;
921     }
922
923     float hits = 0;
924     float misses = 0;
925     public void anneal() throws Exception {
926         double hightemp = 1;
927         temp = hightemp;
928         double last = 10;
929         boolean seek_upward = false;
930         double acceptance = 1;
931         while(true) {
932             synchronized(this) {
933                 if (!anneal) { repaint(); Thread.sleep(10); continue; }
934
935             double ratio = (hits+misses==0) ? 1 : (hits / (hits+misses));
936             hits = 0;
937             misses = 0;
938             double gamma = 1;
939             acceptance = (ratio+acceptance)/2;
940             accepts = (int)(Math.ceil(ratio*100));
941             temps = (int)(Math.ceil(temp*1000));
942             vertss = tile.size();
943             if (breaks > 0) {
944                 while (breaks>0) {
945                     breaks--;
946                     breakit();
947                 }
948                 seek_upward = true;
949                 continue;
950             } else if (acceptance > 0.96) gamma = 0.1f;
951             else if (acceptance > 0.9)    gamma = 0.2f;
952             else if (acceptance > 0.8)    gamma = 0.3f;
953             else if (acceptance > 0.6)    gamma = 0.4f;
954             else if (acceptance > 0.3)    gamma = 0.8f;
955             else if (acceptance > 0.15)   gamma = 0.94f;
956             else if (acceptance > 0.10)   gamma = 0.98f;
957
958             else if (acceptance < 0.01)   breaks++;
959
960             if (seek_upward) {
961                 if (acceptance > 0.25) seek_upward = false;
962                 else gamma = 2-gamma;
963             }
964
965             if (anneal)
966                 temp = temp * gamma;
967
968
969             HashSet<Mesh.Vertex> hs = new HashSet<Mesh.Vertex>();
970             for(Mesh.Vertex p : tile.vertices()) hs.add(p);
971             Mesh.Vertex[] pts = (Mesh.Vertex[])hs.toArray(new Mesh.Vertex[0]);
972
973             int count = 0;
974             long then = System.currentTimeMillis();
975             for(int i=0; i<300; i++) {
976                 if (anneal) {
977                     Mesh.Vertex v = pts[Math.abs(random.nextInt()) % pts.length];
978                     if (breaks>0) break;
979                     if (!rand(temp,v)) { i--; continue; }
980                     v.recomputeFundamentalQuadricIfStale();
981                     v.recomputeFundamentalQuadricIfNeighborChanged();
982                     count++;
983                 }
984                 Thread.yield();
985                 repaint();
986             }
987
988             System.out.println("temp="+temp + " ratio="+(Math.ceil(acceptance*100)) + " " +
989                                "points_per_second=" +
990                                (count*1000)/((double)(System.currentTimeMillis()-then)));
991             for(Mesh.Vertex p : goal.vertices()) {
992                 p.quadricStale = true;
993                 p.recomputeFundamentalQuadricIfNeighborChanged();
994             }
995             }
996         }
997     }
998
999
1000     public static void main(String[] s) throws Exception {
1001         JFrame f = new JFrame();
1002         f.setLayout(new BorderLayout());
1003         Main main = new Main(f);
1004         f.add(main, BorderLayout.CENTER);
1005         f.setJMenuBar(main.new MyMenuBar());
1006         f.pack();
1007         f.show();
1008         f.setSize(900, 900);
1009         f.doLayout();
1010         main.anneal();
1011     }
1012
1013
1014 }