Fix inconsistencies
Use linked hash set for reproducibility when required Split chain generation into three smaller methods Fixed error when certain chains skip certain vertices, specifically after splitting a partition Hide internal classes Add conversion to external classes when returning data Update the polygon viewer Update chain intersections in parallel Sort edges cleaner Resolve some TODOs
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Chain {
|
||||
protected List< Point > points = new ArrayList< Point >();
|
||||
protected List< Chain > intersections = new ArrayList< Chain >();
|
||||
|
||||
public Point getStart() {
|
||||
return points.get( 0 );
|
||||
}
|
||||
|
||||
public Point getEnd() {
|
||||
return points.get( points.size() - 1 );
|
||||
}
|
||||
|
||||
public List< Point > getPoints() {
|
||||
return Collections.unmodifiableList( points );
|
||||
}
|
||||
|
||||
public List< Chain > getIntersections() {
|
||||
return Collections.unmodifiableList( intersections );
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.util.Collections;
|
||||
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;
|
||||
@@ -18,6 +19,7 @@ import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -67,6 +69,7 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.region.RegionRule;
|
||||
* The time complexity for the entire algorithm should be roughly O(n^2logn).
|
||||
*
|
||||
* TODO Remove guaranteed checks or add some compile time thing to remove
|
||||
* TODO Update this description
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
@@ -86,8 +89,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
protected MeshState state = MeshState.TRIANGULATION_READY;
|
||||
|
||||
// TODO Remove
|
||||
public Collection< Chain > chains;
|
||||
protected Collection< MeshEventHandler > handlers = new LinkedHashSet< MeshEventHandler >();
|
||||
|
||||
public Mesh( final Supplier< T > defaultRegionSupplier ) {
|
||||
this.defaultRegionSupplier = defaultRegionSupplier;
|
||||
@@ -120,11 +122,36 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection< Vertex > getVertices() {
|
||||
return vertices;
|
||||
public boolean addHandler( final MeshEventHandler handler ) {
|
||||
return handlers.add( handler );
|
||||
}
|
||||
|
||||
public int getRuleSize() {
|
||||
public boolean removeHandler( final MeshEventHandler handler ) {
|
||||
return handlers.remove( handler );
|
||||
}
|
||||
|
||||
public Collection< MeshEventHandler > getHandlers() {
|
||||
return Collections.unmodifiableCollection( handlers );
|
||||
}
|
||||
|
||||
public Set< Point > getVertices() {
|
||||
return vertices.parallelStream().map( v -> { return new Point( v.getPosition() ); } ).collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
public Set< Segment > getEdges() {
|
||||
final EdgeSet set = new EdgeSet();
|
||||
set.addAll( rules.keySet() );
|
||||
|
||||
final Map< Vertex, Point > pointMap = vertices.parallelStream().collect( Collectors.toMap( Function.identity(), v -> new Point( v.getPosition() ) ) );
|
||||
|
||||
return set.parallelStream().map( e -> new Segment( pointMap.get( e.getOrigin() ), pointMap.get( e.getDest() ) ) ).collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
public int getVertexCount() {
|
||||
return vertices.size();
|
||||
}
|
||||
|
||||
public int getEdgeCount() {
|
||||
return rules.size();
|
||||
}
|
||||
|
||||
@@ -764,6 +791,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
// Sort the edges around this vertex.
|
||||
private static List< HalfEdge > sort( Vertex vertex ) {
|
||||
final Vector2d UP = new Vector2d( 0, 1 );
|
||||
final List< HalfEdge > edges = new ArrayList< HalfEdge >();
|
||||
|
||||
for ( final HalfEdge edge : vertex ) {
|
||||
@@ -773,23 +801,22 @@ public class Mesh< T extends Region > {
|
||||
edges.add( edge );
|
||||
}
|
||||
|
||||
// TODO Can really just figure this out based on the absolute angle?
|
||||
// relative to the (0, 1) vector...
|
||||
// Sort the edges in a counter clockwise direction,
|
||||
// where 11:59 is the least, and 12:00 is the greatest
|
||||
Collections.sort( edges, ( e1, e2 ) -> {
|
||||
// This assumes that no edge has length of 0
|
||||
final boolean e1p = isPositive( e1 );
|
||||
final boolean e2p = isPositive( e2 );
|
||||
|
||||
if ( e1p ^ e2p ) {
|
||||
return e1p ? 1 : -1;
|
||||
} else if ( e1.getDest() == e2.getDest() ) {
|
||||
return 0;
|
||||
} else {
|
||||
final double cross = e2.toVector2d().cross( e1.toVector2d() );
|
||||
return Double.compare( cross, 0 );
|
||||
final Vector2d vec1 = e1.toVector2d().normalize();
|
||||
final Vector2d vec2 = e2.toVector2d().normalize();
|
||||
double cross1 = vec1.angle( UP );
|
||||
if ( cross1 < 0 ) {
|
||||
cross1 += 10;
|
||||
}
|
||||
double cross2 = vec2.angle( UP );
|
||||
if ( cross2 < 0 ) {
|
||||
cross2 += 10;
|
||||
}
|
||||
|
||||
return Double.compare( cross2, cross1 );
|
||||
} );
|
||||
|
||||
// Only need to re-organize the edges if there are 3 or more
|
||||
@@ -891,9 +918,7 @@ public class Mesh< T extends Region > {
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
// TODO Provide triangles
|
||||
// and preferrably a simple polygon
|
||||
public Collection< EdgePolygon > mesh() {
|
||||
public Collection< Polygon > mesh() {
|
||||
if ( state != MeshState.TRIANGULATION_READY ) {
|
||||
throw new IllegalStateException( "Mesh has not been split into regions!" );
|
||||
}
|
||||
@@ -904,7 +929,7 @@ public class Mesh< T extends Region > {
|
||||
final Map< Vertex, Vertex > newVertices = new HashMap< Vertex, Vertex >();
|
||||
|
||||
// Keep track of all newly added interior edges
|
||||
final Collection< HalfEdge > edges = new HashSet< HalfEdge >();
|
||||
final Collection< HalfEdge > edges = new LinkedHashSet< HalfEdge >();
|
||||
|
||||
/*
|
||||
* We now want to create a completely separate PSLG so that we don't modify
|
||||
@@ -954,53 +979,67 @@ public class Mesh< T extends Region > {
|
||||
// Sort each vertex so that the edge they are pointing to is the least edge
|
||||
vertices.parallelStream().forEach( v -> sort( v ) );
|
||||
|
||||
final Collection< Chain > chains = reduceCollinear2( vertices, edges );
|
||||
this.chains = new ArrayDeque< Chain >();
|
||||
for ( Chain chain : chains ) {
|
||||
List< HalfEdge > newEdges = new ArrayList< HalfEdge >();
|
||||
for ( HalfEdge e : chain.links ) {
|
||||
HalfEdge edge = new HalfEdge();
|
||||
edge.getOrigin().setPosition( e.getOrigin().getPosition() );
|
||||
edge.getDest().setPosition( e.getDest().getPosition() );
|
||||
newEdges.add( edge );
|
||||
final Collection< Link > allChains = findChains( vertices, edges );
|
||||
final Collection< Link > chains = maximizeChains( allChains );
|
||||
if ( !handlers.isEmpty() ) {
|
||||
final Collection< Chain > newChains = new HashSet< Chain >();
|
||||
final Collection< Link > seen = new HashSet< Link >();
|
||||
|
||||
for ( final Link link : allChains ) {
|
||||
if ( seen.add( link ) ) {
|
||||
Link head = link;
|
||||
while ( head.previous != null ) {
|
||||
head = head.previous;
|
||||
}
|
||||
|
||||
final Chain chain = new Chain();
|
||||
Link previous;
|
||||
do {
|
||||
chain.points.add( new Point( head.getOrigin().getPosition() ) );
|
||||
seen.add( head );
|
||||
previous = head;
|
||||
} while ( ( head = head.next ) != null );
|
||||
chain.points.add( new Point( previous.getMidpoint().getPosition() ) );
|
||||
chain.points.add( new Point( previous.getDest().getPosition() ) );
|
||||
newChains.add( chain );
|
||||
}
|
||||
}
|
||||
this.chains.add( new Chain( newEdges ) );
|
||||
|
||||
handlers.forEach( h -> h.onChainGenerationEvent( newChains ) );
|
||||
|
||||
final Collection< Link > selectedSeen = new HashSet< Link >();
|
||||
final Collection< Chain > selectedChains = new HashSet< Chain >();
|
||||
for ( final Link link : chains ) {
|
||||
if ( selectedSeen.add( link ) ) {
|
||||
Link head = link;
|
||||
while ( chains.contains( head.previous ) ) {
|
||||
head = head.previous;
|
||||
}
|
||||
|
||||
final Chain chain = new Chain();
|
||||
Link previous;
|
||||
do {
|
||||
chain.points.add( new Point( head.getOrigin().getPosition() ) );
|
||||
selectedSeen.add( head );
|
||||
previous = head;
|
||||
} while ( ( head = head.next ) != null && chains.contains( head ) );
|
||||
chain.points.add( new Point( previous.getMidpoint().getPosition() ) );
|
||||
chain.points.add( new Point( previous.getDest().getPosition() ) );
|
||||
selectedChains.add( chain );
|
||||
}
|
||||
}
|
||||
|
||||
handlers.forEach( h -> h.onPreChainMergeEvent( selectedChains ) );
|
||||
}
|
||||
|
||||
mergeChains( chains, edges );
|
||||
|
||||
return partitionMonotone( vertices, edges ).parallelStream()
|
||||
.map( Mesh::triangulate )
|
||||
.flatMap( p -> p.parallelStream() )
|
||||
.collect( Collectors.toSet() );
|
||||
}
|
||||
|
||||
// TODO Make not static
|
||||
public static class Chain {
|
||||
List< HalfEdge > links;
|
||||
Set< Chain > intersections = new HashSet< Chain >();
|
||||
Chain previous;
|
||||
Chain next;
|
||||
|
||||
Chain( final Collection< HalfEdge > links ) {
|
||||
this.links = new ArrayList< HalfEdge >( links );
|
||||
}
|
||||
|
||||
public Vertex getOrigin() {
|
||||
return links.get( 0 ).getOrigin();
|
||||
}
|
||||
|
||||
public Vertex getMidpoint() {
|
||||
return links.get( 1 ).getOrigin();
|
||||
}
|
||||
|
||||
public Vertex getDest() {
|
||||
return links.get( 2 ).getOrigin();
|
||||
}
|
||||
|
||||
public List< HalfEdge > getLinks() {
|
||||
return links;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This algorithm revolves around first generating chains, then sorting which chains and chain links to keep.
|
||||
*
|
||||
@@ -1015,7 +1054,7 @@ public class Mesh< T extends Region > {
|
||||
*
|
||||
* The complexity comes from analyzing which combinations of chain links provides the greatest reduction of vertices.
|
||||
*/
|
||||
private static Collection< Chain > reduceCollinear2( final TreeSet< Vertex > vertices, final Collection< HalfEdge > interior ) {
|
||||
private static Collection< Link > findChains( final TreeSet< Vertex > vertices, final Collection< HalfEdge > interior ) {
|
||||
final Vector2d CROSS = new Vector2d( 1, 0 );
|
||||
// Set maximum and minimum cross values that won't occur naturally
|
||||
final double MAX_CROSS = 1.1;
|
||||
@@ -1092,7 +1131,7 @@ public class Mesh< T extends Region > {
|
||||
|
||||
// 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< Link > chains = new LinkedHashSet< Link >();
|
||||
|
||||
Partition( final HalfEdge lower, final HalfEdge upper ) {
|
||||
this.lower = lower;
|
||||
@@ -1133,7 +1172,7 @@ public class Mesh< T extends Region > {
|
||||
// and we don't really care about what comes after
|
||||
links.add( edge );
|
||||
|
||||
final Chain chain = new Chain( links );
|
||||
final Link chain = new Link( links );
|
||||
addChain( chain );
|
||||
}
|
||||
|
||||
@@ -1174,7 +1213,7 @@ public class Mesh< T extends Region > {
|
||||
// and we don't really care about what comes after
|
||||
links.add( edge );
|
||||
|
||||
final Chain chain = new Chain( links );
|
||||
final Link chain = new Link( links );
|
||||
addChain( chain );
|
||||
}
|
||||
|
||||
@@ -1263,7 +1302,7 @@ public class Mesh< T extends Region > {
|
||||
// and we don't really care about what comes after
|
||||
links.add( edge );
|
||||
|
||||
final Chain chain = new Chain( links );
|
||||
final Link chain = new Link( links );
|
||||
addChain( chain );
|
||||
}
|
||||
} else if ( lowerCross > region.lower ) {
|
||||
@@ -1302,7 +1341,7 @@ public class Mesh< T extends Region > {
|
||||
// and we don't really care about what comes after
|
||||
links.add( edge );
|
||||
|
||||
final Chain chain = new Chain( links );
|
||||
final Link chain = new Link( links );
|
||||
addChain( chain );
|
||||
}
|
||||
|
||||
@@ -1360,30 +1399,34 @@ public class Mesh< T extends Region > {
|
||||
* Check all previous chains for intersections and stuff. This method
|
||||
* is unfortunately polynomial time(O(n^2)) and there's not much we can do about it.
|
||||
*
|
||||
* Maybe parallelize the intersection checking though? We'd need to ensure that the
|
||||
* intersection collection for each chain is synchronized, but otherwise entirely possible.
|
||||
*
|
||||
* TODO Good idea, now someone needs to implement that.
|
||||
*
|
||||
* Also, just because the origin or destination may be the midpoint of another chain
|
||||
* does not necessarily mean they coincide with each other... such as if the origin
|
||||
* or endpoint are connected to the midpoint for one chain, but not the other
|
||||
*/
|
||||
void addChain( final Chain chain ) {
|
||||
void addChain( final Link chain ) {
|
||||
final Vertex chainOrigin = chain.getOrigin();
|
||||
final Vertex chainMidpoint = chain.getMidpoint();
|
||||
final Vertex chainDestination = chain.getDest();
|
||||
|
||||
for ( final Chain other : chains ) {
|
||||
for ( final Link other : chains ) {
|
||||
if ( other.getOrigin() == chainOrigin && other.getDest() == chainDestination ) {
|
||||
// Already added this chain, so break out early
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize the intersections for this chain so that we can check each
|
||||
// other chain for intersection in parallel. The order of the intersections
|
||||
// doesn't affect the reproducibility either, so it's fine.
|
||||
final Collection< Link > intersections = Collections.synchronizedCollection( chain.intersections );
|
||||
|
||||
final HalfEdge firstEdge = chain.links.get( 0 );
|
||||
final HalfEdge secondEdge = chain.links.get( 1 );
|
||||
final Vector2d secondEdgeVec = secondEdge.toVector2d();
|
||||
|
||||
final Vector2d p = chainOrigin.getPosition();
|
||||
final Vector2d r = chainDestination.getPosition().subtracted( p );
|
||||
for ( final Chain other : chains ) {
|
||||
chains.parallelStream().forEach( other -> {
|
||||
if ( other.getMidpoint() == chainOrigin && other.getDest() == chainMidpoint ) {
|
||||
// The new chain is a continuation of this chain
|
||||
other.next = chain;
|
||||
@@ -1391,8 +1434,56 @@ public class Mesh< T extends Region > {
|
||||
} else if ( compare( other.getDest(), chainOrigin ) > 0 ) {
|
||||
// Do the chains even overlap?
|
||||
if ( chainOrigin == other.getMidpoint() ) {
|
||||
chain.intersections.add( other );
|
||||
other.intersections.add( chain );
|
||||
// In the case that the new chain's origin is another chain's midpoint,
|
||||
// we need to check which direction the other chain is facing.
|
||||
// This is because the new chain only counts as intersecting with the other
|
||||
// chain if and only if the creation of this chain would prevent the other
|
||||
// chain from being linked correctly.
|
||||
|
||||
final Vector2d otherDirection = other.getDest().getPosition().subtracted( other.getOrigin().getPosition() );
|
||||
final double cross = otherDirection.cross( r );
|
||||
|
||||
boolean intersects = true;
|
||||
final HalfEdge firstLink = other.links.get( 0 );
|
||||
final HalfEdge secondLink = other.links.get( 1 );
|
||||
if ( firstLink.getDest() == other.getMidpoint() ) {
|
||||
// Is the first link a direct connection?
|
||||
intersects = interior.contains( firstLink ) ^ cross < 0;
|
||||
} else if ( secondLink.getDest() == other.getDest() ) {
|
||||
// Is the second link a direct connection?
|
||||
intersects = interior.contains( secondLink ) ^ cross < 0;
|
||||
} else {
|
||||
// The chain is comprised of only vertices...
|
||||
intersects = otherDirection.cross( secondLink.toVector2d() ) < 0 ^ cross < 0;
|
||||
}
|
||||
|
||||
if ( intersects ) {
|
||||
synchronized ( intersections ) {
|
||||
intersections.add( other );
|
||||
}
|
||||
other.intersections.add( chain );
|
||||
}
|
||||
} else if ( other.getDest() == chainMidpoint ) {
|
||||
// Likewise, the new chain's midpoint may be another existing chain's
|
||||
// destination. We need to check for that case too.
|
||||
final Vector2d otherDirection = other.getOrigin().getPosition().subtracted( other.getDest().getPosition() ).normalize();
|
||||
final double cross = r.cross( otherDirection );
|
||||
|
||||
boolean intersects = true;
|
||||
if ( firstEdge.getDest() == chainMidpoint ) {
|
||||
intersects = interior.contains( firstEdge ) ^ cross < 0;
|
||||
} else if ( secondEdge.getDest() == chainDestination ) {
|
||||
intersects = interior.contains( secondEdge ) ^ cross < 0;
|
||||
} else {
|
||||
intersects = r.cross( secondEdgeVec ) < 0 ^ cross < 0;
|
||||
}
|
||||
|
||||
if ( intersects ) {
|
||||
synchronized ( intersections ) {
|
||||
intersections.add( other );
|
||||
}
|
||||
other.intersections.add( chain );
|
||||
}
|
||||
} else if ( other.getOrigin() != chainOrigin && other.getDest() != chainDestination ) {
|
||||
// Make sure the chains don't share the same origin or destination
|
||||
|
||||
@@ -1411,20 +1502,23 @@ public class Mesh< T extends Region > {
|
||||
final double t = pq.cross( s ) / rs;
|
||||
final double u = pqr / rs;
|
||||
|
||||
final double tTol = VERTEX_TOLERANCE / r.length();
|
||||
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 ) ) {
|
||||
if ( t > -tTol && t < ( 1 + tTol ) && u > -uTol && u < ( 1 + uTol ) ) {
|
||||
// The chains intersect! We don't care about where.
|
||||
|
||||
chain.intersections.add( other );
|
||||
synchronized ( intersections ) {
|
||||
intersections.add( other );
|
||||
}
|
||||
other.intersections.add( chain );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
chains.add( chain );
|
||||
}
|
||||
|
||||
@@ -1487,7 +1581,7 @@ public class Mesh< T extends Region > {
|
||||
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 >();
|
||||
final Collection< Link > chains = new LinkedHashSet< Link >();
|
||||
|
||||
// Keep track of edges from bottom top
|
||||
final SortedEdgeCollection< HalfEdge > edgeCollection = new SortedEdgeCollection< HalfEdge >( Mesh::greaterThanOrEqualTo );
|
||||
@@ -1620,30 +1714,21 @@ public class Mesh< T extends Region > {
|
||||
// Create a new visibility region
|
||||
final VisibilityRegion copyRegion = new VisibilityRegion( region.lowerEdge, region.upperEdge );
|
||||
|
||||
// Update the new region's lower links
|
||||
if ( region.lower != MIN_CROSS ) {
|
||||
copyRegion.lower = 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;
|
||||
}
|
||||
|
||||
// Inherit the upper region's links
|
||||
// This may cause duplication of chains, but
|
||||
// we should be able to check them when we add them
|
||||
// TODO Can this be simplified?
|
||||
// As in, can we prove that this chain will connect, while
|
||||
// the upper region's chain will not, or vice versa?
|
||||
|
||||
// Copy the old region's lower and upper links
|
||||
if ( region.lower != MIN_CROSS ) {
|
||||
copyRegion.lower = region.lower;
|
||||
copyRegion.links.put( region.lower, region.links.get( region.lower ) );
|
||||
}
|
||||
|
||||
// Inherit the upper region's links
|
||||
if ( region.upper != MAX_CROSS ) {
|
||||
copyRegion.upper = region.upper;
|
||||
|
||||
copyRegion.links.put( region.upper, region.links.get( region.upper ) );
|
||||
}
|
||||
|
||||
@@ -1822,19 +1907,17 @@ public class Mesh< T extends Region > {
|
||||
throw new IllegalStateException( "Dangling partitions!" );
|
||||
}
|
||||
|
||||
// Given a chain link, select the links which intersect the least, and remove any that it intersects with from the
|
||||
// queue. Essentially, a greedy method, which is more or less guaranteed optimal results.
|
||||
// This is also unfortunately an O(n^2) time algorithm that can't be simplified.
|
||||
return chains;
|
||||
}
|
||||
|
||||
// Keep track of all chains that we want
|
||||
final Collection< Chain > selectedChains = new HashSet< Chain >();
|
||||
// Keep track of all remaining chains that needs to be scanned
|
||||
final Collection< Chain > remainingChains = new HashSet< Chain >( chains );
|
||||
private static Collection< Link > maximizeChains( final Collection< Link > chains ) {
|
||||
final Collection< Link > remainingChains = new LinkedHashSet< Link >( chains );
|
||||
final Collection< Link > selectedChains = new HashSet< Link >();
|
||||
|
||||
// Do a quick scan and remove any chains that have 0 intersections, since we can get
|
||||
// this done in linear time and all at once
|
||||
for ( final Iterator< Chain > it = remainingChains.iterator(); it.hasNext(); ) {
|
||||
final Chain chain = it.next();
|
||||
for ( final Iterator< Link > it = remainingChains.iterator(); it.hasNext(); ) {
|
||||
final Link chain = it.next();
|
||||
|
||||
if ( chain.intersections.size() == 0 ) {
|
||||
selectedChains.add( chain );
|
||||
@@ -1845,7 +1928,7 @@ public class Mesh< T extends Region > {
|
||||
// Chain links
|
||||
while ( !remainingChains.isEmpty() ) {
|
||||
// Find the chain with the least amount of intersections
|
||||
final Chain least = remainingChains.parallelStream().min( ( c1, c2 ) -> {
|
||||
final Link least = remainingChains.parallelStream().min( ( c1, c2 ) -> {
|
||||
return Integer.compare( c1.intersections.size(), c2.intersections.size() );
|
||||
} ).get();
|
||||
|
||||
@@ -1861,14 +1944,17 @@ public class Mesh< T extends Region > {
|
||||
selectedChains.add( least );
|
||||
}
|
||||
|
||||
final Collection< Chain > cs = new HashSet< Chain >( selectedChains );
|
||||
return selectedChains;
|
||||
}
|
||||
|
||||
while ( !selectedChains.isEmpty() ) {
|
||||
private static void mergeChains( final Collection< Link > selected, final Collection< HalfEdge > interior ) {
|
||||
final Collection< Link > chains = new HashSet< Link >( selected );
|
||||
while ( !chains.isEmpty() ) {
|
||||
// Get a chain
|
||||
Chain chain = selectedChains.iterator().next();
|
||||
Link chain = chains.iterator().next();
|
||||
|
||||
// Get the head of the chain
|
||||
while ( selectedChains.contains( chain.previous ) ) {
|
||||
while ( chains.contains( chain.previous ) ) {
|
||||
chain = chain.previous;
|
||||
}
|
||||
|
||||
@@ -1893,9 +1979,9 @@ public class Mesh< T extends Region > {
|
||||
interior.add( right.getSym() );
|
||||
}
|
||||
|
||||
Chain previousChain;
|
||||
Link previousChain;
|
||||
do {
|
||||
selectedChains.remove( chain );
|
||||
chains.remove( chain );
|
||||
|
||||
final HalfEdge head = chain.links.get( 0 );
|
||||
final Vertex origin = chain.getOrigin();
|
||||
@@ -2045,7 +2131,7 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
|
||||
previousChain = chain;
|
||||
} while ( ( chain = chain.next ) != null && selectedChains.contains( chain ) );
|
||||
} while ( ( chain = chain.next ) != null && chains.contains( chain ) );
|
||||
|
||||
// Terminate the chain
|
||||
final Vertex destination = previousChain.getDest();
|
||||
@@ -2087,8 +2173,6 @@ public class Mesh< T extends Region > {
|
||||
left.getSym().setOrigin( destination );
|
||||
}
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
// Use the vertex's edge as the hint, basically because we have no better edge to start from
|
||||
@@ -2111,6 +2195,8 @@ public class Mesh< T extends Region > {
|
||||
|
||||
final Vector2d normalized = vector.normalized();
|
||||
|
||||
int size = vertex.size();
|
||||
|
||||
// This is a fairly simple use case, so compare absolute angles
|
||||
HalfEdge edge = hint;
|
||||
while ( true ) {
|
||||
@@ -2127,6 +2213,10 @@ public class Mesh< T extends Region > {
|
||||
lowest = angle;
|
||||
}
|
||||
edge = edge.getPrev();
|
||||
|
||||
if ( --size < 0 ) {
|
||||
throw new IllegalStateException( "Not good vertex! With only " + vertex.size() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2445,7 +2535,7 @@ public class Mesh< T extends Region > {
|
||||
return newPolygons;
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > triangulate( final EdgePolygon polygon ) {
|
||||
private static Collection< Polygon > 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.
|
||||
@@ -2454,7 +2544,7 @@ public class Mesh< T extends Region > {
|
||||
if ( polygon.getEdges().size() < 3 || polygon.getVertices().size() < 3 ) {
|
||||
throw new IllegalStateException( "Invalid amount of verts/edges!" );
|
||||
} else if ( polygon.getEdges().size() == 3 ) {
|
||||
return Arrays.asList( polygon );
|
||||
return Arrays.asList( new Polygon( polygon.getVertices().stream().map( v -> new Point( v.getPosition() ) ).toList() ) );
|
||||
}
|
||||
|
||||
final Queue< HalfEdge > edges = new PriorityQueue< HalfEdge >( ( a, b ) -> {
|
||||
@@ -2553,22 +2643,21 @@ public class Mesh< T extends Region > {
|
||||
// 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 Collection< Polygon > polygons = new ArrayDeque< Polygon >();
|
||||
final Set< HalfEdge > scanned = new HashSet< HalfEdge >();
|
||||
for ( final HalfEdge edge : polygon.getEdges() ) {
|
||||
if ( scanned.add( edge ) ) {
|
||||
HalfEdge temp = edge;
|
||||
EdgePolygon poly = new EdgePolygon();
|
||||
|
||||
final List< Point > points = new ArrayList< Point >();
|
||||
do {
|
||||
poly.addEdge( temp );
|
||||
points.add( new Point( temp.getOrigin().getPosition() ) );
|
||||
} while ( scanned.add( temp = temp.getNext() ) );
|
||||
|
||||
if ( poly.getVertices().size() != 3 ) {
|
||||
throw new IllegalStateException( "Not a triangle! " + poly.getVertices().size() );
|
||||
if ( points.size() != 3 ) {
|
||||
throw new IllegalStateException( "Not a triangle: " + points.size() );
|
||||
}
|
||||
polygons.add( poly );
|
||||
polygons.add( new Polygon( points ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2702,7 +2791,7 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
public static class EdgePolygon {
|
||||
private static class EdgePolygon {
|
||||
private Set< HalfEdge > edges = new HashSet< HalfEdge >();
|
||||
private Set< Vertex > vertices = new HashSet< Vertex >();
|
||||
|
||||
@@ -2720,6 +2809,36 @@ public class Mesh< T extends Region > {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Link {
|
||||
List< HalfEdge > links;
|
||||
Set< Link > intersections = new LinkedHashSet< Link >();
|
||||
Link previous;
|
||||
Link next;
|
||||
|
||||
Link( final Collection< HalfEdge > links ) {
|
||||
this.links = new ArrayList< HalfEdge >( links );
|
||||
}
|
||||
|
||||
Vertex getOrigin() {
|
||||
return links.get( 0 ).getOrigin();
|
||||
}
|
||||
|
||||
Vertex getMidpoint() {
|
||||
return links.get( 1 ).getOrigin();
|
||||
}
|
||||
|
||||
Vertex getDest() {
|
||||
return links.get( 2 ).getOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static interface MeshEventHandler {
|
||||
default void onChainGenerationEvent( final Collection< Chain > chains ) {};
|
||||
default void onPreChainMergeEvent( final Collection< Chain > chains ) {};
|
||||
default void onPartitionEvent( final Collection< Polygon > polygons ) {};
|
||||
}
|
||||
|
||||
protected enum MeshState {
|
||||
DIRTY,
|
||||
SIMPLIFIED,
|
||||
|
||||
@@ -8,6 +8,10 @@ package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||
public class Point {
|
||||
protected double x, y;
|
||||
|
||||
public Point( final Point other ) {
|
||||
this( other.x, other.y );
|
||||
}
|
||||
|
||||
public Point( double x, double y ) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||
|
||||
public class Segment {
|
||||
private final Point start;
|
||||
private final Point end;
|
||||
|
||||
public Segment( Point start, Point end ) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public Point getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public Point getEnd() {
|
||||
return end;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.MouseInfo;
|
||||
@@ -22,20 +24,22 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.EdgeSet;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Chain;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.HalfEdge;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.Chain;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.EdgePolygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.MeshEventHandler;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Segment;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vertex;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||
@@ -69,11 +73,10 @@ public class MeshingTest2 extends JPanel {
|
||||
|
||||
private double scale = 8;
|
||||
|
||||
private Collection< Vertex > data;
|
||||
private Collection< EdgePolygon > polygons;
|
||||
private PlaneData data;
|
||||
|
||||
// Temporary... Remove at some point
|
||||
private static Collection< Chain > chains;
|
||||
private int stringBoardX = -100;
|
||||
private int stringBoardY = 20;
|
||||
|
||||
public static void main( String[] args ) {
|
||||
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||
@@ -91,28 +94,31 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Collections.sort( planes, ( a, b ) -> Integer.compare( b.polygons.size(), a.polygons.size() ) );
|
||||
// draw = planes.get( 4 );
|
||||
|
||||
// Select a random plane to draw
|
||||
// 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 );
|
||||
}
|
||||
// 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 );
|
||||
// final Collection< EdgePolygon > polys = test();
|
||||
final PlaneData data = process( draw );
|
||||
final Collection< Polygon > polys = triangulate( draw );
|
||||
// final Collection< Polygon > polys = test();
|
||||
|
||||
System.out.println( "Polygon count: " + polys.size() );
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new MeshingTest2( data, polys );
|
||||
new MeshingTest2( data );
|
||||
}
|
||||
} );
|
||||
break;
|
||||
@@ -190,7 +196,7 @@ public class MeshingTest2 extends JPanel {
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
mesh.generateRegions();
|
||||
final Collection< EdgePolygon > polys = mesh.copyOf().mesh();
|
||||
final Collection< Polygon > polys = mesh.copyOf().mesh();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println( plane.polygons.size() + " to " + polys.size() + ":\t " + ( end - start ) + "ms" );
|
||||
} catch ( IllegalStateException e ) {
|
||||
@@ -202,8 +208,9 @@ public class MeshingTest2 extends JPanel {
|
||||
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
|
||||
}
|
||||
|
||||
private static Collection< Vertex > process( final Plane plane ) {
|
||||
private static PlaneData process( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
final PlaneData data = new PlaneData();
|
||||
System.out.println( "Norm:\t" + plane.normal );
|
||||
System.out.println( "Ref:\t" + plane.point );
|
||||
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||
@@ -214,30 +221,46 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
|
||||
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
|
||||
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
mesh.generateRegions();
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
data.vertices = mesh.getVertices();
|
||||
data.edges = mesh.getEdges();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
mesh.addHandler( new MeshEventHandler() {
|
||||
@Override
|
||||
public void onChainGenerationEvent( Collection< Chain > chains ) {
|
||||
data.chains = chains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreChainMergeEvent( Collection< Chain > chains ) {
|
||||
data.selectedChains = chains;
|
||||
}
|
||||
} );
|
||||
|
||||
data.polygons = mesh.mesh();
|
||||
|
||||
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||
return mesh.getVertices();
|
||||
return data;
|
||||
} else {
|
||||
System.out.println( "No data!" );
|
||||
return Collections.emptySet();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > triangulate( final Plane plane ) {
|
||||
private static Collection< Polygon > triangulate( final Plane plane ) {
|
||||
if ( plane != null ) {
|
||||
System.out.println( "Norm:\t" + plane.normal );
|
||||
System.out.println( "Ref:\t" + plane.point );
|
||||
@@ -249,32 +272,28 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
|
||||
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
System.out.println( "Initial edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
mesh.simplify();
|
||||
|
||||
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
System.out.println( "After simplify edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
mesh.generateRegions();
|
||||
|
||||
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
||||
|
||||
// Make sure this works
|
||||
mesh = mesh.copyOf();
|
||||
|
||||
Collection< EdgePolygon > polys;
|
||||
Collection< Polygon > polys = new HashSet< Polygon >();
|
||||
|
||||
try {
|
||||
polys = mesh.mesh();
|
||||
chains = mesh.chains;
|
||||
} catch ( IllegalStateException e ) {
|
||||
e.printStackTrace();
|
||||
|
||||
polys = mesh.mesh( );
|
||||
chains = mesh.chains;
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
@@ -287,7 +306,7 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection< EdgePolygon > test() {
|
||||
private static Collection< Polygon > test() {
|
||||
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
|
||||
// mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
@@ -409,20 +428,37 @@ public class MeshingTest2 extends JPanel {
|
||||
// new Point( 2.8151429293905887, -2.747294403063162 )
|
||||
// ) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( 0, 0 ),
|
||||
new Point( 1, 0 ),
|
||||
new Point( 1, 1 ),
|
||||
new Point( 0, 1 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( 0, 2 ),
|
||||
new Point( 1, 2 ),
|
||||
new Point( 1, 3 ),
|
||||
new Point( 0, 3 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||
new Point( -1, -1 ),
|
||||
new Point( 2, -1 ),
|
||||
new Point( 2, 4 ),
|
||||
new Point( -1, 4 )
|
||||
) ), RegionRuleWinding.CLOCKWISE );
|
||||
|
||||
mesh.simplify();
|
||||
|
||||
mesh.generateRegions();
|
||||
|
||||
try {
|
||||
final Collection< EdgePolygon > polys = mesh.mesh();
|
||||
chains = mesh.chains;
|
||||
final Collection< Polygon > polys = mesh.mesh();
|
||||
return polys;
|
||||
} catch ( Exception e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final Collection< EdgePolygon > polys = mesh.mesh();
|
||||
chains = mesh.chains;
|
||||
final Collection< Polygon > polys = mesh.mesh();
|
||||
|
||||
return polys;
|
||||
}
|
||||
@@ -526,9 +562,8 @@ public class MeshingTest2 extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
public MeshingTest2( Collection< Vertex > polys, Collection< EdgePolygon > polygons ) {
|
||||
data = polys;
|
||||
this.polygons = polygons;
|
||||
public MeshingTest2( PlaneData data ) {
|
||||
this.data = data;
|
||||
|
||||
f = new JFrame( "Drawing Board" );
|
||||
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
@@ -596,6 +631,14 @@ public class MeshingTest2 extends JPanel {
|
||||
};
|
||||
} );
|
||||
|
||||
// final JPanel taskbar = new JPanel();
|
||||
// taskbar.setLayout( new FlowLayout() );
|
||||
//
|
||||
// taskbar.add( new JButton( "Previous" ) );
|
||||
// taskbar.add( new JButton( "Next" ) );
|
||||
//
|
||||
// f.add( taskbar, BorderLayout.NORTH );
|
||||
|
||||
f.add( this );
|
||||
|
||||
f.setSize( windowWidth, windowHeight );
|
||||
@@ -613,6 +656,11 @@ public class MeshingTest2 extends JPanel {
|
||||
return new Vector2d( ax, ay );
|
||||
}
|
||||
|
||||
private void drawOnBoard( Graphics g, String str, int x, int y ) {
|
||||
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, ( int ) scale * 5 ) );
|
||||
g.drawString( str, ( int ) ( ( centerX + x + stringBoardX ) * scale ) + offsetX, ( int ) ( ( centerY - y + stringBoardY ) * scale ) + offsetY );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent( Graphics g ) {
|
||||
super.paintComponent( g );
|
||||
@@ -624,131 +672,117 @@ public class MeshingTest2 extends JPanel {
|
||||
|
||||
int highestX = 40;
|
||||
if ( data != null ) {
|
||||
Collection< Vertex > polygons = data;
|
||||
if ( !data.edges.isEmpty() ) {
|
||||
Collection< Point > points = data.vertices;
|
||||
|
||||
highestX = ( int ) ( polygons.parallelStream()
|
||||
.max( ( a, b ) -> { return Double.compare( a.getPosition().getX(), b.getPosition().getX() ); } ).get().getPosition().getX() + 40.5 );
|
||||
highestX = ( int ) ( points.parallelStream()
|
||||
.max( ( a, b ) -> { return Double.compare( a.getX(), b.getX() ); } ).get().getX() + 40.5 );
|
||||
|
||||
EdgeSet edges = new EdgeSet();
|
||||
for ( Vertex vert : polygons ) {
|
||||
for ( HalfEdge edge : vert ) {
|
||||
if ( edges.add( edge ) ) {
|
||||
Point p1 = edge.getOrigin().getPosition();
|
||||
Point p2 = edge.getDest().getPosition();
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
g.setColor( Color.BLACK );
|
||||
for ( final Segment segment : data.edges ) {
|
||||
Point p1 = segment.getStart();
|
||||
Point p2 = segment.getEnd();
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
|
||||
final double diff = scale * 0.15;
|
||||
final Point point = vert.getPosition();
|
||||
g.setColor( Color.BLACK );
|
||||
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( polygons != null ) {
|
||||
g.setColor( Color.BLACK );
|
||||
int count = 2;
|
||||
Random random = new Random( hashCode() );
|
||||
|
||||
final int spacing = 25;
|
||||
for ( final EdgePolygon p : polygons ) {
|
||||
final Collection< HalfEdge > edges = p.getEdges();
|
||||
|
||||
final Set< Vertex > verts = new HashSet< Vertex >();
|
||||
g.setColor( Color.BLACK );
|
||||
for ( HalfEdge edge : edges ) {
|
||||
Point p1 = edge.getOrigin().getPosition();
|
||||
Point p2 = edge.getDest().getPosition();
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + count * spacing ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() - 150 ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + count * spacing ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() - 150 ) * scale ) + offsetY
|
||||
);
|
||||
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() - 150 ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() - 150 ) * scale ) + offsetY
|
||||
);
|
||||
|
||||
for ( final Point point : points ) {
|
||||
final double diff = scale * 0.15;
|
||||
if ( !verts.add( edge.getOrigin() ) ) {
|
||||
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++;
|
||||
|
||||
int size = edges.size();
|
||||
int[] xPoints = new int[ size ];
|
||||
int[] yPoints = new int[ size ];
|
||||
|
||||
final Set< Vertex > verts2 = new HashSet< Vertex >();
|
||||
|
||||
HalfEdge edge = edges.iterator().next();
|
||||
for ( int i = 0; i < edges.size(); ++i ) {
|
||||
final Point point = edge.getOrigin().getPosition();
|
||||
xPoints[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
||||
yPoints[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
|
||||
|
||||
final double diff = scale * 0.15;
|
||||
if ( !verts2.add( edge.getOrigin() ) ) {
|
||||
g.setColor( Color.ORANGE );
|
||||
} else {
|
||||
g.setColor( Color.BLACK );
|
||||
}
|
||||
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||
|
||||
edge = edge.getNext();
|
||||
}
|
||||
|
||||
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
|
||||
g.fillPolygon( xPoints, yPoints, size );
|
||||
drawOnBoard( g, "Edge count: " + data.edges.size(), 0, 0 );
|
||||
drawOnBoard( g, "Vertex count: " + points.size(), 0, -5 );
|
||||
}
|
||||
|
||||
if ( data.polygons != null ) {
|
||||
int count = 2;
|
||||
Random random = new Random( hashCode() );
|
||||
|
||||
final int spacing = 25;
|
||||
|
||||
for ( final Polygon p : data.polygons ) {
|
||||
final List< Point > points = p.getPoints();
|
||||
final int[] x = new int[ points.size() ];
|
||||
final int[] y = new int[ points.size() ];
|
||||
final int[] xOffset = new int[ points.size() ];
|
||||
final int[] yOffset = new int[ points.size() ];
|
||||
final int[] filledX = new int[ points.size() ];
|
||||
final int[] filledY = new int[ points.size() ];
|
||||
for ( int i = 0; i < points.size(); ++i ) {
|
||||
final Point point = points.get( i );
|
||||
x[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
||||
y[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
||||
xOffset[ i ] = ( int ) ( ( centerX + point.getX() + count * spacing ) * scale ) + offsetX;
|
||||
yOffset[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
||||
filledX[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
||||
filledY[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
|
||||
}
|
||||
|
||||
g.setColor( Color.BLACK );
|
||||
g.drawPolygon( x, y, points.size() );
|
||||
g.drawPolygon( xOffset, yOffset, points.size() );
|
||||
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
|
||||
g.fillPolygon( filledX, filledY, points.size() );
|
||||
++count;
|
||||
}
|
||||
|
||||
g.setColor( Color.MAGENTA );
|
||||
drawOnBoard( g, "Polygon count: " + data.polygons.size(), 0, -10 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( chains != null ) {
|
||||
int i = 0;
|
||||
for ( Chain chain : chains ) {
|
||||
Point p1 = chain.getOrigin().getPosition();
|
||||
Point p2 = chain.getDest().getPosition();
|
||||
if ( data.chains != null ) {
|
||||
g.setColor( Color.RED );
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX + i,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY + i,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX + i,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY + i
|
||||
);
|
||||
for ( final Chain chain : data.chains ) {
|
||||
final Point p1 = chain.getStart();
|
||||
final Point p2 = chain.getEnd();
|
||||
|
||||
// for ( HalfEdge e : chain.getLinks() ) {
|
||||
// Point e1 = e.getOrigin().getPosition();
|
||||
// Point e2 = e.getSym().getOrigin().getPosition();
|
||||
// g.setColor( Color.BLUE );
|
||||
// g.drawLine(
|
||||
// ( int ) ( ( centerX + e1.getX() + 40 ) * scale ) + offsetX + i + 1,
|
||||
// ( int ) ( ( centerY - e1.getY() ) * scale ) + offsetY + i + 1,
|
||||
// ( int ) ( ( centerX + e2.getX() + 40 ) * scale ) + offsetX + i + 1,
|
||||
// ( int ) ( ( centerY - e2.getY() ) * scale ) + offsetY + i + 1
|
||||
// );
|
||||
// }
|
||||
// i += 2;
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
|
||||
drawOnBoard( g, "Chain count: " + data.chains.size(), 0, -15 );
|
||||
}
|
||||
|
||||
if ( data.selectedChains != null ) {
|
||||
g.setColor( Color.BLUE );
|
||||
for ( final Chain chain : data.selectedChains ) {
|
||||
final Point p1 = chain.getStart();
|
||||
final Point p2 = chain.getEnd();
|
||||
|
||||
g.drawLine(
|
||||
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
||||
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
||||
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
||||
);
|
||||
}
|
||||
drawOnBoard( g, "Minimum chain count: " + data.selectedChains.size(), 0, -20 );
|
||||
}
|
||||
}
|
||||
|
||||
java.awt.Point p = MouseInfo.getPointerInfo().getLocation();
|
||||
java.awt.Point componentLocation = getLocation();
|
||||
SwingUtilities.convertPointToScreen( componentLocation, this );
|
||||
java.awt.Point componentLocation = getLocationOnScreen();
|
||||
p = new java.awt.Point( p.x - componentLocation.x, p.y - componentLocation.y );
|
||||
g.setColor( Color.RED );
|
||||
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, 30 ) );
|
||||
g.drawString( "X: " + ( ( p.x - offsetX ) / scale - centerX ), 10, 30 );
|
||||
g.drawString( "Y: " + ( - ( ( p.y - offsetY ) / scale - centerY ) ), 10, 60 );
|
||||
}
|
||||
|
||||
private static class PlaneData {
|
||||
Collection< Point > vertices;
|
||||
Collection< Segment > edges;
|
||||
Collection< Chain > chains;
|
||||
Collection< Chain > selectedChains;
|
||||
Collection< Polygon > polygons;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user