Use maps for keeping track of partitions
This commit is contained in:
@@ -980,6 +980,7 @@ public class Mesh< T extends Region > {
|
||||
.collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
// TODO Make not static
|
||||
public static class Chain {
|
||||
List< HalfEdge > links;
|
||||
|
||||
@@ -1058,9 +1059,6 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of all completed chain links that we have
|
||||
final Collection< Chain > chains = new HashSet< Chain >();
|
||||
|
||||
/**
|
||||
* A partition represents a monotone section of a polygon at the current
|
||||
* scan line. At any given time, a vertical scan line which passes through
|
||||
@@ -1091,6 +1089,7 @@ public class Mesh< T extends Region > {
|
||||
// may be merged into a single partition, and each partition
|
||||
// may have a visibility region for the same vertex.
|
||||
Map< Vertex, Collection< VisibilityRegion > > regionMap = new HashMap< Vertex, Collection< VisibilityRegion > >();
|
||||
Collection< Chain > chains = new HashSet< Chain >();
|
||||
|
||||
Partition( final HalfEdge lower, final HalfEdge upper ) {
|
||||
this.lower = lower;
|
||||
@@ -1434,11 +1433,17 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Use a map for faster searching
|
||||
// TODO Can keep two maps, a lower and upper map mapping edges to
|
||||
// a particular partition, which will let us keep constant time
|
||||
// for partition related operations.
|
||||
final Collection< Partition > partitions = new HashSet< Partition >();
|
||||
// We use two maps to keep track of which partition is associated with which upper/lower edge
|
||||
// so that we can find and confirm that a partition exists in constant time. Naturally,
|
||||
// they must be kept up to date at all times, but that's not too difficult.
|
||||
// Since each partition has exactly one upper and lower edge, both maps should have the
|
||||
// same size, along with the same partitions, which means we can use either to loop through
|
||||
// all existing partitions at any given moment
|
||||
final Map< HalfEdge, Partition > upperEdgeMap = new HashMap< HalfEdge, Partition >();
|
||||
final Map< HalfEdge, Partition > lowerEdgeMap = new HashMap< HalfEdge, Partition >();
|
||||
|
||||
// Keep track of all completed chain links that we have
|
||||
final Collection< Chain > chains = new HashSet< Chain >();
|
||||
|
||||
// Keep track of edges from bottom top
|
||||
final SortedEdgeCollection< HalfEdge > edgeCollection = new SortedEdgeCollection< HalfEdge >( Mesh::greaterThanOrEqualTo );
|
||||
@@ -1484,8 +1489,9 @@ public class Mesh< T extends Region > {
|
||||
|
||||
if ( !leftGoing.isEmpty() ) {
|
||||
// Terminate any partitions
|
||||
for ( final Iterator< Partition > it = partitions.iterator(); it.hasNext(); ) {
|
||||
final Partition partition = it.next();
|
||||
for ( final Iterator< Entry< HalfEdge, Partition > > it = upperEdgeMap.entrySet().iterator(); it.hasNext(); ) {
|
||||
final Entry< HalfEdge, Partition > entry = it.next();
|
||||
final Partition partition = entry.getValue();
|
||||
|
||||
// Does both the upper and lower region of this partition end at this vertex?
|
||||
if ( partition.upper.getDest() == event && partition.lower.getDest() == event ) {
|
||||
@@ -1493,6 +1499,11 @@ public class Mesh< T extends Region > {
|
||||
// a difference which one we do since they all converge at the same location.
|
||||
partition.incrementUpper( partition.lower.getSym() );
|
||||
|
||||
// Gather all chains from the partition
|
||||
chains.addAll( partition.chains );
|
||||
|
||||
// Remove this partition from the lower edge map as well
|
||||
lowerEdgeMap.remove( partition.lower );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@@ -1501,10 +1512,9 @@ public class Mesh< T extends Region > {
|
||||
// and is inside a partition. That means
|
||||
// the partition containing this vertex must
|
||||
// be split into two smaller partitions.
|
||||
|
||||
final Optional< Partition > opt = partitions.parallelStream().filter( p -> p.lower == lower ).findAny();
|
||||
if ( opt.isPresent() ) {
|
||||
final Partition toSplit = opt.get();
|
||||
|
||||
final Partition toSplit = lowerEdgeMap.remove( lower );
|
||||
if ( toSplit != null ) {
|
||||
|
||||
if ( rightGoing.size() < 2 ) {
|
||||
throw new IllegalStateException( "Vertex has less than 2 right edges!" );
|
||||
@@ -1514,7 +1524,15 @@ public class Mesh< T extends Region > {
|
||||
final HalfEdge newLower = rightGoing.get( rightGoing.size() - 1 );
|
||||
|
||||
final Partition copy = new Partition( lower, newUpper );
|
||||
|
||||
// Copy all chains from the previous region in case of intersection
|
||||
copy.chains.addAll( toSplit.chains );
|
||||
|
||||
toSplit.lower = newLower;
|
||||
|
||||
lowerEdgeMap.put( newLower, toSplit );
|
||||
lowerEdgeMap.put( lower, copy );
|
||||
upperEdgeMap.put( newUpper, copy );
|
||||
|
||||
/*
|
||||
* A simple method for splitting and updating the visibility regions
|
||||
@@ -1623,29 +1641,16 @@ public class Mesh< T extends Region > {
|
||||
// means there exists 2 partitions which
|
||||
// are converging and need to be merged
|
||||
// into a single partition.
|
||||
Partition top = null;
|
||||
Partition bottom = null;
|
||||
|
||||
|
||||
if ( leftGoing.size() < 2 ) {
|
||||
throw new IllegalStateException( "Vertex has less than 2 left edges!" );
|
||||
}
|
||||
|
||||
final HalfEdge topLeft = leftGoing.get( 0 ).getSym();
|
||||
final HalfEdge bottomLeft = leftGoing.get( leftGoing.size() - 1 ).getSym();
|
||||
for ( final Iterator< Partition > it = partitions.iterator(); it.hasNext() && ( top == null || bottom == null ); ) {
|
||||
final Partition partition = it.next();
|
||||
|
||||
// TODO Benefit from upper and lower partition mappings
|
||||
if ( partition.upper == bottomLeft ) {
|
||||
bottom = partition;
|
||||
|
||||
// Delete the bottom partition
|
||||
// No particular reason for why the bottom over the top
|
||||
it.remove();
|
||||
} else if ( partition.lower == topLeft ) {
|
||||
top = partition;
|
||||
}
|
||||
}
|
||||
|
||||
final Partition top = lowerEdgeMap.remove( topLeft );
|
||||
final Partition bottom = upperEdgeMap.remove( bottomLeft );
|
||||
|
||||
if ( top == null ) {
|
||||
throw new IllegalStateException( "Upper partition not found!" );
|
||||
@@ -1653,9 +1658,15 @@ public class Mesh< T extends Region > {
|
||||
throw new IllegalStateException( "Lower partition not found!" );
|
||||
}
|
||||
|
||||
chains.addAll( bottom.chains );
|
||||
lowerEdgeMap.remove( bottom.upper );
|
||||
|
||||
// Merge the top and bottom partition
|
||||
top.lower = bottom.lower;
|
||||
|
||||
// Update the lower edge map
|
||||
lowerEdgeMap.put( top.lower, top );
|
||||
|
||||
// Update all visibility regions for the top and bottom partitions, separately
|
||||
top.incrementLower( topLeft.getSym() );
|
||||
bottom.incrementUpper( bottomLeft.getSym() );
|
||||
@@ -1680,22 +1691,22 @@ public class Mesh< T extends Region > {
|
||||
final HalfEdge left = leftGoing.get( leftGoing.size() - 1 ).getSym();
|
||||
final HalfEdge right = rightGoing.get( 0 );
|
||||
|
||||
// TODO Benefit from a constant time lower partition mapping
|
||||
final Optional< Partition > opt = partitions.parallelStream().filter( p -> p.lower == lower ).findAny();
|
||||
|
||||
if ( opt.isPresent() ) {
|
||||
final Partition partition = opt.get();
|
||||
final Partition partition = lowerEdgeMap.get( lower );
|
||||
if ( partition != null ) {
|
||||
|
||||
if ( partition.upper != left ) {
|
||||
throw new IllegalStateException( "Partition upper edge is inconsistent!" );
|
||||
}
|
||||
|
||||
upperEdgeMap.remove( partition.upper );
|
||||
upperEdgeMap.put( right, partition );
|
||||
|
||||
// Update the partition's upper edge to the new right going edge
|
||||
partition.upper = right;
|
||||
|
||||
// Update all visibility regions
|
||||
partition.incrementUpper( right );
|
||||
|
||||
|
||||
// Add a new region for this vertex
|
||||
partition.add( event, new VisibilityRegion( left.getSym(), right ) );
|
||||
} else {
|
||||
@@ -1708,16 +1719,16 @@ public class Mesh< T extends Region > {
|
||||
final HalfEdge left = leftGoing.get( 0 ).getSym();
|
||||
final HalfEdge right = rightGoing.get( rightGoing.size() - 1 );
|
||||
|
||||
// TODO Benefit from an upper partition mapping
|
||||
final Optional< Partition > opt = partitions.parallelStream().filter( p -> p.upper == upper ).findAny();
|
||||
final Partition partition = upperEdgeMap.get( upper );
|
||||
if ( partition != null ) {
|
||||
|
||||
if ( opt.isPresent() ) {
|
||||
final Partition partition = opt.get();
|
||||
|
||||
if ( partition.lower != left ) {
|
||||
throw new IllegalStateException( "Partition lower edge is inconsistent!" );
|
||||
}
|
||||
|
||||
lowerEdgeMap.remove( partition.lower );
|
||||
lowerEdgeMap.put( right, partition );
|
||||
|
||||
partition.lower = right;
|
||||
|
||||
// Update all visibility regions
|
||||
@@ -1731,14 +1742,17 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
partitions.addAll( newPartitions );
|
||||
for ( final Partition newPartition : newPartitions ) {
|
||||
upperEdgeMap.put( newPartition.upper, newPartition );
|
||||
lowerEdgeMap.put( newPartition.lower, newPartition );
|
||||
}
|
||||
|
||||
for ( final HalfEdge edge : rightGoing ) {
|
||||
edgeCollection.insert( edge );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !partitions.isEmpty() ) {
|
||||
if ( !upperEdgeMap.isEmpty() || !lowerEdgeMap.isEmpty() ) {
|
||||
throw new IllegalStateException( "Dangling partitions!" );
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user