All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dyn4j.geometry.hull.LinkedVertexHull Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2016 William Bittle  http://www.dyn4j.org/
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted 
 * provided that the following conditions are met:
 * 
 *   * Redistributions of source code must retain the above copyright notice, this list of conditions 
 *     and the following disclaimer.
 *   * 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.
 *   * Neither the name of dyn4j nor the names of its contributors may be used to endorse or 
 *     promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
 */
package org.dyn4j.geometry.hull;

import org.dyn4j.geometry.Vector2;

/**
 * Represents a convex hull of {@link LinkedVertex}es.
 * 

* The root vertex can be any point on the hull. * @author William Bittle * @version 3.2.0 * @since 3.2.0 */ final class LinkedVertexHull { /** The hull's root vertex */ LinkedVertex root; /** The vertex that has the smallest x coordinate */ LinkedVertex leftMost; /** The vertex that has the largest x coordinate */ LinkedVertex rightMost; /** The total number of vertices on the hull */ int size; /** Default constructor */ public LinkedVertexHull() {} /** * Create a convex {@link LinkedVertexHull} of one point. * @param point the point */ public LinkedVertexHull(Vector2 point) { LinkedVertex root = new LinkedVertex(point); this.root = root; this.leftMost = root; this.rightMost = root; this.size = 1; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LinkedVertexHull[Size=").append(this.size) .append("|Root=").append(this.root.point); return sb.toString(); } /** * Returns a new array representing this convex hull. * @return {@link Vector2}[] */ public final Vector2[] toArray() { Vector2[] points = new Vector2[this.size]; LinkedVertex vertex = this.root; for (int i = 0; i < this.size; i++) { points[i] = vertex.point; vertex = vertex.next; } return points; } /** * Merges the two given convex {@link LinkedVertexHull}s into one convex {@link LinkedVertexHull}. *

* The left {@link LinkedVertexHull} should contain only points whose x coordinates are * less than all the points in the right {@link LinkedVertexHull}. * @param left the left convex {@link LinkedVertexHull} * @param right the right convex {@link LinkedVertexHull} * @return {@link LinkedVertexHull} the merged convex hull */ public static final LinkedVertexHull merge(LinkedVertexHull left, LinkedVertexHull right) { // check the size of the hulls if (left.size == 1 && right.size == 1) { // the 1,1 case return LinkedVertexHull.mergePointPoint(left, right); } else if (left.size + right.size == 3) { // the 1,2 or 2,1 cases return LinkedVertexHull.mergePointSegment(left, right); } else { // all other cases return LinkedVertexHull.mergeHulls(left, right); } } /** * Merges the given left and right point hulls. * @param left the left hull * @param right the right hull * @return {@link LinkedVertexHull} a line segment hull */ static final LinkedVertexHull mergePointPoint(LinkedVertexHull left, LinkedVertexHull right) { LinkedVertex leftRoot = left.root; LinkedVertex rightRoot = right.root; // wire up the hulls leftRoot.next = rightRoot; leftRoot.prev = rightRoot; rightRoot.next = leftRoot; rightRoot.prev = leftRoot; // create a hull LinkedVertexHull hull = new LinkedVertexHull(); hull.root = leftRoot; hull.leftMost = leftRoot; hull.rightMost = rightRoot; hull.size = 2; // return the hull return hull; } /** * Performs a merge of a point hull and segment hull returning a triangular hull. * @param left the left hull * @param right the right hull * @return {@link LinkedVertexHull} a triangular hull */ static final LinkedVertexHull mergePointSegment(LinkedVertexHull left, LinkedVertexHull right) { // the 1,2 or 2,1 case LinkedVertexHull hull = new LinkedVertexHull(); hull.size = 3; LinkedVertexHull point = left; LinkedVertexHull segment = right; if (left.size == 1) { // the 1,2 case hull.leftMost = left.root; hull.rightMost = right.rightMost; } else { // the 2,1 case hull.leftMost = left.leftMost; hull.rightMost = right.root; point = right; segment = left; } hull.root = point.root; // get the line segment points Vector2 p1 = segment.root.point; Vector2 p2 = segment.root.next.point; // get the point Vector2 p = point.root.point; // compute the winding Vector2 v1 = p.to(p1); Vector2 v2 = p1.to(p2); double area = v1.cross(v2); // check the winding to find where to place the point if (area < 0.0) { // then we use the point hull as the second point // insert v in between v1->v2 to create v1->v->v2 point.root.next = segment.root.next; // v->v2 segment.root.next.prev = point.root; // v2<-v point.root.prev = segment.root; // v1<-v segment.root.next = point.root; // v1->v } else { // then we use the point hull as the first point // prepend v to v1->v2 to create v->v1->v2 point.root.next = segment.root; // v->v1 segment.root.prev = point.root; // v1<-v point.root.prev = segment.root.next; // v2<-v segment.root.next.next = point.root; // v2->v } return hull; } /** * Performs a merge of the left and right hulls into one hull. * @param left the left hull * @param right the right hull * @return {@link LinkedVertexHull} a convex hull */ static final LinkedVertexHull mergeHulls(LinkedVertexHull left, LinkedVertexHull right) { // otherwise we need to run the algorithm to find the upper and lower edges // that connect the two hulls such that the resulting hull remains convex LinkedVertexHull hull = new LinkedVertexHull(); hull.leftMost = left.leftMost; hull.rightMost = right.rightMost; // find the upper edge connection // start with leftmost to right most LinkedVertex lu = left.rightMost; LinkedVertex ru = right.leftMost; Vector2 upper = lu.point.to(ru.point); // allow the loop to go through every point on the left and right side // before stopping, this condition is really a catch for degenerate // cases. Non-degenerate cases should hit the break; statement for (int i = 0; i < left.size * right.size; i++) { // go counter-clockwise Vector2 lv = lu.point.to(lu.next.point); // go clockwise Vector2 rv = ru.point.to(ru.prev.point); // what's the winding on both hulls given the upper edge connection? double crossR = rv.cross(upper); double crossL = upper.getNegative().cross(lv); // check for both convex if (crossR > 0.0 && crossL > 0.0) { // l and r contain the vertices for the upper bridge break; } // check not convex or colinear if (crossR <= 0.0) { // then we need to move clockwise on the right side by one ru = ru.prev; } // check not convex or colinear if (crossL <= 0.0) { // then we need to move counter-clockwise on the left side by one lu = lu.next; } // compute the new upper edge connection upper = lu.point.to(ru.point); } // find the lower edge connection LinkedVertex ll = left.rightMost; LinkedVertex rl = right.leftMost; Vector2 lower = ll.point.to(rl.point); // allow the loop to go through every point on the left and right side // before stopping, this condition is really a catch for degenerate // cases. Non-degenerate cases should hit the break; statement for (int i = 0; i < left.size * right.size; i++) { // go clockwise Vector2 lv = ll.point.to(ll.prev.point); // go counter-clockwise Vector2 rv = rl.point.to(rl.next.point); // what's the winding on both hulls given the lower edge connection? double crossR = lower.cross(rv); double crossL = lv.cross(lower.getNegative()); // check for both convex if (crossR > 0.0 && crossL > 0.0) { // l and r contain the vertices for the upper bridge break; } // check not convex or colinear if (crossR <= 0.0) { // then we need to move counter-clockwise on the right side by one rl = rl.next; } // check not convex or colinear if (crossL <= 0.0) { // then we need to move clockwise on the left side by one ll = ll.prev; } // compute the new lower edge connection lower = ll.point.to(rl.point); } // wire up the two hulls using the lower and upper edge // connections found above (the other vertices will be // garbage collected) lu.prev = ru; ru.next = lu; ll.next = rl; rl.prev = ll; // set the root of the hull to any of the bridge points // or any point on the convex hull hull.root = lu; // count the number of points LinkedVertex v0 = hull.root; LinkedVertex v = v0; int size = 0; do { size ++; v = v.next; } while (v != v0); // set the size hull.size = size; // return the merged hull return hull; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy