Fixed duplicate collinear edge bug
This commit is contained in:
@@ -983,6 +983,9 @@ public class Mesh< T extends Region > {
|
|||||||
// TODO Make not static
|
// TODO Make not static
|
||||||
public static class Chain {
|
public static class Chain {
|
||||||
List< HalfEdge > links;
|
List< HalfEdge > links;
|
||||||
|
Set< Chain > intersections = new HashSet< Chain >();
|
||||||
|
Chain previous;
|
||||||
|
Chain next;
|
||||||
|
|
||||||
Chain( final Collection< HalfEdge > links ) {
|
Chain( final Collection< HalfEdge > links ) {
|
||||||
this.links = new ArrayList< HalfEdge >( links );
|
this.links = new ArrayList< HalfEdge >( links );
|
||||||
@@ -992,6 +995,10 @@ public class Mesh< T extends Region > {
|
|||||||
return links.get( 0 ).getOrigin();
|
return links.get( 0 ).getOrigin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vertex getMidpoint() {
|
||||||
|
return links.get( 1 ).getOrigin();
|
||||||
|
}
|
||||||
|
|
||||||
public Vertex getDest() {
|
public Vertex getDest() {
|
||||||
return links.get( 2 ).getOrigin();
|
return links.get( 2 ).getOrigin();
|
||||||
}
|
}
|
||||||
@@ -1089,6 +1096,9 @@ public class Mesh< T extends Region > {
|
|||||||
// may be merged into a single partition, and each partition
|
// may be merged into a single partition, and each partition
|
||||||
// may have a visibility region for the same vertex.
|
// may have a visibility region for the same vertex.
|
||||||
Map< Vertex, Collection< VisibilityRegion > > regionMap = new HashMap< Vertex, Collection< VisibilityRegion > >();
|
Map< Vertex, Collection< VisibilityRegion > > regionMap = new HashMap< Vertex, Collection< VisibilityRegion > >();
|
||||||
|
|
||||||
|
// Keep track of all chains that were formed in this current partition.
|
||||||
|
// For all new chains, see what previous chains it may intersect with
|
||||||
Collection< Chain > chains = new HashSet< Chain >();
|
Collection< Chain > chains = new HashSet< Chain >();
|
||||||
|
|
||||||
Partition( final HalfEdge lower, final HalfEdge upper ) {
|
Partition( final HalfEdge lower, final HalfEdge upper ) {
|
||||||
@@ -1131,10 +1141,8 @@ public class Mesh< T extends Region > {
|
|||||||
// and we don't really care about what comes after
|
// and we don't really care about what comes after
|
||||||
links.add( edge );
|
links.add( edge );
|
||||||
|
|
||||||
// TODO Check for intersections
|
|
||||||
|
|
||||||
final Chain chain = new Chain( links );
|
final Chain chain = new Chain( links );
|
||||||
chains.add( chain );
|
addChain( chain );
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( upperCross < region.upper ) {
|
} else if ( upperCross < region.upper ) {
|
||||||
@@ -1148,15 +1156,17 @@ public class Mesh< T extends Region > {
|
|||||||
// The event vertex is not collinear with the previous upper
|
// The event vertex is not collinear with the previous upper
|
||||||
// vertex, but it _is_ collinear with the previous lower vertex!
|
// vertex, but it _is_ collinear with the previous lower vertex!
|
||||||
|
|
||||||
// Remove the previous links
|
// Remove the previous links, but only if the upper is not exactly equal to the lower
|
||||||
|
if ( region.upper != region.lower ) {
|
||||||
region.links.remove( region.upper );
|
region.links.remove( region.upper );
|
||||||
|
region.upper = region.lower;
|
||||||
|
}
|
||||||
|
|
||||||
// There should be exactly 1 link, the lower region's link
|
// There should be exactly 1 link, the lower region's link
|
||||||
if ( region.links.size() != 1 ) {
|
if ( region.links.size() != 1 ) {
|
||||||
throw new IllegalStateException( "Dangling link!" );
|
throw new IllegalStateException( "Dangling link!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
region.upper = region.lower;
|
|
||||||
|
|
||||||
final List< HalfEdge > links = region.links.get( region.upper );
|
final List< HalfEdge > links = region.links.get( region.upper );
|
||||||
|
|
||||||
@@ -1172,10 +1182,8 @@ public class Mesh< T extends Region > {
|
|||||||
// and we don't really care about what comes after
|
// and we don't really care about what comes after
|
||||||
links.add( edge );
|
links.add( edge );
|
||||||
|
|
||||||
// TODO Check for intersections
|
|
||||||
|
|
||||||
final Chain chain = new Chain( links );
|
final Chain chain = new Chain( links );
|
||||||
chains.add( chain );
|
addChain( chain );
|
||||||
|
|
||||||
// Remove this vertex since we know the upper and lower
|
// Remove this vertex since we know the upper and lower
|
||||||
// bounds have already converged, and met at this collinear
|
// bounds have already converged, and met at this collinear
|
||||||
@@ -1286,10 +1294,8 @@ public class Mesh< T extends Region > {
|
|||||||
// and we don't really care about what comes after
|
// and we don't really care about what comes after
|
||||||
links.add( edge );
|
links.add( edge );
|
||||||
|
|
||||||
// TODO Check for intersections
|
|
||||||
|
|
||||||
final Chain chain = new Chain( links );
|
final Chain chain = new Chain( links );
|
||||||
chains.add( chain );
|
addChain( chain );
|
||||||
}
|
}
|
||||||
} else if ( lowerCross > region.lower ) {
|
} else if ( lowerCross > region.lower ) {
|
||||||
// Is the new cross value greater than the previous cross value?
|
// Is the new cross value greater than the previous cross value?
|
||||||
@@ -1302,16 +1308,17 @@ public class Mesh< T extends Region > {
|
|||||||
// The event vertex is not collinear with the previous upper
|
// The event vertex is not collinear with the previous upper
|
||||||
// vertex, but it _is_ collinear with the previous upper vertex!
|
// vertex, but it _is_ collinear with the previous upper vertex!
|
||||||
|
|
||||||
// Remove the previous links
|
// Remove the previous links, but only if the upper is not exactly equal to the lower
|
||||||
|
if ( region.upper != region.lower ) {
|
||||||
region.links.remove( region.lower );
|
region.links.remove( region.lower );
|
||||||
|
region.lower = region.upper;
|
||||||
|
}
|
||||||
|
|
||||||
// There should be exactly 1 link, the lower region's link
|
// There should be exactly 1 link, the lower region's link
|
||||||
if ( region.links.size() != 1 ) {
|
if ( region.links.size() != 1 ) {
|
||||||
throw new IllegalStateException( "Dangling link!" );
|
throw new IllegalStateException( "Dangling link!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
region.lower = region.upper;
|
|
||||||
|
|
||||||
final List< HalfEdge > links = region.links.get( region.lower );
|
final List< HalfEdge > links = region.links.get( region.lower );
|
||||||
|
|
||||||
if ( links == null ) {
|
if ( links == null ) {
|
||||||
@@ -1326,10 +1333,8 @@ public class Mesh< T extends Region > {
|
|||||||
// and we don't really care about what comes after
|
// and we don't really care about what comes after
|
||||||
links.add( edge );
|
links.add( edge );
|
||||||
|
|
||||||
// TODO Check for intersections
|
|
||||||
|
|
||||||
final Chain chain = new Chain( links );
|
final Chain chain = new Chain( links );
|
||||||
chains.add( chain );
|
addChain( chain );
|
||||||
|
|
||||||
// Remove this vertex since we know the upper and lower
|
// Remove this vertex since we know the upper and lower
|
||||||
// bounds have already converged, and met at this collinear
|
// bounds have already converged, and met at this collinear
|
||||||
@@ -1384,6 +1389,58 @@ public class Mesh< T extends Region > {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check all previous chains for intersections and stuff
|
||||||
|
*/
|
||||||
|
void addChain( final Chain chain ) {
|
||||||
|
final Vertex chainOrigin = chain.getOrigin();
|
||||||
|
final Vertex chainMidpoint = chain.getMidpoint();
|
||||||
|
final Vertex chainDestination = chain.getDest();
|
||||||
|
|
||||||
|
final Vector2d p = chainOrigin.getPosition();
|
||||||
|
final Vector2d r = chainDestination.getPosition().subtracted( p );
|
||||||
|
for ( final Chain other : chains ) {
|
||||||
|
// The new chain is a continuation of this chain
|
||||||
|
if ( other.getMidpoint() == chainOrigin && other.getDest() == chainMidpoint ) {
|
||||||
|
other.next = chain;
|
||||||
|
chain.previous = other;
|
||||||
|
} else if ( compare( other.getDest(), chainOrigin ) > 0 ) {
|
||||||
|
// Do the chains even overlap?
|
||||||
|
if ( other.getOrigin() != chainOrigin && other.getDest() != chainDestination ) {
|
||||||
|
// Make sure the chains don't share the same origin or destination
|
||||||
|
|
||||||
|
// Perform a basic check
|
||||||
|
// Both edges may intersect somewhere in the middle
|
||||||
|
final Vector2d q = other.getOrigin().getPosition();
|
||||||
|
final Vector2d s = other.getDest().getPosition().subtracted( q );
|
||||||
|
|
||||||
|
final Vector2d pq = q.subtracted( p );
|
||||||
|
final double pqr = pq.cross( r );
|
||||||
|
final double rs = r.cross( s );
|
||||||
|
|
||||||
|
// Check if parallel
|
||||||
|
if ( rs != 0 ) {
|
||||||
|
// Calculate t and u
|
||||||
|
final double t = pq.cross( s ) / rs;
|
||||||
|
final double u = pqr / rs;
|
||||||
|
|
||||||
|
final double uTol = VERTEX_TOLERANCE / s.length();
|
||||||
|
|
||||||
|
// Do the line segments intersect
|
||||||
|
// Allow for overlap
|
||||||
|
if ( t >= 0 && t <= 1 && u > -uTol && u < ( 1 + uTol ) ) {
|
||||||
|
// The chains intersect! We don't care about where.
|
||||||
|
|
||||||
|
chain.intersections.add( other );
|
||||||
|
other.intersections.add( chain );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chains.add( chain );
|
||||||
|
}
|
||||||
|
|
||||||
void add( final Vertex vertex, final VisibilityRegion region ) {
|
void add( final Vertex vertex, final VisibilityRegion region ) {
|
||||||
Collection< VisibilityRegion > regions;
|
Collection< VisibilityRegion > regions;
|
||||||
if ( regionMap.containsKey( vertex ) ) {
|
if ( regionMap.containsKey( vertex ) ) {
|
||||||
@@ -1579,6 +1636,14 @@ public class Mesh< T extends Region > {
|
|||||||
if ( region.lower != MIN_CROSS ) {
|
if ( region.lower != MIN_CROSS ) {
|
||||||
copyRegion.lower = region.lower;
|
copyRegion.lower = region.lower;
|
||||||
copyRegion.links.put( region.lower, region.links.remove( region.lower ) );
|
copyRegion.links.put( region.lower, region.links.remove( region.lower ) );
|
||||||
|
|
||||||
|
// Obviously, whenever we remove a region from the links, we need to check
|
||||||
|
// if the region even has any more links... otherwise it's clearly dead and
|
||||||
|
// needs to be removed entirely.
|
||||||
|
if ( region.links.isEmpty() ) {
|
||||||
|
regionIt.remove();
|
||||||
|
}
|
||||||
|
|
||||||
region.lower = MIN_CROSS;
|
region.lower = MIN_CROSS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1646,11 +1711,11 @@ public class Mesh< T extends Region > {
|
|||||||
throw new IllegalStateException( "Vertex has less than 2 left edges!" );
|
throw new IllegalStateException( "Vertex has less than 2 left edges!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
final HalfEdge topLeft = leftGoing.get( 0 ).getSym();
|
final HalfEdge topLeft = leftGoing.get( 0 );
|
||||||
final HalfEdge bottomLeft = leftGoing.get( leftGoing.size() - 1 ).getSym();
|
final HalfEdge bottomLeft = leftGoing.get( leftGoing.size() - 1 );
|
||||||
|
|
||||||
final Partition top = lowerEdgeMap.remove( topLeft );
|
final Partition top = lowerEdgeMap.remove( topLeft.getSym() );
|
||||||
final Partition bottom = upperEdgeMap.remove( bottomLeft );
|
final Partition bottom = upperEdgeMap.remove( bottomLeft.getSym() );
|
||||||
|
|
||||||
if ( top == null ) {
|
if ( top == null ) {
|
||||||
throw new IllegalStateException( "Upper partition not found!" );
|
throw new IllegalStateException( "Upper partition not found!" );
|
||||||
@@ -1658,7 +1723,7 @@ public class Mesh< T extends Region > {
|
|||||||
throw new IllegalStateException( "Lower partition not found!" );
|
throw new IllegalStateException( "Lower partition not found!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
chains.addAll( bottom.chains );
|
top.chains.addAll( bottom.chains );
|
||||||
lowerEdgeMap.remove( bottom.upper );
|
lowerEdgeMap.remove( bottom.upper );
|
||||||
|
|
||||||
// Merge the top and bottom partition
|
// Merge the top and bottom partition
|
||||||
@@ -1668,14 +1733,14 @@ public class Mesh< T extends Region > {
|
|||||||
lowerEdgeMap.put( top.lower, top );
|
lowerEdgeMap.put( top.lower, top );
|
||||||
|
|
||||||
// Update all visibility regions for the top and bottom partitions, separately
|
// Update all visibility regions for the top and bottom partitions, separately
|
||||||
top.incrementLower( topLeft.getSym() );
|
top.incrementLower( topLeft );
|
||||||
bottom.incrementUpper( bottomLeft.getSym() );
|
bottom.incrementUpper( bottomLeft );
|
||||||
|
|
||||||
// Merge the two partitions
|
// Merge the two partitions
|
||||||
top.add( bottom );
|
top.add( bottom );
|
||||||
|
|
||||||
// Add a new visibility region
|
// Add a new visibility region
|
||||||
top.add( event, new VisibilityRegion( bottomLeft.getSym(), topLeft.getSym() ) );
|
top.add( event, new VisibilityRegion( bottomLeft, topLeft ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// This vertex has both left and right going edges
|
// This vertex has both left and right going edges
|
||||||
@@ -1688,13 +1753,13 @@ public class Mesh< T extends Region > {
|
|||||||
|
|
||||||
if ( interior.contains( lower ) ) {
|
if ( interior.contains( lower ) ) {
|
||||||
// There exists a partition below this vertex
|
// There exists a partition below this vertex
|
||||||
final HalfEdge left = leftGoing.get( leftGoing.size() - 1 ).getSym();
|
final HalfEdge left = leftGoing.get( leftGoing.size() - 1 );
|
||||||
final HalfEdge right = rightGoing.get( 0 );
|
final HalfEdge right = rightGoing.get( 0 );
|
||||||
|
|
||||||
final Partition partition = lowerEdgeMap.get( lower );
|
final Partition partition = lowerEdgeMap.get( lower );
|
||||||
if ( partition != null ) {
|
if ( partition != null ) {
|
||||||
|
|
||||||
if ( partition.upper != left ) {
|
if ( partition.upper != left.getSym() ) {
|
||||||
throw new IllegalStateException( "Partition upper edge is inconsistent!" );
|
throw new IllegalStateException( "Partition upper edge is inconsistent!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1708,7 +1773,7 @@ public class Mesh< T extends Region > {
|
|||||||
partition.incrementUpper( right );
|
partition.incrementUpper( right );
|
||||||
|
|
||||||
// Add a new region for this vertex
|
// Add a new region for this vertex
|
||||||
partition.add( event, new VisibilityRegion( left.getSym(), right ) );
|
partition.add( event, new VisibilityRegion( left, right ) );
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException( "Interior edge does not have a partition!" );
|
throw new IllegalStateException( "Interior edge does not have a partition!" );
|
||||||
}
|
}
|
||||||
@@ -1716,13 +1781,13 @@ public class Mesh< T extends Region > {
|
|||||||
|
|
||||||
if ( upper != null && interior.contains( upper.getSym() ) ) {
|
if ( upper != null && interior.contains( upper.getSym() ) ) {
|
||||||
// There exists a partition above this vertex
|
// There exists a partition above this vertex
|
||||||
final HalfEdge left = leftGoing.get( 0 ).getSym();
|
final HalfEdge left = leftGoing.get( 0 );
|
||||||
final HalfEdge right = rightGoing.get( rightGoing.size() - 1 );
|
final HalfEdge right = rightGoing.get( rightGoing.size() - 1 );
|
||||||
|
|
||||||
final Partition partition = upperEdgeMap.get( upper );
|
final Partition partition = upperEdgeMap.get( upper );
|
||||||
if ( partition != null ) {
|
if ( partition != null ) {
|
||||||
|
|
||||||
if ( partition.lower != left ) {
|
if ( partition.lower != left.getSym() ) {
|
||||||
throw new IllegalStateException( "Partition lower edge is inconsistent!" );
|
throw new IllegalStateException( "Partition lower edge is inconsistent!" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1735,7 +1800,7 @@ public class Mesh< T extends Region > {
|
|||||||
partition.incrementLower( right );
|
partition.incrementLower( right );
|
||||||
|
|
||||||
// Add a new region for this vertex
|
// Add a new region for this vertex
|
||||||
partition.add( event, new VisibilityRegion( left.getSym(), right ) );
|
partition.add( event, new VisibilityRegion( right, left ) );
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException( "Interior edge does not have a partition!" );
|
throw new IllegalStateException( "Interior edge does not have a partition!" );
|
||||||
}
|
}
|
||||||
@@ -1759,7 +1824,17 @@ public class Mesh< T extends Region > {
|
|||||||
System.out.println( "Found links: " + chains.size() );
|
System.out.println( "Found links: " + chains.size() );
|
||||||
Map< Vertex, Collection< Vertex > > mappings = new HashMap< Vertex, Collection< Vertex > >();
|
Map< Vertex, Collection< Vertex > > mappings = new HashMap< Vertex, Collection< Vertex > >();
|
||||||
for ( final Chain c : chains ) {
|
for ( final Chain c : chains ) {
|
||||||
System.out.println( c.getOrigin() + "\t to \t" + c.getDest() );
|
System.out.println( c + ":\t" + c.links.get( 0 ) + "\t to \t" + c.links.get( 1 ) + "\t to \t" + c.links.get( 2 ) );
|
||||||
|
// System.out.println( "\tIntersects with: " + c.intersections.size() );
|
||||||
|
for ( final Chain i : c.intersections ) {
|
||||||
|
// System.out.println( "\t\t" + i + ":\t" + i.getOrigin() + "\t to \t" + i.getDest() );
|
||||||
|
}
|
||||||
|
if ( c.previous != null ) {
|
||||||
|
// System.out.println( "\tPrev: " + c.previous );
|
||||||
|
}
|
||||||
|
if ( c.next != null ) {
|
||||||
|
// System.out.println( "\tNext: " + c.next );
|
||||||
|
}
|
||||||
|
|
||||||
Collection< Vertex > destinations;
|
Collection< Vertex > destinations;
|
||||||
if ( mappings.containsKey( c.getOrigin() ) ) {
|
if ( mappings.containsKey( c.getOrigin() ) ) {
|
||||||
|
|||||||
@@ -279,17 +279,22 @@ public class MeshingTest2 extends JPanel {
|
|||||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||||
|
|
||||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||||
new Point( -1, -1 ),
|
new Point( -1, 0 ),
|
||||||
|
new Point( 1, -2 ),
|
||||||
new Point( 1, -1 ),
|
new Point( 1, -1 ),
|
||||||
new Point( 1, 1 ),
|
new Point( 2, -1 ),
|
||||||
new Point( -1, 1 )
|
new Point( 1, 0 ),
|
||||||
|
new Point( 1, 1 )
|
||||||
) ), RegionRuleWinding.CLOCKWISE );
|
) ), RegionRuleWinding.CLOCKWISE );
|
||||||
|
|
||||||
mesh.simplify();
|
mesh.simplify();
|
||||||
|
|
||||||
mesh.generateRegions();
|
mesh.generateRegions();
|
||||||
|
|
||||||
return mesh.mesh();
|
final Collection< EdgePolygon > polys = mesh.mesh();
|
||||||
|
chains = mesh.chains;
|
||||||
|
|
||||||
|
return polys;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List< Facet > generateFacetsFor( AABB box ) {
|
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||||
|
|||||||
Reference in New Issue
Block a user