Fixed monotone partioning and finished triangulation algorithm
Removed a lot of debug code Added comments Fixed partition monotone not setting the vertex after unlinking edges Added some better java awt stuff Added a method to copy meshes
This commit is contained in:
@@ -16,23 +16,63 @@ import java.util.Optional;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.Region;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.RegionRule;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple.GluWindingRule;
|
||||
|
||||
/**
|
||||
* A general mesh class containing polygons
|
||||
* A general mesh class containing polygons. Inspiration taken from: https://github.com/memononen/libtess2
|
||||
*
|
||||
* This general algorithm loosely follows the libtess2 source code, but is more or less re-done
|
||||
* from scratch to be simpler and easier to follow and comprehend. In addition, there is support
|
||||
* for custom region winding.
|
||||
*
|
||||
* The entire algorithm from polygons to triangles can be summarized as follows:
|
||||
* 1. Simplify the polygons into one massive DCEL/PSLG. This includes:
|
||||
* - Merging vertices that are within VERTEX_TOLERANCE of each other
|
||||
* - Merging vertices on edges that are at most VERTEX_TOLERANCE away
|
||||
* - Merging colinear edges
|
||||
* - Creating new vertices where two or more edges intersect
|
||||
* This makes the resulting graph much easier to traverse and process. Unlike libtess2,
|
||||
* we do the simplification separate from step 2, so that we don't run into cases where
|
||||
* modifying/fixing a vertex will change some property of a previous vertex, thus invalidating
|
||||
* previously processed sections.
|
||||
*
|
||||
* In addition to the simplification, this also merges overlapping edges, which is performed
|
||||
* before generating regions. All RegionRules for each polygon _must_ be able to be merged.
|
||||
* That is, given rule A, B and C, and region u and v, v = B * A * u, then C = B * A and v = C * u.
|
||||
*
|
||||
* 2. Generating regions based on the PSLG and RegionRules. Given the default region outside
|
||||
* the PSLG, determine which regions enclosed by edges are interior or exterior regions.
|
||||
* Any edges between two interior/exterior regions is a no-op edge, and can be removed.
|
||||
* This step in the algorithm is destructive in the sense that it removes edges, but it
|
||||
* does not move any vertices. This step also removes merges colinear edges where possible.
|
||||
*
|
||||
* 3. Meshing and triangulating the resulting polygons. Firstly, a copy of the PSLG is generated
|
||||
* since this step adds edges and vertices to the PSLG, which may be undesirable for reusability.
|
||||
* Then, we remove colinear edges which may have been generated as a result of the monotone partitioning.
|
||||
* Following a general O(nlogn) algorithm, the PSLG is split into polygons strictly monotone with
|
||||
* respect to Y. The PSLG is not guaranteed to be comprised of simple polygons, and may contain
|
||||
* holes.
|
||||
*
|
||||
* Once complete, the simple polygons are separated into their own PSLG(that is, they do not share
|
||||
* any vertices or edge POJOs), and can be triangulated in parallel. The triangulation method used
|
||||
* is the algorithm described in Computation Geometry Algorithms and Applications 3rd Ed., which
|
||||
* is a linear-time algorithm.
|
||||
*
|
||||
* The time complexity for the entire algorithm should be roughly O(nlogn).
|
||||
*
|
||||
* TODO Remove guaranteed checks or add some compile time thing to remove
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
public class Mesh< T extends Region > {
|
||||
public static final double VERTEX_TOLERANCE = 1e-7;
|
||||
public static final double ANGLE_TOLERANCE = Math.toRadians( 0.01 );
|
||||
public static final double ANGLE_TOLERANCE = Math.toRadians( 0.0001 );
|
||||
|
||||
// All points in the graph
|
||||
protected Collection< Vertex > vertices = new ArrayDeque< Vertex >();
|
||||
@@ -42,88 +82,24 @@ public class Mesh< T extends Region > {
|
||||
|
||||
protected final Supplier< T > defaultRegionSupplier;
|
||||
|
||||
protected MeshState state = MeshState.SIMPLIFIED;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( 1, -1 ),
|
||||
new Point( 3, -1 ),
|
||||
new Point( 3, 1 ),
|
||||
new Point( 1, 1 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -1, -1 ),
|
||||
// new Point( 1, -1 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( -1, 1 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -1, 1 ),
|
||||
// new Point( 1, 1 ),
|
||||
// new Point( 1, 3 ),
|
||||
// new Point( -1, 3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -2, -1 ),
|
||||
// new Point( -1, -1 ),
|
||||
// new Point( -1, 2 ),
|
||||
// new Point( -2, 2 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -4, -2 ),
|
||||
// new Point( -1, -2 ),
|
||||
// new Point( -1, 1.5 ),
|
||||
// new Point( -4, 1.5 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -3, 0 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( -1.5, -3 ),
|
||||
// new Point( 0, 1.5 ),
|
||||
// new Point( 1.5, -3 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( -1, 0 ),
|
||||
// new Point( 0, 1.5 ),
|
||||
// new Point( 1, 0 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 0, -2 ),
|
||||
// new Point( -1, 0 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
System.out.println( "Vertices: " + mesh.vertices.size() );
|
||||
System.out.println( "Edges: " + ( mesh.rules.size() / 2 ) );
|
||||
mesh.simplify();
|
||||
System.out.println( "Vertices: " + mesh.vertices.size() );
|
||||
System.out.println( "Edges: " + ( mesh.rules.size() / 2 ) );
|
||||
mesh.generateRegions();
|
||||
System.out.println( "Vertices: " + mesh.vertices.size() );
|
||||
System.out.println( "Edges: " + ( mesh.rules.size() / 2 ) );
|
||||
System.out.println( "Partitions: " + mesh.mesh().size() );
|
||||
}
|
||||
protected MeshState state = MeshState.TRIANGULATION_READY;
|
||||
|
||||
public Mesh( final Supplier< T > defaultRegionSupplier ) {
|
||||
this.defaultRegionSupplier = defaultRegionSupplier;
|
||||
}
|
||||
|
||||
public void addPolygon( final Polygon poly, final RegionRule< T > rule ) {
|
||||
// Is it at least a triangle?
|
||||
if ( poly.getPoints().size() < 3 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
HalfEdge edge = null;
|
||||
for ( Point point : poly.getPoints() ) {
|
||||
if ( edge == null ) {
|
||||
edge = new HalfEdge();
|
||||
HalfEdge.splice( edge, edge.getSym() );
|
||||
edge.getOrigin().update();
|
||||
edge.getSym().setOrigin( edge.getOrigin() );
|
||||
} else {
|
||||
edge = edge.split();
|
||||
}
|
||||
@@ -148,8 +124,77 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
state = MeshState.TRIANGULATION_READY;
|
||||
vertices.clear();
|
||||
rules.clear();
|
||||
interiorEdges.clear();
|
||||
}
|
||||
|
||||
public Mesh< T > copyOf() {
|
||||
final Mesh< T > copy = new Mesh< T >( defaultRegionSupplier );
|
||||
|
||||
// Keep track of all edges that we've seen
|
||||
final EdgeSet scanned = new EdgeSet();
|
||||
// Map each old vertex to a new vertex
|
||||
final Map< Vertex, Vertex > newVertices = new HashMap< Vertex, Vertex >();
|
||||
|
||||
/*
|
||||
* We now want to create a completely separate PSLG so that we don't modify
|
||||
* the original one.
|
||||
*/
|
||||
for ( Vertex vertex : vertices ) {
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
if ( scanned.add( edge ) ) {
|
||||
HalfEdge temp = edge;
|
||||
|
||||
HalfEdge newEdge = null;
|
||||
do {
|
||||
if ( newEdge == null ) {
|
||||
newEdge = new HalfEdge();
|
||||
HalfEdge.splice( newEdge, newEdge.getSym() );
|
||||
newEdge.getSym().setOrigin( newEdge.getOrigin() );
|
||||
} else {
|
||||
newEdge = newEdge.split();
|
||||
}
|
||||
|
||||
final Vertex tempVert = temp.getOrigin();
|
||||
|
||||
// Re-use a previously created vertex if there is one
|
||||
Vertex vert;
|
||||
if ( newVertices.containsKey( tempVert ) ) {
|
||||
vert = newVertices.get( tempVert );
|
||||
|
||||
newEdge.setOrigin( vert );
|
||||
newEdge.getPrev().setOrigin( vert );
|
||||
|
||||
HalfEdge.splice( newEdge.getPrev(), vert.getEdge() );
|
||||
} else {
|
||||
vert = newEdge.getOrigin();
|
||||
newVertices.put( tempVert, vert );
|
||||
|
||||
vert.setPosition( tempVert.getPosition() );
|
||||
|
||||
copy.vertices.add( vert );
|
||||
}
|
||||
|
||||
if ( interiorEdges.contains( temp ) ) {
|
||||
copy.interiorEdges.add( temp );
|
||||
}
|
||||
if ( interiorEdges.contains( temp.getSym() ) ) {
|
||||
copy.interiorEdges.add( temp.getSym() );
|
||||
}
|
||||
|
||||
copy.rules.put( newEdge, rules.get( temp ) );
|
||||
copy.rules.put( newEdge.getSym(), rules.get( temp.getSym() ) );
|
||||
} while ( scanned.add( temp = temp.getNext() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy.vertices.parallelStream().forEach( v -> sort( v ) );
|
||||
copy.state = state;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +205,9 @@ public class Mesh< T extends Region > {
|
||||
*
|
||||
* In addition, remove vertices without any edges, and solve any
|
||||
* intersections by splitting edges and/or adding new vertices as required.
|
||||
*
|
||||
* This method only removes intersections. It does NOT guarantee that polygons
|
||||
* will be simple, since there can be holes.
|
||||
*/
|
||||
public void simplify() {
|
||||
// Create a queue and insert all vertices in O(nlogn) time.
|
||||
@@ -573,7 +621,10 @@ public class Mesh< T extends Region > {
|
||||
* intersections or overlaps.
|
||||
*/
|
||||
public void generateRegions() {
|
||||
if ( state != MeshState.SIMPLIFIED ) {
|
||||
if ( state == MeshState.TRIANGULATION_READY ) {
|
||||
// The regions have already been generated
|
||||
return;
|
||||
} else if ( state != MeshState.SIMPLIFIED ) {
|
||||
throw new IllegalStateException( "Mesh is not simplified!" );
|
||||
}
|
||||
|
||||
@@ -691,7 +742,10 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
|
||||
// Merge any colinear edges before returning
|
||||
mergeColinearEdges( keep, interiorAboveEdges );
|
||||
mergeColinearEdges( keep, interiorAboveEdges ).forEach( e -> {
|
||||
rules.remove( e );
|
||||
rules.remove( e.getSym() );
|
||||
} );
|
||||
|
||||
interiorEdges.clear();
|
||||
interiorEdges.addAll( interiorAboveEdges );
|
||||
@@ -731,7 +785,7 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
} );
|
||||
|
||||
for ( int i = 0; i < edges.size(); i++ ) {
|
||||
for ( int i = 0; i < edges.size(); ++i ) {
|
||||
// Get this edge and the next edge
|
||||
final HalfEdge e1 = edges.get( i );
|
||||
final HalfEdge e2 = edges.get( ( i + 1 ) % edges.size() );
|
||||
@@ -749,7 +803,7 @@ public class Mesh< T extends Region > {
|
||||
return edges;
|
||||
}
|
||||
|
||||
private void mergeColinearEdges( final Collection< Vertex > vertices, final Collection< HalfEdge > internalEdges ) {
|
||||
private static Collection< HalfEdge > mergeColinearEdges( final Collection< Vertex > vertices, final Collection< HalfEdge > internalEdges ) {
|
||||
final Collection< HalfEdge > toRemove = new ArrayDeque< HalfEdge >();
|
||||
|
||||
final Set< HalfEdge > edges = new HashSet< HalfEdge >();
|
||||
@@ -814,9 +868,6 @@ public class Mesh< T extends Region > {
|
||||
|
||||
edges.add( next );
|
||||
|
||||
rules.remove( next );
|
||||
rules.remove( next.getSym() );
|
||||
|
||||
toRemove.add( next );
|
||||
} else {
|
||||
edges.add( edge );
|
||||
@@ -826,8 +877,10 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
|
||||
internalEdges.removeAll( toRemove );
|
||||
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
|
||||
public Collection< EdgePolygon > mesh() {
|
||||
if ( state != MeshState.TRIANGULATION_READY ) {
|
||||
throw new IllegalStateException( "Mesh has not been split into regions!" );
|
||||
@@ -844,6 +897,10 @@ public class Mesh< T extends Region > {
|
||||
// Get a collection of vertices that need to be sorted, again
|
||||
final Collection< Vertex > toSort = new HashSet< Vertex >();
|
||||
|
||||
/*
|
||||
* We now want to create a completely separate PSLG so that we don't modify
|
||||
* the original one.
|
||||
*/
|
||||
for ( final HalfEdge edge : interiorEdges ) {
|
||||
if ( scanned.add( edge ) ) {
|
||||
HalfEdge temp = edge;
|
||||
@@ -853,6 +910,7 @@ public class Mesh< T extends Region > {
|
||||
if ( newEdge == null ) {
|
||||
newEdge = new HalfEdge();
|
||||
HalfEdge.splice( newEdge, newEdge.getSym() );
|
||||
newEdge.getSym().setOrigin( newEdge.getOrigin() );
|
||||
} else {
|
||||
newEdge = newEdge.split();
|
||||
}
|
||||
@@ -864,6 +922,9 @@ public class Mesh< T extends Region > {
|
||||
if ( newVertices.containsKey( tempVert ) ) {
|
||||
vert = newVertices.get( tempVert );
|
||||
|
||||
newEdge.setOrigin( vert );
|
||||
newEdge.getPrev().setOrigin( vert );
|
||||
|
||||
HalfEdge.splice( newEdge.getPrev(), vert.getEdge() );
|
||||
|
||||
toSort.add( vert );
|
||||
@@ -873,24 +934,27 @@ public class Mesh< T extends Region > {
|
||||
|
||||
vert.setPosition( tempVert.getPosition() );
|
||||
}
|
||||
vert.update();
|
||||
|
||||
edges.add( newEdge );
|
||||
} while ( scanned.add( temp = temp.getNext() ) );
|
||||
}
|
||||
}
|
||||
|
||||
for ( final Vertex v : toSort ) {
|
||||
sort( v );
|
||||
}
|
||||
toSort.parallelStream().forEach( v -> sort( v ) );
|
||||
|
||||
// Have a single set of edges
|
||||
final TreeSet< Vertex > vertices = new TreeSet< Vertex >( Mesh::compare );
|
||||
vertices.addAll( newVertices.values() );
|
||||
|
||||
final Collection< EdgePolygon > polygons = partitionMonotone( vertices, edges );
|
||||
|
||||
return polygons;
|
||||
return partitionMonotone( vertices, edges ).parallelStream()
|
||||
.map( p -> {
|
||||
mergeColinearEdges( p.getVertices(), p.getEdges() ).forEach( e -> p.getVertices().remove( e.getOrigin() ) );
|
||||
|
||||
return p;
|
||||
} )
|
||||
.map( Mesh::triangulate )
|
||||
.flatMap( p -> p.parallelStream() )
|
||||
.collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > partitionMonotone( final TreeSet< Vertex > vertices, final Collection< HalfEdge > interior ) {
|
||||
@@ -1111,7 +1175,7 @@ public class Mesh< T extends Region > {
|
||||
edge.setOrigin( prev );
|
||||
edge.getSym().setOrigin( vertex );
|
||||
|
||||
HalfEdge.splice( lower, edge );
|
||||
HalfEdge.splice( edge, lower );
|
||||
HalfEdge.splice( edge.getSym(), vertex.getEdge() );
|
||||
|
||||
supportEdges.add( edge );
|
||||
@@ -1121,7 +1185,7 @@ public class Mesh< T extends Region > {
|
||||
edge.setOrigin( prev );
|
||||
edge.getSym().setOrigin( vertex );
|
||||
|
||||
HalfEdge.splice( upper.getSym().getNext(), edge );
|
||||
HalfEdge.splice( edge, upper.getSym().getNext() );
|
||||
HalfEdge.splice( edge.getSym(), vertex.getEdge() );
|
||||
|
||||
supportEdges.add( edge );
|
||||
@@ -1172,11 +1236,12 @@ public class Mesh< T extends Region > {
|
||||
// Now that we've split the polygon into monotone regions, we must
|
||||
// also return a collection of the newly created polygon, since each
|
||||
// monotone region is its own separate polygon
|
||||
// That means each polygon should not share any vertices or edges,
|
||||
// which is perfect for us so we can triangulate each polygon in parallel
|
||||
final Collection< EdgePolygon > newPolygons = new ArrayDeque< EdgePolygon >();
|
||||
|
||||
// Keep track of all edges that we've seen
|
||||
final Set< HalfEdge > scanned = new HashSet< HalfEdge >();
|
||||
|
||||
for ( final HalfEdge edge : interior ) {
|
||||
if ( scanned.add( edge ) ) {
|
||||
// We have not looked at this edge yet, so loop over it
|
||||
@@ -1188,11 +1253,13 @@ public class Mesh< T extends Region > {
|
||||
// As we iterate over each edge, if the origin has more than
|
||||
// 2 edges, then we need to split the vertex.
|
||||
if ( temp.getPrev().getPrev() != temp ) {
|
||||
temp.getOrigin().setEdge( temp.getPrev().getPrev() );
|
||||
|
||||
HalfEdge.splice( temp.getPrev(), temp.getSym().getNext() );
|
||||
|
||||
// Set their origins to a new vertex
|
||||
final Vertex vertex = new Vertex( temp, temp.getOrigin().getPosition() );
|
||||
|
||||
|
||||
temp.setOrigin( vertex );
|
||||
temp.getPrev().setOrigin( vertex );
|
||||
}
|
||||
@@ -1205,8 +1272,136 @@ public class Mesh< T extends Region > {
|
||||
return newPolygons;
|
||||
}
|
||||
|
||||
private void triangulate() {
|
||||
// TODO
|
||||
private static Collection< EdgePolygon > triangulate( final EdgePolygon polygon ) {
|
||||
// Go down each monotone chain and connect the vertices where possible.
|
||||
// Implements the O(n) triangulation of a polygon as described in
|
||||
// Computation Geometry Algorithms and Applications 3rd Ed.
|
||||
final Collection< Vertex > toSort = new HashSet< Vertex >();
|
||||
|
||||
if ( polygon.getEdges().size() == 3 ) {
|
||||
return Arrays.asList( polygon );
|
||||
}
|
||||
|
||||
final Queue< HalfEdge > edges = new PriorityQueue< HalfEdge >( ( a, b ) -> {
|
||||
return compare( a.getOrigin(), b.getOrigin() );
|
||||
} );
|
||||
edges.addAll( polygon.getEdges() );
|
||||
|
||||
final Stack< HalfEdge > stack = new Stack< HalfEdge >();
|
||||
// Add the first two vertices/edges
|
||||
stack.add( edges.poll() );
|
||||
stack.add( edges.poll() );
|
||||
|
||||
HalfEdge prev = stack.peek();
|
||||
while ( edges.size() > 1 ) {
|
||||
final HalfEdge edge = edges.poll();
|
||||
|
||||
if ( isPositive( edge ) ^ isPositive( stack.peek() ) ) {
|
||||
// Insert an edge from the current event to each vertex in the stack
|
||||
HalfEdge current = stack.pop();
|
||||
while ( !stack.isEmpty() ) {
|
||||
final HalfEdge newEdge = new HalfEdge();
|
||||
|
||||
newEdge.setOrigin( edge.getOrigin() );
|
||||
newEdge.getSym().setOrigin( current.getOrigin() );
|
||||
|
||||
HalfEdge.splice( newEdge, edge );
|
||||
HalfEdge.splice( newEdge.getSym(), current );
|
||||
|
||||
polygon.addEdge( newEdge );
|
||||
|
||||
toSort.add( edge.getOrigin() );
|
||||
toSort.add( current.getOrigin() );
|
||||
|
||||
current = stack.pop();
|
||||
}
|
||||
stack.push( prev );
|
||||
} else {
|
||||
HalfEdge current = stack.pop();
|
||||
final boolean isPositive = isPositive( edge );
|
||||
HalfEdge leftEdge = isPositive ? edge.getPrev() : edge;
|
||||
while ( !stack.isEmpty() ) {
|
||||
final HalfEdge next = stack.peek();
|
||||
|
||||
final Vector2d diagonal = next.getOrigin().getPosition().subtracted( edge.getOrigin().getPosition() );
|
||||
final double cross = diagonal.normalize().cross( leftEdge.toVector2d().normalize() );
|
||||
|
||||
// Check if the diagonal is inside the polygon
|
||||
final boolean isInside = isPositive ? cross > ANGLE_TOLERANCE : cross < - ANGLE_TOLERANCE;
|
||||
if ( isInside ) {
|
||||
final HalfEdge newEdge = new HalfEdge();
|
||||
|
||||
newEdge.setOrigin( edge.getOrigin() );
|
||||
newEdge.getSym().setOrigin( next.getOrigin() );
|
||||
|
||||
HalfEdge.splice( newEdge, edge );
|
||||
HalfEdge.splice( newEdge.getSym(), next );
|
||||
|
||||
toSort.add( edge.getOrigin() );
|
||||
toSort.add( next.getOrigin() );
|
||||
|
||||
polygon.addEdge( newEdge );
|
||||
|
||||
leftEdge = newEdge;
|
||||
|
||||
current = stack.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
stack.push( current );
|
||||
}
|
||||
stack.push( edge );
|
||||
|
||||
prev = edge;
|
||||
}
|
||||
|
||||
final HalfEdge last = edges.poll();
|
||||
stack.pop();
|
||||
while ( stack.size() > 1 ) {
|
||||
final HalfEdge popped = stack.pop();
|
||||
final HalfEdge newEdge = new HalfEdge();
|
||||
|
||||
newEdge.setOrigin( last.getOrigin() );
|
||||
newEdge.getSym().setOrigin( popped.getOrigin() );
|
||||
|
||||
HalfEdge.splice( newEdge, last );
|
||||
HalfEdge.splice( newEdge.getSym(), popped );
|
||||
|
||||
polygon.addEdge( newEdge );
|
||||
|
||||
toSort.add( last.getOrigin() );
|
||||
toSort.add( popped.getOrigin() );
|
||||
}
|
||||
|
||||
// Lazy solution, just sort the vertices
|
||||
// Eventually we will remove this
|
||||
toSort.parallelStream().forEach( v -> sort( v ) );
|
||||
|
||||
// TODO Convert to triangles or something?
|
||||
final Collection< EdgePolygon > polygons = new ArrayDeque< EdgePolygon >();
|
||||
final Set< HalfEdge > scanned = new HashSet< HalfEdge >();
|
||||
for ( final HalfEdge edge : polygon.getEdges() ) {
|
||||
if ( scanned.add( edge ) ) {
|
||||
HalfEdge temp = edge;
|
||||
EdgePolygon poly = new EdgePolygon();
|
||||
|
||||
do {
|
||||
poly.addEdge( temp );
|
||||
} while ( scanned.add( temp = temp.getNext() ) );
|
||||
|
||||
if ( poly.getVertices().size() != poly.getEdges().size() ) {
|
||||
throw new IllegalStateException( "Polygon has inconsistent edges/vertices!" );
|
||||
}
|
||||
|
||||
if ( poly.getVertices().size() != 3 ) {
|
||||
throw new IllegalStateException( "Not a triangle! " + poly.getVertices().size() );
|
||||
}
|
||||
polygons.add( poly );
|
||||
}
|
||||
}
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1328,7 +1523,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
public static class EdgePolygon {
|
||||
private Set< HalfEdge > edges = new HashSet< HalfEdge >();
|
||||
private TreeSet< Vertex > vertices = new TreeSet< Vertex >( Mesh::compare );
|
||||
private Set< Vertex > vertices = new HashSet< Vertex >();
|
||||
|
||||
private void addEdge( HalfEdge edge ) {
|
||||
edges.add( edge );
|
||||
@@ -1339,7 +1534,7 @@ public class Mesh< T extends Region > {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public TreeSet< Vertex > getVertices() {
|
||||
public Set< Vertex > getVertices() {
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user