From 3426829c2b07fe7f9b26722234263b3c6d383460 Mon Sep 17 00:00:00 2001 From: BananaPuncher714 Date: Wed, 21 May 2025 21:27:37 -0400 Subject: [PATCH] Fixed problems with sorting collinear and intersecting edges caused by chain resolution in partition monotone --- .../bananapuncher714/mesh/Mesh.java | 79 +++++++++++++------ .../minietest/MeshingTest2.java | 35 +++++--- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java index 501794e..8ea8f35 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java @@ -72,7 +72,8 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.region.RegionRule; */ public class Mesh< T extends Region > { public static final double VERTEX_TOLERANCE = 1e-7; - public static final double ANGLE_TOLERANCE = Math.sin( Math.toRadians( 1e-10 ) ); + public static final double ANGLE_TOLERANCE = Math.toRadians( 1e-10 ); + public static final double CROSS_TOLERANCE = Math.sin( ANGLE_TOLERANCE ); // All points in the graph protected Collection< Vertex > vertices = new ArrayDeque< Vertex >(); @@ -512,7 +513,7 @@ public class Mesh< T extends Region > { final double angle = r.cross( s ); // Ignore edges that are not collinear - if ( Math.abs( angle ) <= ANGLE_TOLERANCE ) { + if ( Math.abs( angle ) <= CROSS_TOLERANCE ) { // Compare which edge is shorter if ( r.lengthSquared() < s.lengthSquared() ) { @@ -829,7 +830,7 @@ public class Mesh< T extends Region > { } // Fast forward to the start of a potential collinear chain, since this edge may be in the middle of one - while ( Math.abs( edge.toVector2d().normalize().cross( edge.getPrev().toVector2d().normalize() ) ) <= ANGLE_TOLERANCE ) { + while ( Math.abs( edge.toVector2d().normalize().cross( edge.getPrev().toVector2d().normalize() ) ) <= CROSS_TOLERANCE ) { edge = edge.getNext(); } @@ -839,7 +840,7 @@ public class Mesh< T extends Region > { final Vector2d a = edge.toVector2d().normalize(); final Vector2d b = next.toVector2d().normalize(); - if ( Math.abs( a.cross( b ) ) <= ANGLE_TOLERANCE ) { + if ( Math.abs( a.cross( b ) ) <= CROSS_TOLERANCE ) { // We can more or less unlink edges for free at this point // since we can guarantee each edge borders one exterior // and one interior region, and no two regions share an edge. @@ -1160,12 +1161,11 @@ public class Mesh< T extends Region > { // Get the cross value for this vertex to the current event vertex final Vector2d toEvent = event.getPosition().subtracted( vert.getPosition() ).normalize(); final double upperCross = clamp( CROSS.cross( toEvent ), -1, 1 ); - final double upperAngle = Math.asin( upperCross ); // Check over each visibility region and update the upper cross value for ( final Iterator< VisibilityRegion > regionIt = regions.iterator(); regionIt.hasNext(); ) { final VisibilityRegion region = regionIt.next(); - if ( region.upper != MAX_CROSS && Math.abs( upperAngle - Math.asin( region.upper ) ) < ANGLE_TOLERANCE ) { + if ( region.upper != MAX_CROSS && Math.abs( upperCross - region.upper ) < CROSS_TOLERANCE ) { // Make sure the region upper value has been initialized, before checking // if this vertex is collinear with the previous vertex @@ -1194,9 +1194,9 @@ public class Mesh< T extends Region > { // Get the angle difference between the region's upper and lower bounds // but set it to -1 if it the lower region has not been initialized - final double angleDifference = region.lower == MIN_CROSS ? -1 : ( Math.asin( region.lower ) - upperAngle ); + final double crossDifference = region.lower == MIN_CROSS ? -1 : ( region.lower - upperCross ); - if ( Math.abs( angleDifference ) <= ANGLE_TOLERANCE ) { + if ( Math.abs( crossDifference ) <= CROSS_TOLERANCE ) { // The event vertex is not collinear with the previous upper // vertex, but it _is_ collinear with the previous lower vertex! @@ -1234,7 +1234,7 @@ public class Mesh< T extends Region > { // bounds have already converged, and met at this collinear // vertex. regionIt.remove(); - } else if ( angleDifference > ANGLE_TOLERANCE ) { + } else if ( crossDifference > CROSS_TOLERANCE ) { // If the upper angle is less than the lower angle, then we know // that the region is no longer visible to this vertex regionIt.remove(); @@ -1291,12 +1291,11 @@ public class Mesh< T extends Region > { // Get the cross value for this vertex to the current event vertex final Vector2d toEvent = event.getPosition().subtracted( vert.getPosition() ).normalize(); final double lowerCross = clamp( CROSS.cross( toEvent ), -1, 1 ); - final double lowerAngle = Math.asin( lowerCross ); // Check over each visibility region and update the upper cross value for ( final Iterator< VisibilityRegion > regionIt = regions.iterator(); regionIt.hasNext(); ) { final VisibilityRegion region = regionIt.next(); - if ( region.lower != MAX_CROSS && Math.abs( lowerAngle - Math.asin( region.lower ) ) < ANGLE_TOLERANCE ) { + if ( region.lower != MAX_CROSS && Math.abs( lowerCross - region.lower ) < CROSS_TOLERANCE ) { // Make sure the region lower value has been initialized, before checking // if this vertex is collinear with the previous vertex @@ -1324,9 +1323,9 @@ public class Mesh< T extends Region > { // Get the angle difference between the region's upper and lower bounds // but set it to -1 if it the upper region has not been initialized - final double angleDifference = region.upper == MAX_CROSS ? -1 : ( lowerAngle - Math.asin( region.upper ) ); + final double crossDifference = region.upper == MAX_CROSS ? -1 : ( lowerCross -region.upper ); - if ( Math.abs( angleDifference ) <= ANGLE_TOLERANCE ) { + if ( Math.abs( crossDifference ) <= CROSS_TOLERANCE ) { // The event vertex is not collinear with the previous upper // vertex, but it _is_ collinear with the previous upper vertex! @@ -1363,7 +1362,7 @@ public class Mesh< T extends Region > { // bounds have already converged, and met at this collinear // vertex. regionIt.remove(); - } else if ( angleDifference > ANGLE_TOLERANCE ) { + } else if ( crossDifference > CROSS_TOLERANCE ) { // If the upper angle is less than the lower angle, then we know // that the region is no longer visible to this vertex regionIt.remove(); @@ -1961,7 +1960,6 @@ public class Mesh< T extends Region > { // Get the direction of this chain final Vector2d chainDirection = chain.getDest().getPosition().subtracted( chain.getOrigin().getPosition() ); - System.out.println( chain + ":\t" + chain.links.get( 0 ) + "\t to \t" + chain.links.get( 1 ) + "\t to \t" + chain.links.get( 2 ) ); System.out.println( "Linking chain" ); // Setup the left and right edges if the first link is not a direct edge from the origin to the midpoint if ( chain.links.get( 0 ).getDest() != chain.getMidpoint() ) { @@ -1984,8 +1982,7 @@ public class Mesh< T extends Region > { System.out.println( "Starting chain " + chain.getOrigin() ); - - + Chain initial = chain; Chain previousChain; do { selectedChains.remove( chain ); @@ -2177,7 +2174,7 @@ public class Mesh< T extends Region > { // selectedChains.remove( chain.next ); // cs.remove( chain.next ); // break; - } while ( ( chain = chain.next ) != null ); + } while ( ( chain = chain.next ) != null && selectedChains.contains( chain ) ); // TODO Make sure the edges are fully attached/detached! Dangling edges are detected! @@ -2232,6 +2229,22 @@ public class Mesh< T extends Region > { // System.out.println( "-1 \t" + previousChain.getMidpoint() ); } + System.out.println( "Start:" ); + for ( HalfEdge e : initial.getOrigin() ) { + System.out.println( e ); + } + + do { + System.out.println( "Mid:" ); + for ( HalfEdge e : initial.getMidpoint() ) { + System.out.println( e ); + } + } while ( previousChain != initial && ( initial = initial.next ) != null ); + System.out.println( "End:" ); + for ( HalfEdge e : previousChain.getDest() ) { + System.out.println( e ); + } + // System.out.println( "Origin: " + previousChain.getOrigin() ); // for ( HalfEdge e : previousChain.getOrigin() ) { // System.out.println( e + "\t" + e.toVector2d() ); @@ -2413,21 +2426,34 @@ public class Mesh< T extends Region > { // a1 < a2 final Vector2d a1 = a.getOrigin().getPosition(); final Vector2d a2 = a.getDest().getPosition(); + final Vector2d aLine = a.toVector2d().normalize(); + // b1 < b2 final Vector2d b1 = b.getOrigin().getPosition(); final Vector2d b2 = b.getDest().getPosition(); + final Vector2d bLine = b.toVector2d().normalize(); final int compared = compare( a.getOrigin(), b.getOrigin() ); double cross; if ( compared == 0 ) { - cross = b2.subtracted( b1 ).normalized().cross( a2.subtracted( a1 ).normalized() ); + cross = bLine.cross( aLine ); } else if ( compared < 0 ) { - cross = b1.subtracted( a1 ).normalized().cross( a2.subtracted( a1 ).normalized() ); + // If cross is still 0, compare the other + cross = b1.subtracted( a1 ).normalized().cross( aLine ); + if ( Math.abs( cross ) < CROSS_TOLERANCE ) { + cross = b2.subtracted( a1 ).normalized().cross( aLine ); + } } else { - cross = b2.subtracted( b1 ).normalized().cross( a1.subtracted( b1 ).normalized() ); + // If cross is still 0, compare the other + cross = bLine.cross( a1.subtracted( b1 ).normalized() ); + if ( Math.abs( cross ) < CROSS_TOLERANCE ) { + cross = bLine.cross( a2.subtracted( b1 ).normalize() ); + } } + +// System.out.println( a + " vs " + b + " is " + cross ); - if ( Math.asin( Math.min( 1, Math.abs( cross ) ) ) > ANGLE_TOLERANCE ) { + if ( Math.abs( cross ) > CROSS_TOLERANCE ) { return cross > 0; } else { // Collinear @@ -2452,6 +2478,7 @@ public class Mesh< T extends Region > { rightGoing.add( edge ); } else { leftGoing.add( edge ); +// System.out.println( "Removing " + edge.getSym() ); if ( !edgeCollection.remove( edge.getSym() ) ) { throw new IllegalStateException( "Edge not added to the edge collection!" ); } @@ -2494,6 +2521,13 @@ public class Mesh< T extends Region > { } throw new IllegalStateException( "Lower does not have an upper edge!" ); } if ( !interior.contains( upper.getSym() ) ) { + System.out.println( "Current: " + event ); + System.out.println( "Lower: " + lower ); + System.out.println( "Upper: " + upper ); + System.out.println( "Edges:" ); + for ( final HalfEdge e : edgeCollection ) { + System.out.println( e + "\t" + interior.contains( e ) + "\t" + interior.contains( e.getSym() ) ); + } throw new IllegalStateException( "Polygon is not simple!" ); } else if ( upper.getOrigin() == event || lower.getOrigin() == event ) { throw new IllegalStateException( "Vertex is invalid!" ); @@ -2644,6 +2678,7 @@ public class Mesh< T extends Region > { // now that we have finally finished adding new edges. for ( final HalfEdge edge : rightGoing ) { if ( isPositive( edge ) ) { +// System.out.println( "Adding " + edge ); edgeCollection.insert( edge ); } else { throw new IllegalStateException( "Edge suddenly changed sign!" ); diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java index e557382..1deb022 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MeshingTest2.java @@ -95,13 +95,13 @@ public class MeshingTest2 extends JPanel { // draw = planes.get( new Random().nextInt( planes.size() ) ); System.out.println( "Draw is " + draw ); - // Attempt to mesh all planes -// try { -// test( planes ); -// } catch ( PolygonException e ) { -// draw = e.getPlane(); -// System.out.println( "Draw is now " + draw ); -// } +// Attempt to mesh all planes + try { + test( planes ); + } catch ( PolygonException e ) { + draw = e.getPlane(); + System.out.println( "Draw is now " + draw ); + } final Collection< Vertex > data = process( draw ); final Collection< EdgePolygon > polys = triangulate( draw ); @@ -265,8 +265,18 @@ public class MeshingTest2 extends JPanel { // Make sure this works mesh = mesh.copyOf(); - final Collection< EdgePolygon > polys = mesh.mesh( false ); - chains = mesh.chains; + Collection< EdgePolygon > polys; + + try { + polys = mesh.mesh( false ); + chains = mesh.chains; + } catch ( IllegalStateException e ) { + e.printStackTrace(); + + polys = mesh.mesh( true ); + chains = mesh.chains; + } + long end = System.currentTimeMillis(); System.out.println( "Took " + ( end - start ) + "ms" ); @@ -646,6 +656,7 @@ public class MeshingTest2 extends JPanel { int count = 2; Random random = new Random( hashCode() ); + final int spacing = 25; for ( final EdgePolygon p : polygons ) { final Collection< HalfEdge > edges = p.getEdges(); @@ -655,9 +666,9 @@ public class MeshingTest2 extends JPanel { Point p1 = edge.getOrigin().getPosition(); Point p2 = edge.getDest().getPosition(); g.drawLine( - ( int ) ( ( centerX + p1.getX() + count * 25 ) * scale ) + offsetX, + ( int ) ( ( centerX + p1.getX() + count * spacing ) * scale ) + offsetX, ( int ) ( ( centerY - p1.getY() - 150 ) * scale ) + offsetY, - ( int ) ( ( centerX + p2.getX() + count * 25 ) * scale ) + offsetX, + ( int ) ( ( centerX + p2.getX() + count * spacing ) * scale ) + offsetX, ( int ) ( ( centerY - p2.getY() - 150 ) * scale ) + offsetY ); @@ -670,7 +681,7 @@ public class MeshingTest2 extends JPanel { final double diff = scale * 0.15; if ( !verts.add( edge.getOrigin() ) ) { - g.drawRect( ( int ) ( ( centerX + p1.getX() + count * 25 ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - p1.getY() - 150 ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) ); + g.drawRect( ( int ) ( ( centerX + p1.getX() + count * spacing ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - p1.getY() - 150 ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) ); } } count++;