package edu.stanford.rsl.conrad.geometry; import java.util.ArrayList; import java.util.Stack; import edu.stanford.rsl.conrad.geometry.bounds.HalfSpaceBoundingCondition; import edu.stanford.rsl.conrad.geometry.shapes.simple.Edge; import edu.stanford.rsl.conrad.geometry.shapes.simple.Plane3D; import edu.stanford.rsl.conrad.geometry.shapes.simple.Point3D; import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND; import edu.stanford.rsl.conrad.geometry.shapes.simple.StraightLine; import edu.stanford.rsl.conrad.geometry.shapes.simple.Triangle; import edu.stanford.rsl.conrad.numerics.SimpleOperators; import edu.stanford.rsl.conrad.numerics.SimpleVector; /** super class for hull constructing algorithms * * Based on the gift wrapping code from Tim Lambert. Demo applets are presented <a href="http://www.cse.unsw.edu.au/~lambert/java/3d/hull.html">here</a>. * */ public class ConvexHull { protected PointND[] pts; protected PointND [] hullPoints; protected Triangle [] faces; int[] extraColors(){ return new int[0]; } public ConvexHull(PointND[] pts) { this.pts = pts; } int index(PointND p) { for(int i=0; i<pts.length; i++){ if (p==pts[i]) { return i; } } return -1; } protected PointND search(Edge line) { int i; PointND p1 = line.getPoint(); PointND p2 = line.getEnd(); for(i = 0; pts[i].equals(p1) || pts[i].equals(p2); i++) { /* nothing */ } //System.out.println("Found at " + i); PointND cand = pts[i]; SimpleVector edgeDirection = SimpleOperators.subtract(p2.getAbstractVector(), p1.getAbstractVector()); SimpleVector otherDirection = SimpleOperators.subtract(cand.getAbstractVector(), p1.getAbstractVector()); HalfSpaceBoundingCondition candh = new HalfSpaceBoundingCondition(new Plane3D(p1, edgeDirection, otherDirection)); for(i=i+1; i < pts.length; i++) { if ((!pts[i].equals(p1)) && (!pts[i].equals(p2)) && candh.isSatisfiedBy(pts[i])) { cand = pts[i]; otherDirection = SimpleOperators.subtract(cand.getAbstractVector(), p1.getAbstractVector()); candh = new HalfSpaceBoundingCondition(new Plane3D(p1,edgeDirection,otherDirection)); } } return cand; } protected PointND search2d(PointND p) { int i; Point3D k = new Point3D(0,0,1); i = pts[0] == p?1:0; PointND cand = pts[i]; SimpleVector edgeDirection = SimpleOperators.subtract(cand.getAbstractVector(), p.getAbstractVector()); SimpleVector otherDirection = k.getAbstractVector(); HalfSpaceBoundingCondition candh = new HalfSpaceBoundingCondition(new Plane3D(cand, edgeDirection, otherDirection)); for(i=i+1; i < pts.length; i++) { if (pts[i] != p && candh.isSatisfiedBy(pts[i])) { cand = pts[i]; edgeDirection = SimpleOperators.subtract(cand.getAbstractVector(), p.getAbstractVector()); candh = new HalfSpaceBoundingCondition(new Plane3D(cand, edgeDirection, otherDirection)); } } return cand; } /* bottom point */ protected PointND bottom(){ PointND bot = pts[0]; for (int i = 1; i < pts.length; i++) { if (pts[i].get(1) < bot.get(1)) { bot = pts[i]; } } return bot; } public void build () { /* First find a hull edge -- just connect bottommost to second from bottom */ PointND bot, bot2; /* bottom point and adjacent point*/ bot = bottom(); bot2 = search2d(bot); ArrayList<PointND> hull = new ArrayList<PointND>(); hull.add(bot); hull.add(bot2); /* intialize the edge stack */ Stack<Edge> es = new Stack<Edge>(); Stack<Edge> done = new Stack<Edge>(); es.push(new Edge(bot,bot2)); es.push(new Edge(bot2,bot)); ArrayList<AbstractShape> faces = new ArrayList<AbstractShape>(); Edge e = null; System.out.println(pts.length); //System.exit(-1); /* now the main loop -- keep finding faces till there are no more to be found */ while (! es.isEmpty() ) { e = es.pop(); PointND cand = search(e); hull.add(cand); Triangle t = new Triangle(e.getPoint(),cand,e.getEnd()); faces.add(t); Edge edge1 = new Edge(e.getPoint(),cand); Edge edge2 = new Edge(cand,e.getEnd()); if (es.contains(edge1)) { es.remove(edge1); done.add(edge1); } else { if (! done.contains(edge1)) { es.push(edge1); } } if (es.contains(edge2)) { es.remove(edge2); done.add(edge2); } else { if (!done.contains(edge2)) { es.push(edge2); } } System.out.println("current size " + es.size() + " " + faces.size()); } hullPoints = new PointND[hull.size()]; hullPoints = hull.toArray(hullPoints); this.faces = new Triangle [faces.size()]; this.faces = faces.toArray(this.faces); } public void build2D() { /* First find a hull vertex -- just bottommost*/ PointND p; /* current hull vertex */ PointND bot = bottom(); /* bottom point */ ArrayList<PointND> hull = new ArrayList<PointND>(); hull.add(bot); /* now the main loop -- keep finding edges till we get back */ p = bot; do { PointND cand = search2d(p); hull.add(cand); p = cand; } while (p!=bot); hullPoints = new PointND[hull.size()]; hullPoints = hull.toArray(hullPoints); PointND center = General.getGeometricCenter(hull); PointND offCenter = new PointND(center); offCenter.set(2, offCenter.get(2)+10); faces = new Triangle [hullPoints.length-1]; for (int i = 1; i < hullPoints.length; i++){ faces[i-1] = new Triangle(offCenter, hullPoints[i], hullPoints[i-1]); //System.out.println("Center distance 1 " + faces[i-1].computeDistance(center) + " " + faces[i-1].normalN); //if (faces[i-1].computeDistance(center) < 0) { // faces[i-1].normalN.negate(); // faces[i-1].offsetD *= -1; //} //System.out.println("Center distance 2 " + faces[i-1].computeDistance(center) + " " + faces[i-1].normalN + "\n"); } } /** * returns the hull as an array of Points * @return the array of Points */ public PointND [] getHullPoints(){ return hullPoints; } /** * Returns the hull as an array of triangles * * @return the array of faces */ public Triangle [] getFaces(){ return faces; } /** * Tests whether the point is inside the convex hull. * @param point * @return true if the point is inside */ public boolean isInside(PointND point){ boolean revan = true; for (int i=1; i< faces.length; i++){ Triangle t = faces[i]; HalfSpaceBoundingCondition bound = new HalfSpaceBoundingCondition(t); if (!bound.isSatisfiedBy(point)) { revan = false; System.out.println(point + " " + t.computeDistance(point) + " " + i +" "+ t.getNormal() + " " + t.getPoint()); break; } } return revan; } /** * Intersects lines between subsequent hull points. Only applicable in 2D. * @param line the line to intersect with the hull * @return the array of intersection points. */ public PointND [] intersect2D(StraightLine line){ ArrayList<PointND> list = new ArrayList<PointND>(); for (int i = 1; i < hullPoints.length; i++){ Edge test = new Edge(hullPoints[i-1], hullPoints[i]); PointND p = test.intersect(line); if (p != null){ list.add(p); } } PointND [] points = new PointND [list.size()]; points = list.toArray(points); return points; } public PointND [] intersect3D(StraightLine line){ ArrayList<PointND> list = new ArrayList<PointND>(); for (int i = 0; i < faces.length; i++){ PointND p = faces[i].intersect(line); //System.out.println("Intersection " + i + " " + p); if (p != null){ list.add(p); } } PointND [] points = new PointND [list.size()]; points = list.toArray(points); return points; } public PointND[] getRasterPoints(int number){ ArrayList<PointND[]> lists = new ArrayList<PointND[]>(); int sum = 0; for (Triangle t : faces){ PointND [] pts = t.getRasterPoints(number/ faces.length); sum += pts.length; lists.add(pts); } PointND [] points = new PointND[sum]; int increment = 0; for (PointND [] pts : lists){ System.arraycopy(pts, 0, points, increment, pts.length); increment += pts.length; } return points; } } /* * Copyright (C) 2010-2014 Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */