aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogamp/graph/curve/tess/Loop.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogamp/graph/curve/tess/Loop.java')
-rw-r--r--src/jogamp/graph/curve/tess/Loop.java376
1 files changed, 376 insertions, 0 deletions
diff --git a/src/jogamp/graph/curve/tess/Loop.java b/src/jogamp/graph/curve/tess/Loop.java
new file mode 100644
index 000000000..caebd64e4
--- /dev/null
+++ b/src/jogamp/graph/curve/tess/Loop.java
@@ -0,0 +1,376 @@
+/**
+ * Copyright 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.graph.curve.tess;
+
+import java.util.ArrayList;
+
+import jogamp.graph.math.VectorFloatUtil;
+
+import com.jogamp.graph.geom.AABBox;
+import com.jogamp.graph.geom.PointTex;
+import com.jogamp.graph.geom.Triangle;
+
+public class Loop <T extends PointTex> {
+ private HEdge<T> root = null;
+ private AABBox box = new AABBox();
+ private GraphOutline<T> initialOutline = null;
+
+ public Loop(GraphOutline<T> polyline, int direction){
+ initialOutline = polyline;
+ this.root = initFromPolyline(initialOutline, direction);
+ }
+
+ public HEdge<T> getHEdge(){
+ return root;
+ }
+
+ public Triangle<T> cut(boolean delaunay){
+ if(isSimplex()){
+ @SuppressWarnings("unchecked")
+ Triangle<T> t = new Triangle<T>(root.getGraphPoint().getPoint(), root.getNext().getGraphPoint().getPoint(),
+ root.getNext().getNext().getGraphPoint().getPoint());
+ t.setVerticesBoundary(checkVerticesBoundary(root));
+ return t;
+ }
+ HEdge<T> prev = root.getPrev();
+ HEdge<T> next1 = root.getNext();
+
+ HEdge<T> next2 =findClosestValidNeighbor(next1.getNext(), delaunay);
+ if(next2 == null){
+ root = root.getNext();
+ return null;
+ }
+
+ GraphPoint<T> v1 = root.getGraphPoint();
+ GraphPoint<T> v2 = next1.getGraphPoint();
+ GraphPoint<T> v3 = next2.getGraphPoint();
+
+ HEdge<T> v3Edge = new HEdge<T>(v3, HEdge.INNER);
+
+ HEdge.connect(v3Edge, root);
+ HEdge.connect(next1, v3Edge);
+
+ HEdge<T> v3EdgeSib = v3Edge.getSibling();
+ if(v3EdgeSib == null){
+ v3EdgeSib = new HEdge<T>(v3Edge.getNext().getGraphPoint(), HEdge.INNER);
+ HEdge.makeSiblings(v3Edge, v3EdgeSib);
+ }
+
+ HEdge.connect(prev, v3EdgeSib);
+ HEdge.connect(v3EdgeSib, next2);
+
+ Triangle<T> t = createTriangle(v1.getPoint(), v2.getPoint(), v3.getPoint(), root);
+ this.root = next2;
+ return t;
+ }
+
+ public boolean isSimplex(){
+ return (root.getNext().getNext().getNext() == root);
+ }
+
+ /**Create a connected list of half edges (loop)
+ * from the boundary profile
+ * @param direction requested winding of edges (CCW or CW)
+ */
+ private HEdge<T> initFromPolyline(GraphOutline<T> outline, int direction){
+ ArrayList<GraphPoint<T>> vertices = outline.getGraphPoint();
+
+ if(vertices.size()<3) {
+ throw new IllegalArgumentException("outline's vertices < 3: " + vertices.size());
+ }
+ boolean isCCW = VectorFloatUtil.ccw(vertices.get(0).getPoint(), vertices.get(1).getPoint(),
+ vertices.get(2).getPoint());
+ boolean invert = isCCW && (direction == VectorFloatUtil.CW);
+
+ HEdge<T> firstEdge = null;
+ HEdge<T> lastEdge = null;
+ int index =0;
+ int max = vertices.size();
+
+ int edgeType = HEdge.BOUNDARY;
+ if(invert){
+ index = vertices.size() -1;
+ max = -1;
+ edgeType = HEdge.HOLE;
+ }
+
+ while(index != max){
+ GraphPoint<T> v1 = vertices.get(index);
+ box.resize(v1.getX(), v1.getY(), v1.getZ());
+
+ HEdge<T> edge = new HEdge<T>(v1, edgeType);
+
+ v1.addEdge(edge);
+ if(lastEdge != null){
+ lastEdge.setNext(edge);
+ edge.setPrev(lastEdge);
+ }
+ else{
+ firstEdge = edge;
+ }
+
+ if(!invert){
+ if(index == vertices.size()-1){
+ edge.setNext(firstEdge);
+ firstEdge.setPrev(edge);
+ }
+ }
+ else if (index == 0){
+ edge.setNext(firstEdge);
+ firstEdge.setPrev(edge);
+ }
+
+ lastEdge = edge;
+
+ if(!invert){
+ index++;
+ }
+ else{
+ index--;
+ }
+ }
+ return firstEdge;
+ }
+
+ public void addConstraintCurve(GraphOutline<T> polyline) {
+ // GraphOutline outline = new GraphOutline(polyline);
+ /**needed to generate vertex references.*/
+ initFromPolyline(polyline, VectorFloatUtil.CW);
+
+ GraphPoint<T> v3 = locateClosestVertex(polyline);
+ HEdge<T> v3Edge = v3.findBoundEdge();
+ HEdge<T> v3EdgeP = v3Edge.getPrev();
+ HEdge<T> crossEdge = new HEdge<T>(root.getGraphPoint(), HEdge.INNER);
+
+ HEdge.connect(root.getPrev(), crossEdge);
+ HEdge.connect(crossEdge, v3Edge);
+
+ HEdge<T> crossEdgeSib = crossEdge.getSibling();
+ if(crossEdgeSib == null) {
+ crossEdgeSib = new HEdge<T>(crossEdge.getNext().getGraphPoint(), HEdge.INNER);
+ HEdge.makeSiblings(crossEdge, crossEdgeSib);
+ }
+
+ HEdge.connect(v3EdgeP, crossEdgeSib);
+ HEdge.connect(crossEdgeSib, root);
+ }
+
+ /** Locates the vertex and update the loops root
+ * to have (root + vertex) as closest pair
+ * @param polyline the control polyline
+ * to search for closestvertices
+ * @return the vertex that is closest to the newly set root Hedge.
+ */
+ private GraphPoint<T> locateClosestVertex(GraphOutline<T> polyline) {
+ HEdge<T> closestE = null;
+ GraphPoint<T> closestV = null;
+
+ float minDistance = Float.MAX_VALUE;
+ boolean inValid = false;
+ ArrayList<GraphPoint<T>> initVertices = initialOutline.getGraphPoint();
+ ArrayList<GraphPoint<T>> vertices = polyline.getGraphPoint();
+
+ for(int i=0; i< initVertices.size()-1; i++){
+ GraphPoint<T> v = initVertices.get(i);
+ GraphPoint<T> nextV = initVertices.get(i+1);
+ for(GraphPoint<T> cand:vertices){
+ float distance = VectorFloatUtil.computeLength(v.getCoord(), cand.getCoord());
+ if(distance < minDistance){
+ for (GraphPoint<T> vert:vertices){
+ if(vert == v || vert == nextV || vert == cand)
+ continue;
+ inValid = VectorFloatUtil.inCircle(v.getPoint(), nextV.getPoint(),
+ cand.getPoint(), vert.getPoint());
+ if(inValid){
+ break;
+ }
+ }
+ if(!inValid){
+ closestV = cand;
+ minDistance = distance;
+ closestE = v.findBoundEdge();
+ }
+ }
+
+ }
+ }
+
+ if(closestE != null){
+ root = closestE;
+ }
+
+ return closestV;
+ }
+
+ private HEdge<T> findClosestValidNeighbor(HEdge<T> edge, boolean delaunay) {
+ HEdge<T> next = root.getNext();
+
+ if(!VectorFloatUtil.ccw(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(),
+ edge.getGraphPoint().getPoint())){
+ return null;
+ }
+
+ HEdge<T> candEdge = edge;
+ boolean inValid = false;
+
+ if(delaunay){
+ T cand = candEdge.getGraphPoint().getPoint();
+ HEdge<T> e = candEdge.getNext();
+ while (e != candEdge){
+ if(e.getGraphPoint() == root.getGraphPoint()
+ || e.getGraphPoint() == next.getGraphPoint()
+ || e.getGraphPoint().getPoint() == cand){
+ e = e.getNext();
+ continue;
+ }
+ inValid = VectorFloatUtil.inCircle(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(),
+ cand, e.getGraphPoint().getPoint());
+ if(inValid){
+ break;
+ }
+ e = e.getNext();
+ }
+ }
+ if(!inValid){
+ return candEdge;
+ }
+ return null;
+ }
+
+ /** Create a triangle from the param vertices only if
+ * the triangle is valid. IE not outside region.
+ * @param v1 vertex 1
+ * @param v2 vertex 2
+ * @param v3 vertex 3
+ * @param root and edge of this triangle
+ * @return the triangle iff it satisfies, null otherwise
+ */
+ private Triangle<T> createTriangle(T v1, T v2, T v3, HEdge<T> rootT){
+ @SuppressWarnings("unchecked")
+ Triangle<T> t = new Triangle<T>(v1, v2, v3);
+ t.setVerticesBoundary(checkVerticesBoundary(rootT));
+ return t;
+ }
+
+ private boolean[] checkVerticesBoundary(HEdge<T> rootT) {
+ boolean[] boundary = new boolean[3];
+ HEdge<T> e1 = rootT;
+ HEdge<T> e2 = rootT.getNext();
+ HEdge<T> e3 = rootT.getNext().getNext();
+
+ if(e1.getGraphPoint().isBoundaryContained()){
+ boundary[0] = true;
+ }
+ if(e2.getGraphPoint().isBoundaryContained()){
+ boundary[1] = true;
+ }
+ if(e3.getGraphPoint().isBoundaryContained()){
+ boundary[2] = true;
+ }
+ return boundary;
+ }
+
+
+ /** Check if vertex inside the Loop
+ * @param vertex the Vertex
+ * @return true if the vertex is inside, false otherwise
+ */
+ public boolean checkInside(T vertex) {
+ if(!box.contains(vertex.getX(), vertex.getY(), vertex.getZ())){
+ return false;
+ }
+
+ float[] center = box.getCenter();
+
+ int hits = 0;
+ HEdge<T> current = root;
+ HEdge<T> next = root.getNext();
+ while(next!= root){
+ if(current.getType() == HEdge.INNER || next.getType() == HEdge.INNER){
+ current = next;
+ next = current.getNext();
+ continue;
+ }
+
+ T vert1 = current.getGraphPoint().getPoint();
+ T vert2 = next.getGraphPoint().getPoint();
+
+ /** The ray is P0+s*D0, where P0 is the ray origin, D0 is a direction vector and s >= 0.
+ * The segment is P1+t*D1, where P1 and P1+D1 are the endpoints, and 0 <= t <= 1.
+ * perp(x,y) = (y,-x).
+ * if Dot(perp(D1),D0) is not zero,
+ * s = Dot(perp(D1),P1-P0)/Dot(perp(D1),D0)
+ * t = Dot(perp(D0),P1-P0)/Dot(perp(D1),D0)
+ */
+
+ float[] d0 = new float[]{center[0] - vertex.getX(), center[1]-vertex.getY(),
+ center[2]-vertex.getZ()};
+ float[] d1 = {vert2.getX() - vert1.getX(), vert2.getY() - vert1.getY(),
+ vert2.getZ() - vert1.getZ()};
+
+ float[] prepD1 = {d1[1],-1*d1[0], d1[2]};
+ float[] prepD0 = {d0[1],-1*d0[0], d0[2]};
+
+ float[] p0p1 = new float[]{vert1.getX() - vertex.getX(), vert1.getY() - vertex.getY(),
+ vert1.getZ() - vertex.getZ()};
+
+ float dotD1D0 = VectorFloatUtil.dot(prepD1, d0);
+ if(dotD1D0 == 0){
+ /** ray parallel to segment */
+ current = next;
+ next = current.getNext();
+ continue;
+ }
+
+ float s = VectorFloatUtil.dot(prepD1,p0p1)/dotD1D0;
+ float t = VectorFloatUtil.dot(prepD0,p0p1)/dotD1D0;
+
+ if(s >= 0 && t >= 0 && t<= 1){
+ hits++;
+ }
+ current = next;
+ next = current.getNext();
+ }
+
+ if(hits % 2 != 0){
+ /** check if hit count is even */
+ return true;
+ }
+ return false;
+ }
+
+ public int computeLoopSize(){
+ int size = 0;
+ HEdge<T> e = root;
+ do{
+ size++;
+ e = e.getNext();
+ }while(e != root);
+ return size;
+ }
+}