Remove outdated and obsolete files

This commit is contained in:
2025-05-24 00:10:05 -04:00
parent e9b8bea944
commit 1599e1feb1
21 changed files with 0 additions and 4787 deletions

View File

@@ -1,418 +0,0 @@
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.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.bukkit.util.Vector;
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.mesh.Facet;
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Tess4j;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.RegionSimple.GluWindingRule;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
public class MeshingTest 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 = 400; // 600
private int centerY = 1000; // 400
private Graphics g;
private int scale = 10;
private List< Polygon > data;
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() ) {
List< Polygon > polys = mesh( file );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
new MeshingTest( polys );
}
} );
break;
}
} else {
System.err.println( "No such directory exists: " + CHUNK_DIR );
}
}
private static List< Polygon > 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 );
}
}
System.out.println( "Planes: " + builder.planes.size() );
for ( Plane plane : builder.planes ) {
if ( plane.polygons.size() > 100 ) {
draw = plane;
break;
}
}
for ( Plane plane : builder.planes ) {
try {
Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
for ( Polygon poly : plane.polygons ) {
tess4j.addPolygon( poly, new WindingRuleSimple() );
}
tess4j.tessellate();
} catch ( IllegalStateException e ) {
e.printStackTrace();
draw = plane;
// Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
// tess4j.setDebug( true );
// System.out.println( "Polygon count: " + plane.polygons.size() );
// for ( Polygon poly : plane.polygons ) {
// tess4j.addPolygon( poly, new WindingRuleSimple() );
// }
// tess4j.tessellate();
}
}
if ( draw != null ) {
System.out.println( "Norm:\t" + draw.normal );
System.out.println( "Ref:\t" + draw.point );
System.out.println( "Size:\t" + draw.polygons.size() );
Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
tess4j.setDebug( true );
for ( int i = 0; i < draw.polygons.size(); i++ ) {
if ( i >= 4 ) {
break;
}
Polygon poly = draw.polygons.get( i );
System.out.println( "Adding polygon " + poly.getPoints().size() );
for ( Point point : poly.getPoints() ) {
System.out.println( point.getX() + ", " + point.getY() );
}
tess4j.addPolygon( poly, new WindingRuleSimple() );
}
long start = System.currentTimeMillis();
tess4j.tessellate();
long end = System.currentTimeMillis();
System.out.println( "Took " + ( end - start ) + "ms" );
return tess4j.getPolygons();
} else {
System.out.println( "No data!" );
}
return new ArrayList< Polygon >();
}
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( p7 );
facet.points.add( p8 );
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 MeshingTest( List< Polygon > polys) {
data = polys;
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;
// Vertex v = null;
// for ( int i = 0; i < 100; i++ ) {
// double deg = Math.random() * 360;
// double dist = Math.random() * 100 + 100;
// double x = dist * Math.cos( Math.toRadians( deg ) );
// double y = dist * Math.sin( Math.toRadians( deg ) );
// HalfWing edge = new HalfWing();
// edge.getDest().setPosition( new Vector2d( x, y ) );
// if ( v == null ) {
// v = edge.getOrigin();
// } else {
// HalfWing.splice( edge, v.getWing() );
// }
// }
// v.update();
//
// HalfWing edge = v.getWing();
// int i = 0;
// double ic = 0;
// do {
// Vector2d dest = edge.getDest().getPosition();
// g.drawLine( centerX, centerY, ( int ) dest.getX() + centerX, ( int ) dest.getY() + centerY );
// dest = dest.normalized();
// g.drawString( i++ + "", ( int ) ( dest.x * 200 * ( ic + 1.1 ) ) + centerX, ( int ) ( dest.y * 200 * ( ic + 1.1 ) ) + centerY );
// ic = ( ic + 0.07 ) % .7;
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
//
// v.sort();
//
// g.drawLine( centerX + 700, centerY, centerX + 700, centerY - 400 );
// edge = v.getWing();
// i = 0;
// ic = 0;
// do {
// Vector2d dest = edge.getDest().getPosition();
// g.drawLine( centerX + 700, centerY, ( int ) dest.getX() + centerX + 700, ( int ) dest.y + centerY );
// dest = dest.normalized();
// g.drawString( i++ + "", ( int ) ( dest.x * 200 * ( ic + 1.1 ) ) + centerX + 700, ( int ) ( dest.y * 200 * ( ic + 1.1 ) ) + centerY );
// ic = ( ic + 0.07 ) % .7;
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
List< Polygon > polygons = data;
System.out.println( "Polygon count: " + polygons.size() );
System.out.println( polygons.size() );
for ( Polygon p : polygons ) {
List< Point > points = p.getPoints();
// System.out.println( points.size() );
int size = points.size();
int[] xPoints = new int[ size ];
int[] yPoints = new int[ size ];
for ( int i = 0; i < points.size(); i++ ) {
Point point = points.get( i );
// System.out.println( point.getX() + ", " + point.getY() );
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
pVec = rotate( pVec, 0 );
point = new Point( pVec.getX(), pVec.getY() );
xPoints[ i ] = ( int ) ( point.getX() * scale ) + centerX;
yPoints[ i ] = 100 - ( int ) ( point.getY() * scale ) + centerY;
}
g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
g.fillPolygon( xPoints, yPoints, size );
}
g.setColor( Color.BLACK );
for ( Polygon p : polygons ) {
for ( Point point : p.getPoints() ) {
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
pVec = rotate( pVec, 0 );
point = new Point( pVec.getX(), pVec.getY() );
double diff = scale * .05;
g.drawRect( ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, 100 - ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
}
}
g.drawLine( centerX, 0, centerX, 2000 );
g.drawLine( 0, centerY + 100, 2000, centerY + 100 );
// 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 );
// }
}
}

View File

@@ -1,143 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
/*
* A primitive sorted linked list of edges
* - O(n) insert, search
* - O(1) upper, lower, remove
*/
public class EdgeCollection< T > implements Iterable< T > {
Map< T, Node > quickMap = new ConcurrentHashMap< T, Node >();
Node head = new Node( null );
BiFunction< T, T, Boolean > isGreaterThan;
public EdgeCollection( BiFunction< T, T, Boolean > compare ) {
isGreaterThan = compare;
}
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;
}
// Not necessary, for now
// @Override
// public void remove() {
// T val = current.value;
// current = current.lower;
// EdgeCollection.this.remove( val );
// }
};
}
}

View File

@@ -1,128 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
/**
* This edge dictionary is a loosely sorted collection of regions that are organized by the _current_ event vertex,
* and its relationship with the upper edge of each region. Regions are sorted on insertion, and stored in a doubly
* linked list thereafter. Searching for a region entails moving backwards through the list starting at the tail
* until a region which matches the sorting method is found.
*
* There are no checks in place to prevent undefined behavior of any kind.
*
* Regions are sorted in ascending order.
*
* Searching for a region is done from head to tail, until a region that is greater than or equal has been found.
*/
public class EdgeDict {
Map< RegionOld, Node > quickMap = new HashMap< RegionOld, Node >();
/*
* The comparator function is responsible for determining whether a region is less than or equal to another
*
* Given o1 and o2, return true if o1 is less than or equal to o2
*/
BiFunction< RegionOld, RegionOld, Boolean > comparator;
Node head;
public EdgeDict( Supplier< VertexOld > supplier ) {
head = new Node( null );
head.after = head;
head.before = head;
comparator = ( o1, o2 ) -> {
VertexOld vert = supplier.get();
HalfEdge e1 = o1.upperEdge;
HalfEdge e2 = o2.upperEdge;
// Check if the left vertex is the current event
if ( e1.sym().origin == vert ) {
if ( e2.sym().origin == vert ) {
// Compare right vertices
// Contrived, why not check slope instead?
// Edge case is if slope is infinity
if ( e1.origin.lessThanOrEqualTo( e2.origin ) ) {
// Check if the right vertex is below the edge
return e1.origin.compareTo( e2.sym().origin, e2.origin ) <= 0;
}
return e2.origin.compareTo( e1.sym().origin, e1.origin ) >= 0;
}
return vert.compareTo( e2.sym().origin, e2.origin ) <= 0;
}
if ( e2.sym().origin == vert ) {
return vert.compareTo( e1.sym().origin, e1.origin ) >= 0;
}
// Measure the distance and see if one is shorter
double d1 = vert.verticalDistance( e1.sym().origin, e1.origin );
double d2 = vert.verticalDistance( e2.sym().origin, e2.origin );
return d1 >= d2;
};
}
public RegionOld above( RegionOld region ) {
return quickMap.get( region ).after.region;
}
public RegionOld below( RegionOld region ) {
return quickMap.get( region ).before.region;
}
public void insert( RegionOld region ) {
// Insert from the back
insertBefore( null, region );
}
public void remove( RegionOld region ) {
Node node = quickMap.remove( region );
if ( node != null ) {
node.before.after = node.after.before;
}
region.upperEdge.region = null;
}
public void insertBefore( RegionOld startRegion, RegionOld region ) {
Node node = new Node( region );
quickMap.put( region, node );
// Start at the tail if null
Node temp = startRegion == null ? head : quickMap.get( startRegion );
// Look for a region that is less than or equal to region, and insert after
do {
temp = temp.before;
} while ( temp.region != null && !comparator.apply( temp.region, region ) );
node.after = temp.after;
node.after.before = node;
temp.after = node;
node.before = temp;
}
// Similar to above
public RegionOld search( RegionOld region ) {
Node temp = head;
// Find the first region that is greater than or equal to region
do {
temp = temp.after;
} while ( temp.region != null && !comparator.apply( region, temp.region ) );
return temp.region;
}
protected class Node {
Node before;
Node after;
RegionOld region;
Node( RegionOld region ) {
this.region = region;
}
}
}

View File

@@ -1,19 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
public class Face {
// An edge that is attached to this face
HalfEdge edge;
boolean inside;
public Face( HalfEdge edge ) {
this.edge = edge;
}
public void assign() {
HalfEdge temp = edge;
do {
temp.face = this;
temp = temp.nextLeft;
} while ( temp != edge );
}
}

View File

@@ -1,249 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
public class HalfEdge {
// The half edge opposite of this one
HalfEdge opposite;
// The vertex that this half edge extends from
VertexOld origin;
// The face to the left of this edge
Face face;
// The next edge CCW around the origin
// Keeps the same origin
HalfEdge nextOrigin;
// The next edge CCW around the left face
// The destination becomes the origin
HalfEdge nextLeft;
RegionOld region;
int winding;
private HalfEdge() {
origin = new VertexOld( this );
nextOrigin = this;
}
// The opposite, or symmetrical half edge to this one
public HalfEdge sym() {
return opposite;
}
public HalfEdge prevOrigin() {
return sym().nextLeft;
}
public HalfEdge prevR() {
return sym().nextOrigin;
}
public HalfEdge nextD() {
return prevR().sym();
}
// Positive means left to right, aka the destination > origin
public boolean isPositiveEdge() {
return origin.lessThanOrEqualTo( opposite.origin );
}
@Override
public String toString() {
return "HalfEdge{origin=" + origin + ",opposite=" + opposite.origin + ",nextOrigin=" + nextOrigin.origin + ",nextLeft=" + nextLeft.origin + "}";
}
public static HalfEdge makeEdge() {
// TODO Half edges are always made in pairs, should really simplify this and make it cleaner
HalfEdge main = new HalfEdge();
HalfEdge opposite = new HalfEdge();
main.opposite = opposite;
main.nextLeft = opposite;
opposite.opposite = main;
opposite.nextLeft = main;
Face face = new Face( main );
main.face = face;
opposite.face = face;
return main;
}
public static void swapLinks( HalfEdge e1, HalfEdge e2 ) {
HalfEdge e1Next = e1.nextOrigin;
HalfEdge e2Next = e2.nextOrigin;
e1Next.sym().nextLeft = e2;
e2Next.sym().nextLeft = e1;
e1.nextOrigin = e2Next;
e2.nextOrigin = e1Next;
}
/**
* The basic operation for changing the mesh connectivity and topology. It changes the mesh so that
* original.nextOrigin = OLD( destination.nextOrigin )
* destination.nextOrigin = OLD( original.nextOrigin )
* where OLD( ... ) means the value before the splice operation
*
* This can have two effects on the vertex structure:
* if original.face == destination.face, then the face is split into two
* if original.face != destination.face, then the two faces are combined into one
* In both cases, destination.face is changed, but original.face is not.
*/
public static void splice( HalfEdge original, HalfEdge destination ) {
boolean mergeVertices = false;
boolean mergeFaces = false;
// Merging vertices
if ( original.origin != destination.origin ) {
mergeVertices = true;
HalfEdge prev = destination.origin.edge;
HalfEdge dest = prev;
do {
dest.origin = original.origin;
dest = dest.nextOrigin;
} while ( dest != prev );
}
// Merging faces
if ( original.face != destination.face ) {
mergeFaces = true;
HalfEdge prev = destination.face.edge;
HalfEdge dest = prev;
do {
dest.face = original.face;
dest = dest.nextLeft;
} while ( dest != prev );
}
swapLinks( original, destination );
if ( !mergeVertices ) {
// Splitting a vertex into two, so create
// a new vertex with destination as the edge,
// update all edges in the destination loop,
// and set the old vertex edge to this
// TODO Vertices always need a half edge, constructor?
new VertexOld( destination ).assign();
original.origin.edge = original;
}
if ( !mergeFaces ) {
// Splitting a face/loop into two
// Similar to splitting a vertex
new Face( destination ).assign();;
original.face.edge = original;
}
}
/**
* Delete the edge. There are several cases:
* if edge.leftFace != edge.rightFace, then two faces/loops are joined into one
* else, one loop is being split into two, and the new loop will contain the opposite vertex
* This function could be implemented as two calls to splice, but it would be less efficient
*/
public static void delete( HalfEdge edge ) {
boolean mergeFaces = false;
HalfEdge sym = edge.sym();
if ( edge.face != sym.face ) {
mergeFaces = true;
HalfEdge original = edge.face.edge;
HalfEdge dest = original;
do {
dest.face = sym.face;
dest = dest.nextLeft;
} while ( dest != original );
}
if ( edge.nextOrigin == edge ) {
HalfEdge original = edge.origin.edge;
HalfEdge dest = original;
do {
dest.origin = null;
dest = dest.nextOrigin;
} while ( dest != original );
} else {
sym.face.edge = edge.prevOrigin();
edge.origin.edge = edge.nextOrigin;
splice( edge, edge.prevOrigin() );
if ( !mergeFaces ) {
// Creating a new face
new Face( edge ).assign();
}
}
if ( sym.nextOrigin == sym ) {
sym.face.assign();
sym.origin.assign();
} else {
edge.face.edge = sym.prevOrigin();
sym.origin.edge = sym.nextOrigin;
splice( sym, sym.prevOrigin() );
}
}
public static HalfEdge addEdgeVertex( HalfEdge orig ) {
HalfEdge edge = makeEdge();
HalfEdge sym = edge.sym();
swapLinks( edge, orig.nextLeft );
edge.origin = orig.sym().origin;
VertexOld vert = new VertexOld( sym );
HalfEdge dest = sym;
do {
dest.origin = vert;
dest = dest.nextOrigin;
} while ( dest != sym );
sym.face = orig.face;
edge.face = orig.face;
return edge;
}
public static HalfEdge splitEdge( HalfEdge edge ) {
HalfEdge tempEdge = addEdgeVertex( edge );
HalfEdge sym = tempEdge.sym();
swapLinks( edge.sym(), edge.sym().prevOrigin() );
swapLinks( edge.sym(), sym );
edge.sym().origin = sym.origin;
sym.sym().origin.edge = sym.sym();
sym.sym().face = edge.sym().face;
return sym;
}
public static HalfEdge connect( HalfEdge e1, HalfEdge e2 ) {
boolean mergeFaces = false;
HalfEdge edge = makeEdge();
HalfEdge sym = edge.sym();
if ( e1.face != e2.face ) {
mergeFaces = true;
e2.face.edge = e1;
e2.face.assign();
}
swapLinks( edge, e1.nextLeft );
swapLinks( sym, e2 );
edge.origin = e1.sym().origin;
sym.origin = e2.origin;
edge.face = e1.face;
sym.face = e1.face;
e1.face.edge = sym;
if ( !mergeFaces ) {
new Face( edge ).assign();
}
return edge;
}
}

View File

@@ -1,107 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
public class HalfWing {
protected HalfWing sym;
protected HalfWing prev;
protected HalfWing next;
protected Vertex origin;
public HalfWing() {
init();
new HalfWing( this );
}
private HalfWing( HalfWing 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;
}
public HalfWing setOrigin( Vertex vert ) {
origin = vert;
return this;
}
public HalfWing getSym() {
return sym;
}
public HalfWing getPrev() {
return prev;
}
public HalfWing setPrev( HalfWing wing ) {
this.prev = wing;
return this;
}
public HalfWing getNext() {
return next;
}
public HalfWing setNext( HalfWing wing ) {
this.next = wing;
return this;
}
public Vertex getDest() {
return sym.origin;
}
public Vector2d toVector2d() {
return getDest().getPosition().subtracted( getOrigin().getPosition() );
}
public boolean isZero() {
return getOrigin().equals( getDest() );
}
// Split this edge in half, and return the new edge
public HalfWing split() {
HalfWing wing = new HalfWing();
splice( wing, getNext() );
splice( getSym(), getNext() );
splice( getSym(), wing.getSym() );
wing.setOrigin( getDest() );
wing.getOrigin().setWing( wing );
getSym().setOrigin( wing.getDest() );
getDest().setWing( getSym() );
wing.getOrigin().update();
wing.getDest().update();
return wing.getSym();
}
public static void splice( HalfWing a, HalfWing b ) {
HalfWing ap = a.prev;
HalfWing bp = b.prev;
a.prev = bp;
b.prev = ap;
ap.sym.next = b;
bp.sym.next = a;
}
}

View File

@@ -1,7 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
public abstract class Region {
// Represents the area under a wing
public abstract boolean isInterior();
public abstract boolean equals( Object other );
}

View File

@@ -1,18 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
public class RegionOld {
HalfEdge upperEdge;
int windingNumber = 0;
boolean inside = false;
boolean fixUpperEdge = false;
boolean sentinel = false;
boolean dirty = false;
public void fixUpperEdge( HalfEdge edge ) {
HalfEdge.delete( upperEdge );
fixUpperEdge = false;
upperEdge = edge;
edge.region = this;
}
}

View File

@@ -1,416 +0,0 @@
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 static class Test {
int value;
Test( int v ) {
value = v;
}
@Override
public String toString() {
return "Test(" + value + ")";
}
}
public static void main( String[] args ) {
Map< Test, String > weakMap = new WeakHashMap< Test, String >();
{
List< Test > values = new ArrayList< Test >();
{
for ( int i = 0; i < 10000; ++i ) {
Test test = new Test(i);
weakMap.put( test, test.toString() );
values.add( test );
}
}
System.out.println( "Size: " + weakMap.size() );
for ( int i = 45; i < 55; ++i ) {
values.set( i, null );
}
System.out.println( "Size: " + weakMap.size() );
for ( int i = 0; i < 100; i++ ) {
weakMap.size();
weakMap.isEmpty();
}
values = null;
try {
Thread.sleep( 5000 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
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 ) ) );
//
// HalfWing toInsert = new HalfWing();
// HalfWing first = new HalfWing();
//
// toInsert.getOrigin().setPosition( new Vector2d( -.95454545454, -1.36363636 ) );
// toInsert.getDest().setPosition( new Vector2d( 0, -2 ) );
// first.getOrigin().setPosition( new Vector2d( -1.5, -3 ) );
// first.getDest().setPosition( new Vector2d( 0, -2 ) );
//
// toInsert.getOrigin().setPosition( new Vector2d( 0, 0 ) );
// toInsert.getDest().setPosition( new Vector2d( 2, 0 ) );
// first.getOrigin().setPosition( new Vector2d( 1, 1 ) );
// first.getDest().setPosition( new Vector2d( 2, 3 ) );
//
// System.out.println( greaterThanOrEqualTo( toInsert, first ) );
// }
// {
// Point[] points = {
// new Point( 1, -1 ),
// new Point( 1, 1 ),
// new Point( -1, 1 ),
// new Point( 0, 1 ),
// new Point( 1, 1 ),
// new Point( 0, -1 )
// };
//
// Vertex v = null;
// for ( Point point : points ) {
// HalfWing edge = new HalfWing();
// edge.getDest().setPosition( new Vector2d( point.x, point.y ) );
// if ( v == null ) {
// v = edge.getOrigin();
// } else {
// HalfWing.splice( edge, v.getWing() );
// }
// }
// v.update();
//
// System.out.println( "Before sort" );
// HalfWing edge = v.getWing();
// do {
// System.out.println( edge.getDest() );
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
//
// v.sort();
//
// System.out.println( "After sort" );
// edge = v.getWing();
// do {
// System.out.println( edge.getDest() );
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
// }
// {
// HalfWing a = new HalfWing();
// HalfWing b = new HalfWing();
//
// b.getSym().setOrigin( a.getDest() );
//
// a.getOrigin().setPosition( new Vector2d( -1, 1 ) );
// a.getSym().getOrigin().setPosition( new Vector2d( 1, 1 ) );
// b.getOrigin().setPosition( new Vector2d( 1, 0 ) );
//
// System.out.println( greaterThanOrEqualTo( b, a ) );
// }
// {
// Vertex v = null;
// for ( int i = 0; i < 100; i++ ) {
// double deg = Math.random() * 360;
// double dist = Math.random() * 100 + 100;
// double x = dist * Math.cos( Math.toRadians( deg ) );
// double y = dist * Math.sin( Math.toRadians( deg ) );
// HalfWing edge = new HalfWing();
// edge.getDest().setPosition( new Vector2d( x, y ) );
// if ( v == null ) {
// v = edge.getOrigin();
// } else {
// HalfWing.splice( edge, v.getWing() );
// }
// }
// v.update();
//
// System.out.println( "Before sort" );
// HalfWing edge = v.getWing();
// do {
// System.out.println( edge.getDest() );
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
//
// v.sort();
//
// System.out.println( "After sort" );
// edge = v.getWing();
// do {
// System.out.println( edge.getDest() );
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
// }
// {
// Vector2d a1 = new Vector2d( 0, 0 );
// Vector2d a2 = new Vector2d( 0, 5 );
// Vector2d b1 = new Vector2d( 0, 0 );
// Vector2d b2 = new Vector2d( 0, 5 );
// System.out.println( isGreaterThan( a1, a2, b1, b2 ) );
// }
// HalfEdge upper = HalfEdge.makeEdge();
// HalfEdge lower = HalfEdge.makeEdge();
//
// upper.sym().origin = lower.sym().origin;
// upper.sym().origin.x = -1;
// upper.sym().origin.y = -1;
//
// upper.origin.x = 1;
// upper.origin.y = 1;
// lower.origin.x = 2;
// lower.origin.y = 2;
//
// System.out.println( "Right spliced? " + checkForRightSplice( upper, lower ) );
// System.out.println( upper );
// System.out.println( lower );
// System.out.println( upper.prevOrigin() );
// {
// Vertex v1 = new Vertex( null );
// v1.x = 0;
// v1.y = 0;
// Vertex v2 = new Vertex( null );
// v2.x = 5;
// v2.y = 0;
// Vertex v3 = new Vertex( null );
// v3.x = 1;
// v3.y = 1;
// Vertex v4 = new Vertex( null );
// v4.x = 6;
// v4.y = -4;
//
// System.out.println( "Colinear and overlapping" );
// System.out.println( Tessellator.intersection( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).x );
// }
// {
// Vertex v1 = new Vertex( null );
// v1.x = 0;
// v1.y = 0;
// Vertex v2 = new Vertex( null );
// v2.x = 5;
// v2.y = 0;
// Vertex v3 = new Vertex( null );
// v3.x = 6;
// v3.y = 0;
// Vertex v4 = new Vertex( null );
// v4.x = 10;
// v4.y = 0;
//
// System.out.println( "Colinear but not overlapping" );
// System.out.println( Tessellator.intersection( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).x );
// }
// {
// Vertex v1 = new Vertex( null );
// v1.x = 0;
// v1.y = 3;
// Vertex v2 = new Vertex( null );
// v2.x = 5;
// v2.y = 3;
// Vertex v3 = new Vertex( null );
// v3.x = -6;
// v3.y = 6;
// Vertex v4 = new Vertex( null );
// v4.x = 2.5;
// v4.y = 6;
//
// System.out.println( "Parallel and overlapping but not colinear" );
// System.out.println( Tessellator.intersection( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).y );
// }
// {
// Vertex v1 = new Vertex( null );
// v1.x = 0;
// v1.y = 3;
// Vertex v2 = new Vertex( null );
// v2.x = 5;
// v2.y = 3;
// Vertex v3 = new Vertex( null );
// v3.x = -100;
// v3.y = 6;
// Vertex v4 = new Vertex( null );
// v4.x = -1;
// v4.y = 6;
//
// System.out.println( "Parallel but not overlapping nor colinear" );
// System.out.println( Tessellator.intersection( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).x );
// System.out.println( Util.closestPoint( v1, v2, v3, v4 ).y );
// }
// {
// HalfEdge root = HalfEdge.makeEdge();
// root.origin.y = -1;
// HalfEdge next = HalfEdge.makeEdge();
// next.sym().origin.x = 1;
// HalfEdge.swapLinks( root.sym(), next );
// HalfEdge newEdge = HalfEdge.addEdgeVertex( root );
// newEdge.sym().origin.x = -1;
// System.out.println( root );
// System.out.println( root.nextLeft );
// System.out.println( root.nextLeft.sym() );
// System.out.println( root.nextLeft.nextLeft.nextLeft.sym() );
// }
//
// System.out.println( "---" );
// {
// HalfEdge root = HalfEdge.makeEdge();
// root.origin.y = -1;
// HalfEdge next = HalfEdge.makeEdge();
// next.sym().origin.x = 1;
// HalfEdge.swapLinks( root.sym(), next );
// HalfEdge newEdge = HalfEdge.splitEdge( root );
// newEdge.origin.x = -1;
// System.out.println( root );
// System.out.println( root.nextLeft );
// System.out.println( root.nextLeft.nextLeft );
// System.out.println( root.nextLeft.nextLeft.nextLeft );
// }
}
private static boolean greaterThanOrEqualTo( HalfWing a, HalfWing b ) {
// Assume each region is marked by an upper edge going from right to left, up to down
if ( a.isZero() || b.isZero() ) {
throw new IllegalStateException( "Zero length edge detected" );
}
// if ( !( a.isPositive() && b.isPositive() ) ) {
// throw new IllegalStateException( "Negative edge detected" );
// }
// a1 < a2
Vector2d a1 = a.getOrigin().getPosition();
Vector2d a2 = a.getDest().getPosition();
// b1 < b2
Vector2d b1 = b.getOrigin().getPosition();
Vector2d b2 = b.getDest().getPosition();
if ( a1.equals( b1 ) ) {
return b2.subtracted( b1 ).cross( a2.subtracted( a1 ) ) >= 0;
} else if ( a1.getX() < b1.getX() ) {
System.out.println( "LESS A" );
return b1.subtracted( a1 ).cross( a2.subtracted( a1 ) ) >= 0;
} else if ( a1.getX() > b1.getX() ) {
System.out.println( "MORAY" );
return b2.subtracted( b1 ).cross( a1.subtracted( b1 ) ) >= 0;
} else {
return a1.getY() > b1.getY();
}
}
public static boolean isGreaterThan( Vector2dOld a1, Vector2dOld a2, Vector2dOld b1, Vector2dOld b2 ) {
// Invariants:
// a1 < a2
// b1 < b2
// a.length > 0
// b.length > 0
// a and b do not intersect
// a and b do not overlap
if ( a1.equals( b1 ) ) {
return b2.subtract( b1 ).cross( a2.subtract( a1 ) ) >= 0;
} else if ( a1.x < b1.x ) {
return b1.subtract( a1 ).cross( a2.subtract( a1 ) ) >= 0;
} else if ( a1.x > b1.x ) {
return a1.subtract( b1 ).cross( b2.subtract( b1 ) ) >= 0;
} else {
return a1.y > b1.y;
}
}
public static boolean checkForRightSplice( HalfEdge upEdge, HalfEdge downEdge) {
// Check if the right vertex of the upper edge is to the left or below the lower edge's right vertex
if ( upEdge.origin.lessThanOrEqualTo( downEdge.origin ) ) {
// Check if the right vertex is above the lower edge
if ( upEdge.origin.compareTo( downEdge.sym().origin, downEdge.origin ) > 0 ) {
// If so, no splicing is necessary
return false;
}
// If the right vertex of the upper edge is not equal to the right vertex of the lower edge
if ( !upEdge.origin.equals( downEdge.origin ) ) {
// Split the lower edge in two, with the new edge on the right
HalfEdge.splitEdge( downEdge.sym() );
// Move the right vertex of the lower edge to the
HalfEdge.splice( upEdge, downEdge.prevOrigin() );
} else if ( upEdge.origin != downEdge.origin ) {
// Merge the two vertices if they are not already the same
HalfEdge.splice( downEdge.prevOrigin(), upEdge );
}
} else {
// Check if the lower edge's right vertex is on or below the upper edge
if ( downEdge.origin.compareTo( upEdge.sym().origin, upEdge.origin ) <= 0 ) {
return false;
}
// Split the upper edge
HalfEdge.splitEdge( upEdge.sym() );
// Move the upper edge to the lower edge's vertex
HalfEdge.splice( downEdge.prevOrigin(), upEdge );
}
return true;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,266 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.GluWindingRule;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.region.WindingRuleSimple;
public class Tess4jTest extends JPanel {
JFrame f;
private int windowWidth = 1200;
private int windowHeight = 1000;
private int centerX = 600;
private int centerY = 400;
private Graphics g;
private int scale = 100;
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
new Tess4jTest();
}
} );
}
public Tess4jTest() {
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;
// Vertex v = null;
// for ( int i = 0; i < 100; i++ ) {
// double deg = Math.random() * 360;
// double dist = Math.random() * 100 + 100;
// double x = dist * Math.cos( Math.toRadians( deg ) );
// double y = dist * Math.sin( Math.toRadians( deg ) );
// HalfWing edge = new HalfWing();
// edge.getDest().setPosition( new Vector2d( x, y ) );
// if ( v == null ) {
// v = edge.getOrigin();
// } else {
// HalfWing.splice( edge, v.getWing() );
// }
// }
// v.update();
//
// HalfWing edge = v.getWing();
// int i = 0;
// double ic = 0;
// do {
// Vector2d dest = edge.getDest().getPosition();
// g.drawLine( centerX, centerY, ( int ) dest.getX() + centerX, ( int ) dest.getY() + centerY );
// dest = dest.normalized();
// g.drawString( i++ + "", ( int ) ( dest.x * 200 * ( ic + 1.1 ) ) + centerX, ( int ) ( dest.y * 200 * ( ic + 1.1 ) ) + centerY );
// ic = ( ic + 0.07 ) % .7;
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
//
// v.sort();
//
// g.drawLine( centerX + 700, centerY, centerX + 700, centerY - 400 );
// edge = v.getWing();
// i = 0;
// ic = 0;
// do {
// Vector2d dest = edge.getDest().getPosition();
// g.drawLine( centerX + 700, centerY, ( int ) dest.getX() + centerX + 700, ( int ) dest.y + centerY );
// dest = dest.normalized();
// g.drawString( i++ + "", ( int ) ( dest.x * 200 * ( ic + 1.1 ) ) + centerX + 700, ( int ) ( dest.y * 200 * ( ic + 1.1 ) ) + centerY );
// ic = ( ic + 0.07 ) % .7;
// edge = edge.getPrev();
// } while ( edge != v.getWing() );
// Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
Tess4j< RegionSimple > tess4j = new Tess4j< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); }, new Vector2dComparator() {
@Override
public double compareX( Vector2d a, Vector2d b ) {
a = rotate( a, 128 );
b = rotate( b, 128 );
return a.getX() - b.getX();
}
@Override
public double compareY(Vector2d a, Vector2d b ) {
a = rotate( a, 128 );
b = rotate( b, 128 );
return b.getY() - a.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, 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( -5, 0 ),
// new Point( -2, 0 ),
// new Point( -2, 1 ),
// new Point( -5, 1 )
// ) ), new WindingRuleSimple() );
// tess4j.addPolygon( new Polygon( Arrays.asList(
// new Point( -1.75, .5 ),
// new Point( 4.25, .5 ),
// new Point( -0.25, -2.5 ),
// new Point( 1.25, 2 ),
// new Point( 2.75, -2.5 )
// ) ), 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( -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() );
for ( int i = 0; i < 4; i++ ) {
for ( int j = 0; j < 4; j++ ) {
tess4j.addPolygon( new Polygon( Arrays.asList(
new Point( i, j ),
new Point( i, j + 1 ),
new Point( i + 1, j + 1 ),
new Point( i + 1, j )
) ), new WindingRuleSimple() );
// tess4j.addPolygon( new Polygon( Arrays.asList(
// new Point( i, j ),
// new Point( i, j + 1 ),
// new Point( i + 1, j ),
// new Point( i + 1, j + 1 )
// ) ), new WindingRuleSimple() );
}
}
tess4j.tessellate();
List< Polygon > polygons = tess4j.getPolygons();
System.out.println( "Polygon count" );
System.out.println( polygons.size() );
for ( Polygon p : polygons ) {
System.out.println( p.getPoints().size() );
int size = p.getPoints().size();
int[] xPoints = new int[ size ];
int[] yPoints = new int[ size ];
for ( int i = 0; i < p.getPoints().size(); i++ ) {
Point point = p.getPoints().get( i );
System.out.println( point.getX() + ", " + point.getY() );
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
pVec = rotate( pVec, 0 );
point = new Point( pVec.getX(), pVec.getY() );
xPoints[ i ] = ( int ) ( point.getX() * scale ) + centerX;
yPoints[ i ] = 100 - ( int ) ( point.getY() * scale ) + centerY;
}
g.setColor( new Color( ( int ) ( Math.random() * 0xFFFFFF ) ) );
g.fillPolygon( xPoints, yPoints, size );
}
g.setColor( Color.BLACK );
for ( Polygon p : polygons ) {
for ( Point point : p.getPoints() ) {
Vector2d pVec = new Vector2d( point.getX(), point.getY() );
pVec = rotate( pVec, 0 );
point = new Point( pVec.getX(), pVec.getY() );
double diff = scale * .05;
g.drawRect( ( int ) ( point.getX() * scale ) + centerX - ( int ) diff, 100 - ( int ) ( point.getY() * scale ) + centerY - ( int ) diff, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
}
}
g.drawLine( centerX, 0, centerX, 2000 );
g.drawLine( 0, centerY + 100, 2000, centerY + 100 );
// 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 );
// }
}
}

View File

@@ -1,508 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import java.util.WeakHashMap;
import org.joml.Math;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
public class Tessellator {
// TODO Insecure
private static VertexOld vertex;
private static EdgeDict dict;
private static Queue< VertexOld > eventQueue;
public static void main( String[] args ) {
List< Polygon > polygons = new ArrayList< Polygon >();
polygons.add( new Polygon( Arrays.asList(
new Point( -1, -1 ),
new Point( 1, -1 ),
new Point( 1, 1 ),
new Point( -1, 1 )
) ) );
tessellate( polygons );
}
public static void tessellate( Collection< Polygon > shapes ) {
eventQueue = new PriorityQueue< VertexOld >();
dict = new EdgeDict( () -> { return vertex; } );
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double maxY = Double.MIN_VALUE;
// Get all unique vertices that are in use
Set< VertexOld > vertexCollection = Collections.newSetFromMap( new WeakHashMap< VertexOld, Boolean >() );
for ( Polygon polygon : shapes ) {
HalfEdge edge = null;
for ( Point point : polygon.getPoints() ) {
if ( edge == null ) {
// Make a self loop with one edge and one vertex
edge = HalfEdge.makeEdge();
HalfEdge.splice( edge, edge.sym() );
} else {
HalfEdge.splitEdge( edge );
edge = edge.nextLeft;
}
edge.origin.x = point.getX();
edge.origin.y = point.getY();
// For now, assume reverse contours is false
edge.winding = 1;
edge.sym().winding = -1;
minX = Math.min( minX, point.getX() );
minY = Math.min( minY, point.getY() );
maxX = Math.max( maxX, point.getX() );
maxY = Math.max( maxY, point.getY() );
vertexCollection.add( edge.origin );
vertexCollection.add( edge.sym().origin );
}
}
eventQueue.addAll( vertexCollection );
// Add sentinel regions so that all polygons are between two edges/regions
addSentinel( minX, minY, maxX, maxY );
// Sweep over each vertex, from left to right, bottom to top
VertexOld current;
while ( !eventQueue.isEmpty() ) {
current = eventQueue.poll();
while ( true ) {
VertexOld next = eventQueue.peek();
// Get all vertices that overlap and merge them together
if ( next == null || !current.equals( next ) ) {
break;
}
next = eventQueue.poll();
HalfEdge.splice( current.edge, next.edge );
}
System.out.println( "SCANNING " + current );
sweepEvent( current );
}
}
public static void addSentinel( double minX, double minY, double maxX, double maxY ) {
double width = ( maxX - minX ) + 0.01;
double height = ( maxY - minY ) + 0.01;
double lowerX = minX - width;
double lowerY = minY - height;
double upperX = maxX + width;
double upperY = maxY + height;
addSentinelRegion( lowerX, upperX, lowerY );
addSentinelRegion( lowerX, upperX, upperY );
}
private static void addSentinelRegion( double startX, double endX, double y ) {
HalfEdge edge = HalfEdge.makeEdge();
edge.origin.x = endX;
edge.origin.y = y;
edge.sym().origin.x = startX;
edge.sym().origin.y = y;
vertex = edge.sym().origin;
RegionOld region = new RegionOld();
region.upperEdge = edge;
region.inside = false;
region.sentinel = true;
dict.insert( region );
}
public static void sweepEvent( VertexOld vert ) {
vertex = vert;
// Find the region that this vertex belongs to
HalfEdge edge = vert.edge;
while ( edge.region == null ) {
edge = edge.nextOrigin;
if ( edge == vert.edge ) {
// This is a new vertex which is not associated with any region
// Connect it to something
connectLeftVertex( vert );
return;
}
}
}
public static void connectLeftVertex( VertexOld vert ) {
RegionOld tmpRegion = new RegionOld();
tmpRegion.upperEdge = vert.edge.sym();
// Find the regions immediately above and below this vertex
RegionOld up = dict.search( tmpRegion );
RegionOld lower = dict.below( up );
if ( lower == null ) {
// Coplanar polygon
return;
}
// Get the edges associated with both regions
HalfEdge upperEdge = up.upperEdge;
HalfEdge lowerEdge = lower.upperEdge;
// If the vertex lies on the upper edge... ???
if ( vert.compareTo( upperEdge.sym().origin, upperEdge.origin ) == 0 ) {
connectLeftDegenerate( up, vert );
return;
}
// Get the higher region
RegionOld region = lowerEdge.sym().origin.lessThanOrEqualTo( upperEdge.sym().origin ) ? up : lower;
if ( region.inside || region.fixUpperEdge ) {
HalfEdge edge;
if ( region == up ) {
edge = HalfEdge.connect( vert.edge.sym(), upperEdge.nextLeft );
} else {
edge = HalfEdge.connect( lowerEdge.nextD(), vert.edge ).sym();
}
if ( region.fixUpperEdge ) {
region.fixUpperEdge( edge );
} else {
computeWinding( addRegionBelow( up, edge ) );
}
} else {
addRightEdges( up, vertex.edge, vertex.edge, null, true );
}
}
public static void addRightEdges( RegionOld region, HalfEdge first, HalfEdge last, HalfEdge topLeft, boolean clean ) {
// Add all edges from first to last
// The last edge to be inserted is the edge CCW of last
System.out.println( "Region is " + region.upperEdge );
HalfEdge edge = first;
do {
// Create regions for all edges starting from first, before last, in CCW fashion
System.out.println( "Inserting region for edge " + edge );
// The region defining edge is always going to be right to left
addRegionBelow( region, edge.sym() );
edge = edge.nextOrigin;
} while ( edge != last );
// Get the most CCW edge that is to the right of the sweep line
if ( topLeft == null ) {
topLeft = dict.below( region ).upperEdge.prevR();
System.out.println( "Top left region is " + topLeft );
}
RegionOld previousRegion = region;
HalfEdge previousEdge = topLeft;
// Update winding and fix any problems with ordering
boolean firstTime = true;
while ( true ) {
// Iterate over each region from top to bottom
RegionOld tempRegion = dict.below( previousRegion );
// Get the left to right, bottom to top edge
HalfEdge tempEdge = tempRegion.upperEdge.sym();
System.out.println( "Looping for edge " + tempEdge );
// Break if the vertex changes
if ( tempEdge.origin != previousEdge.origin ) {
break;
}
// Check if the previous edge is equal to the previous edge
// Otherwise there's a discrepancy and it needs to be relinked accordingly
if ( tempEdge.nextOrigin != previousEdge ) {
// Unlink the edge from its current position, and relink below the previous edge
HalfEdge.splice( tempEdge.nextOrigin, tempEdge );
HalfEdge.splice( previousEdge.prevOrigin(), tempEdge );
}
// Calculate the winding number and if it is inside
tempRegion.windingNumber = previousRegion.windingNumber - tempEdge.winding;
tempRegion.inside = tempRegion.windingNumber % 2 == 1;
previousRegion.dirty = true;
if ( !firstTime && checkForRightSplice( previousRegion ) ) {
tempEdge.winding += previousEdge.winding;
HalfEdge.delete( previousEdge );
}
firstTime = false;
previousRegion = tempRegion;
previousEdge = tempEdge;
}
if ( clean ) {
walkDirtyRegions( previousRegion );
}
}
public static boolean checkForLeftSplice( RegionOld region ) {
// Check the upper and lower edge of the region for any inconsistencies
// Simiar to checkForRightSplice, but the left vertices
RegionOld below = dict.below( region );
HalfEdge upEdge = region.upperEdge;
HalfEdge downEdge = below.upperEdge;
if ( upEdge.sym().origin.lessThanOrEqualTo( downEdge.sym().origin ) ) {
if ( downEdge.sym().origin.compareTo( upEdge.sym().origin, upEdge.origin ) < 0 ) {
return false;
}
// The lower edge is above the upper edge, so splice
dict.above( region ).dirty = true;
region.dirty = true;
HalfEdge edge = HalfEdge.splitEdge( upEdge );
HalfEdge.splice( downEdge.sym(), edge );
edge.face.inside = region.inside;
} else {
if ( upEdge.sym().origin.compareTo( downEdge.sym().origin, downEdge.origin ) > 0 ) {
return false;
}
region.dirty = true;
below.dirty = true;
HalfEdge edge = HalfEdge.splitEdge( downEdge );
HalfEdge.splice( upEdge.nextLeft, downEdge.sym() );
edge.sym().face.inside = region.inside;
}
return true;
}
public static boolean checkForRightSplice( RegionOld region ) {
RegionOld below = dict.below( region );
HalfEdge upEdge = region.upperEdge;
HalfEdge downEdge = below.upperEdge;
// Check if the right vertex of the upper edge is to the left or below the lower edge's right vertex
if ( upEdge.origin.lessThanOrEqualTo( downEdge.origin ) ) {
// Check if the right vertex is above the lower edge
if ( upEdge.origin.compareTo( downEdge.sym().origin, downEdge.origin ) > 0 ) {
// If so, no splicing is necessary
return false;
}
// If the right vertex of the upper edge is not equal to the right vertex of the lower edge
if ( !upEdge.origin.equals( downEdge.origin ) ) {
// Split the lower edge in two, with the new edge on the right
HalfEdge.splitEdge( downEdge.sym() );
// Move the right vertex of the lower edge to the
HalfEdge.splice( upEdge, downEdge.prevOrigin() );
region.dirty = true;
below.dirty = true;
} else if ( upEdge.origin != downEdge.origin ) {
// Merge the two vertices if they are not already the same
eventQueue.remove( upEdge.origin );
HalfEdge.splice( downEdge.prevOrigin(), upEdge );
}
} else {
// Check if the lower edge's right vertex is on or below the upper edge
if ( downEdge.origin.compareTo( upEdge.sym().origin, upEdge.origin ) <= 0 ) {
return false;
}
dict.above( region ).dirty = true;
region.dirty = true;
// Split the upper edge
HalfEdge.splitEdge( upEdge.sym() );
// Move the upper edge to the lower edge's vertex
HalfEdge.splice( downEdge.prevOrigin(), upEdge );
}
return true;
}
public static void walkDirtyRegions( RegionOld region ) {
RegionOld lower = dict.below( region );
while ( true ) {
// Find the lowest dirty region
while ( lower.dirty ) {
region = lower;
lower = dict.below( lower );
}
if ( !region.dirty ) {
lower = region;
region = dict.above( region );
if ( region == null || !region.dirty ) {
// No dirty regions left
return;
}
}
region.dirty = false;
HalfEdge upperEdge = region.upperEdge;
HalfEdge lowerEdge = lower.upperEdge;
// If the start of the region edge is not the same as the start of the lower region edge
if ( upperEdge.sym().origin != lowerEdge.sym().origin ) {
// Consistency check
if ( checkForLeftSplice( region ) ) {
if ( lower.fixUpperEdge ) {
dict.remove( lower );
HalfEdge.delete( lowerEdge );
lower = dict.below( region );
lowerEdge = lower.upperEdge;
} else if ( region.fixUpperEdge ) {
dict.remove( region );
HalfEdge.delete( upperEdge );
region = dict.above( lower );
upperEdge = region.upperEdge;
}
}
}
// If the end of the region edge is not the same as the end of the lower region edge
if ( upperEdge.origin != lowerEdge.origin ) {
if ( upperEdge.sym().origin != lowerEdge.sym().origin
&& !region.fixUpperEdge
&& !lower.fixUpperEdge
&& ( upperEdge.sym().origin == vertex || lowerEdge.sym().origin == vertex ) ) {
if ( checkForIntersect( region ) ) {
return;
}
} else {
checkForRightSplice( region );
}
}
// Matching edges
if ( upperEdge.origin == lowerEdge.origin && upperEdge.sym().origin == lowerEdge.sym().origin ) {
// Degenerate loop
lowerEdge.winding += upperEdge.winding;
dict.remove( region );
HalfEdge.delete( upperEdge );
region = dict.above( lower );
}
}
}
public static boolean checkForIntersect( RegionOld upper ) {
// Check the upper and lower edges of this region to see if they intersect
RegionOld lower = dict.below( upper );
HalfEdge upperEdge = upper.upperEdge;
HalfEdge lowerEdge = lower.upperEdge;
VertexOld upLeft = upperEdge.sym().origin;
VertexOld upRight = upperEdge.origin;
VertexOld downLeft = lowerEdge.sym().origin;
VertexOld downRight = lowerEdge.origin;
// Endpoints are the same, so no intersection
if ( upRight == downRight ) {
return false;
}
double upY = Math.min( upRight.y, upLeft.y );
double downY = Math.max( downRight.y, downLeft.y );
// The edges do not overlap vertically
if ( upY > downY ) {
return false;
}
// Check for any vertical intersection of the right point
if ( upRight.lessThanOrEqualTo( downRight ) ) {
if ( upRight.compareTo( downLeft, downRight ) > 0 ) {
return false;
}
} else {
if ( downRight.compareTo( upLeft, upRight ) < 0 ) {
return false;
}
}
// TODO Compute intersection
return false;
}
public static Point intersection( VertexOld x1, VertexOld x2, VertexOld y1, VertexOld y2 ) {
Point point = new Point( 0, 0 );
if ( !x1.lessThanOrEqualTo( x2 ) ) {
VertexOld temp = x1;
x1 = x2;
x2 = temp;
}
if ( !y1.lessThanOrEqualTo( y2 ) ) {
VertexOld temp = y1;
y1 = y2;
y2 = temp;
}
if ( !x1.lessThanOrEqualTo( y1 ) ) {
VertexOld temp = x1;
x1 = y1;
y1 = temp;
VertexOld temp2 = x2;
x2 = y2;
y2 = temp2;
}
// Compare points
if ( !y1.lessThanOrEqualTo( x2 ) ) {
point.setX( ( y1.x + x2.x ) / 2 );
} else if ( x2.lessThanOrEqualTo( y2 ) ) {
double z1 = y1.verticalDistance( x1, x2 );
double z2 = x2.verticalDistance( y1, y2 );
if ( z1 + z1 < 0 ) {
z1 *= -1;
z2 *= -1;
}
point.setX( interpolate( z1, y1.x, z2, x2.x ) );
} else {
double z1 = y1.compareTo( x1, x2 );
double z2 = - y2.compareTo( x1, x2 );
if ( z1 + z2 < 0 ) {
z1 *= -1;
z2 *= -1;
}
point.setX( interpolate( z1, y1.x, z2, y2.x ) );
}
return point;
}
public static double interpolate( double a, double x, double b, double y ) {
a = Math.max( a, 0 );
b = Math.max( b, 0 );
if ( a > b ) {
return y - ( ( x - y ) * ( b / ( a + b ) ) );
}
if ( b != 0 ) {
return x + ( ( y - x ) * ( a / ( a + b ) ) );
}
return ( x + y ) / 2;
}
public static void computeWinding( RegionOld region ) {
region.windingNumber = dict.above( region ).windingNumber + region.upperEdge.winding;
// TODO Using odd for now
region.inside = region.windingNumber % 2 == 1;
}
public static void connectLeftDegenerate( RegionOld region, VertexOld vert ) {
throw new IllegalArgumentException( "connectLeftDegenerate is unimplemented!" );
}
public static RegionOld addRegionBelow( RegionOld above, HalfEdge edge ) {
RegionOld region = new RegionOld();
region.upperEdge = edge;
dict.insertBefore( above, region );
region.fixUpperEdge = false;
region.sentinel = false;
edge.region = region;
return region;
}
}

View File

@@ -1,196 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.joml.Math;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
public class Util {
protected static String toString( Object obj ) {
StringBuilder builder = new StringBuilder();
builder.append( obj.getClass().getSimpleName() );
builder.append( '{' );
List< Field > fields = new ArrayList< Field >();
fields.addAll( Arrays.asList( obj.getClass().getDeclaredFields() ) );
fields.addAll( Arrays.asList( obj.getClass().getFields() ) );
for ( Iterator< Field > it = fields.iterator(); it.hasNext(); builder.append( "," ) ) {
Field field = it.next();
builder.append( field.getName() );
builder.append( '=' );
try {
builder.append( field.get( obj ) );
} catch ( IllegalArgumentException | IllegalAccessException e ) {
builder.append( "?" );
}
}
builder.append( '}' );
return builder.toString();
}
public static Optional< Point > intersection( final VertexOld x1, final VertexOld x2, final VertexOld x3, final VertexOld x4 ) {
Point point = null;
final Vector2dOld p = x1.toVector();
final Vector2dOld r = x2.subtract( x1 );
final Vector2dOld q = x3.toVector();
final Vector2dOld s = x4.subtract( x3 );
final Vector2dOld qp = q.subtract( p );
final double qpr = qp.cross( r );
final double rs = r.cross( s );
// Are the two lines parallel
if ( rs == 0 ) {
final Vector2dOld u = x2.toVector();
final Vector2dOld v = x4.toVector();
// For normalizing against pr
final double rr = r.dot( r );
// t0 and t1 represent the distance of the second edge endpoints relative to the first edge normalized by r
// t0 = ( ( q - p ) . r ) / rr;
double t0 = qp.dot( r ) / rr;
// t1 = ( ( q + s - p ) . r ) / rr
double t1 = v.subtract( p ).dot( r ) / rr;
// If the vectors are pointing in the opposite directions, then swap t0 and t1
final boolean inverted = s.dot( r ) < 0;
if ( inverted ) {
double temp = t0;
t0 = t1;
t1 = temp;
}
if ( qpr == 0 ) {
if ( t0 < 1 & t1 > 0 ) {
final double midt = Math.max( t0, 0 ) + Math.min( t1, 1 ) / 2.0;
final Vector2dOld midpoint = r.multiply( midt ).add( p );
point = midpoint.toPoint();
} else {
final Vector2dOld pu = t0 > 1 ? u : p;
// Same as the first edge but the second edge may be inverted
final Vector2dOld qv = ( inverted ^ t0 > 1 ) ? q : v;
final Vector2dOld midpoint = pu.add( qv ).divide( 2.0 );
point = midpoint.toPoint();
}
}
} else {
// Calculate t and u
final double t = qp.cross( s ) / rs;
final double u = qpr / rs;
// Do the line segments intersect
if ( t >= 0 && t <= 1 && u >= 0 && u <= 1 ) {
// Calculate the point of intersection
final Vector2dOld intersection = r.multiply( t ).add( p );
point = intersection.toPoint();
}
}
return Optional.ofNullable( point );
}
// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
// Vector based closest intersection method
public static Point closestPoint( final VertexOld x1, final VertexOld x2, final VertexOld x3, final VertexOld x4 ) {
Point point = new Point( 0, 0 );
final Vector2dOld p = x1.toVector();
final Vector2dOld r = x2.subtract( x1 );
final Vector2dOld q = x3.toVector();
final Vector2dOld s = x4.subtract( x3 );
final Vector2dOld qp = q.subtract( p );
final double qpr = qp.cross( r );
final double rs = r.cross( s );
// Are the two lines parallel
if ( rs == 0 ) {
final Vector2dOld u = x2.toVector();
final Vector2dOld v = x4.toVector();
// For normalizing against pr
final double rr = r.dot( r );
// t0 and t1 represent the distance of the second edge endpoints relative to the first edge normalized by r
// t0 = ( ( q - p ) . r ) / rr;
double t0 = qp.dot( r ) / rr;
// t1 = ( ( q + s - p ) . r ) / rr
double t1 = v.subtract( p ).dot( r ) / rr;
// If the vectors are pointing in the opposite directions, then swap t0 and t1
final boolean inverted = s.dot( r ) < 0;
if ( inverted ) {
double temp = t0;
t0 = t1;
t1 = temp;
}
// Do the lines overlap
if ( t0 < 1 && t1 > 0 ) {
// The midpoint is an arbitrary point that just happens to look good as an intersection
// Find the midpoint between the overlapping region
final double midt = Math.max( t0, 0 ) + Math.min( t1, 1 ) / 2.0;
// If colinear
if ( qpr == 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 );
point.setX( midpoint.x );
point.setY( midpoint.y );
} else {
// Calculate the midpoint along pr
final Vector2dOld midp = r.multiply( midt ).add( p );
// Calculate the perpendicular distance between both lines
final Vector2dOld perp = r.perpendicular();
final Vector2dOld z = perp.multiply( qp.dot( perp ) / rr );
// Sum the components
final Vector2dOld midpoint = midp.add( z.divide( 2.0 ) );
point.setX( midpoint.x );
point.setY( midpoint.y );
}
} else {
// The lines aren't really anywhere close to each other, so just find the midpoint between the 2 closest points
// If t0 > 1, then the second edge must lie to the right of the first edge
final Vector2dOld pu = t0 > 1 ? u : p;
// Same as the first edge but the second edge may be inverted
final Vector2dOld qv = ( inverted ^ t0 > 1 ) ? q : v;
final Vector2dOld midpoint = pu.add( qv ).divide( 2.0 );
point.setX( midpoint.x );
point.setY( midpoint.y );
}
} else {
// Calculate t and u
final double t = qp.cross( s ) / rs;
final double u = qpr / rs;
// Do the line segments intersect
if ( t >= 0 && t <= 1 && u >= 0 && u <= 1 ) {
// Calculate the point of intersection
final Vector2dOld intersection = r.multiply( t ).add( p );
point.setX( intersection.x );
point.setY( intersection.y );
} else {
// 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
final double nearT = Math.max( Math.min( t, 1 ), 0 );
final double nearU = Math.max( Math.min( u, 1 ), 0 );
final Vector2dOld nearP = r.multiply( nearT ).add( p );
final Vector2dOld nearQ = s.multiply( nearU ).add( q );
point.setX( ( nearP.x + nearQ.x ) / 2.0 );
point.setY( ( nearP.y + nearQ.y ) / 2.0 );
}
}
return point;
}
}

View File

@@ -1,19 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import java.util.Comparator;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
public abstract class Vector2dComparator implements Comparator< Vector2d > {
public abstract double compareX( Vector2d a, Vector2d b );
public abstract double compareY( Vector2d a, Vector2d b );
// Sorts by x, then y
public final int compare( Vector2d a, Vector2d b ) {
double x = compareX( a, b );
if ( x == 0 ) {
return Double.compare( compareY( a, b ), 0 );
}
return Double.compare( x, 0 );
}
}

View File

@@ -1,86 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
public class Vector2dOld {
final double x;
final double y;
Vector2dOld( double x, double y ) {
this.x = x;
this.y = y;
}
public double cross( Vector2dOld other ) {
return ( x * other.y ) - ( y * other.x );
}
public double dot( Vector2dOld other ) {
return x * other.x + y * other.y;
}
public Vector2dOld subtract( Vector2dOld other ) {
return new Vector2dOld( x - other.x , y - other.y );
}
public Vector2dOld add( Vector2dOld other ) {
return new Vector2dOld( x + other.x, y + other.y );
}
public Vector2dOld perpendicular() {
return new Vector2dOld( -y, x );
}
public Vector2dOld multiply( double v ) {
return new Vector2dOld( x * v, y * v );
}
public Vector2dOld divide( double v ) {
return new Vector2dOld( x / v, y / v );
}
public Vector2dOld normalize() {
return divide( Math.sqrt( dot( this ) ) );
}
public Point toPoint() {
return new Point( x, y );
}
public double length() {
return Math.sqrt( dot( this ) );
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(x);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vector2dOld other = (Vector2dOld) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
return true;
}
@Override
public String toString() {
return "Vector2d{x=" + x + ",y=" + y + "}";
}
}

View File

@@ -1,68 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
public class Vertex {
private HalfWing wing;
private Vector2d position;
public Vertex( HalfWing wing ) {
setWing( wing );
setPosition( new Vector2d() );
}
public HalfWing getWing() {
return wing;
}
public Vertex setWing( HalfWing wing ) {
this.wing = wing;
return this;
}
public Vector2d getPosition() {
return position;
}
public Vertex setPosition( Vector2d vec ) {
this.position = vec;
return this;
}
public void update() {
HalfWing wing = this.wing;
do {
wing.setOrigin( this );
} while ( ( wing = wing.getPrev() ) != this.wing );
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((position == null) ? 0 : position.hashCode());
return result;
}
@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() + "}";
}
}

View File

@@ -1,104 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
public class VertexOld implements Comparable< VertexOld > {
double x;
double y;
// An edge that is attached to this vertex
HalfEdge edge;
public VertexOld( HalfEdge edge ) {
this.edge = edge;
}
public void assign() {
HalfEdge temp = edge;
do {
temp.origin = this;
temp = temp.nextOrigin;
} while ( temp != edge );
}
public boolean isEqualTo( VertexOld o ) {
return x == o.x && y == o.y;
}
public boolean lessThanOrEqualTo( VertexOld o ) {
return ( x < o.x ) || ( x == o.x && y <= o.y );
}
public boolean lessThanOrEqualTo2( VertexOld o ) {
return ( y < o.y ) || ( y == o.y && x <= o.x );
}
public double compareTo( VertexOld v1, VertexOld v2 ) {
double x1 = x - v1.x;
double x2 = v2.x - x;
if ( x1 + x2 > 0 ) {
return ( y - v2.y ) * x1 + ( y - v1.y ) * x2;
}
return 0;
}
public Vector2dOld subtract( VertexOld other ) {
return new Vector2dOld( x - other.x, y - other.y );
}
public Vector2dOld toVector() {
return new Vector2dOld( x, y );
}
/*
* Get the vertical distance from this point to the line segment represented by v1 and v2
*
* This is assuming the following is true:
* - v1 <= this <= v2
* If v1v2 is vertical (and thus passes through this), the result is 0
*/
public double verticalDistance( VertexOld v1, VertexOld v2 ) {
// x1 >= 0
double x1 = x - v1.x;
// x2 >= 0
double x2 = v2.x - x;
// Check if it is not vertical
if ( x1 + x2 > 0 ) {
// Find the slope
// Use smaller x value for better double precision, I suppose
if ( x1 < x2 ) {
return ( y - v1.y ) + ( ( v1.y - v2.y ) * ( x1 / ( x1 + x2 ) ) );
} else {
return ( y - v2.y ) + ( ( v2.y - v1.y ) * ( x2 / ( x1 + x2 ) ) );
}
}
return 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
VertexOld other = (VertexOld) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
return true;
}
@Override
public String toString() {
return "Vertex{x=" + x + ",y=" + y + "}";
}
@Override
public int compareTo( final VertexOld o ) {
final int sortX = Double.compare( x, o.x );
return sortX == 0 ? Double.compare( y, o.y ) : sortX;
}
}

View File

@@ -1,23 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j;
/**
* A winding rule describes how a region is modified, particularly when crossing a border
* between a known region to an unknown region. The inverse winding rule must provide a
* symmetric change such that regions can be converted consistently. Normally, the same
* winding 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.
*
* @author BananaPuncher714
*
* @param <T> A region type that can be modified by this rule
*/
public interface WindingRule< T extends Region > {
// Apply this rule to the region, and return a new region
T apply( T region );
// Get the inverse of this rule
WindingRule< T > inverse();
}

View File

@@ -1,82 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j.region;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.Region;
public class RegionSimple extends Region {
private int windingNumber;
private 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 "Region: " + windingNumber + ", " + rule;
}
public int getWindingNumber() {
return windingNumber;
}
public RegionSimple setWindingNumber( int number ) {
this.windingNumber = number;
return this;
}
public int increment() {
return ++windingNumber;
}
public int decrement() {
return --windingNumber;
}
public enum GluWindingRule {
ODD,
NONZERO,
POSITIVE,
NEGATIVE,
ABS_GEQ_TWO
}
}

View File

@@ -1,36 +0,0 @@
package com.aaaaahhhhhhh.bananapuncher714.tess4j.region;
import com.aaaaahhhhhhh.bananapuncher714.tess4j.WindingRule;
public class WindingRuleSimple implements WindingRule< RegionSimple > {
private final boolean inverse;
private WindingRuleSimple opposite;
public WindingRuleSimple() {
this( false );
}
public WindingRuleSimple( boolean inverse ) {
this.inverse = inverse;
}
@Override
public RegionSimple apply( RegionSimple region ) {
RegionSimple copy = new RegionSimple( region );
if ( inverse ) {
copy.decrement();
} else {
copy.increment();
}
return copy;
}
@Override
public WindingRuleSimple inverse() {
if ( opposite == null ) {
opposite = new WindingRuleSimple( !inverse );
opposite.opposite = this;
}
return opposite;
}
}

View File

@@ -1,41 +0,0 @@
The task of triangulation can be completed in logarithmic time, based on the amount of regions and how many nodes each region contains. However, it is not
uncommon for regions to contain many segments that lie on the same line. When this is the case, often times the region can be split into multiple smaller
regions. The benefit of doing so is that there are less triangles that are generated, and each region can be triangulated in parallel, for a bonus speed
increase. The downside is that splitting a region in the most optimal method can be quite costly. It would take polynomial time based on the number of
nodes in order to find the combination of regions that would decrease triangle count the most. However, it can be argued that for certain region shapes
which may occur frequently such as the topology of connected Minecraft blocks, it may be worth the investment if time is less of an issue, and memory
usage matters more.
Ideal time: O(n) or O(nlog(n))
For each point:
- Find all intersecting edges
- Easy check, see which side both ends of each segment are on
- If different sides, then it intersects with the line
- Find all other segments on the same line
- Easy check, find all lines with the same slope and intersection point or something
- Need to check if segment can be connected to
- This means checking all intersecting edges
- Also need to check for individual points
- A segment and a point results in 1 less triangle
- Two segments results in 2 less triangles
- Find all intersections with potential segments
- For each potential segment, check for intersection with each other
- Count how many it intersects with
- Greedy method... Add lines with least amount of intersections first?
- According to the rule of mutual crosses, the total amount of intersections is always even
The overall algorithm can be broken down into 3 parts:
- Finding all possible segments between 3 or more points
- Removing all connections that intersect with the polygon edges
- Determining how many other possible segments intersect with each other
The first two are both O(n^2) time, which is unfortunate. It is unknown if it could
be made into O(logn) or better.
The last part is O(logn)
Obviously, one can determine which points are visible from one point by iterating
over every edge and building a depth field. Then, all potential connecting segments
can be found by checking each visible point.
Alternatively, one can check each point to see if it lies on the same segment?
A logical structure to hold a segment would be the two endpoints, and any points that are
contained by the...