package com.vitco.low.triangulate.tests; import com.vitco.low.triangulate.Grid2TriPolySlow; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.impl.CoordinateArraySequence; import org.jaitools.imageutils.ImageUtils; import org.poly2tri.triangulation.delaunay.DelaunayTriangle; import javax.imageio.ImageIO; import javax.media.jai.TiledImage; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Random; /** * Abstract test class for all triangulation that implements the basics. */ public abstract class AbstractTriangulationTest { // helper - true if point c is in between point a and b private static boolean inBetween(Point a, Point b, Point c) { return (!a.equals(c) && !b.equals(c)) && // not the same points ((b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y)) && // on one line ((a.x < c.x == c.x < b.x) && (a.y < c.y == c.y < b.y)); // in between on that line } // execute the test public final void testTriangulation(int start, int stop, boolean printDebugImage, boolean tJunctionCheck) throws IOException { // do test for the specified range for (int i = start; i < stop; i++) { Random rand = new Random(i); // create image int sizex = rand.nextInt(100)+5; int sizey = rand.nextInt(100)+5; boolean[][] data = new boolean[sizex][sizey]; TiledImage src = ImageUtils.createConstantImage(sizex, sizey, 0); // fill with random data int count = rand.nextInt(sizex * sizey * 2); for (int j = 0; j < count; j++) { int x = rand.nextInt(sizex); int y = rand.nextInt(sizey); data[x][y] = true; src.setSample(x, y, 0, 1); } if (printDebugImage) { // save image (for checking) BufferedImage bufferedImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB); for (int x = 0; x < src.getWidth(); x++) { for (int y = 0; y < src.getHeight(); y++) { bufferedImage.setRGB(x, y, src.getSample(x, y, 0) == 1 ? Color.BLACK.getRGB() : Color.WHITE.getRGB()); } } File outputfile = new File("image" + i + ".png"); try { ImageIO.write(bufferedImage, "png", outputfile); } catch (IOException e) { e.printStackTrace(); } } // print information System.out.print("Test " + i + " @ " + sizex + " x " + sizey + " :: "); Collection<Polygon> geometry = Grid2TriPolySlow.doVectorize(src); System.out.print(geometry.size()); System.out.print(" :: "); ArrayList<DelaunayTriangle> tris = triangulate(data); System.out.print(tris.size()); // ========= if (tJunctionCheck) { // check that no points are overlapping HashSet<Point> points = new HashSet<Point>(); for (DelaunayTriangle tri : tris) { points.add(new Point((int)Math.round(tri.points[0].getX()),(int)Math.round(tri.points[0].getY()))); points.add(new Point((int)Math.round(tri.points[1].getX()),(int)Math.round(tri.points[1].getY()))); points.add(new Point((int)Math.round(tri.points[2].getX()),(int)Math.round(tri.points[2].getY()))); } for (Point p : points) { for (DelaunayTriangle tri : tris) { //System.out.println(p.toString() + " " + tri.points[0] + " " + tri.points[1] + " " + tri.points[2]); assert !inBetween(new Point((int)Math.round(tri.points[0].getX()),(int)Math.round(tri.points[0].getY())), new Point((int)Math.round(tri.points[1].getX()),(int)Math.round(tri.points[1].getY())), p); assert !inBetween(new Point((int)Math.round(tri.points[0].getX()),(int)Math.round(tri.points[0].getY())), new Point((int)Math.round(tri.points[2].getX()),(int)Math.round(tri.points[2].getY())), p); assert !inBetween(new Point((int)Math.round(tri.points[1].getX()),(int)Math.round(tri.points[1].getY())), new Point((int)Math.round(tri.points[2].getX()),(int)Math.round(tri.points[2].getY())), p); } } } // ========= // variables GeometryFactory geometryFactory = new GeometryFactory(); // stores the area sum of all triangles double aTri = 0; int statusCount = 0; for (DelaunayTriangle tri: tris) { // handle triangle area double area = tri.area(); aTri += area; assert area > 0.25; // print info if (statusCount%((tris.size()/100)+1) == 0) { System.out.print("."); } statusCount++; // convert into geometry LinearRing ring = new LinearRing(new CoordinateArraySequence( new Coordinate[]{ new Coordinate(tri.points[0].getX(), tri.points[0].getY()), new Coordinate(tri.points[1].getX(), tri.points[1].getY()), new Coordinate(tri.points[2].getX(), tri.points[2].getY()), new Coordinate(tri.points[0].getX(), tri.points[0].getY()) } ), geometryFactory); Polygon triPoly = new Polygon(ring, new LinearRing[0], geometryFactory); // check that points are different (area exists) assert triPoly.getArea() > 0.25; // check containment boolean contain = false; for (Polygon poly : geometry) { // check for containment if (poly.contains(triPoly)) { contain = true; break; } } assert contain; } // check that areas match double aPoly = 0; for (Polygon poly : geometry) { double area = poly.getArea(); aPoly += area; assert area > 0.25; } assert Math.round(aTri) == Math.round(aPoly); System.out.println(" :: "); } } // execute the case test public final void testTriangulationCase(String filename, String outputFile, int zoom, boolean drawCoordinates) throws IOException { // load the image into the data array BufferedImage imgIn = ImageIO.read(new File(filename)); boolean[][] data = new boolean[imgIn.getWidth()][imgIn.getHeight()]; for (int x = 0; x < imgIn.getWidth(); x++) { for (int y = 0; y < imgIn.getHeight(); y++) { //System.out.println(img.getRGB(x,y)); data[x][y] = imgIn.getRGB(x,y) != -1; } } // create triangles java.util.List<DelaunayTriangle> tris = triangulate(data); // print triangle information for (DelaunayTriangle tri : tris) { System.out.println(tri.points[0] + " " + tri.points[1] + " " + tri.points[2]); } BufferedImage img = new BufferedImage(zoom*imgIn.getWidth() + zoom, zoom*imgIn.getHeight() + zoom, BufferedImage.TYPE_INT_RGB); Graphics2D gr = (Graphics2D) img.getGraphics(); gr.setColor(Color.WHITE); gr.fillRect(0, 0, img.getWidth(), img.getHeight()); gr.setFont(gr.getFont().deriveFont(Font.BOLD,15f)); // shift to center AffineTransform af = new AffineTransform(); af.setToTranslation(zoom/2, zoom/2); gr.setTransform(af); gr.drawImage(imgIn,0,0,img.getWidth()-zoom,img.getHeight()-zoom, null); gr.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); gr.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); gr.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); int i = 0; for (DelaunayTriangle tri : tris) { gr.setColor(Color.GRAY); gr.drawLine((int)Math.round(tri.points[0].getX()*zoom), (int)Math.round(tri.points[0].getY()*zoom), (int)Math.round(tri.points[1].getX()*zoom), (int)Math.round(tri.points[1].getY()*zoom)); gr.drawLine((int)Math.round(tri.points[1].getX()*zoom), (int)Math.round(tri.points[1].getY()*zoom), (int)Math.round(tri.points[2].getX()*zoom), (int)Math.round(tri.points[2].getY()*zoom)); gr.drawLine((int)Math.round(tri.points[2].getX()*zoom), (int)Math.round(tri.points[2].getY()*zoom), (int)Math.round(tri.points[0].getX()*zoom), (int)Math.round(tri.points[0].getY()*zoom)); gr.setColor(Color.BLACK); gr.drawString(String.valueOf(++i), (int)(tri.centroid().getX() * zoom) - 5, (int)(tri.centroid().getY() * zoom) + 5); } gr.setFont(gr.getFont().deriveFont(Font.PLAIN,15f)); for (DelaunayTriangle tri : tris) { gr.setColor(Color.RED); gr.drawRect((int) Math.round(tri.points[0].getX() * zoom) - 2, (int) Math.round(tri.points[0].getY() * zoom) - 2, 4, 4); gr.drawRect((int) Math.round(tri.points[1].getX() * zoom) - 2, (int) Math.round(tri.points[1].getY() * zoom) - 2, 4, 4); gr.drawRect((int) Math.round(tri.points[2].getX() * zoom) - 2, (int) Math.round(tri.points[2].getY() * zoom) - 2, 4, 4); if (drawCoordinates) { gr.setColor(Color.GRAY); gr.drawString("(" + (int) Math.round(tri.points[0].getX()) + "," + (int) Math.round(tri.points[0].getY()) + ")", (int) Math.round(tri.points[0].getX() * zoom), (int) Math.round(tri.points[0].getY() * zoom) + 5); gr.drawString("(" + (int) Math.round(tri.points[1].getX()) + "," + (int) Math.round(tri.points[1].getY()) + ")", (int) Math.round(tri.points[1].getX() * zoom), (int) Math.round(tri.points[1].getY() * zoom) + 5); gr.drawString("(" + (int) Math.round(tri.points[2].getX()) + "," + (int) Math.round(tri.points[2].getY()) + ")", (int) Math.round(tri.points[2].getX() * zoom), (int) Math.round(tri.points[2].getY() * zoom) + 5); } } gr.dispose(); ImageIO.write(img, "png", new File(outputFile)); // ---------- } // to implement by the test abstract ArrayList<DelaunayTriangle> triangulate(boolean[][] data); }