// // DelaunayTest.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.rmi.RemoteException; import visad.*; import visad.java3d.DisplayImplJ3D; /** DelaunayTest provides a graphical demonstration of implemented Delaunay triangulation algorithms, in 2-D or 3-D. */ public class DelaunayTest { public static final int CLARKSON = 1; public static final int WATSON = 2; public static final int FAST = 3; public static final int NONE = 1; public static final int BOXES = 2; public static final int TRIANGLES = 3; public static final int VERTICES = 4; /** Run 'java DelaunayTest' for usage instructions */ public static void main(String[] argv) throws VisADException, RemoteException { boolean problem = false; int numpass = 0; int dim = 0; int points = 0; int type = 0; int l = 1; boolean test = false; if (argv.length < 3) problem = true; else { try { dim = Integer.parseInt(argv[0]); points = Integer.parseInt(argv[1]); type = Integer.parseInt(argv[2]); if (argv.length > 3) l = Integer.parseInt(argv[3]); test = argv.length > 4; if (dim < 2 || dim > 3 || points < 1 || type < 1 || l < 1 || l > 4) { problem = true; } if (dim == 3 && type > 2) { System.out.println("Only Clarkson and Watson support " + "3-D triangulation.\n"); System.exit(2); } } catch (NumberFormatException exc) { problem = true; } } if (problem) { System.out.println("Usage:\n" + " java DelaunayTest dim points type [label] [test]\n" + "dim = The dimension of the triangulation\n" + " 2 = 2-D\n" + " 3 = 3-D\n" + "points = The number of points to triangulate.\n" + "type = The triangulation method to use:\n" + " 1 = Clarkson\n" + " 2 = Watson\n" + " 3 = Fast\n" + " X + 3 = Fast with X improvement passes\n" + "label = How to label the diagram:\n" + " 1 = No labels (default)\n" + " 2 = Vertex boxes\n" + " 3 = Triangle numbers\n" + " 4 = Vertex numbers\n" + "test = Whether to test the triangulation (default: no)\n"); System.exit(1); } if (type > 3) { numpass = type - 3; type = 3; } float[][] samples = null; if (dim == 2) samples = new float[2][points]; else samples = new float[3][points]; float[] samp0 = samples[0]; float[] samp1 = samples[1]; float[] samp2 = null; if (dim == 3) samp2 = samples[2]; for (int i=0; i<points; i++) { samp0[i] = (float) (500 * Math.random()); samp1[i] = (float) (500 * Math.random()); } if (dim == 3) { for (int i=0; i<points; i++) { samp2[i] = (float) (500 * Math.random()); } } visTriang(makeTriang(samples, type, numpass, test), samples, l); } /** * Triangulates the given samples according to the specified algorithm. * * @param type One of CLARKSON, WATSON, FAST * @param numpass Number of improvement passes * @param test Whether to test the triangulation for errors */ public static Delaunay makeTriang(float[][] samples, int type, int numpass, boolean test) throws VisADException, RemoteException { int dim = samples.length; int points = samples[0].length; System.out.print("Triangulating " + points + " points " + "in " + dim + "-D with "); long start = 0; long end = 0; Delaunay delaun = null; if (type == CLARKSON) { System.out.println("the Clarkson algorithm."); start = System.currentTimeMillis(); delaun = (Delaunay) new DelaunayClarkson(samples); end = System.currentTimeMillis(); } else if (type == WATSON) { System.out.println("the Watson algorithm."); start = System.currentTimeMillis(); delaun = (Delaunay) new DelaunayWatson(samples); end = System.currentTimeMillis(); } else if (type == FAST) { System.out.println("the Fast algorithm."); start = System.currentTimeMillis(); delaun = (Delaunay) new DelaunayFast(samples); end = System.currentTimeMillis(); } float time = (end - start) / 1000f; System.out.println("Triangulation took " + time + " seconds."); if (numpass > 0) { System.out.println("Improving samples: " + numpass + " pass" + (numpass > 1 ? "es..." : "...")); start = System.currentTimeMillis(); delaun.improve(samples, numpass); end = System.currentTimeMillis(); time = (end - start) / 1000f; System.out.println("Improvement took " + time + " seconds."); } if (test) { System.out.print("Testing triangulation integrity..."); if (delaun.test(samples)) System.out.println("OK"); else System.out.println("FAILED!"); } return delaun; } /** * Displays the results for the given Delaunay triangulation of the * specified samples in a window. * * @param delaun The triangulation to visualize * @param samples The samples corresponding to the triangulation * @param label One of NONE, BOXES, TRIANGLES, VERTICES */ public static void visTriang(Delaunay delaun, float[][] samples, int labels) throws VisADException, RemoteException { int dim = samples.length; int points = samples[0].length; // set up final variables final int label = labels; final int[][] tri = delaun.Tri; final int[][] edges = delaun.Edges; final int numedges = delaun.NumEdges; // set up frame JFrame frame = new JFrame(); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); float[] samp0 = samples[0]; float[] samp1 = samples[1]; float[] samp2 = null; if (dim == 3) samp2 = samples[2]; if (dim == 2) { // set up GUI components in 2-D final float[] s0 = samp0; final float[] s1 = samp1; JComponent jc = new JComponent() { public void paint(Graphics gr) { // draw triangles for (int i=0; i<tri.length; i++) { int[] t = tri[i]; gr.drawLine((int) s0[t[0]], (int) s1[t[0]], (int) s0[t[1]], (int) s1[t[1]]); gr.drawLine((int) s0[t[1]], (int) s1[t[1]], (int) s0[t[2]], (int) s1[t[2]]); gr.drawLine((int) s0[t[2]], (int) s1[t[2]], (int) s0[t[0]], (int) s1[t[0]]); } // draw labels if specified if (label == 2) { // vertex boxes for (int i=0; i<s0.length; i++) { gr.drawRect((int) s0[i]-2, (int) s1[i]-2, 4, 4); } } else if (label == 3) { // triangle numbers for (int i=0; i<tri.length; i++) { int t0 = tri[i][0]; int t1 = tri[i][1]; int t2 = tri[i][2]; int avgX = (int) ((s0[t0] + s0[t1] + s0[t2])/3); int avgY = (int) ((s1[t0] + s1[t1] + s1[t2])/3); gr.drawString(String.valueOf(i), avgX-4, avgY); } } else if (label == 4) { // vertex numbers for (int i=0; i<s0.length; i++) { gr.drawString("" + i, (int) s0[i], (int) s1[i]); } } } }; frame.getContentPane().add(jc); } else { // set up GUI components in 3-D final float[][] samps = samples; final float[] s0 = samp0; final float[] s1 = samp1; final float[] s2 = samp2; // construct a UnionSet of line segments (tetrahedra edges) final RealType x = RealType.getRealType("x"); final RealType y = RealType.getRealType("y"); final RealType z = RealType.getRealType("z"); RealTupleType xyz = new RealTupleType(x, y, z); int[] e0 = {0, 0, 0, 1, 1, 2}; int[] e1 = {1, 2, 3, 2, 3, 3}; Gridded3DSet[] gsp = new Gridded3DSet[numedges]; for (int i=0; i<numedges; i++) gsp[i] = null; for (int i=0; i<edges.length; i++) { int[] trii = tri[i]; int[] edgesi = edges[i]; for (int j=0; j<6; j++) { if (gsp[edgesi[j]] == null) { float[][] pts = new float[3][2]; float[] p0 = pts[0]; float[] p1 = pts[1]; float[] p2 = pts[2]; int tp0 = trii[e0[j]]; int tp1 = trii[e1[j]]; p0[0] = samp0[tp0]; p1[0] = samp1[tp0]; p2[0] = samp2[tp0]; p0[1] = samp0[tp1]; p1[1] = samp1[tp1]; p2[1] = samp2[tp1]; gsp[edgesi[j]] = new Gridded3DSet(xyz, pts, 2); } } } UnionSet tet = new UnionSet(xyz, gsp); final DataReference tetref = new DataReferenceImpl("tet"); tetref.setData(tet); // set up Java3D Display DisplayImpl display = new DisplayImplJ3D("image display"); display.addMap(new ScalarMap(x, Display.XAxis)); display.addMap(new ScalarMap(y, Display.YAxis)); display.addMap(new ScalarMap(z, Display.ZAxis)); display.addMap(new ConstantMap(1, Display.Red)); display.addMap(new ConstantMap(1, Display.Green)); display.addMap(new ConstantMap(0, Display.Blue)); // draw labels if specified if (label == 2) { throw new UnimplementedException( "DelaunayTest.testTriang: vertex boxes"); } else if (label == 3) { // triangle numbers int len = tri.length; TextType text = new TextType("text"); RealType t = RealType.getRealType("t"); RealTupleType rtt = new RealTupleType(new RealType[] {t}); Linear1DSet timeSet = new Linear1DSet(rtt, 0, len - 1, len); TupleType textTuple = new TupleType(new MathType[] {x, y, z, text}); FunctionType textFunction = new FunctionType(t, textTuple); FieldImpl textField = new FieldImpl(textFunction, timeSet); for (int i=0; i<len; i++) { int t0 = tri[i][0]; int t1 = tri[i][1]; int t2 = tri[i][2]; int t3 = tri[i][3]; int avgX = (int) ((s0[t0] + s0[t1] + s0[t2] + s0[t3])/4); int avgY = (int) ((s1[t0] + s1[t1] + s1[t2] + s1[t3])/4); int avgZ = (int) ((s2[t0] + s2[t1] + s2[t2] + s2[t3])/4); Data[] td = {new Real(x, avgX), new Real(y, avgY), new Real(z, avgZ), new Text(text, "" + i)}; TupleIface tt = new Tuple(textTuple, td); textField.setSample(i, tt); } display.addMap(new ScalarMap(text, Display.Text)); DataReferenceImpl rtf = new DataReferenceImpl("rtf"); rtf.setData(textField); display.addReference(rtf, null); } else if (label == 4) { // vertex numbers int len = s0.length; TextType text = new TextType("text"); RealType t = RealType.getRealType("t"); RealTupleType rtt = new RealTupleType(new RealType[] {t}); Linear1DSet timeSet = new Linear1DSet(rtt, 0, len - 1, len); TupleType textTuple = new TupleType(new MathType[] {x, y, z, text}); FunctionType textFunction = new FunctionType(t, textTuple); FieldImpl textField = new FieldImpl(textFunction, timeSet); for (int i=0; i<len; i++) { Data[] td = {new Real(x, s0[i]), new Real(y, s1[i]), new Real(z, s2[i]), new Text(text, "" + i)}; TupleIface tt = new Tuple(textTuple, td); textField.setSample(i, tt); } display.addMap(new ScalarMap(text, Display.Text)); DataReferenceImpl rtf = new DataReferenceImpl("rtf"); rtf.setData(textField); display.addReference(rtf, null); } // finish setting up Java3D Display display.getDisplayRenderer().setBoxOn(false); display.addReference(tetref); // set up frame's panel JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(display.getComponent()); frame.getContentPane().add(panel); } frame.setSize(new Dimension(510, 530)); frame.setTitle("Triangulation results"); frame.setVisible(true); } }