Speedup the mesh simplification
Change EdgeSet to use LinkedHashSet for better iteration, potentially. Map polygon points to existing vertices to reduce unnecessary vertex duplication Attempt to merge overlapping edges initially before doing anything else when simplifying Use tree sets instead of priority queues for constant time poll Split intersection calculation method into two separate methods Rework which edges are actively within the scan zone to reduce the amount of edges to check when looking for intersections. This should help make the entire algorithm closer to O(nlogn) Removed intersection classes Added method to resolve only collinear edges around a vertex Added method to resolve edge intersections directly from the edge, rather than the positive side Added simple Y check to see if two edges can even intersect Added method to calculate absolute vector Added test plane by default to MeshingTest2(the plane viewer)
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* A set for easily checking if a set contains/does not contain a half-edge or its sym.
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
public class EdgeSet extends HashSet< HalfEdge > {
|
||||
public class EdgeSet extends LinkedHashSet< HalfEdge > {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -9,12 +9,9 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@@ -81,6 +78,9 @@ public class Mesh< T extends Region > {
|
||||
public static final double ANGLE_TOLERANCE = Math.toRadians( 1e-10 );
|
||||
public static final double CROSS_TOLERANCE = Math.sin( ANGLE_TOLERANCE );
|
||||
|
||||
// Point to vertex map, to avoid duplicating vertices when inserting polygons
|
||||
protected Map< Point, Vertex > vertexMap = new HashMap< Point, Vertex >();
|
||||
|
||||
// All points in the graph
|
||||
protected Collection< Vertex > vertices = new ArrayDeque< Vertex >();
|
||||
// All rules associated with a given half edge
|
||||
@@ -122,13 +122,18 @@ public class Mesh< T extends Region > {
|
||||
edge = edge.split();
|
||||
}
|
||||
|
||||
final Vertex vertex = vertexMap.get( point );
|
||||
if ( vertex != null ) {
|
||||
HalfEdge.splice( edge.getPrev(), vertex.getEdge() );
|
||||
} else {
|
||||
edge.getOrigin().setPosition( new Vector2d( point ) );
|
||||
|
||||
vertices.add( edge.getOrigin() );
|
||||
}
|
||||
|
||||
rules.put( edge, rule );
|
||||
rules.put( edge.getSym(), rule.inverse() );
|
||||
|
||||
vertices.add( edge.getOrigin() );
|
||||
|
||||
state = MeshState.DIRTY;
|
||||
}
|
||||
}
|
||||
@@ -179,6 +184,7 @@ public class Mesh< T extends Region > {
|
||||
vertices.clear();
|
||||
rules.clear();
|
||||
interiorEdges.clear();
|
||||
vertexMap.clear();
|
||||
}
|
||||
|
||||
public Mesh< T > copyOf() {
|
||||
@@ -244,7 +250,7 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
copy.vertices.parallelStream().forEach( v -> sort( v ) );
|
||||
copy.vertices.parallelStream().forEach( v -> { sort( v ); copy.vertexMap.put( v.getPosition(), v ); } );
|
||||
copy.state = state;
|
||||
|
||||
return copy;
|
||||
@@ -282,9 +288,24 @@ public class Mesh< T extends Region > {
|
||||
* will be simple, since there can be holes.
|
||||
*/
|
||||
public void simplify() {
|
||||
// Create a queue and insert all vertices in O(nlogn) time.
|
||||
final Queue< Vertex > queue = new PriorityQueue< Vertex >( Mesh::compare );
|
||||
queue.addAll( vertices );
|
||||
// Create a sorted set with duplicate vertices allowed
|
||||
// Insert all vertices in O(nlogn) time.
|
||||
// Use a treeset instead of a priority queue for constant time poll first
|
||||
final TreeSet< Vertex > vertexSet = new TreeSet< Vertex >( ( a, b ) -> {
|
||||
final int compare = compare( a, b );
|
||||
if ( compare == 0 ) {
|
||||
return Integer.compare( a.hashCode(), b.hashCode() );
|
||||
} else {
|
||||
return compare;
|
||||
}
|
||||
} );
|
||||
vertexSet.addAll( vertices );
|
||||
|
||||
// Need to update vertices if using the vertex map when inserting polygons
|
||||
vertexSet.parallelStream().forEach( Vertex::update );
|
||||
|
||||
// Do a quick merging of edges so we don't have to deal with them later
|
||||
mergeOverlappingEdges( vertexSet );
|
||||
|
||||
/*
|
||||
* As we iterate through each vertex, there are certain operations
|
||||
@@ -299,23 +320,21 @@ public class Mesh< T extends Region > {
|
||||
// This set contains the non-redundant vertices.
|
||||
final Set< Vertex > scannedVertices = new HashSet< Vertex >();
|
||||
|
||||
Vertex vertex;
|
||||
while ( ( vertex = queue.poll() ) != null ) {
|
||||
while ( !vertexSet.isEmpty() ) {
|
||||
final Vertex vertex = vertexSet.pollFirst();
|
||||
final Vector2d pos = vertex.getPosition();
|
||||
{
|
||||
final List< Vertex > addBack = new LinkedList< Vertex >();
|
||||
Vertex other;
|
||||
|
||||
// We must poll and add back later since priority queues are not
|
||||
// sorted, so an iterator over its elements would not guarantee
|
||||
// the correct ordering unless we poll it.
|
||||
while ( ( other = queue.poll() ) != null ) {
|
||||
for ( final Iterator< Vertex > it = vertexSet.iterator(); it.hasNext(); ) {
|
||||
final Vertex other = it.next();
|
||||
final Vector2d otherPos = other.getPosition();
|
||||
|
||||
// Once the vertex scanned is past the tolerance in the X direction,
|
||||
// we know there are no more vertices which can be merged with the
|
||||
// current vertex.
|
||||
if ( compareX( otherPos, pos ) > VERTEX_TOLERANCE ) {
|
||||
addBack.add( other );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -323,12 +342,10 @@ public class Mesh< T extends Region > {
|
||||
// be considered "the same"
|
||||
if ( isSimilar( pos, otherPos ) ) {
|
||||
HalfEdge.splice( vertex.getEdge(), other.getEdge() );
|
||||
} else {
|
||||
addBack.add( other );
|
||||
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
queue.addAll( addBack );
|
||||
}
|
||||
|
||||
{
|
||||
// Remove any zero-edges
|
||||
@@ -358,14 +375,8 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
// Add all edges from this vertex if they do not already exist in
|
||||
// the set of scanned edges
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
scannedEdges.add( edge );
|
||||
}
|
||||
|
||||
// Add edges that belong to vertices up to VERTEX_TOLERANCE away in the X direction
|
||||
for ( final Iterator< Vertex > it = queue.iterator(); it.hasNext(); ) {
|
||||
for ( final Iterator< Vertex > it = vertexSet.iterator(); it.hasNext(); ) {
|
||||
final Vertex other = it.next();
|
||||
final Vector2d otherPos = other.getPosition();
|
||||
|
||||
@@ -377,28 +388,36 @@ public class Mesh< T extends Region > {
|
||||
// horizontally onto the exact same x position as this vertex.
|
||||
// It saves us from having fuzzy errors in the future.
|
||||
otherPos.setX( pos.getX() );
|
||||
|
||||
for ( final HalfEdge edge : other ) {
|
||||
scannedEdges.add( edge );
|
||||
}
|
||||
}
|
||||
|
||||
// Update all edges this vertex is connected to for consistency
|
||||
vertex.update();
|
||||
|
||||
// Merge any vertices with edges that they may be on
|
||||
// Merge any vertices that are on edges
|
||||
resolveVertexIntersections( scannedEdges, vertex );
|
||||
|
||||
// Remove any collinear edges attached to this vertex
|
||||
resolveCollinearEdges( scannedEdges, vertex );
|
||||
|
||||
// Remove any intersections with edges from other vertices
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
// Resolve any intersections belonging to this edge which are still
|
||||
// being scanned
|
||||
if ( scannedEdges.contains( edge ) ) {
|
||||
resolveEdgeIntersections( queue, scannedEdges, edge );
|
||||
// being scanned, but only if it's a new edge
|
||||
if ( !scannedEdges.contains( edge ) && !scannedVertices.contains( edge.getDest() ) ) {
|
||||
resolveEdgeIntersections( vertexSet, scannedEdges, edge );
|
||||
}
|
||||
}
|
||||
|
||||
// Consider this vertex "scanned"
|
||||
scannedVertices.add( vertex );
|
||||
|
||||
// Remove all edges from previously scanned vertices that are no longer useful
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
if ( vertex == edge.getDest() ) {
|
||||
// TODO Remove this
|
||||
throw new IllegalStateException( "Zero length edge!" );
|
||||
}
|
||||
|
||||
if ( scannedVertices.contains( edge.getDest() ) ) {
|
||||
/*
|
||||
* Remove the half edge and it's sym if the destination
|
||||
@@ -412,13 +431,16 @@ public class Mesh< T extends Region > {
|
||||
// TODO Remove this
|
||||
throw new IllegalStateException( "Tried to remove an invalid edge!" );
|
||||
}
|
||||
} else {
|
||||
scannedEdges.add( edge );
|
||||
}
|
||||
}
|
||||
scannedVertices.add( vertex );
|
||||
}
|
||||
|
||||
mergeOverlappingEdges( scannedVertices );
|
||||
|
||||
scannedVertices.parallelStream().forEach( Mesh::sort );
|
||||
|
||||
// Update the list of vertices with our new updated list of vertices
|
||||
vertices = scannedVertices;
|
||||
|
||||
@@ -446,7 +468,7 @@ public class Mesh< T extends Region > {
|
||||
* @param scannedEdges
|
||||
* @param vertex
|
||||
*/
|
||||
private void resolveVertexIntersections( final EdgeSet scannedEdges, final Vertex vertex ) {
|
||||
private Collection< HalfEdge > resolveVertexIntersections( final EdgeSet scannedEdges, final Vertex vertex ) {
|
||||
// Split any edges passing through vertex, which do not include
|
||||
// the vertex as the origin or destination
|
||||
final Vector2d v = vertex.getPosition();
|
||||
@@ -455,6 +477,8 @@ public class Mesh< T extends Region > {
|
||||
final Vector2d a = edge.getOrigin().getPosition();
|
||||
final Vector2d b = edge.getDest().getPosition();
|
||||
|
||||
// TODO Can parallelize finding the edges, then do the merging sequentially afterwards
|
||||
|
||||
if ( v == a || v == b ) {
|
||||
// Skip if this edge
|
||||
continue;
|
||||
@@ -497,71 +521,45 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
}
|
||||
scannedEdges.addAll( toInsert );
|
||||
|
||||
return toInsert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of edges within the scan region, check for
|
||||
* intersections with a half edge.
|
||||
*
|
||||
* @param scannedEdges
|
||||
* @param edge
|
||||
*/
|
||||
private void resolveEdgeIntersections( final Collection< Vertex > vertices, final EdgeSet scannedEdges, final HalfEdge edge ) {
|
||||
// Use the positive edge for finding the intersection
|
||||
final HalfEdge positiveEdge = isPositive( edge ) ? edge : edge.getSym();
|
||||
for ( HalfEdge other : scannedEdges ) {
|
||||
if ( !isPositive( other ) ) {
|
||||
other = other.getSym();
|
||||
private void resolveCollinearEdges( final EdgeSet scannedEdges, final Vertex vertex ) {
|
||||
final Collection< HalfEdge > checked = new HashSet< HalfEdge >();
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
if ( scannedEdges.contains( edge ) ) {
|
||||
checked.add( edge );
|
||||
}
|
||||
final Optional< Intersection > optInt = getIntersection( positiveEdge, other );
|
||||
if ( optInt.isPresent() ) {
|
||||
final Intersection intersection = optInt.get();
|
||||
if ( intersection instanceof IntersectionEdgeToEdge ) {
|
||||
final IntersectionEdgeToEdge intEdge = ( IntersectionEdgeToEdge ) intersection;
|
||||
|
||||
if ( positiveEdge.getDest() == other.getDest() ) {
|
||||
// TODO Remove this
|
||||
throw new IllegalStateException( "Invalid intersection" );
|
||||
}
|
||||
|
||||
final Vector2d point = intEdge.getPoint();
|
||||
if ( isSimilar( point, other.getDest().getPosition() ) || isSimilar( point, positiveEdge.getDest().getPosition() ) ) {
|
||||
// TODO Remove this
|
||||
throw new IllegalStateException( "Invalid intersection point" );
|
||||
}
|
||||
// Assume a and b are both positive
|
||||
// This assumes that if a and b are collinear, then they _must_ share
|
||||
// the same origin, since we are traversing through the vertices
|
||||
// starting with the smallest.
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
// Only check this edge if it is not already scanned
|
||||
if ( checked.add( edge ) ) {
|
||||
final Vector2d r = edge.toVector2d().normalize();
|
||||
for ( final HalfEdge other : vertex ) {
|
||||
if ( !checked.contains( other ) ) {
|
||||
// Do not split edges that are pretty much equal, since they will get merged later
|
||||
if ( !isSimilar( edge.getDest(), other.getDest() ) ) {
|
||||
final Vector2d s = other.toVector2d().normalize();
|
||||
final double angle = r.cross( s );
|
||||
|
||||
// Split the first edge
|
||||
HalfEdge aSplit = edge.split();
|
||||
RegionRule< T > aRule = rules.get( edge );
|
||||
rules.put( aSplit, aRule );
|
||||
rules.put( aSplit.getSym(), aRule.inverse() );
|
||||
// Ignore edges that are not collinear
|
||||
if ( Math.abs( angle ) <= CROSS_TOLERANCE ) {
|
||||
|
||||
// Update the position
|
||||
final Vertex origin = aSplit.getOrigin();
|
||||
origin.setPosition( point );
|
||||
HalfEdge shorter;
|
||||
HalfEdge longer;
|
||||
|
||||
// Split the other edge
|
||||
HalfEdge bSplit = other.split();
|
||||
RegionRule< T > bRule = rules.get( other );
|
||||
rules.put( bSplit, bRule );
|
||||
rules.put( bSplit.getSym(), bRule.inverse() );
|
||||
|
||||
// Splice the edges together into the same vertex
|
||||
HalfEdge.splice( bSplit, aSplit );
|
||||
|
||||
origin.update();
|
||||
|
||||
vertices.add( origin );
|
||||
} else if ( intersection instanceof IntersectionCollinear ) {
|
||||
final IntersectionCollinear intCol = ( IntersectionCollinear ) intersection;
|
||||
|
||||
final HalfEdge shorter = intCol.getShorterEdge();
|
||||
HalfEdge longer = intCol.getLongerEdge();
|
||||
|
||||
// Use the original edge for splitting to preserve the order of
|
||||
// edges around the current vertex
|
||||
if ( longer == positiveEdge ) {
|
||||
// Compare which edge is shorter
|
||||
if ( r.lengthSquared() < s.lengthSquared() ) {
|
||||
shorter = edge;
|
||||
longer = other;
|
||||
} else {
|
||||
shorter = other;
|
||||
longer = edge;
|
||||
}
|
||||
|
||||
@@ -578,57 +576,59 @@ public class Mesh< T extends Region > {
|
||||
|
||||
// Splice the two together
|
||||
HalfEdge.splice( shorter.getSym(), split );
|
||||
} else {
|
||||
// TODO Remove this
|
||||
throw new IllegalStateException( "Unknown intersection type" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the intersection for two edges
|
||||
* Given a set of edges within the scan region, check for
|
||||
* intersections with a half edge.
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return
|
||||
* @param scannedEdges
|
||||
* @param edge
|
||||
*/
|
||||
private static Optional< Intersection > getIntersection( HalfEdge a, HalfEdge b ) {
|
||||
// Assume a and b are both positive
|
||||
// This assumes that if a and b are collinear, then they _must_ share
|
||||
// the same origin, since we are traversing through the vertices
|
||||
// starting with the smallest.
|
||||
if ( a.getOrigin() == b.getOrigin() ) {
|
||||
// Do not split edges that are pretty much equal, since they will get merged later
|
||||
if ( !isSimilar( a.getDest(), b.getDest() ) ) {
|
||||
final Vector2d r = a.toVector2d().normalize();
|
||||
final Vector2d s = b.toVector2d().normalize();
|
||||
final double angle = r.cross( s );
|
||||
private void resolveEdgeIntersections( final Collection< Vertex > vertices, final EdgeSet scannedEdges, final HalfEdge edge ) {
|
||||
final Vector2d p = edge.getOrigin().getPosition();
|
||||
final double edgeOriginY = p.getY();
|
||||
|
||||
// Ignore edges that are not collinear
|
||||
if ( Math.abs( angle ) <= CROSS_TOLERANCE ) {
|
||||
// Recalculate these if the edge is split
|
||||
double edgeDestY = edge.getDest().getPosition().getY();
|
||||
Vector2d r = edge.toVector2d();
|
||||
|
||||
// Compare which edge is shorter
|
||||
if ( r.lengthSquared() < s.lengthSquared() ) {
|
||||
return Optional.of( new IntersectionCollinear( a, b ) );
|
||||
} else {
|
||||
return Optional.of( new IntersectionCollinear( b, a ) );
|
||||
double edgeUpperY = Math.max( edgeOriginY, edgeDestY );
|
||||
double edgeLowerY = Math.min( edgeOriginY, edgeDestY );
|
||||
|
||||
// Use the positive edge for finding the intersection
|
||||
for ( HalfEdge other : scannedEdges ) {
|
||||
if ( edge == other || edge == other.getSym() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do simple Y axis check between the two edges to see if they overlap
|
||||
{
|
||||
final double otherOriginY = other.getOrigin().getPosition().getY();
|
||||
final double otherDestY = other.getDest().getPosition().getY();
|
||||
|
||||
final double otherUpperY = Math.max( otherOriginY, otherDestY );
|
||||
final double otherLowerY = Math.min( otherOriginY, otherDestY );
|
||||
|
||||
if ( !( edgeLowerY < otherUpperY && otherLowerY < edgeUpperY ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ( isSimilar( a.getOrigin(), b.getOrigin() ) ) {
|
||||
// TODO Remove this
|
||||
throw new IllegalArgumentException( "Vertex not merged!" );
|
||||
} else if ( !isSimilar( a.getDest(), b.getDest() ) ) {
|
||||
|
||||
if ( edge.getOrigin() != other.getOrigin() && edge.getOrigin() != other.getDest() && !isSimilar( edge.getDest(), other.getDest() ) ) {
|
||||
// Both edges may intersect somewhere in the middle
|
||||
final Vector2d p = a.getOrigin().getPosition();
|
||||
final Vector2d r = a.toVector2d();
|
||||
final Vector2d q = b.getOrigin().getPosition();
|
||||
final Vector2d s = b.toVector2d();
|
||||
final Vector2d q = other.getOrigin().getPosition();
|
||||
final Vector2d s = other.toVector2d();
|
||||
|
||||
final Vector2d pq = q.subtracted( p );
|
||||
final double pqr = pq.cross( r );
|
||||
final double rs = r.cross( s );
|
||||
final double pqr = pq.cross( r );
|
||||
|
||||
// Check if parallel
|
||||
if ( rs != 0 ) {
|
||||
@@ -643,13 +643,40 @@ public class Mesh< T extends Region > {
|
||||
// They should NOT intersect at their endpoints
|
||||
if ( t > tTol && t < ( 1 - tTol ) && u > uTol && u < ( 1 - uTol ) ) {
|
||||
// Calculate the point of intersection
|
||||
final Vector2d intersection = r.multiply( t ).add( p );
|
||||
final Vector2d intersection = r.multiplied( t ).add( p );
|
||||
|
||||
return Optional.of( new IntersectionEdgeToEdge( intersection ) );
|
||||
// Split the first edge
|
||||
HalfEdge aSplit = edge.split();
|
||||
RegionRule< T > aRule = rules.get( edge );
|
||||
rules.put( aSplit, aRule );
|
||||
rules.put( aSplit.getSym(), aRule.inverse() );
|
||||
|
||||
// Update the position
|
||||
final Vertex aSplitOrigin = aSplit.getOrigin();
|
||||
aSplitOrigin.setPosition( intersection );
|
||||
|
||||
// Split the other edge
|
||||
HalfEdge bSplit = other.split();
|
||||
RegionRule< T > bRule = rules.get( other );
|
||||
rules.put( bSplit, bRule );
|
||||
rules.put( bSplit.getSym(), bRule.inverse() );
|
||||
|
||||
// Splice the edges together into the same vertex
|
||||
HalfEdge.splice( bSplit, aSplit );
|
||||
|
||||
aSplitOrigin.update();
|
||||
|
||||
vertices.add( aSplitOrigin );
|
||||
|
||||
// Recalculate the edge values
|
||||
edgeDestY = intersection.getY();
|
||||
r = edge.toVector2d();
|
||||
edgeUpperY = Math.max( edgeOriginY, edgeDestY );
|
||||
edgeLowerY = Math.min( edgeOriginY, edgeDestY );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,8 +686,6 @@ public class Mesh< T extends Region > {
|
||||
*/
|
||||
private void mergeOverlappingEdges( final Collection< Vertex > vertices ) {
|
||||
for ( final Vertex vertex : vertices ) {
|
||||
sort( vertex );
|
||||
|
||||
// Keep track of which ranges of edges are attached to which
|
||||
final Map< Vertex, Queue< HalfEdge > > ranges = new HashMap< Vertex, Queue< HalfEdge > >();
|
||||
|
||||
@@ -729,8 +754,15 @@ public class Mesh< T extends Region > {
|
||||
throw new IllegalStateException( "Mesh is not simplified!" );
|
||||
}
|
||||
|
||||
final Queue< Vertex > queue = new PriorityQueue< Vertex >( Mesh::compare );
|
||||
queue.addAll( vertices );
|
||||
final Collection< Vertex > vertexSet = new TreeSet< Vertex >( ( a, b ) -> {
|
||||
final int compare = compare( a, b );
|
||||
if ( compare == 0 ) {
|
||||
return Integer.compare( a.hashCode(), b.hashCode() );
|
||||
} else {
|
||||
return compare;
|
||||
}
|
||||
} );
|
||||
vertexSet.addAll( vertices );
|
||||
|
||||
// Keep track of what regions belong to what edge
|
||||
final Map< HalfEdge, T > regions = new HashMap< HalfEdge, T >();
|
||||
@@ -747,8 +779,7 @@ public class Mesh< T extends Region > {
|
||||
final Set< HalfEdge > toRemove = new HashSet< HalfEdge >();
|
||||
|
||||
// Assume the edges on each vertex is already sorted
|
||||
Vertex vertex;
|
||||
while ( ( vertex = queue.poll() ) != null ) {
|
||||
for ( Vertex vertex : vertexSet ) {
|
||||
// Insert all edges into the edge set
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
if ( !isPositive( edge ) ) {
|
||||
@@ -854,6 +885,9 @@ public class Mesh< T extends Region > {
|
||||
|
||||
vertices = keep;
|
||||
|
||||
vertexMap.clear();
|
||||
vertices.forEach( v -> vertexMap.put( v.getPosition(), v ) );
|
||||
|
||||
/*
|
||||
* Invariants:
|
||||
* - All vertices have an even number of edges
|
||||
@@ -2760,7 +2794,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
// Keep track of which edge to insert new edges for the given vertex
|
||||
final Map< Vertex, HalfEdge > insertEdges = new HashMap< Vertex, HalfEdge >();
|
||||
final Queue< HalfEdge > edges = new PriorityQueue< HalfEdge >( ( a, b ) -> {
|
||||
final TreeSet< HalfEdge > edges = new TreeSet< HalfEdge >( ( a, b ) -> {
|
||||
return compare( a.getOrigin(), b.getOrigin() );
|
||||
} );
|
||||
edges.addAll( polygon.getEdges() );
|
||||
@@ -2768,12 +2802,12 @@ public class Mesh< T extends Region > {
|
||||
|
||||
final Stack< HalfEdge > stack = new Stack< HalfEdge >();
|
||||
// Add the first two vertices/edges
|
||||
stack.add( edges.poll() );
|
||||
stack.add( edges.poll() );
|
||||
stack.add( edges.pollFirst() );
|
||||
stack.add( edges.pollFirst() );
|
||||
|
||||
HalfEdge prev = stack.peek();
|
||||
while ( edges.size() > 1 ) {
|
||||
final HalfEdge edge = edges.poll();
|
||||
final HalfEdge edge = edges.pollFirst();
|
||||
|
||||
final boolean isEdgePositive = isPositive( edge );
|
||||
if ( isEdgePositive ^ isPositive( stack.peek() ) ) {
|
||||
@@ -2865,7 +2899,7 @@ public class Mesh< T extends Region > {
|
||||
// The last edge must be a negative edge, since
|
||||
// the last vertex must terminate the polygon and
|
||||
// therefore have no right-going edges.
|
||||
final HalfEdge last = edges.poll();
|
||||
final HalfEdge last = edges.pollFirst();
|
||||
stack.pop();
|
||||
final boolean isPositive = isPositive( stack.peek() );
|
||||
HalfEdge insertEdge = last;
|
||||
@@ -2999,48 +3033,6 @@ public class Mesh< T extends Region > {
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents an intersection
|
||||
*/
|
||||
private static abstract class Intersection {
|
||||
}
|
||||
|
||||
/*
|
||||
* When 2 edges both intersect somewhere in the middle
|
||||
*/
|
||||
private static class IntersectionEdgeToEdge extends Intersection {
|
||||
private final Vector2d point;
|
||||
|
||||
IntersectionEdgeToEdge( Vector2d point ) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
Vector2d getPoint() {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When 2 edges are collinear, and do not share the same endpoint
|
||||
*/
|
||||
private static class IntersectionCollinear extends Intersection {
|
||||
private final HalfEdge shorter;
|
||||
private final HalfEdge longer;
|
||||
|
||||
IntersectionCollinear( HalfEdge shorter, HalfEdge longer ) {
|
||||
this.shorter = shorter;
|
||||
this.longer = longer;
|
||||
}
|
||||
|
||||
public HalfEdge getShorterEdge() {
|
||||
return shorter;
|
||||
}
|
||||
|
||||
public HalfEdge getLongerEdge() {
|
||||
return longer;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EdgePolygon {
|
||||
private Set< HalfEdge > edges = new HashSet< HalfEdge >();
|
||||
private Set< Vertex > vertices = new HashSet< Vertex >();
|
||||
|
||||
@@ -66,6 +66,17 @@ public class Vector2d extends Point {
|
||||
return add( this, o );
|
||||
}
|
||||
|
||||
public Vector2d absolute() {
|
||||
x = Math.abs( x );
|
||||
y = Math.abs( y );
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector2d absoluteOf() {
|
||||
return new Vector2d( Math.abs( x ), Math.abs( y ) );
|
||||
}
|
||||
|
||||
public Vector2d subtract( double v ) {
|
||||
x -= v;
|
||||
y -= v;
|
||||
|
||||
@@ -133,6 +133,9 @@ public class MeshingTest2 extends JPanel {
|
||||
// }
|
||||
|
||||
final List< Plane > masterPlanes = new ArrayList< Plane >();
|
||||
System.out.println( "Adding test plane" );
|
||||
masterPlanes.add( getTestPlane() );
|
||||
|
||||
System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
||||
int limit = 1;
|
||||
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||
@@ -144,12 +147,10 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-1,-1" ) );
|
||||
// masterPlanes.add( planes.get( 20 ) );
|
||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-10,-10" ) );
|
||||
// masterPlanes.add( planes.get( 27 ) );
|
||||
// masterPlanes.addAll( planes );
|
||||
|
||||
// masterPlanes.add( getTestPlane() );
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -262,11 +263,18 @@ public class MeshingTest2 extends JPanel {
|
||||
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
|
||||
try {
|
||||
mesh.generateRegions();
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
} catch ( Exception e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
@@ -547,6 +555,9 @@ public class MeshingTest2 extends JPanel {
|
||||
private static Plane getTestPlane() {
|
||||
final Plane plane = new Plane();
|
||||
|
||||
plane.normal = new Vector( 0, 0, 1 );
|
||||
plane.point = new Vector( 1, 0, 0 );
|
||||
|
||||
// plane.polygons.add( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
@@ -560,19 +571,74 @@ public class MeshingTest2 extends JPanel {
|
||||
// new Point( 0, 3 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 0, 1 )
|
||||
// plane.polygons.add( new Polygon( Arrays.asList(
|
||||
// new Point( 1, 0 ),
|
||||
// new Point( 2, 1 ),
|
||||
// new Point( 3, 0 ),
|
||||
// new Point( 4, 1 ),
|
||||
// new Point( 3, 2 ),
|
||||
// new Point( 4, 3 ),
|
||||
// new Point( 3, 4 ),
|
||||
// new Point( 2, 3 ),
|
||||
// new Point( 1, 4 ),
|
||||
// new Point( 0, 3 ),
|
||||
// new Point( 1, 2 ),
|
||||
// new Point( 0, 1 )
|
||||
// ) ) );
|
||||
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 1, -1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( -1, 1 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, 1 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( 1, 3 ),
|
||||
new Point( -1, 3 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -2, -1 ),
|
||||
new Point( -1, -1 ),
|
||||
new Point( -1, 2 ),
|
||||
new Point( -2, 2 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -4, -2 ),
|
||||
new Point( -1, -2 ),
|
||||
new Point( -1, 1.5 ),
|
||||
new Point( -4, 1.5 )
|
||||
) ) );
|
||||
plane.polygons.add( 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 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( -1, 0 ),
|
||||
new Point( 0, 1.5 ),
|
||||
new Point( 1, 0 )
|
||||
) ) );
|
||||
plane.polygons.add( new Polygon( Arrays.asList(
|
||||
new Point( 1, 0 ),
|
||||
new Point( 2, 1 ),
|
||||
new Point( 3, 0 ),
|
||||
new Point( 4, 1 ),
|
||||
new Point( 3, 2 ),
|
||||
new Point( 4, 3 ),
|
||||
new Point( 3, 4 ),
|
||||
new Point( 2, 3 ),
|
||||
new Point( 1, 4 ),
|
||||
new Point( 0, 3 ),
|
||||
new Point( 1, 2 ),
|
||||
new Point( 0, 1 )
|
||||
new Point( 0, -2 ),
|
||||
new Point( -1, 0 )
|
||||
) ) );
|
||||
|
||||
return plane;
|
||||
|
||||
@@ -48,13 +48,13 @@ public class MeshingTest3 {
|
||||
System.out.println( "\tTook " + time + "ms" );
|
||||
}
|
||||
|
||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-1,-1" ) ); // 123
|
||||
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-10,-10" ) ); // 25
|
||||
// for ( int i = 0; i < planes.size(); ++i ) {
|
||||
// System.out.println( "Meshing plane " + i );
|
||||
// process( planes.get( i ) );
|
||||
// }
|
||||
|
||||
// process( planes.get( 20 ) );
|
||||
// process( planes.get( 27 ) );
|
||||
} else {
|
||||
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
final Location loc = block.getLocation();
|
||||
|
||||
// Start the box at 0, 0, 0
|
||||
final BoundingBox[] boundingBoxes = convertFrom( getShape( data, loc, BlockShapeType.VISUAL_SHAPE ), new Vector( x, y - worldMinHeight, z ) );
|
||||
final BoundingBox[] boundingBoxes = convertFrom( getShape( data, loc, BlockShapeType.COLLISION_SHAPE ), new Vector( x, y - worldMinHeight, z ) );
|
||||
|
||||
for ( BoundingBox box : boundingBoxes ) {
|
||||
boxes.add( box );
|
||||
|
||||
Reference in New Issue
Block a user