Remove outdated and obsolete files
This commit is contained in:
@@ -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 );
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 );
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 );
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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 );
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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() + "}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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...
|
|
||||||
Reference in New Issue
Block a user