a0e236d900c57e0f75278bdc12e14810b1e3ba88
[anneal.git] / src / edu / berkeley / qfat / MeshViewer.java
1 package edu.berkeley.qfat;
2 import java.io.*;
3 import java.nio.*;
4 import java.awt.*;
5 import java.awt.event.*;
6 import javax.swing.*;
7 import javax.media.opengl.*;
8 import javax.media.opengl.glu.*;
9 import com.sun.opengl.util.*;
10 import java.util.*;
11 import edu.berkeley.qfat.bind.*;
12 import edu.berkeley.qfat.geom.*;
13 import edu.berkeley.qfat.geom.Point;
14
15 /**
16  *  A basic MeshViewer displays zero or more meshes to the user, in
17  *  wireframe or shaded panels.
18  */
19 public class MeshViewer extends JPanel implements GLEventListener, MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {
20
21     Main main;
22
23     private float tz = 0;
24     private float anglex = 0;
25     private float angley = 0;
26
27     boolean drawEdge = false;
28
29     private Mesh.Vertex closest = null;
30     private Mesh.E      closestEdge = null;
31     private Point       closestOriginallyAt = null;
32     private int         mousex;
33     private int         mousey;
34     private Matrix      projection = null;
35     private Point       clickPoint = null;
36     private GLCanvas    glcanvas;
37     private boolean     updateVisibilities = false;
38     private boolean     updateClosest = false;
39     private boolean     mouseInside = false;
40
41     private HashSet<Mesh> meshes = new HashSet<Mesh>();
42
43     public synchronized void addMesh(Mesh m) { meshes.add(m); }
44     public synchronized void removeMesh(Mesh m) { meshes.remove(m); }
45
46     public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
47     public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { }
48
49     public void addKeyListener(KeyListener kl) { glcanvas.addKeyListener(kl); }
50
51     public synchronized void init(GLAutoDrawable gld) {
52         GL gl = glcanvas.getGL();//gld.getGL();
53         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
54         gl.glViewport(0, 0, 500, 300);
55         gl.glEnable(GL.GL_DEPTH_TEST);
56         gl.glClearDepth(1.0);
57         gl.glDepthFunc(GL.GL_LEQUAL);
58         gl.glMatrixMode(GL.GL_PROJECTION);
59         gl.glLoadIdentity();
60         gl.glMatrixMode(GL.GL_MODELVIEW);
61
62         float mat_specular[] = { 0.5f, 0.5f, 0.5f, 0.5f };
63         float mat_shininess[] = { 50.0f };
64         gl.glShadeModel(GL.GL_SMOOTH);
65         gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, new float[] { 1.0f,    4.0f,  -10.0f, 0.0f }, 0);
66         gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, new float[] { -10.0f, 10.0f,   10.0f, 0.0f }, 0);
67         gl.glLightfv(GL.GL_LIGHT2, GL.GL_POSITION, new float[] { 10.0f, -10.0f,   10.0f, 0.0f }, 0);
68         gl.glLightfv(GL.GL_LIGHT3, GL.GL_POSITION, new float[] { 10.0f,  10.0f,  -10.0f, 0.0f }, 0);
69         gl.glLightfv(GL.GL_LIGHT4, GL.GL_POSITION, new float[] { -10.0f, 10.0f,  -10.0f, 0.0f }, 0);
70         gl.glLightfv(GL.GL_LIGHT5, GL.GL_POSITION, new float[] { 10.0f, -10.0f,  -10.0f, 0.0f }, 0);
71         gl.glEnable(GL.GL_LIGHTING);
72         gl.glEnable(GL.GL_LIGHT0);
73
74         gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE);
75         gl.glEnable(GL.GL_COLOR_MATERIAL);
76
77         display(gld);
78
79         // hack to get around Mac OS bug
80         IntBuffer buf = ByteBuffer.allocateDirect(9*4*4).order(ByteOrder.nativeOrder()).asIntBuffer();
81         gl.glReadPixels(0,0, 1, 1, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf);
82     }
83
84     public synchronized final void display(GLAutoDrawable drawable) {
85         glcanvas.setSize(glcanvas.getParent().getWidth(), glcanvas.getParent().getHeight());
86
87         GL gl = glcanvas.getGL();//drawable.getGL();
88         GLU glu = new GLU();
89
90         if (!mouseInside) gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
91         else gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
92
93         gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
94         gl.glPointSize(5.0f);
95         gl.glLoadIdentity();
96         glu.gluPerspective(50, ((float)drawable.getWidth())/drawable.getHeight(), 0.5, 10);
97
98         glu.gluLookAt(0, 0, -((tz/10)-1), 0, 0, 0, 0, 1, 0);
99         gl.glRotatef(anglex/3, 0, 1, 0);
100         gl.glRotatef(-(angley/3), 1, 0, 0);
101
102         gl.glEnable(GL.GL_LIGHTING);
103         gl.glShadeModel(GL.GL_SMOOTH);
104         for(Mesh mesh : meshes) {
105             mesh.render(gl, Matrix.ONE);
106             if (main != null && main.whichNeighbor>0 && main.transforms!=null && !mesh.option_wireframe) {
107                 mesh.option_wireframe = true;
108                 mesh.render(gl, main.transforms[main.whichNeighbor-1]);
109                 mesh.option_wireframe = false;
110             }
111         }
112
113         // highlight the point closest to the mouse; we do this here to avoid flicker
114         if (closest != null) {
115             gl.glDisable(GL.GL_LIGHTING);
116             gl.glShadeModel(GL.GL_FLAT);
117
118             if (drawEdge) {
119                 for(Mesh.E e : (Iterable<Mesh.E>)closestEdge.getBoundPeers()) {
120                     gl.glColor3f(0,1,0);
121                     gl.glBegin(gl.GL_LINES);
122                     e.glVertices(gl);
123                     gl.glEnd();
124                     gl.glColor3f(1,0,0);
125                     gl.glBegin(gl.GL_LINES);
126                     e.pair.glVertices(gl);
127                     gl.glEnd();
128                 }
129                 /*
130                 if (closestEdge != null) {
131                     gl.glColor3f(1,1,0);
132                     gl.glBegin(gl.GL_TRIANGLES);
133                     Mesh.T t = closestEdge.t;
134                     t.p1().glVertex(gl);
135                     t.p2().glVertex(gl);
136                     t.p3().glVertex(gl);
137                     gl.glColor3f(0,1,1);
138                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p1()).glVertex(gl);
139                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p2()).glVertex(gl);
140                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p3()).glVertex(gl);
141                     System.out.println("t="
142                                        +t.p1()+"\n  "
143                                        +t.p2()+"\n  "
144                                        +t.p3()+"\n  "
145                                        );
146                     System.out.println("x="
147                                        +main.transforms[main.whichNeighbor-1].times(t.p1())+"\n  "
148                                        +main.transforms[main.whichNeighbor-1].times(t.p2())+"\n  "
149                                        +main.transforms[main.whichNeighbor-1].times(t.p3())+"\n  "
150                                        );
151                     gl.glEnd();
152                 }
153                 */
154             }
155
156             if (closest.visible) {
157                 gl.glDisable(GL.GL_DEPTH_TEST);
158                 gl.glColor3f(1,1,0);
159                 gl.glBegin(gl.GL_POINTS);
160                 closest.getPoint().glVertex(gl);
161                 gl.glColor3f(0.5f,0.5f,0);
162                 for(Mesh.Vertex v : (Iterable<Mesh.Vertex>)closest.getBoundPeers())
163                     if (v!=closest)
164                         v.getPoint().glVertex(gl);
165                 gl.glEnd();
166
167                 if (closest.getBindingConstraint() instanceof Plane) {
168                     Plane p = (Plane)closest.getBindingConstraint();
169                     Vec v = p.norm();
170                     gl.glEnable(gl.GL_BLEND);
171                     gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
172                     gl.glColor4f(1,1,0,0.3f);
173                     gl.glBegin(gl.GL_LINES);
174                     closest.getPoint().glVertex(gl);
175                     v.plus(closest.getPoint()).glVertex(gl);
176                     closest.getPoint().glVertex(gl);
177                     v.times(-1).plus(closest.getPoint()).glVertex(gl);
178                     gl.glEnd();
179                     gl.glDisable(gl.GL_BLEND);
180                 }
181
182                 gl.glEnable(GL.GL_DEPTH_TEST);
183             }
184         }
185
186         projection = Matrix.getProjectionMatrix(gl);
187
188         if (updateVisibilities) {
189             updateVisibilities = false;
190             // update vertex visibilities
191             updateVisibility(gl);
192         }
193
194         if (updateClosest) {
195             updateClosest = false;
196             double dist = Double.MAX_VALUE;
197             double distE = Double.MAX_VALUE;
198             closest = null;
199             closestOriginallyAt = null;
200             closestEdge = null;
201             for(Mesh mesh : meshes)
202                 if (mesh.option_selectable)
203                     for(Mesh.Vertex v : mesh.vertices()) {
204                         if (!v.visible) continue;
205                         Point p = projection.times(v.getPoint());
206                         int x = (int)p.x;
207                         int y = (int)p.y;
208                         int mousex = (int)getMouse().x;
209                         int mousey = (int)getMouse().y;
210                         if (closest==null || (x-mousex)*(x-mousex)+(y-mousey)*(y-mousey) < dist) {
211                             dist = (x-mousex)*(x-mousex)+(y-mousey)*(y-mousey);
212                             closest = v;
213                         }
214                         for(Mesh.E e = v.e; e!=null; e=e.pair.next==v.e?null:e.pair.next) {
215                             if (!e.v2.visible) continue;
216                             Segment s =
217                                 new Segment(projection.times(e.v1.getPoint()),
218                                             projection.times(e.v2.getPoint()));
219                             double dist2 = s.distance(getMouse());
220                             if (dist2 < distE) {
221                                 distE = dist2;
222                                 closestEdge = e;
223                             }
224                         }
225                     }
226         }
227     }
228
229     protected synchronized void updateVisibility(GL gl) {
230         IntBuffer buf = ByteBuffer.allocateDirect(9*4*4).order(ByteOrder.nativeOrder()).asIntBuffer();
231         gl.glFlush();
232         gl.glDrawBuffer(GL.GL_BACK);
233         gl.glReadBuffer(GL.GL_BACK);
234         gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);
235         gl.glFlush();
236         gl.glDisable(GL.GL_LIGHTING);
237         gl.glShadeModel(GL.GL_FLAT);
238         gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
239         gl.glColor3f(0,0,0);
240         for(Mesh mesh : meshes) mesh.render(gl, Matrix.ONE, true);
241         for(Mesh mesh : meshes)
242             if (mesh.option_selectable)
243                 for(Mesh.Vertex v : mesh.vertices()) {
244                     Point p = v.getPoint();
245                     Point projected = projection.times(p);
246                     boolean vis = false;
247
248                     gl.glColor3f(1,1,1);
249                     gl.glBegin(gl.GL_POINTS);
250                     p.glVertex(gl);
251                     gl.glEnd();
252                     gl.glFlush();
253
254                     vis = false;
255                     gl.glReadPixels((int)projected.x-1, (int)projected.y-1, 3, 3, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf);
256                     for(int j=0; j<9*4; j++) vis |= buf.get(j)!=0;
257
258                     v.visible = vis;
259                     if (vis) {
260                         gl.glColor3f(0,0,0);
261                         gl.glBegin(gl.GL_POINTS);
262                         p.glVertex(gl);
263                         gl.glEnd();
264                     }
265                 }
266         gl.glShadeModel(GL.GL_SMOOTH);
267         gl.glEnable(GL.GL_LIGHTING);
268         gl.glDrawBuffer(GL.GL_FRONT);
269     }
270
271     /** return the position of the mouse as a point in window-space */
272     public Point getMouse() {
273         return new Point(mousex, glcanvas.getHeight()-mousey, 0);
274     }
275
276     /** return the position where the mouse button was pressed, or null if it is not currently pressed */
277     public Point getMouseClick() {
278         return clickPoint;
279     }
280
281     public void mouseWheelMoved(MouseWheelEvent e) {
282         tz -= e.getWheelRotation();
283     }
284
285     public void keyTyped(KeyEvent e)  { }
286     public void keyPressed(KeyEvent e)  { }
287     public void keyReleased(KeyEvent e) { }
288
289     public void mouseClicked(MouseEvent e) { }
290     public void mouseEntered(MouseEvent e) { mouseInside = true; }
291     public void mouseExited(MouseEvent e) { mouseInside = false; }
292     public void mousePressed(MouseEvent e) {
293         clickPoint = getMouse();
294     }
295
296     public void mouseReleased(MouseEvent e) {
297         clickPoint = null;
298     }
299
300     public void mouseMoved(MouseEvent e) {
301         mousex = e.getX();
302         mousey = e.getY();
303
304         if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
305             updateVisibilities = true;
306             drawEdge = true;
307             updateClosest = true;
308         }
309     }
310
311     public void mouseDragged(MouseEvent e) {
312         if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
313             updateVisibilities = true;
314             drawEdge = true;
315             if (closest != null && projection != null) {
316                 synchronized(this) {
317                     if (closestOriginallyAt==null) closestOriginallyAt = closest.getPoint();
318                     Vec d1 = projection.inverse().times(getMouse()).minus(projection.inverse().times(clickPoint));
319                     Vec delta = d1.plus(closestOriginallyAt).minus(closest.getPoint());
320                     closest.move(delta, false);
321                 }
322             }
323         } else if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
324             // move edge
325         } else {
326             updateVisibilities = true;
327             anglex -= mousex - e.getX();
328             angley += mousey - e.getY();
329         }
330         mousex = e.getX();
331         mousey = e.getY();
332     }
333
334     public MeshViewer() {
335         glcanvas = new GLCanvas();
336         glcanvas.addGLEventListener(this);
337         setLayout(new BorderLayout());
338         this.add(glcanvas, BorderLayout.CENTER);
339         glcanvas.addMouseListener(this);
340         glcanvas.addMouseMotionListener(this);
341         glcanvas.addMouseWheelListener(this);
342         glcanvas.addKeyListener(this);
343     }
344
345     public void repaint() {
346         if (glcanvas != null) glcanvas.repaint();
347     }
348
349 }