Improve tessellation algorithm
Added new mesh class Rewrite mesh algorithms to be less error prone Make code more concise Tried to add some documentation Added half edge set
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -10,7 +10,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot</artifactId>
|
<artifactId>spigot</artifactId>
|
||||||
<version>1.19.4-R0.1-SNAPSHOT</version>
|
<version>1.21.5-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set for easily checking if a set contains/does not contain a half-edge or its sym.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class EdgeSet extends HashSet< HalfEdge > {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 4042266887750818558L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add( HalfEdge edge ) {
|
||||||
|
if ( !contains( edge ) ) {
|
||||||
|
return super.add( edge );
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove( Object object ) {
|
||||||
|
if ( object instanceof HalfEdge ) {
|
||||||
|
HalfEdge edge = ( HalfEdge ) object;
|
||||||
|
final boolean r1 = super.remove( edge );
|
||||||
|
final boolean r2 = super.remove( edge.getSym() );
|
||||||
|
|
||||||
|
if ( r1 && r2 ) {
|
||||||
|
throw new IllegalStateException( "Removed edge was added twice!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1 || r2;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains( Object object ) {
|
||||||
|
if ( object instanceof HalfEdge ) {
|
||||||
|
HalfEdge edge = ( HalfEdge ) object;
|
||||||
|
return super.contains( edge ) || super.contains( edge.getSym() );
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A half-edge class used to represent a DCEL(doubly connected edge list): https://en.wikipedia.org/wiki/Doubly_connected_edge_list
|
||||||
|
*
|
||||||
|
* In this case, it is intended to be used for a 2d PSLG(planar straight-line graph), but can probably be repurposed
|
||||||
|
* for some other nefarious use case.
|
||||||
|
*
|
||||||
|
* An edge is a line segment connecting an origin to a destination. A half-edge is a partial line segment containing only
|
||||||
|
* the origin, but not the destination.
|
||||||
|
*
|
||||||
|
* Each half-edge structure contains:
|
||||||
|
* - A reference to the origin
|
||||||
|
* - A reference to the symmetrical, or opposite half-edge which is attached to the destination.
|
||||||
|
* - A reference to the next clockwise half-edge of the opposite half edge. This can be used to
|
||||||
|
* traverse counter-clockwise through the half-edges of a polygon.
|
||||||
|
* - A reference to the previous edge, which is the next counter-clockwise edge of this half edge.
|
||||||
|
* This can be used to traverse all the edges of a vertex, in counter clockwise order.
|
||||||
|
*
|
||||||
|
* For a given half-edge attached to two points that contain no other edges, the previous edge is
|
||||||
|
* itself, and the next edge is the same as the opposite edge.
|
||||||
|
*
|
||||||
|
* There are 2 main operations that are used when manipulating half-edges to attach/detach them from
|
||||||
|
* a vertex:
|
||||||
|
* - splicing
|
||||||
|
* - splitting
|
||||||
|
*
|
||||||
|
* Splicing is the most important of the 2 operations, by a large margin. Given two half-edges, A and B,
|
||||||
|
* swap the previous edges, and set the next edge for both of the previous edges to the opposite half edge.
|
||||||
|
* This has the desired effect of either detaching two edges if they are connected to the same vertex, or
|
||||||
|
* attaching two separate half-edges together to the same vertex.
|
||||||
|
*
|
||||||
|
* Splitting is adding an edge between the opposite of an edge and attaching it to the destination. It
|
||||||
|
* splits a single edge into two edges with a vertex in the middle.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class HalfEdge {
|
||||||
|
/**
|
||||||
|
* The origin
|
||||||
|
*/
|
||||||
|
protected Vertex origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The symmetrical half-edge
|
||||||
|
*/
|
||||||
|
protected HalfEdge sym;
|
||||||
|
/**
|
||||||
|
* The half-edge counter-clockwise on the same origin
|
||||||
|
*/
|
||||||
|
protected HalfEdge prev;
|
||||||
|
/**
|
||||||
|
* The half-edge clockwise from the symmetrical half-edge
|
||||||
|
*/
|
||||||
|
protected HalfEdge next;
|
||||||
|
|
||||||
|
// TODO Consider adding a constructor with a origin and destination
|
||||||
|
|
||||||
|
public HalfEdge() {
|
||||||
|
init();
|
||||||
|
|
||||||
|
// Whenever we create a new half-edge, we need to make
|
||||||
|
// sure that it has a symmetrical half-edge.
|
||||||
|
new HalfEdge( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private HalfEdge( HalfEdge o ) {
|
||||||
|
init();
|
||||||
|
|
||||||
|
sym = o;
|
||||||
|
o.sym = this;
|
||||||
|
|
||||||
|
next = o;
|
||||||
|
o.next = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
origin = new Vertex( this );
|
||||||
|
prev = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vertex getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the origin. Does not affect any additional
|
||||||
|
*
|
||||||
|
* @param vert
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HalfEdge setOrigin( Vertex vert ) {
|
||||||
|
origin = vert;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HalfEdge getSym() {
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HalfEdge getPrev() {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the previous half-edge. Does not update additional properties.
|
||||||
|
*
|
||||||
|
* @param edge The half-edge to set as the previous half-edge.
|
||||||
|
* @return This half-edge
|
||||||
|
*/
|
||||||
|
public HalfEdge setPrev( HalfEdge edge ) {
|
||||||
|
this.prev = edge;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HalfEdge getNext() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the next half-edge. Does not update additional properties.
|
||||||
|
*
|
||||||
|
* @param edge The half-edge to set as the next half-edge.
|
||||||
|
* @return This half-edge
|
||||||
|
*/
|
||||||
|
public HalfEdge setNext( HalfEdge edge ) {
|
||||||
|
this.next = edge;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vertex getDest() {
|
||||||
|
return sym.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the difference between the destination position and the origin position
|
||||||
|
*
|
||||||
|
* @return A vector representing the destination minus the origin
|
||||||
|
*/
|
||||||
|
public Vector2d toVector2d() {
|
||||||
|
return getDest().getPosition().subtracted( getOrigin().getPosition() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the origin and destination are the same
|
||||||
|
*
|
||||||
|
* @return If the origin is equal to the destination
|
||||||
|
*/
|
||||||
|
public boolean isZero() {
|
||||||
|
return getOrigin().equals( getDest() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split this half-edge in half and return the newly created
|
||||||
|
* half-edge, as the next half-edge of this half-edge.
|
||||||
|
*
|
||||||
|
* @return A new half-edge with a default initialized origin.
|
||||||
|
*/
|
||||||
|
public HalfEdge split() {
|
||||||
|
final HalfEdge edge = new HalfEdge();
|
||||||
|
|
||||||
|
splice( edge, getNext() );
|
||||||
|
|
||||||
|
splice( getSym(), getNext() );
|
||||||
|
splice( getSym(), edge.getSym() );
|
||||||
|
|
||||||
|
edge.setOrigin( getDest() );
|
||||||
|
edge.getOrigin().setEdge( edge );
|
||||||
|
|
||||||
|
getSym().setOrigin( edge.getDest() );
|
||||||
|
getDest().setEdge( getSym() );
|
||||||
|
|
||||||
|
edge.getOrigin().setEdge( edge );
|
||||||
|
|
||||||
|
return edge.getSym();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splice two half-edges together. A commutative operation.
|
||||||
|
*
|
||||||
|
* This does _not_ modify any vertices.
|
||||||
|
*
|
||||||
|
* @param a The first half-edge
|
||||||
|
* @param b The second half-edge
|
||||||
|
*/
|
||||||
|
public static void splice( HalfEdge a, HalfEdge b ) {
|
||||||
|
final HalfEdge ap = a.prev;
|
||||||
|
final HalfEdge bp = b.prev;
|
||||||
|
|
||||||
|
a.prev = bp;
|
||||||
|
b.prev = ap;
|
||||||
|
|
||||||
|
ap.sym.next = b;
|
||||||
|
bp.sym.next = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "HalfEdge{orig=%1$s,dest=%2$s}", getOrigin(), getDest() );
|
||||||
|
}
|
||||||
|
}
|
||||||
1324
src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java
Normal file
1324
src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Mesh.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,12 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 2d cartesian coordinate
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
public class Point {
|
public class Point {
|
||||||
double x, y;
|
protected double x, y;
|
||||||
|
|
||||||
public Point( double x, double y ) {
|
public Point( double x, double y ) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
@@ -15,6 +20,16 @@ public class Point {
|
|||||||
public double getY() {
|
public double getY() {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Point setX( double x ) {
|
||||||
|
this.x = x;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point setY( double y ) {
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
@@ -43,4 +58,9 @@ public class Point {
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "Point{x=%f,y=%f}", x, y );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of points denoting the shape of the polygon. May contain self intersections,
|
||||||
|
* duplicate vertices, and overlapping edges.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class Polygon {
|
||||||
|
protected List< Point > points;
|
||||||
|
|
||||||
|
public Polygon( List< Point > points ) {
|
||||||
|
this.points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List< Point > getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double area() {
|
||||||
|
double area = 0;
|
||||||
|
for ( int i = 0; i < points.size(); ++i ) {
|
||||||
|
final Vector2d a = new Vector2d( points.get( i ) );
|
||||||
|
final Vector2d b = new Vector2d( points.get( ( i + 1 ) % points.size() ) );
|
||||||
|
area += a.cross( b );
|
||||||
|
}
|
||||||
|
return Math.abs( area ) / 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class SortedEdgeCollection< T > implements Iterable< T > {
|
||||||
|
Map< T, Node > quickMap = new ConcurrentHashMap< T, Node >();
|
||||||
|
Node head = new Node( null );
|
||||||
|
|
||||||
|
BiFunction< T, T, Boolean > isGreaterThan;
|
||||||
|
|
||||||
|
public SortedEdgeCollection( BiFunction< T, T, Boolean > compare ) {
|
||||||
|
isGreaterThan = compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T bottom() {
|
||||||
|
return head.upper == head ? null : head.upper.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T upper( T val ) {
|
||||||
|
Node n = quickMap.get( val );
|
||||||
|
return n == null ? null : n.upper.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T lower( T val ) {
|
||||||
|
Node n = quickMap.get( val );
|
||||||
|
return n == null ? null : n.lower.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove( T val ) {
|
||||||
|
Node n = quickMap.remove( val );
|
||||||
|
if ( n != null ) {
|
||||||
|
n.lower.upper = n.upper;
|
||||||
|
n.upper.lower = n.lower;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains( T val ) {
|
||||||
|
return quickMap.containsKey( val );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert( T val ) {
|
||||||
|
if ( !quickMap.containsKey( val ) ) {
|
||||||
|
Node insertBefore = head.upper;
|
||||||
|
// Find the first node that the value is NOT greater than
|
||||||
|
// Otherwise, break and insert before
|
||||||
|
while ( insertBefore.value != null && isGreaterThan.apply( val, insertBefore.value ) ) {
|
||||||
|
insertBefore = insertBefore.upper;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node newNode = new Node( val );
|
||||||
|
|
||||||
|
newNode.lower = insertBefore.lower;
|
||||||
|
newNode.lower.upper = newNode;
|
||||||
|
newNode.upper = insertBefore;
|
||||||
|
|
||||||
|
insertBefore.lower = newNode;
|
||||||
|
|
||||||
|
quickMap.put( val, newNode );
|
||||||
|
} else {
|
||||||
|
// Should not really happen
|
||||||
|
throw new IllegalStateException( "Tried to insert value twice" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first value that is greater than the one provided
|
||||||
|
public T searchUpper( T val ) {
|
||||||
|
Node n = quickMap.get( val );
|
||||||
|
if ( n == null ) {
|
||||||
|
Node greater = head.upper;
|
||||||
|
while ( greater.value != null && isGreaterThan.apply( val, greater.value ) ) {
|
||||||
|
greater = greater.upper;
|
||||||
|
}
|
||||||
|
return greater.value;
|
||||||
|
} else {
|
||||||
|
return n.upper.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the highest value that is lower than the one provided
|
||||||
|
public T searchLower( T val ) {
|
||||||
|
Node n = quickMap.get( val );
|
||||||
|
if ( n == null ) {
|
||||||
|
Node lower = head.upper;
|
||||||
|
while ( lower.value != null && isGreaterThan.apply( val, lower.value ) ) {
|
||||||
|
lower = lower.upper;
|
||||||
|
}
|
||||||
|
return lower.lower.value;
|
||||||
|
} else {
|
||||||
|
return n.lower.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
head = new Node( null );
|
||||||
|
quickMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class Node {
|
||||||
|
Node lower;
|
||||||
|
Node upper;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
Node( T val ) {
|
||||||
|
lower = this;
|
||||||
|
upper = this;
|
||||||
|
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator< T > iterator() {
|
||||||
|
return new Iterator< T >() {
|
||||||
|
Node current = head;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return current.upper != head;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
current = current.upper;
|
||||||
|
return current.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
T val = current.value;
|
||||||
|
current = current.lower;
|
||||||
|
SortedEdgeCollection.this.remove( val );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
final Vector2d a = new Vector2d( -1, -1 );
|
||||||
|
final Vector2d b = new Vector2d( 10, 10 );
|
||||||
|
final Vector2d c = new Vector2d( 0, -1 );
|
||||||
|
final Vector2d d = new Vector2d( 10, 11 );
|
||||||
|
|
||||||
|
test( a, b, c, d );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test( final Vector2d a, final Vector2d b, final Vector2d c, final Vector2d d ) {
|
||||||
|
final Vector2d ab = b.subtracted( a );
|
||||||
|
final Vector2d cd = d.subtracted( c );
|
||||||
|
|
||||||
|
final Vector2d ac = c.subtracted( a );
|
||||||
|
|
||||||
|
final double acab = ac.cross( ab );
|
||||||
|
final double abcd = ab.cross( cd );
|
||||||
|
|
||||||
|
System.out.println( "Parallel value: " + abcd );
|
||||||
|
if ( abcd != 0 ) {
|
||||||
|
final double s = ac.cross( cd );
|
||||||
|
final double t = s / abcd;
|
||||||
|
final double u = acab / abcd;
|
||||||
|
|
||||||
|
System.out.println( "S: " + s );
|
||||||
|
System.out.println( "T: " + t );
|
||||||
|
System.out.println( "U: " + u );
|
||||||
|
|
||||||
|
System.out.println( "AB Length: " + ( t * ab.length() ) );
|
||||||
|
System.out.println( "CD Length: " + ( u * cd.length() ) );
|
||||||
|
|
||||||
|
// Gives the intersection correctly
|
||||||
|
System.out.println( "AB int: " + ab.multiplied( t ).add( a ) );
|
||||||
|
System.out.println( "CD int: " + cd.multiplied( u ).add( c ) );
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +1,28 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
public class Vector2d {
|
/**
|
||||||
double x;
|
* A basic 2d vector implementation.
|
||||||
double y;
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class Vector2d extends Point {
|
||||||
public Vector2d() {
|
public Vector2d() {
|
||||||
this( 0, 0 );
|
this( 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d( double x, double y ) {
|
public Vector2d( double x, double y ) {
|
||||||
this.x = x;
|
super( x, y );
|
||||||
this.y = y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d( Vector2d o ) {
|
public Vector2d( Point o ) {
|
||||||
this( o.x, o.y );
|
this( o.x, o.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getX() {
|
public double angle( Point o ) {
|
||||||
return x;
|
return Math.atan2( cross( o ), dot( o ) );
|
||||||
}
|
|
||||||
|
|
||||||
public double getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2d setX( double x ) {
|
|
||||||
this.x = x;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2d setY( double y ) {
|
|
||||||
this.y = y;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double cross( Vector2d o ) {
|
public double cross( Point o ) {
|
||||||
return ( x * o.y ) - ( y * o.x );
|
return ( x * o.y ) - ( y * o.x );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,15 +34,15 @@ public class Vector2d {
|
|||||||
return Math.sqrt( lengthSquared() );
|
return Math.sqrt( lengthSquared() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public double distanceSquared( Vector2d o ) {
|
public double distanceSquared( Point o ) {
|
||||||
return distanceSquared( this, o );
|
return distanceSquared( this, o );
|
||||||
}
|
}
|
||||||
|
|
||||||
public double distance( Vector2d o ) {
|
public double distance( Point o ) {
|
||||||
return distance( this, o );
|
return distance( this, o );
|
||||||
}
|
}
|
||||||
|
|
||||||
public double dot( Vector2d o ) {
|
public double dot( Point o ) {
|
||||||
return dot( this, o );
|
return dot( this, o );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +56,13 @@ public class Vector2d {
|
|||||||
return new Vector2d( x + v, y + v );
|
return new Vector2d( x + v, y + v );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d add( Vector2d o ) {
|
public Vector2d add( Point o ) {
|
||||||
x += o.x;
|
x += o.x;
|
||||||
y += o.y;
|
y += o.y;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d added( Vector2d o ) {
|
public Vector2d added( Point o ) {
|
||||||
return add( this, o );
|
return add( this, o );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +76,13 @@ public class Vector2d {
|
|||||||
return new Vector2d( x - v, y - v );
|
return new Vector2d( x - v, y - v );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d subtract( Vector2d o ) {
|
public Vector2d subtract( Point o ) {
|
||||||
x -= o.x;
|
x -= o.x;
|
||||||
y -= o.y;
|
y -= o.y;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d subtracted( Vector2d o ) {
|
public Vector2d subtracted( Point o ) {
|
||||||
return new Vector2d( x - o.x, y - o.y );
|
return new Vector2d( x - o.x, y - o.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +96,13 @@ public class Vector2d {
|
|||||||
return new Vector2d( x * v, y * v );
|
return new Vector2d( x * v, y * v );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d multiply( Vector2d o ) {
|
public Vector2d multiply( Point o ) {
|
||||||
x *= o.x;
|
x *= o.x;
|
||||||
y *= o.y;
|
y *= o.y;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d multiplied( Vector2d o ) {
|
public Vector2d multiplied( Point o ) {
|
||||||
return multiply( this, o );
|
return multiply( this, o );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,13 +116,13 @@ public class Vector2d {
|
|||||||
return new Vector2d( x / v, y / v );
|
return new Vector2d( x / v, y / v );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d divide( Vector2d o ) {
|
public Vector2d divide( Point o ) {
|
||||||
x /= o.x;
|
x /= o.x;
|
||||||
y /= o.y;
|
y /= o.y;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2d divided( Vector2d o ) {
|
public Vector2d divided( Point o ) {
|
||||||
return new Vector2d( x / o.x, y / o.y );
|
return new Vector2d( x / o.x, y / o.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,23 +138,23 @@ public class Vector2d {
|
|||||||
return x == 0 && y == 0;
|
return x == 0 && y == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double dot( Vector2d a, Vector2d b ) {
|
public static double dot( Point a, Point b ) {
|
||||||
return a.x * b.x + a.y * b.y;
|
return a.x * b.x + a.y * b.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2d add( Vector2d a, Vector2d b ) {
|
public static Vector2d add( Point a, Point b ) {
|
||||||
return new Vector2d( a.x + b.x, a.y + b.y );
|
return new Vector2d( a.x + b.x, a.y + b.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2d multiply( Vector2d a, Vector2d b ) {
|
public static Vector2d multiply( Point a, Point b ) {
|
||||||
return new Vector2d( a.x * b.x, a.y * b.y );
|
return new Vector2d( a.x * b.x, a.y * b.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double distanceSquared( Vector2d a, Vector2d b ) {
|
public static double distanceSquared( Vector2d a, Point b ) {
|
||||||
return a.subtracted( b ).lengthSquared();
|
return a.subtracted( b ).lengthSquared();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double distance( Vector2d a, Vector2d b ) {
|
public static double distance( Vector2d a, Point b ) {
|
||||||
return Math.sqrt( distanceSquared( a, b ) );
|
return Math.sqrt( distanceSquared( a, b ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +188,6 @@ public class Vector2d {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Vector2d{x=" + x + ",y=" + y + "}";
|
return String.format( "Vector2d{x=%f,y=%f}", x, y );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
121
src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Vertex.java
Normal file
121
src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/Vertex.java
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vertex class is used to represent a point in space. For our use cases, it is going to be 2d.
|
||||||
|
*
|
||||||
|
* A vertex contains two pieces of information:
|
||||||
|
* - The position
|
||||||
|
* - A half-edge which is attached to this vertex
|
||||||
|
*
|
||||||
|
* To traverse over all the edges which are connected to this vertex,
|
||||||
|
* loop over the previous edge of the half edge until all of the edges
|
||||||
|
* have been reached at least once.
|
||||||
|
*
|
||||||
|
* Whenever performing a splicing operation on an edge, it is a good
|
||||||
|
* idea to update all edges attached to this vertex to point to this
|
||||||
|
* vertex, in case if they are pointing at a different one.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class Vertex implements Iterable< HalfEdge > {
|
||||||
|
protected HalfEdge edge;
|
||||||
|
protected Vector2d position;
|
||||||
|
|
||||||
|
public Vertex( final HalfEdge edge ) {
|
||||||
|
this( edge, new Vector2d() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vertex( final HalfEdge edge, final Vector2d position ) {
|
||||||
|
setEdge( edge );
|
||||||
|
setPosition( position );
|
||||||
|
}
|
||||||
|
|
||||||
|
public HalfEdge getEdge() {
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vertex setEdge( HalfEdge edge ) {
|
||||||
|
this.edge = edge;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2d getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vertex setPosition( Vector2d vec ) {
|
||||||
|
this.position = vec;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O(n)
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
int size = 0;
|
||||||
|
for ( final Iterator< HalfEdge > it = iterator(); it.hasNext(); it.next() ) {
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all attached half-edges to point to this vertex
|
||||||
|
* as the origin
|
||||||
|
*/
|
||||||
|
public void update() {
|
||||||
|
HalfEdge edge = this.edge;
|
||||||
|
do {
|
||||||
|
edge.setOrigin( this );
|
||||||
|
} while ( ( edge = edge.getPrev() ) != this.edge );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator< HalfEdge > iterator() {
|
||||||
|
return new Iterator< HalfEdge >() {
|
||||||
|
private final HalfEdge start = edge;
|
||||||
|
private HalfEdge index = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return index != start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HalfEdge next() {
|
||||||
|
if ( index == null ) {
|
||||||
|
index = start.getPrev();
|
||||||
|
return start;
|
||||||
|
} else {
|
||||||
|
final HalfEdge curr = index;
|
||||||
|
index = index.getPrev();
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
Vertex other = (Vertex) obj;
|
||||||
|
if (position == null) {
|
||||||
|
if (other.position != null)
|
||||||
|
return false;
|
||||||
|
} else if (!position.equals(other.position))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Vertex{x=" + position.getX() + ",y=" + position.getY() + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.region;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
public class CompositeRegionRule< T extends Region > implements RegionRule< T > {
|
||||||
|
final protected List< RegionRule< T > > rules;
|
||||||
|
|
||||||
|
public CompositeRegionRule() {
|
||||||
|
this.rules = new ArrayList< RegionRule< T > >();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositeRegionRule( final List< RegionRule< T > > rules ) {
|
||||||
|
this.rules = new ArrayList< RegionRule< T > >( rules );
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositeRegionRule< T > addRule( final RegionRule< T > rule ) {
|
||||||
|
rules.add( rule );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List< RegionRule< T > > getRules() {
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply( T region ) {
|
||||||
|
for ( final RegionRule< T > rule : rules ) {
|
||||||
|
region = rule.apply( region );
|
||||||
|
}
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionRule< T > inverse() {
|
||||||
|
CompositeRegionRule< T > rule = new CompositeRegionRule< T >();
|
||||||
|
for ( ListIterator< RegionRule< T > > it = rules.listIterator(); it.hasPrevious(); ) {
|
||||||
|
rule.rules.add( it.previous().inverse() );
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionRule< T > apply( RegionRule< T > rule ) {
|
||||||
|
final CompositeRegionRule< T > copy = new CompositeRegionRule< T >( rules );
|
||||||
|
if ( rule instanceof CompositeRegionRule ) {
|
||||||
|
final CompositeRegionRule< T > other = ( CompositeRegionRule< T > ) rule;
|
||||||
|
for ( RegionRule< T > r : other.getRules() ) {
|
||||||
|
copy.addRule( r );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy.addRule( rule );
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A continuous portion in space that can be clearly defined as "inside" or "outside".
|
||||||
|
*
|
||||||
|
* The equality check is mainly used to guarantee winding rule consistency.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public abstract class Region {
|
||||||
|
// Represents the area under a wing
|
||||||
|
public abstract boolean isInterior();
|
||||||
|
public abstract boolean equals( Object other );
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A region rule describes how a region is modified, particularly when crossing a border
|
||||||
|
* between a known region to an unknown region. The inverse region rule must provide a
|
||||||
|
* symmetric change such that applying the region rule then the inverse, the resulting region
|
||||||
|
* must be equivalent to the original region. Normally, the same rule is applied to all polygons
|
||||||
|
* that need to be tessellated, such as the odd number rule, but if your polygons conform to different or
|
||||||
|
* more complex interior/exterior critera, then you can easily set different rules per polygon.
|
||||||
|
* An example might be if you have two sets of polygons that you would like to tessellate
|
||||||
|
* together, and distinctly mark regions where they might overlap. You can use a combination
|
||||||
|
* of two odd number rules to properly mark the boundaries.
|
||||||
|
*
|
||||||
|
* For more information:
|
||||||
|
* - https://en.wikipedia.org/wiki/Nonzero-rule
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*
|
||||||
|
* @param <T> A region type that can be modified by this rule
|
||||||
|
*/
|
||||||
|
public interface RegionRule< T extends Region > {
|
||||||
|
// Apply this rule to the region, and return a new region
|
||||||
|
T apply( T region );
|
||||||
|
// Get the inverse of this rule
|
||||||
|
RegionRule< T > inverse();
|
||||||
|
// Apply this rule to the given rule, and return a new rule
|
||||||
|
RegionRule< T > apply( RegionRule< T > rule );
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.CompositeRegionRule;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.RegionRule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple winding number rule.
|
||||||
|
*
|
||||||
|
* Changes the winding number for a given region.
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RegionRuleWinding implements RegionRule< RegionSimple > {
|
||||||
|
public static final RegionRuleWinding CLOCKWISE = new RegionRuleWinding( 1 );
|
||||||
|
public static final RegionRuleWinding COUNTER_CLOCKWISE = CLOCKWISE.inverse();
|
||||||
|
|
||||||
|
private final int amount;
|
||||||
|
private RegionRuleWinding inverse;
|
||||||
|
|
||||||
|
private RegionRuleWinding( final int amount ) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionSimple apply( final RegionSimple region ) {
|
||||||
|
RegionSimple copy = new RegionSimple( region );
|
||||||
|
copy.increment( amount );
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionRuleWinding inverse() {
|
||||||
|
if ( inverse == null ) {
|
||||||
|
inverse = new RegionRuleWinding( -amount );
|
||||||
|
inverse.inverse = this;
|
||||||
|
}
|
||||||
|
return inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegionRule< RegionSimple > apply( final RegionRule< RegionSimple > rule ) {
|
||||||
|
if ( rule instanceof RegionRuleWinding ) {
|
||||||
|
final RegionRuleWinding other = ( RegionRuleWinding ) rule;
|
||||||
|
return new RegionRuleWinding( amount + other.amount );
|
||||||
|
} else {
|
||||||
|
final CompositeRegionRule< RegionSimple > composite = new CompositeRegionRule< RegionSimple >();
|
||||||
|
composite.addRule( this ).addRule( rule );
|
||||||
|
return composite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "RegionRuleWinding{amount=%1$d}", amount );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.Region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A region with a winding number. The interior-ness of
|
||||||
|
* this region is determined by the GLU winding rule:
|
||||||
|
*
|
||||||
|
* https://www.songho.ca/opengl/gl_tessellation.html#winding
|
||||||
|
*
|
||||||
|
* @author BananaPuncher714
|
||||||
|
*/
|
||||||
|
public class RegionSimple extends Region {
|
||||||
|
protected int windingNumber;
|
||||||
|
protected GluWindingRule rule;
|
||||||
|
|
||||||
|
public RegionSimple( RegionSimple o ) {
|
||||||
|
windingNumber = o.windingNumber;
|
||||||
|
rule = o.rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegionSimple( GluWindingRule rule ) {
|
||||||
|
this.windingNumber = 0;
|
||||||
|
this.rule = rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInterior() {
|
||||||
|
switch ( rule ) {
|
||||||
|
case ODD:
|
||||||
|
return ( windingNumber & 1 ) == 1;
|
||||||
|
case NONZERO:
|
||||||
|
return windingNumber != 0;
|
||||||
|
case POSITIVE:
|
||||||
|
return windingNumber > 0;
|
||||||
|
case NEGATIVE:
|
||||||
|
return windingNumber < 0;
|
||||||
|
case ABS_GEQ_TWO:
|
||||||
|
return Math.abs( windingNumber ) >= 2;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
RegionSimple other = (RegionSimple) obj;
|
||||||
|
if (rule != other.rule)
|
||||||
|
return false;
|
||||||
|
if (windingNumber != other.windingNumber)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "Region{number=%1$d,rule=%2$s}", windingNumber, rule );
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWindingNumber() {
|
||||||
|
return windingNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegionSimple setWindingNumber( int number ) {
|
||||||
|
this.windingNumber = number;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int increment( final int amount ) {
|
||||||
|
return windingNumber += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GluWindingRule {
|
||||||
|
ODD,
|
||||||
|
NONZERO,
|
||||||
|
POSITIVE,
|
||||||
|
NEGATIVE,
|
||||||
|
ABS_GEQ_TWO
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
@@ -17,16 +16,14 @@ import javax.swing.SwingUtilities;
|
|||||||
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.MeshTest.AABB;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet;
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Point;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Polygon;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Tess4j;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Tess4j;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Vector2d;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Vector2dComparator;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
||||||
|
|||||||
@@ -0,0 +1,485 @@
|
|||||||
|
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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.HalfEdge;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.EdgePolygon;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vertex;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple.GluWindingRule;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
|
||||||
|
|
||||||
|
public class MeshingTest2 extends JPanel {
|
||||||
|
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
||||||
|
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
||||||
|
|
||||||
|
JFrame f;
|
||||||
|
|
||||||
|
private int windowWidth = 1200;
|
||||||
|
private int windowHeight = 1000;
|
||||||
|
|
||||||
|
// private int centerX = 600;
|
||||||
|
// private int centerY = 400;
|
||||||
|
|
||||||
|
private int centerX = 400;
|
||||||
|
private int centerY = 1200;
|
||||||
|
|
||||||
|
private Graphics g;
|
||||||
|
|
||||||
|
private int scale = 8;
|
||||||
|
|
||||||
|
private Collection< Vertex > data;
|
||||||
|
private Collection< EdgePolygon > polygons;
|
||||||
|
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
|
||||||
|
System.out.println( "Found " + CHUNK_DIR.list().length + " files" );
|
||||||
|
for ( File file : CHUNK_DIR.listFiles() ) {
|
||||||
|
Collection< Plane > planes = mesh( file );
|
||||||
|
|
||||||
|
Plane draw = null;
|
||||||
|
|
||||||
|
System.out.println( "Planes: " + planes.size() );
|
||||||
|
for ( Plane plane : planes ) {
|
||||||
|
if ( plane.polygons.size() > 100 ) {
|
||||||
|
draw = plane;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw = planes.get( new Random().nextInt( planes.size() ) );
|
||||||
|
|
||||||
|
// test( planes );
|
||||||
|
|
||||||
|
final Collection< Vertex > data = process( draw );
|
||||||
|
final Collection< EdgePolygon > polys = triangulate( draw );
|
||||||
|
// final Collection< EdgePolygon > polys = test();
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater( new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new MeshingTest2( data, polys );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection< Plane > mesh( File file ) {
|
||||||
|
String name = file.getName();
|
||||||
|
String[] split = name.split( "," );
|
||||||
|
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
||||||
|
|
||||||
|
System.out.println( "Parsing chunk " + location );
|
||||||
|
|
||||||
|
// First parse the file to get a list of all bounding boxes that we can use
|
||||||
|
List< AABB > boxes = new ArrayList< AABB >();
|
||||||
|
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
||||||
|
String line;
|
||||||
|
while ( ( line = reader.readLine() ) != null ) {
|
||||||
|
if ( !line.isEmpty() ) {
|
||||||
|
String[] values = line.split( "," );
|
||||||
|
double minX = Double.parseDouble( values[ 0 ] );
|
||||||
|
double minY = Double.parseDouble( values[ 1 ] );
|
||||||
|
double minZ = Double.parseDouble( values[ 2 ] );
|
||||||
|
double maxX = Double.parseDouble( values[ 3 ] );
|
||||||
|
double maxY = Double.parseDouble( values[ 4 ] );
|
||||||
|
double maxZ = Double.parseDouble( values[ 5 ] );
|
||||||
|
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ( FileNotFoundException e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} catch ( IOException e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println( "Found " + boxes.size() + " boxes" );
|
||||||
|
// Now that we have a bunch of bounding boxes, do whatever
|
||||||
|
|
||||||
|
MeshBuilder builder = new MeshBuilder();
|
||||||
|
Plane draw = null;
|
||||||
|
for ( AABB box : boxes ) {
|
||||||
|
for ( Facet facet : generateFacetsFor( box ) ) {
|
||||||
|
builder.addFacet( facet );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.planes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test( final Collection< Plane > planes ) {
|
||||||
|
final long processAllStart = System.currentTimeMillis();
|
||||||
|
planes.parallelStream().forEach( plane -> {
|
||||||
|
try {
|
||||||
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||||
|
|
||||||
|
for ( Polygon poly : plane.polygons ) {
|
||||||
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||||
|
}
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
mesh.simplify();
|
||||||
|
mesh.generateRegions();
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
System.out.println( plane.polygons.size() + ":\t " + ( end - start ) + "ms" );
|
||||||
|
} catch ( IllegalStateException e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
final long processAllEnd = System.currentTimeMillis();
|
||||||
|
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection< Vertex > process( final Plane plane ) {
|
||||||
|
if ( plane != null ) {
|
||||||
|
System.out.println( "Norm:\t" + plane.normal );
|
||||||
|
System.out.println( "Ref:\t" + plane.point );
|
||||||
|
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||||
|
|
||||||
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||||
|
for ( Polygon poly : plane.polygons ) {
|
||||||
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||||
|
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 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 ) );
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||||
|
return mesh.getVertices();
|
||||||
|
} else {
|
||||||
|
System.out.println( "No data!" );
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection< EdgePolygon > triangulate( final Plane plane ) {
|
||||||
|
if ( plane != null ) {
|
||||||
|
System.out.println( "Norm:\t" + plane.normal );
|
||||||
|
System.out.println( "Ref:\t" + plane.point );
|
||||||
|
System.out.println( "Size:\t" + plane.polygons.size() );
|
||||||
|
|
||||||
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||||
|
for ( Polygon poly : plane.polygons ) {
|
||||||
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
||||||
|
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 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 ) );
|
||||||
|
|
||||||
|
mesh.generateRegions();
|
||||||
|
|
||||||
|
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
||||||
|
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
|
||||||
|
|
||||||
|
final Collection< EdgePolygon > polys = mesh.mesh();
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
System.out.println( "Took " + ( end - start ) + "ms" );
|
||||||
|
|
||||||
|
return polys;
|
||||||
|
} else {
|
||||||
|
System.out.println( "No data!" );
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection< EdgePolygon > test() {
|
||||||
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||||
|
|
||||||
|
mesh.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -1, -1 ),
|
||||||
|
new Point( 1, -1 ),
|
||||||
|
new Point( 1, 1 ),
|
||||||
|
new Point( -1, 1 )
|
||||||
|
) ), RegionRuleWinding.CLOCKWISE );
|
||||||
|
|
||||||
|
mesh.simplify();
|
||||||
|
|
||||||
|
mesh.generateRegions();
|
||||||
|
|
||||||
|
return mesh.mesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List< Facet > generateFacetsFor( AABB box ) {
|
||||||
|
List< Facet > facets = new ArrayList< Facet >();
|
||||||
|
|
||||||
|
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
||||||
|
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
||||||
|
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
||||||
|
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
||||||
|
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
||||||
|
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
||||||
|
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
||||||
|
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p1 );
|
||||||
|
facet.points.add( p2 );
|
||||||
|
facet.points.add( p4 );
|
||||||
|
facet.points.add( p3 );
|
||||||
|
facet.normal = new Vector( -1, 0, 0 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p5 );
|
||||||
|
facet.points.add( p6 );
|
||||||
|
facet.points.add( p8 );
|
||||||
|
facet.points.add( p7 );
|
||||||
|
facet.normal = new Vector( 1, 0, 0 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p1 );
|
||||||
|
facet.points.add( p2 );
|
||||||
|
facet.points.add( p6 );
|
||||||
|
facet.points.add( p5 );
|
||||||
|
facet.normal = new Vector( 0, -1, 0 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p3 );
|
||||||
|
facet.points.add( p4 );
|
||||||
|
facet.points.add( p8 );
|
||||||
|
facet.points.add( p7 );
|
||||||
|
facet.normal = new Vector( 0, 1, 0 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p1 );
|
||||||
|
facet.points.add( p3 );
|
||||||
|
facet.points.add( p7 );
|
||||||
|
facet.points.add( p5 );
|
||||||
|
facet.normal = new Vector( 0, 0, -1 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Facet facet = new Facet();
|
||||||
|
facet.points.add( p2 );
|
||||||
|
facet.points.add( p4 );
|
||||||
|
facet.points.add( p8 );
|
||||||
|
facet.points.add( p6 );
|
||||||
|
facet.normal = new Vector( 0, 0, 1 );
|
||||||
|
facets.add( facet );
|
||||||
|
}
|
||||||
|
|
||||||
|
return facets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AABB {
|
||||||
|
double xmin, ymin, zmin;
|
||||||
|
double xmax, ymax, zmax;
|
||||||
|
|
||||||
|
AABB( double xmin, double ymin, double zmin, double xmax, double ymax, double zmax ) {
|
||||||
|
this.xmin = xmin;
|
||||||
|
this.ymin = ymin;
|
||||||
|
this.zmin = zmin;
|
||||||
|
|
||||||
|
this.xmax = xmax;
|
||||||
|
this.ymax = ymax;
|
||||||
|
this.zmax = zmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getVolume() {
|
||||||
|
return ( xmax - xmin ) * ( ymax - ymin ) * ( zmax - zmin );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format( "(%f, %f, %f) -> (%f, %f, %f)", xmin, ymin, zmin, xmax, ymax, zmax );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MeshingTest2( Collection< Vertex > polys, Collection< EdgePolygon > polygons ) {
|
||||||
|
data = polys;
|
||||||
|
this.polygons = polygons;
|
||||||
|
|
||||||
|
f = new JFrame( "Drawing Board" );
|
||||||
|
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||||
|
|
||||||
|
f.add( this );
|
||||||
|
|
||||||
|
f.setSize( windowWidth, windowHeight );
|
||||||
|
f.setVisible( true );
|
||||||
|
f.setResizable( true );
|
||||||
|
|
||||||
|
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawPoint( double x, double y ) {
|
||||||
|
g.fillRect( ( int ) x * scale, ( int ) y * scale, scale, scale );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2d rotate( Vector2d a, double angle ) {
|
||||||
|
double cos = Math.cos( angle );
|
||||||
|
double sin = Math.sin( angle );
|
||||||
|
double ax = a.getX() * cos - a.getY() * sin;
|
||||||
|
double ay = a.getX() * sin + a.getY() * cos;
|
||||||
|
return new Vector2d( ax, ay );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintComponent( Graphics g ) {
|
||||||
|
super.paintComponent( g );
|
||||||
|
this.g = g;
|
||||||
|
|
||||||
|
g.setColor( new Color( 200, 200, 200 ) );
|
||||||
|
g.drawLine( centerX, 0, centerX, 2000 );
|
||||||
|
g.drawLine( 0, centerY, 2000, centerY );
|
||||||
|
g.setColor( Color.BLACK );
|
||||||
|
|
||||||
|
if ( data != null ) {
|
||||||
|
Collection< Vertex > polygons = data;
|
||||||
|
|
||||||
|
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 ) ( p1.getX() * scale ) + centerX,
|
||||||
|
( int ) ( p1.getY() * scale ) + centerY,
|
||||||
|
( int ) ( p2.getX() * scale ) + centerX,
|
||||||
|
( int ) ( p2.getY() * scale ) + centerY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final double diff = scale * 0.3;
|
||||||
|
final Point point = vert.getPosition();
|
||||||
|
g.drawRect( ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( polygons != null ) {
|
||||||
|
System.out.println( "Polygon count: " + polygons.size() );
|
||||||
|
g.setColor( Color.BLACK );
|
||||||
|
for ( final EdgePolygon p : polygons ) {
|
||||||
|
final Collection< HalfEdge > edges = p.getEdges();
|
||||||
|
|
||||||
|
for ( HalfEdge edge : edges ) {
|
||||||
|
Point p1 = edge.getOrigin().getPosition();
|
||||||
|
Point p2 = edge.getDest().getPosition();
|
||||||
|
g.drawLine(
|
||||||
|
( int ) ( p1.getX() * scale ) + centerX + 400,
|
||||||
|
( int ) ( p1.getY() * scale ) + centerY,
|
||||||
|
( int ) ( p2.getX() * scale ) + centerX + 400,
|
||||||
|
( int ) ( p2.getY() * scale ) + centerY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// int size = edges.size();
|
||||||
|
// int[] xPoints = new int[ size ];
|
||||||
|
// int[] yPoints = new int[ size ];
|
||||||
|
//
|
||||||
|
// HalfEdge edge = edges.iterator().next();
|
||||||
|
// for ( int i = 0; i < edges.size(); ++i ) {
|
||||||
|
// final Point point = edge.getOrigin().getPosition();
|
||||||
|
// xPoints[ i ] = ( int ) ( point.getX() * scale ) + centerX;
|
||||||
|
// yPoints[ i ] = ( int ) ( point.getY() * scale ) + centerY;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
|
||||||
|
// g.fillPolygon( xPoints, yPoints, size );
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setColor( Color.BLACK );
|
||||||
|
for ( final EdgePolygon p : polygons ) {
|
||||||
|
for ( final Vertex v : p.getVertices() ) {
|
||||||
|
final Point point = v.getPosition();
|
||||||
|
|
||||||
|
double diff = scale * .3;
|
||||||
|
g.drawRect( 400 + ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point[] points = {
|
||||||
|
// new Point( 0, -2 ),
|
||||||
|
// new Point( 1.5, -3.0 ),
|
||||||
|
// new Point( 0.0, -2.0 ),
|
||||||
|
// new Point( 0.9545454545454546, -1.3636363636363638 ),
|
||||||
|
// new Point( -0.8333333333333334, -1.0 ),
|
||||||
|
// new Point( 0.8333333333333334, -1.0 ),
|
||||||
|
// new Point( -0.5, 0.0 ),
|
||||||
|
// new Point( 0.5, 0.0 ),
|
||||||
|
// new Point( -0.16666666666666669, 1.0 ),
|
||||||
|
// new Point( 0.16666666666666669, 1.0 ),
|
||||||
|
// new Point( -0.16666666666666669, 1.0 ),
|
||||||
|
// new Point( 1, 1 ),
|
||||||
|
// new Point( -0.16666666666666669, 1.0 ),
|
||||||
|
// new Point( 0, 1.5 ),
|
||||||
|
// new Point( 0, 1.5 ),
|
||||||
|
// new Point( 0.16666666666666669, 1.0 ),
|
||||||
|
// new Point( -1, 2 ),
|
||||||
|
// new Point( 1, 2 ),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// int size = points.length >> 1;
|
||||||
|
// for ( int i = 0; i < size;i++ ) {
|
||||||
|
// Point a = points[ i << 1 ];
|
||||||
|
// Point b = points[ ( i << 1 ) + 1 ];
|
||||||
|
//
|
||||||
|
// g.drawLine( ( int ) ( a.x * scale ) + centerX, ( int ) ( a.y * scale ) + centerY, ( int ) ( b.x * scale ) + centerX, ( int ) ( b.y * scale ) + centerY );
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,6 @@ public class MeshBuilder {
|
|||||||
Optional< Vector > ref = facet.points.stream().filter( v -> v.lengthSquared() > 0.001 && Math.abs( v.clone().normalize().dot( facet.normal ) ) < .999999 ).findAny();
|
Optional< Vector > ref = facet.points.stream().filter( v -> v.lengthSquared() > 0.001 && Math.abs( v.clone().normalize().dot( facet.normal ) ) < .999999 ).findAny();
|
||||||
if ( ref.isPresent() ) {
|
if ( ref.isPresent() ) {
|
||||||
plane.point = ref.get();
|
plane.point = ref.get();
|
||||||
} else {
|
|
||||||
System.out.println( "Normal " + facet.normal );
|
|
||||||
for ( Vector point : facet.points ) {
|
|
||||||
System.out.println( point );
|
|
||||||
}
|
|
||||||
System.exit( 1 );
|
|
||||||
}
|
}
|
||||||
plane.addShape( facet );
|
plane.addShape( facet );
|
||||||
planes.add( plane );
|
planes.add( plane );
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import org.bukkit.util.Vector;
|
|||||||
import org.joml.Matrix3d;
|
import org.joml.Matrix3d;
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Point;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Polygon;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
|
||||||
public class Plane {
|
public class Plane {
|
||||||
public Vector normal;
|
public Vector normal;
|
||||||
@@ -17,30 +17,35 @@ public class Plane {
|
|||||||
public List< Polygon > polygons = new ArrayList< Polygon >();
|
public List< Polygon > polygons = new ArrayList< Polygon >();
|
||||||
|
|
||||||
public void addShape( Facet facet ) {
|
public void addShape( Facet facet ) {
|
||||||
// TODO Flatten the polygons into 2d points
|
Matrix3d mat = getProjectionMatrix();
|
||||||
// Get the basis of this plane
|
|
||||||
Vector basis1 = normal.clone().multiply( point.clone().multiply( -1 ).dot( normal ) ).add( point ).normalize();
|
|
||||||
Vector3d b1 = new Vector3d( basis1.getX(), basis1.getY(), basis1.getZ() );
|
|
||||||
Vector3d b3 = new Vector3d( normal.getX(), normal.getY(), normal.getZ() );
|
|
||||||
Vector3d b2 = new Vector3d( b1 ).cross( b3 ).normalize();
|
|
||||||
Matrix3d mat = new Matrix3d( b1, b2, b3 ).transpose();
|
|
||||||
|
|
||||||
List< Point > polygon = new ArrayList< Point >();
|
List< Point > polygon = new ArrayList< Point >();
|
||||||
for ( Vector p : facet.points ) {
|
for ( Vector p : facet.points ) {
|
||||||
double t = point.clone().subtract( p ).dot( normal );
|
double t = point.clone().subtract( p ).dot( normal );
|
||||||
|
// TODO Subtract point or to not subtract point?!?
|
||||||
Vector i = normal.clone().multiply( t ).add( p ).subtract( point );
|
Vector i = normal.clone().multiply( t ).add( p ).subtract( point );
|
||||||
|
|
||||||
Vector3d v = new Vector3d( i.getX(), i.getY(), i.getZ() );
|
Vector3d v = new Vector3d( i.getX(), i.getY(), i.getZ() );
|
||||||
|
|
||||||
Vector3d r = v.mul( mat );
|
Vector3d r = v.mul( mat );
|
||||||
|
|
||||||
polygon.add( new Point( r.x(), r.y() ) );
|
polygon.add( new Point( r.x(), r.y() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
polygons.add( new Polygon( polygon ) );
|
polygons.add( new Polygon( polygon ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches( Vector normal, Vector point ) {
|
public boolean matches( Vector normal, Vector point ) {
|
||||||
return Math.abs( this.normal.dot( normal ) ) > 0.99 && Math.abs( this.point.clone().subtract( point ).dot( normal ) ) < 0.01;
|
return Math.abs( this.normal.dot( normal ) ) > 0.99 && Math.abs( this.point.clone().subtract( point ).dot( normal ) ) < 0.01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Matrix3d getProjectionMatrix() {
|
||||||
|
final Vector basis1 = normal.clone().multiply( point.clone().multiply( -1 ).dot( normal ) ).add( point ).normalize();
|
||||||
|
final Vector3d b1 = new Vector3d( basis1.getX(), basis1.getY(), basis1.getZ() );
|
||||||
|
final Vector3d b3 = new Vector3d( normal.getX(), normal.getY(), normal.getZ() );
|
||||||
|
final Vector3d b2 = new Vector3d( b1 ).cross( b3 ).normalize();
|
||||||
|
final Matrix3d mat = new Matrix3d( b1, b2, b3 ).transpose();
|
||||||
|
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
|
|
||||||
public class HalfWing {
|
public class HalfWing {
|
||||||
protected HalfWing sym;
|
protected HalfWing sym;
|
||||||
protected HalfWing prev;
|
protected HalfWing prev;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Mesh {
|
|
||||||
List< HalfEdge > edges;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Polygon {
|
|
||||||
List< Point > points;
|
|
||||||
|
|
||||||
public Polygon( List< Point > points ) {
|
|
||||||
this.points = points;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List< Point > getPoints() {
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +1,104 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
|
|
||||||
public class Scratch {
|
public class Scratch {
|
||||||
|
public static class Test {
|
||||||
|
int value;
|
||||||
|
|
||||||
|
Test( int v ) {
|
||||||
|
value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Test(" + value + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main( String[] args ) {
|
public static void main( String[] args ) {
|
||||||
|
Map< Test, String > weakMap = new WeakHashMap< Test, String >();
|
||||||
{
|
{
|
||||||
String t = "Vertex{x=4.25,y=0.5}\r\n" +
|
List< Test > values = new ArrayList< Test >();
|
||||||
"Vertex{x=2.75,y=-2.5}\r\n" +
|
{
|
||||||
"Vertex{x=2.2045454545454546,y=-0.8636363636363638}\r\n" +
|
for ( int i = 0; i < 10000; ++i ) {
|
||||||
"Vertex{x=1.75,y=0.5}\r\n" +
|
Test test = new Test(i);
|
||||||
"Vertex{x=1.25,y=-1.5}\r\n" +
|
weakMap.put( test, test.toString() );
|
||||||
"Vertex{x=1.25,y=2.0}\r\n" +
|
values.add( test );
|
||||||
"Vertex{x=1.0,y=-1.0}\r\n" +
|
}
|
||||||
"Vertex{x=1.0,y=0.0}\r\n" +
|
}
|
||||||
"Vertex{x=1.0,y=0.5}\r\n" +
|
|
||||||
"Vertex{x=1.0,y=1.0}\r\n" +
|
System.out.println( "Size: " + weakMap.size() );
|
||||||
"Vertex{x=1.0,y=1.25}\r\n" +
|
for ( int i = 45; i < 55; ++i ) {
|
||||||
"Vertex{x=1.0,y=3.0}\r\n" +
|
values.set( i, null );
|
||||||
"Vertex{x=0.9166666666666666,y=1.0}\r\n" +
|
}
|
||||||
"Vertex{x=0.75,y=0.5}\r\n" +
|
System.out.println( "Size: " + weakMap.size() );
|
||||||
"Vertex{x=0.7222222222222222,y=0.41666666666666674}\r\n" +
|
for ( int i = 0; i < 100; i++ ) {
|
||||||
"Vertex{x=0.6666666666666667,y=0.5}\r\n" +
|
weakMap.size();
|
||||||
"Vertex{x=0.5833333333333333,y=0.0}\r\n" +
|
weakMap.isEmpty();
|
||||||
"Vertex{x=0.5,y=-1.0}\r\n" +
|
}
|
||||||
"Vertex{x=0.33333333333333337,y=1.0}\r\n" +
|
values = null;
|
||||||
"Vertex{x=0.2954545454545454,y=-0.8636363636363636}\r\n" +
|
try {
|
||||||
"Vertex{x=0.24999999999999994,y=-1.0}\r\n" +
|
Thread.sleep( 5000 );
|
||||||
"Vertex{x=0.0,y=-2.0}\r\n" +
|
} catch ( InterruptedException e ) {
|
||||||
"Vertex{x=0.0,y=1.5}\r\n" +
|
e.printStackTrace();
|
||||||
"Vertex{x=-0.050000000000000024,y=-1.9}\r\n" +
|
|
||||||
"Vertex{x=-0.25,y=-2.5}\r\n" +
|
|
||||||
"Vertex{x=-0.33333333333333337,y=1.0}\r\n" +
|
|
||||||
"Vertex{x=-0.5,y=-1.0}\r\n" +
|
|
||||||
"Vertex{x=-0.6666666666666667,y=0.5}\r\n" +
|
|
||||||
"Vertex{x=-0.9999999999999999,y=-2.220446049250313E-16}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=-1.0}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=0.0}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=0.5}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=1.0}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=2.0}\r\n" +
|
|
||||||
"Vertex{x=-1.0,y=3.0}\r\n" +
|
|
||||||
"Vertex{x=-1.75,y=0.5}\r\n" +
|
|
||||||
"Vertex{x=-2.0,y=-1.0}\r\n" +
|
|
||||||
"Vertex{x=-2.0,y=0.0}\r\n" +
|
|
||||||
"Vertex{x=-2.0,y=1.0}\r\n" +
|
|
||||||
"Vertex{x=-2.0,y=2.0}\r\n" +
|
|
||||||
"Vertex{x=-5.0,y=0.0}\r\n" +
|
|
||||||
"Vertex{x=-5.0,y=1.0}";
|
|
||||||
String[] split = t.split( "\r\n" );
|
|
||||||
for ( int i = split.length - 1; i >= 0; i-- ) {
|
|
||||||
System.out.println( split[ i ] );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println( "Size: " + weakMap.size() );
|
||||||
|
|
||||||
|
// {
|
||||||
|
// String t = "Vertex{x=4.25,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=2.75,y=-2.5}\r\n" +
|
||||||
|
// "Vertex{x=2.2045454545454546,y=-0.8636363636363638}\r\n" +
|
||||||
|
// "Vertex{x=1.75,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=1.25,y=-1.5}\r\n" +
|
||||||
|
// "Vertex{x=1.25,y=2.0}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=0.0}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=1.25}\r\n" +
|
||||||
|
// "Vertex{x=1.0,y=3.0}\r\n" +
|
||||||
|
// "Vertex{x=0.9166666666666666,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=0.75,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=0.7222222222222222,y=0.41666666666666674}\r\n" +
|
||||||
|
// "Vertex{x=0.6666666666666667,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=0.5833333333333333,y=0.0}\r\n" +
|
||||||
|
// "Vertex{x=0.5,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=0.33333333333333337,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=0.2954545454545454,y=-0.8636363636363636}\r\n" +
|
||||||
|
// "Vertex{x=0.24999999999999994,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=0.0,y=-2.0}\r\n" +
|
||||||
|
// "Vertex{x=0.0,y=1.5}\r\n" +
|
||||||
|
// "Vertex{x=-0.050000000000000024,y=-1.9}\r\n" +
|
||||||
|
// "Vertex{x=-0.25,y=-2.5}\r\n" +
|
||||||
|
// "Vertex{x=-0.33333333333333337,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=-0.5,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=-0.6666666666666667,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=-0.9999999999999999,y=-2.220446049250313E-16}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=0.0}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=2.0}\r\n" +
|
||||||
|
// "Vertex{x=-1.0,y=3.0}\r\n" +
|
||||||
|
// "Vertex{x=-1.75,y=0.5}\r\n" +
|
||||||
|
// "Vertex{x=-2.0,y=-1.0}\r\n" +
|
||||||
|
// "Vertex{x=-2.0,y=0.0}\r\n" +
|
||||||
|
// "Vertex{x=-2.0,y=1.0}\r\n" +
|
||||||
|
// "Vertex{x=-2.0,y=2.0}\r\n" +
|
||||||
|
// "Vertex{x=-5.0,y=0.0}\r\n" +
|
||||||
|
// "Vertex{x=-5.0,y=1.0}";
|
||||||
|
// String[] split = t.split( "\r\n" );
|
||||||
|
// for ( int i = split.length - 1; i >= 0; i-- ) {
|
||||||
|
// System.out.println( split[ i ] );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
// {
|
// {
|
||||||
// System.out.println( new Vector2d( 1, 0 ).cross( new Vector2d( 1, 1 ) ) );
|
// System.out.println( new Vector2d( 1, 0 ).cross( new Vector2d( 1, 1 ) ) );
|
||||||
//
|
//
|
||||||
@@ -299,14 +348,14 @@ public class Scratch {
|
|||||||
|
|
||||||
if ( a1.equals( b1 ) ) {
|
if ( a1.equals( b1 ) ) {
|
||||||
return b2.subtracted( b1 ).cross( a2.subtracted( a1 ) ) >= 0;
|
return b2.subtracted( b1 ).cross( a2.subtracted( a1 ) ) >= 0;
|
||||||
} else if ( a1.x < b1.x ) {
|
} else if ( a1.getX() < b1.getX() ) {
|
||||||
System.out.println( "LESS A" );
|
System.out.println( "LESS A" );
|
||||||
return b1.subtracted( a1 ).cross( a2.subtracted( a1 ) ) >= 0;
|
return b1.subtracted( a1 ).cross( a2.subtracted( a1 ) ) >= 0;
|
||||||
} else if ( a1.x > b1.x ) {
|
} else if ( a1.getX() > b1.getX() ) {
|
||||||
System.out.println( "MORAY" );
|
System.out.println( "MORAY" );
|
||||||
return b2.subtracted( b1 ).cross( a1.subtracted( b1 ) ) >= 0;
|
return b2.subtracted( b1 ).cross( a1.subtracted( b1 ) ) >= 0;
|
||||||
} else {
|
} else {
|
||||||
return a1.y > b1.y;
|
return a1.getY() > b1.getY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import java.util.TreeSet;
|
|||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
||||||
@@ -39,12 +42,12 @@ public class Tess4j< T extends Region > {
|
|||||||
private Vector2dComparator comparator = new Vector2dComparator() {
|
private Vector2dComparator comparator = new Vector2dComparator() {
|
||||||
@Override
|
@Override
|
||||||
public double compareX( Vector2d a, Vector2d b ) {
|
public double compareX( Vector2d a, Vector2d b ) {
|
||||||
return a.x - b.x;
|
return a.getX() - b.getX();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double compareY( Vector2d a, Vector2d b ) {
|
public double compareY( Vector2d a, Vector2d b ) {
|
||||||
return a.y - b.y;
|
return a.getY() - b.getY();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ public class Tess4j< T extends Region > {
|
|||||||
// Populate the vertex queue
|
// Populate the vertex queue
|
||||||
// TODO Ensure that the polygon has at least 3 points?
|
// TODO Ensure that the polygon has at least 3 points?
|
||||||
HalfWing wing = null;
|
HalfWing wing = null;
|
||||||
for ( Point point : polygon.points ) {
|
for ( Point point : polygon.getPoints() ) {
|
||||||
if ( wing == null ) {
|
if ( wing == null ) {
|
||||||
// Make a self loop with one edge and one vertex
|
// Make a self loop with one edge and one vertex
|
||||||
wing = new HalfWing();
|
wing = new HalfWing();
|
||||||
@@ -81,7 +84,7 @@ public class Tess4j< T extends Region > {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the origin
|
// Set the origin
|
||||||
wing.getOrigin().setPosition( new Vector2d( point.x, point.y ) );
|
wing.getOrigin().setPosition( new Vector2d( point ) );
|
||||||
|
|
||||||
// TODO Temporary angle check
|
// TODO Temporary angle check
|
||||||
// final double angle = 128;
|
// final double angle = 128;
|
||||||
@@ -100,11 +103,19 @@ public class Tess4j< T extends Region > {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void tessellate() {
|
public void tessellate() {
|
||||||
|
StagePreTess stage = tessStage.get();
|
||||||
|
Queue< Vertex > queue = stage.vertexQueue;
|
||||||
|
System.out.println( "Initial vertex count: " + queue.size() );
|
||||||
|
System.out.println( "Initial vertices: " + ( stage.windingMap.size() / 2 ) );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a heap of vertices and edges, remove any conflicting intersections and form interior regions
|
* Given a heap of vertices and edges, remove any conflicting intersections and form interior regions
|
||||||
*/
|
*/
|
||||||
generateRegions();
|
generateRegions();
|
||||||
|
|
||||||
|
System.out.println( "After vertex count: " + queue.size() );
|
||||||
|
System.out.println( "After vertices: " + ( stage.windingMap.size() / 2 ) );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Merge all adjacent collinear edges
|
* Merge all adjacent collinear edges
|
||||||
*/
|
*/
|
||||||
@@ -1478,27 +1489,14 @@ public class Tess4j< T extends Region > {
|
|||||||
Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); }, new Vector2dComparator() {
|
Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); }, new Vector2dComparator() {
|
||||||
@Override
|
@Override
|
||||||
public double compareX( Vector2d a, Vector2d b ) {
|
public double compareX( Vector2d a, Vector2d b ) {
|
||||||
return a.x - b.x;
|
return a.getX() - b.getX();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double compareY( Vector2d a, Vector2d b ) {
|
public double compareY( Vector2d a, Vector2d b ) {
|
||||||
return a.y - b.y;
|
return a.getY() - b.getY();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
new Point( -1, -1 ),
|
|
||||||
new Point( 1, -1 ),
|
|
||||||
new Point( 1, 1 ),
|
|
||||||
new Point( -1, 1 )
|
|
||||||
) ), new WindingRuleSimple() );
|
|
||||||
tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
new Point( -1, -1 ),
|
|
||||||
new Point( 1, -1 ),
|
|
||||||
new Point( 1, 1 ),
|
|
||||||
new Point( -1, 1 )
|
|
||||||
) ), new WindingRuleSimple() );
|
|
||||||
|
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
// new Point( -1, -1 ),
|
// new Point( -1, -1 ),
|
||||||
// new Point( 1, -1 ),
|
// new Point( 1, -1 ),
|
||||||
@@ -1506,42 +1504,55 @@ public class Tess4j< T extends Region > {
|
|||||||
// new Point( -1, 1 )
|
// new Point( -1, 1 )
|
||||||
// ) ), new WindingRuleSimple() );
|
// ) ), new WindingRuleSimple() );
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
// new Point( -1, 1 ),
|
|
||||||
// new Point( 1, 1 ),
|
|
||||||
// new Point( 1, 3 ),
|
|
||||||
// new Point( -1, 3 )
|
|
||||||
// ) ), new WindingRuleSimple() );
|
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
// new Point( -2, -1 ),
|
|
||||||
// new Point( -1, -1 ),
|
// new Point( -1, -1 ),
|
||||||
// new Point( -1, 2 ),
|
// new Point( 1, -1 ),
|
||||||
// new Point( -2, 2 )
|
// new Point( 1, 1 ),
|
||||||
|
// new Point( -1, 1 )
|
||||||
// ) ), new WindingRuleSimple() );
|
// ) ), new WindingRuleSimple() );
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
// new Point( -4, -2 ),
|
|
||||||
// new Point( -1, -2 ),
|
|
||||||
// new Point( -1, 1.5 ),
|
|
||||||
// new Point( -4, 1.5 )
|
|
||||||
// ) ), new WindingRuleSimple() );
|
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
// new Point( -3, 0 ),
|
|
||||||
// new Point( 3, 0 ),
|
|
||||||
// new Point( -1.5, -3 ),
|
|
||||||
// new Point( 0, 1.5 ),
|
|
||||||
// new Point( 1.5, -3 )
|
|
||||||
// ) ), new WindingRuleSimple() );
|
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
// new Point( -1, 0 ),
|
|
||||||
// new Point( 0, 1.5 ),
|
|
||||||
// new Point( 1, 0 )
|
|
||||||
// ) ), new WindingRuleSimple() );
|
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
|
||||||
// new Point( 1, 0 ),
|
|
||||||
// new Point( 0, -2 ),
|
|
||||||
// new Point( -1, 0 )
|
|
||||||
// ) ), new WindingRuleSimple() );
|
|
||||||
tess4j.tessellate();
|
|
||||||
|
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -1, -1 ),
|
||||||
|
new Point( 1, -1 ),
|
||||||
|
new Point( 1, 1 ),
|
||||||
|
new Point( -1, 1 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -1, 1 ),
|
||||||
|
new Point( 1, 1 ),
|
||||||
|
new Point( 1, 3 ),
|
||||||
|
new Point( -1, 3 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -2, -1 ),
|
||||||
|
new Point( -1, -1 ),
|
||||||
|
new Point( -1, 2 ),
|
||||||
|
new Point( -2, 2 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -4, -2 ),
|
||||||
|
new Point( -1, -2 ),
|
||||||
|
new Point( -1, 1.5 ),
|
||||||
|
new Point( -4, 1.5 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -3, 0 ),
|
||||||
|
new Point( 3, 0 ),
|
||||||
|
new Point( -1.5, -3 ),
|
||||||
|
new Point( 0, 1.5 ),
|
||||||
|
new Point( 1.5, -3 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( -1, 0 ),
|
||||||
|
new Point( 0, 1.5 ),
|
||||||
|
new Point( 1, 0 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
|
new Point( 1, 0 ),
|
||||||
|
new Point( 0, -2 ),
|
||||||
|
new Point( -1, 0 )
|
||||||
|
) ), new WindingRuleSimple() );
|
||||||
|
|
||||||
|
tess4j.tessellate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1763,7 +1774,7 @@ public class Tess4j< T extends Region > {
|
|||||||
|
|
||||||
for ( HalfWing wing : poly ) {
|
for ( HalfWing wing : poly ) {
|
||||||
Vector2d v = wing.getOrigin().getPosition();
|
Vector2d v = wing.getOrigin().getPosition();
|
||||||
points.add( new Point( v.x, v.y ) );
|
points.add( v );
|
||||||
}
|
}
|
||||||
|
|
||||||
filledPolygons.add( new Polygon( points ) );
|
filledPolygons.add( new Polygon( points ) );
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import javax.swing.JFrame;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
|
||||||
@@ -114,14 +117,14 @@ public class Tess4jTest extends JPanel {
|
|||||||
public double compareX( Vector2d a, Vector2d b ) {
|
public double compareX( Vector2d a, Vector2d b ) {
|
||||||
a = rotate( a, 128 );
|
a = rotate( a, 128 );
|
||||||
b = rotate( b, 128 );
|
b = rotate( b, 128 );
|
||||||
return a.x - b.x;
|
return a.getX() - b.getX();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double compareY(Vector2d a, Vector2d b ) {
|
public double compareY(Vector2d a, Vector2d b ) {
|
||||||
a = rotate( a, 128 );
|
a = rotate( a, 128 );
|
||||||
b = rotate( b, 128 );
|
b = rotate( b, 128 );
|
||||||
return b.y - a.y;
|
return b.getY() - a.getY();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
// tess4j.addPolygon( new Polygon( Arrays.asList(
|
||||||
@@ -195,21 +198,21 @@ public class Tess4jTest extends JPanel {
|
|||||||
System.out.println( "Polygon count" );
|
System.out.println( "Polygon count" );
|
||||||
System.out.println( polygons.size() );
|
System.out.println( polygons.size() );
|
||||||
for ( Polygon p : polygons ) {
|
for ( Polygon p : polygons ) {
|
||||||
System.out.println( p.points.size() );
|
System.out.println( p.getPoints().size() );
|
||||||
int size = p.points.size();
|
int size = p.getPoints().size();
|
||||||
int[] xPoints = new int[ size ];
|
int[] xPoints = new int[ size ];
|
||||||
int[] yPoints = new int[ size ];
|
int[] yPoints = new int[ size ];
|
||||||
|
|
||||||
for ( int i = 0; i < p.points.size(); i++ ) {
|
for ( int i = 0; i < p.getPoints().size(); i++ ) {
|
||||||
Point point = p.points.get( i );
|
Point point = p.getPoints().get( i );
|
||||||
|
|
||||||
System.out.println( point.x + ", " + point.y );
|
System.out.println( point.getX() + ", " + point.getY() );
|
||||||
Vector2d pVec = new Vector2d( point.x, point.y );
|
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
|
||||||
pVec = rotate( pVec, 0 );
|
pVec = rotate( pVec, 0 );
|
||||||
point = new Point( pVec.getX(), pVec.getY() );
|
point = new Point( pVec.getX(), pVec.getY() );
|
||||||
|
|
||||||
xPoints[ i ] = ( int ) ( point.x * scale ) + centerX;
|
xPoints[ i ] = ( int ) ( point.getX() * scale ) + centerX;
|
||||||
yPoints[ i ] = 100 - ( int ) ( point.y * scale ) + centerY;
|
yPoints[ i ] = 100 - ( int ) ( point.getY() * scale ) + centerY;
|
||||||
}
|
}
|
||||||
|
|
||||||
g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
|
g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
|
||||||
@@ -218,13 +221,13 @@ public class Tess4jTest extends JPanel {
|
|||||||
|
|
||||||
g.setColor( Color.BLACK );
|
g.setColor( Color.BLACK );
|
||||||
for ( Polygon p : polygons ) {
|
for ( Polygon p : polygons ) {
|
||||||
for ( Point point : p.points ) {
|
for ( Point point : p.getPoints() ) {
|
||||||
Vector2d pVec = new Vector2d( point.x, point.y );
|
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
|
||||||
pVec = rotate( pVec, 0 );
|
pVec = rotate( pVec, 0 );
|
||||||
point = new Point( pVec.getX(), pVec.getY() );
|
point = new Point( pVec.getX(), pVec.getY() );
|
||||||
|
|
||||||
double diff = scale * .05;
|
double diff = scale * .05;
|
||||||
g.drawRect( ( int ) ( point.x * scale ) + centerX - ( int ) diff, 100 - ( int ) ( point.y * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
g.drawRect( ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, 100 - ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -13,6 +12,9 @@ import java.util.WeakHashMap;
|
|||||||
|
|
||||||
import org.joml.Math;
|
import org.joml.Math;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||||
|
|
||||||
public class Tessellator {
|
public class Tessellator {
|
||||||
// TODO Insecure
|
// TODO Insecure
|
||||||
private static VertexOld vertex;
|
private static VertexOld vertex;
|
||||||
@@ -43,7 +45,7 @@ public class Tessellator {
|
|||||||
Set< VertexOld > vertexCollection = Collections.newSetFromMap( new WeakHashMap< VertexOld, Boolean >() );
|
Set< VertexOld > vertexCollection = Collections.newSetFromMap( new WeakHashMap< VertexOld, Boolean >() );
|
||||||
for ( Polygon polygon : shapes ) {
|
for ( Polygon polygon : shapes ) {
|
||||||
HalfEdge edge = null;
|
HalfEdge edge = null;
|
||||||
for ( Point point : polygon.points ) {
|
for ( Point point : polygon.getPoints() ) {
|
||||||
if ( edge == null ) {
|
if ( edge == null ) {
|
||||||
// Make a self loop with one edge and one vertex
|
// Make a self loop with one edge and one vertex
|
||||||
edge = HalfEdge.makeEdge();
|
edge = HalfEdge.makeEdge();
|
||||||
@@ -53,17 +55,17 @@ public class Tessellator {
|
|||||||
edge = edge.nextLeft;
|
edge = edge.nextLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
edge.origin.x = point.x;
|
edge.origin.x = point.getX();
|
||||||
edge.origin.y = point.y;
|
edge.origin.y = point.getY();
|
||||||
|
|
||||||
// For now, assume reverse contours is false
|
// For now, assume reverse contours is false
|
||||||
edge.winding = 1;
|
edge.winding = 1;
|
||||||
edge.sym().winding = -1;
|
edge.sym().winding = -1;
|
||||||
|
|
||||||
minX = Math.min( minX, point.x );
|
minX = Math.min( minX, point.getX() );
|
||||||
minY = Math.min( minY, point.y );
|
minY = Math.min( minY, point.getY() );
|
||||||
maxX = Math.max( maxX, point.x );
|
maxX = Math.max( maxX, point.getX() );
|
||||||
maxY = Math.max( maxY, point.y );
|
maxY = Math.max( maxY, point.getY() );
|
||||||
|
|
||||||
vertexCollection.add( edge.origin );
|
vertexCollection.add( edge.origin );
|
||||||
vertexCollection.add( edge.sym().origin );
|
vertexCollection.add( edge.sym().origin );
|
||||||
@@ -450,7 +452,7 @@ public class Tessellator {
|
|||||||
|
|
||||||
// Compare points
|
// Compare points
|
||||||
if ( !y1.lessThanOrEqualTo( x2 ) ) {
|
if ( !y1.lessThanOrEqualTo( x2 ) ) {
|
||||||
point.x = ( y1.x + x2.x ) / 2;
|
point.setX( ( y1.x + x2.x ) / 2 );
|
||||||
} else if ( x2.lessThanOrEqualTo( y2 ) ) {
|
} else if ( x2.lessThanOrEqualTo( y2 ) ) {
|
||||||
double z1 = y1.verticalDistance( x1, x2 );
|
double z1 = y1.verticalDistance( x1, x2 );
|
||||||
double z2 = x2.verticalDistance( y1, y2 );
|
double z2 = x2.verticalDistance( y1, y2 );
|
||||||
@@ -458,7 +460,7 @@ public class Tessellator {
|
|||||||
z1 *= -1;
|
z1 *= -1;
|
||||||
z2 *= -1;
|
z2 *= -1;
|
||||||
}
|
}
|
||||||
point.x = interpolate( z1, y1.x, z2, x2.x );
|
point.setX( interpolate( z1, y1.x, z2, x2.x ) );
|
||||||
} else {
|
} else {
|
||||||
double z1 = y1.compareTo( x1, x2 );
|
double z1 = y1.compareTo( x1, x2 );
|
||||||
double z2 = - y2.compareTo( x1, x2 );
|
double z2 = - y2.compareTo( x1, x2 );
|
||||||
@@ -466,7 +468,7 @@ public class Tessellator {
|
|||||||
z1 *= -1;
|
z1 *= -1;
|
||||||
z2 *= -1;
|
z2 *= -1;
|
||||||
}
|
}
|
||||||
point.x = interpolate( z1, y1.x, z2, y2.x );
|
point.setX( interpolate( z1, y1.x, z2, y2.x ) );
|
||||||
}
|
}
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import org.joml.Math;
|
import org.joml.Math;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
protected static String toString( Object obj ) {
|
protected static String toString( Object obj ) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
@@ -142,8 +144,8 @@ public class Util {
|
|||||||
if ( qpr == 0 ) {
|
if ( qpr == 0 ) {
|
||||||
// Do not need to calculate the distance between the two parallel lines since it is 0
|
// Do not need to calculate the distance between the two parallel lines since it is 0
|
||||||
final Vector2dOld midpoint = r.multiply( midt ).add( p );
|
final Vector2dOld midpoint = r.multiply( midt ).add( p );
|
||||||
point.x = midpoint.x;
|
point.setX( midpoint.x );
|
||||||
point.y = midpoint.y;
|
point.setY( midpoint.y );
|
||||||
} else {
|
} else {
|
||||||
// Calculate the midpoint along pr
|
// Calculate the midpoint along pr
|
||||||
final Vector2dOld midp = r.multiply( midt ).add( p );
|
final Vector2dOld midp = r.multiply( midt ).add( p );
|
||||||
@@ -152,8 +154,8 @@ public class Util {
|
|||||||
final Vector2dOld z = perp.multiply( qp.dot( perp ) / rr );
|
final Vector2dOld z = perp.multiply( qp.dot( perp ) / rr );
|
||||||
// Sum the components
|
// Sum the components
|
||||||
final Vector2dOld midpoint = midp.add( z.divide( 2.0 ) );
|
final Vector2dOld midpoint = midp.add( z.divide( 2.0 ) );
|
||||||
point.x = midpoint.x;
|
point.setX( midpoint.x );
|
||||||
point.y = midpoint.y;
|
point.setY( midpoint.y );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The lines aren't really anywhere close to each other, so just find the midpoint between the 2 closest points
|
// The lines aren't really anywhere close to each other, so just find the midpoint between the 2 closest points
|
||||||
@@ -162,8 +164,8 @@ public class Util {
|
|||||||
// Same as the first edge but the second edge may be inverted
|
// Same as the first edge but the second edge may be inverted
|
||||||
final Vector2dOld qv = ( inverted ^ t0 > 1 ) ? q : v;
|
final Vector2dOld qv = ( inverted ^ t0 > 1 ) ? q : v;
|
||||||
final Vector2dOld midpoint = pu.add( qv ).divide( 2.0 );
|
final Vector2dOld midpoint = pu.add( qv ).divide( 2.0 );
|
||||||
point.x = midpoint.x;
|
point.setX( midpoint.x );
|
||||||
point.y = midpoint.y;
|
point.setY( midpoint.y );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Calculate t and u
|
// Calculate t and u
|
||||||
@@ -174,8 +176,8 @@ public class Util {
|
|||||||
if ( t >= 0 && t <= 1 && u >= 0 && u <= 1 ) {
|
if ( t >= 0 && t <= 1 && u >= 0 && u <= 1 ) {
|
||||||
// Calculate the point of intersection
|
// Calculate the point of intersection
|
||||||
final Vector2dOld intersection = r.multiply( t ).add( p );
|
final Vector2dOld intersection = r.multiply( t ).add( p );
|
||||||
point.x = intersection.x;
|
point.setX( intersection.x );
|
||||||
point.y = intersection.y;
|
point.setY( intersection.y );
|
||||||
} else {
|
} else {
|
||||||
// No intersection, so calculate the closest point between the two segments
|
// No intersection, so calculate the closest point between the two segments
|
||||||
// We have t and u, which we can use to find the closest endpoints
|
// We have t and u, which we can use to find the closest endpoints
|
||||||
@@ -185,8 +187,8 @@ public class Util {
|
|||||||
final Vector2dOld nearP = r.multiply( nearT ).add( p );
|
final Vector2dOld nearP = r.multiply( nearT ).add( p );
|
||||||
final Vector2dOld nearQ = s.multiply( nearU ).add( q );
|
final Vector2dOld nearQ = s.multiply( nearU ).add( q );
|
||||||
|
|
||||||
point.x = ( nearP.x + nearQ.x ) / 2.0;
|
point.setX( ( nearP.x + nearQ.x ) / 2.0 );
|
||||||
point.y = ( nearP.y + nearQ.y ) / 2.0;
|
point.setY( ( nearP.y + nearQ.y ) / 2.0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return point;
|
return point;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
|||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
|
|
||||||
public abstract class Vector2dComparator implements Comparator< Vector2d > {
|
public abstract class Vector2dComparator implements Comparator< Vector2d > {
|
||||||
public abstract double compareX( Vector2d a, Vector2d b );
|
public abstract double compareX( Vector2d a, Vector2d b );
|
||||||
public abstract double compareY( Vector2d a, Vector2d b );
|
public abstract double compareY( Vector2d a, Vector2d b );
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||||
|
|
||||||
public class Vector2dOld {
|
public class Vector2dOld {
|
||||||
final double x;
|
final double x;
|
||||||
final double y;
|
final double y;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
|
||||||
|
|
||||||
public class Vertex {
|
public class Vertex {
|
||||||
private HalfWing wing;
|
private HalfWing wing;
|
||||||
private Vector2d position;
|
private Vector2d position;
|
||||||
|
|||||||
Reference in New Issue
Block a user