Fix inconsistencies

Use linked hash set for reproducibility when required
Split chain generation into three smaller methods
Fixed error when certain chains skip certain vertices, specifically after splitting a partition
Hide internal classes
Add conversion to external classes when returning data
Update the polygon viewer
Update chain intersections in parallel
Sort edges cleaner
Resolve some TODOs
This commit is contained in:
2025-05-23 00:35:39 -04:00
parent e5f98f6844
commit 29a0c04fae
5 changed files with 480 additions and 278 deletions

View File

@@ -1,6 +1,8 @@
package com.aaaaahhhhhhh.bananapuncher714.minietest;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.MouseInfo;
@@ -22,20 +24,22 @@ import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.bukkit.util.Vector;
import com.aaaaahhhhhhh.bananapuncher714.mesh.EdgeSet;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Chain;
import com.aaaaahhhhhhh.bananapuncher714.mesh.HalfEdge;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.Chain;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.EdgePolygon;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh.MeshEventHandler;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Segment;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vector2d;
import com.aaaaahhhhhhh.bananapuncher714.mesh.Vertex;
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
@@ -69,11 +73,10 @@ public class MeshingTest2 extends JPanel {
private double scale = 8;
private Collection< Vertex > data;
private Collection< EdgePolygon > polygons;
private PlaneData data;
// Temporary... Remove at some point
private static Collection< Chain > chains;
private int stringBoardX = -100;
private int stringBoardY = 20;
public static void main( String[] args ) {
if ( CHUNK_DIR.exists() && CHUNK_DIR.isDirectory() ) {
@@ -91,28 +94,31 @@ public class MeshingTest2 extends JPanel {
}
}
// Collections.sort( planes, ( a, b ) -> Integer.compare( b.polygons.size(), a.polygons.size() ) );
// draw = planes.get( 4 );
// Select a random plane to draw
// draw = planes.get( new Random().nextInt( planes.size() ) );
System.out.println( "Draw is " + draw );
// Attempt to mesh all planes
try {
test( planes );
} catch ( PolygonException e ) {
draw = e.getPlane();
System.out.println( "Draw is now " + draw );
}
// try {
// test( planes );
// } catch ( PolygonException e ) {
// draw = e.getPlane();
// System.out.println( "Draw is now " + draw );
// }
final Collection< Vertex > data = process( draw );
final Collection< EdgePolygon > polys = triangulate( draw );
// final Collection< EdgePolygon > polys = test();
final PlaneData data = process( draw );
final Collection< Polygon > polys = triangulate( draw );
// final Collection< Polygon > polys = test();
System.out.println( "Polygon count: " + polys.size() );
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
new MeshingTest2( data, polys );
new MeshingTest2( data );
}
} );
break;
@@ -190,7 +196,7 @@ public class MeshingTest2 extends JPanel {
long start = System.currentTimeMillis();
mesh.simplify();
mesh.generateRegions();
final Collection< EdgePolygon > polys = mesh.copyOf().mesh();
final Collection< Polygon > polys = mesh.copyOf().mesh();
long end = System.currentTimeMillis();
System.out.println( plane.polygons.size() + " to " + polys.size() + ":\t " + ( end - start ) + "ms" );
} catch ( IllegalStateException e ) {
@@ -202,8 +208,9 @@ public class MeshingTest2 extends JPanel {
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
}
private static Collection< Vertex > process( final Plane plane ) {
private static PlaneData process( final Plane plane ) {
if ( plane != null ) {
final PlaneData data = new PlaneData();
System.out.println( "Norm:\t" + plane.normal );
System.out.println( "Ref:\t" + plane.point );
System.out.println( "Size:\t" + plane.polygons.size() );
@@ -214,30 +221,46 @@ public class MeshingTest2 extends JPanel {
}
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
System.out.println( "Initial edge count: " + ( mesh.getEdgeCount() / 2 ) );
long start = System.currentTimeMillis();
mesh.simplify();
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
System.out.println( "After simplify edge count: " + ( mesh.getEdgeCount() / 2 ) );
mesh.generateRegions();
long end = System.currentTimeMillis();
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
data.vertices = mesh.getVertices();
data.edges = mesh.getEdges();
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
mesh.addHandler( new MeshEventHandler() {
@Override
public void onChainGenerationEvent( Collection< Chain > chains ) {
data.chains = chains;
}
@Override
public void onPreChainMergeEvent( Collection< Chain > chains ) {
data.selectedChains = chains;
}
} );
data.polygons = mesh.mesh();
System.out.println( "Took " + ( end - start ) + "ms" );
return mesh.getVertices();
return data;
} else {
System.out.println( "No data!" );
return Collections.emptySet();
return null;
}
}
private static Collection< EdgePolygon > triangulate( final Plane plane ) {
private static Collection< Polygon > triangulate( final Plane plane ) {
if ( plane != null ) {
System.out.println( "Norm:\t" + plane.normal );
System.out.println( "Ref:\t" + plane.point );
@@ -249,32 +272,28 @@ public class MeshingTest2 extends JPanel {
}
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
System.out.println( "Initial edge count: " + ( mesh.getRuleSize() / 2 ) );
System.out.println( "Initial edge count: " + ( mesh.getEdgeCount() / 2 ) );
long start = System.currentTimeMillis();
mesh.simplify();
System.out.println( "After simplify vertex count: " + mesh.getVertices().size() );
System.out.println( "After simplify edge count: " + ( mesh.getRuleSize() / 2 ) );
System.out.println( "After simplify edge count: " + ( mesh.getEdgeCount() / 2 ) );
mesh.generateRegions();
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
System.out.println( "After generating regions edge count: " + ( mesh.getRuleSize() / 2 ) );
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
// Make sure this works
mesh = mesh.copyOf();
Collection< EdgePolygon > polys;
Collection< Polygon > polys = new HashSet< Polygon >();
try {
polys = mesh.mesh();
chains = mesh.chains;
} catch ( IllegalStateException e ) {
e.printStackTrace();
polys = mesh.mesh( );
chains = mesh.chains;
}
long end = System.currentTimeMillis();
@@ -287,7 +306,7 @@ public class MeshingTest2 extends JPanel {
}
}
private static Collection< EdgePolygon > test() {
private static Collection< Polygon > test() {
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
// mesh.addPolygon( new Polygon( Arrays.asList(
@@ -409,20 +428,37 @@ public class MeshingTest2 extends JPanel {
// new Point( 2.8151429293905887, -2.747294403063162 )
// ) ), RegionRuleWinding.CLOCKWISE );
mesh.addPolygon( new Polygon( Arrays.asList(
new Point( 0, 0 ),
new Point( 1, 0 ),
new Point( 1, 1 ),
new Point( 0, 1 )
) ), RegionRuleWinding.CLOCKWISE );
mesh.addPolygon( new Polygon( Arrays.asList(
new Point( 0, 2 ),
new Point( 1, 2 ),
new Point( 1, 3 ),
new Point( 0, 3 )
) ), RegionRuleWinding.CLOCKWISE );
mesh.addPolygon( new Polygon( Arrays.asList(
new Point( -1, -1 ),
new Point( 2, -1 ),
new Point( 2, 4 ),
new Point( -1, 4 )
) ), RegionRuleWinding.CLOCKWISE );
mesh.simplify();
mesh.generateRegions();
try {
final Collection< EdgePolygon > polys = mesh.mesh();
chains = mesh.chains;
final Collection< Polygon > polys = mesh.mesh();
return polys;
} catch ( Exception e ) {
e.printStackTrace();
}
final Collection< EdgePolygon > polys = mesh.mesh();
chains = mesh.chains;
final Collection< Polygon > polys = mesh.mesh();
return polys;
}
@@ -526,9 +562,8 @@ public class MeshingTest2 extends JPanel {
}
}
public MeshingTest2( Collection< Vertex > polys, Collection< EdgePolygon > polygons ) {
data = polys;
this.polygons = polygons;
public MeshingTest2( PlaneData data ) {
this.data = data;
f = new JFrame( "Drawing Board" );
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
@@ -596,6 +631,14 @@ public class MeshingTest2 extends JPanel {
};
} );
// final JPanel taskbar = new JPanel();
// taskbar.setLayout( new FlowLayout() );
//
// taskbar.add( new JButton( "Previous" ) );
// taskbar.add( new JButton( "Next" ) );
//
// f.add( taskbar, BorderLayout.NORTH );
f.add( this );
f.setSize( windowWidth, windowHeight );
@@ -613,6 +656,11 @@ public class MeshingTest2 extends JPanel {
return new Vector2d( ax, ay );
}
private void drawOnBoard( Graphics g, String str, int x, int y ) {
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, ( int ) scale * 5 ) );
g.drawString( str, ( int ) ( ( centerX + x + stringBoardX ) * scale ) + offsetX, ( int ) ( ( centerY - y + stringBoardY ) * scale ) + offsetY );
}
@Override
public void paintComponent( Graphics g ) {
super.paintComponent( g );
@@ -624,131 +672,117 @@ public class MeshingTest2 extends JPanel {
int highestX = 40;
if ( data != null ) {
Collection< Vertex > polygons = data;
highestX = ( int ) ( polygons.parallelStream()
.max( ( a, b ) -> { return Double.compare( a.getPosition().getX(), b.getPosition().getX() ); } ).get().getPosition().getX() + 40.5 );
EdgeSet edges = new EdgeSet();
for ( Vertex vert : polygons ) {
for ( HalfEdge edge : vert ) {
if ( edges.add( edge ) ) {
Point p1 = edge.getOrigin().getPosition();
Point p2 = edge.getDest().getPosition();
g.drawLine(
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
);
}
}
if ( !data.edges.isEmpty() ) {
Collection< Point > points = data.vertices;
final double diff = scale * 0.15;
final Point point = vert.getPosition();
highestX = ( int ) ( points.parallelStream()
.max( ( a, b ) -> { return Double.compare( a.getX(), b.getX() ); } ).get().getX() + 40.5 );
g.setColor( Color.BLACK );
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
}
}
if ( polygons != null ) {
g.setColor( Color.BLACK );
int count = 2;
Random random = new Random( hashCode() );
for ( final Segment segment : data.edges ) {
Point p1 = segment.getStart();
Point p2 = segment.getEnd();
g.drawLine(
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
);
}
for ( final Point point : points ) {
final double diff = scale * 0.15;
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
}
drawOnBoard( g, "Edge count: " + data.edges.size(), 0, 0 );
drawOnBoard( g, "Vertex count: " + points.size(), 0, -5 );
}
final int spacing = 25;
for ( final EdgePolygon p : polygons ) {
final Collection< HalfEdge > edges = p.getEdges();
final Set< Vertex > verts = new HashSet< Vertex >();
g.setColor( Color.BLACK );
for ( HalfEdge edge : edges ) {
Point p1 = edge.getOrigin().getPosition();
Point p2 = edge.getDest().getPosition();
g.drawLine(
( int ) ( ( centerX + p1.getX() + count * spacing ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() - 150 ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() + count * spacing ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() - 150 ) * scale ) + offsetY
);
g.drawLine(
( int ) ( ( centerX + p1.getX() ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() - 150 ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() - 150 ) * scale ) + offsetY
);
final double diff = scale * 0.15;
if ( !verts.add( edge.getOrigin() ) ) {
g.drawRect( ( int ) ( ( centerX + p1.getX() + count * spacing ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - p1.getY() - 150 ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
if ( data.polygons != null ) {
int count = 2;
Random random = new Random( hashCode() );
final int spacing = 25;
for ( final Polygon p : data.polygons ) {
final List< Point > points = p.getPoints();
final int[] x = new int[ points.size() ];
final int[] y = new int[ points.size() ];
final int[] xOffset = new int[ points.size() ];
final int[] yOffset = new int[ points.size() ];
final int[] filledX = new int[ points.size() ];
final int[] filledY = new int[ points.size() ];
for ( int i = 0; i < points.size(); ++i ) {
final Point point = points.get( i );
x[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
y[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
xOffset[ i ] = ( int ) ( ( centerX + point.getX() + count * spacing ) * scale ) + offsetX;
yOffset[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
filledX[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
filledY[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
}
}
count++;
int size = edges.size();
int[] xPoints = new int[ size ];
int[] yPoints = new int[ size ];
final Set< Vertex > verts2 = new HashSet< Vertex >();
HalfEdge edge = edges.iterator().next();
for ( int i = 0; i < edges.size(); ++i ) {
final Point point = edge.getOrigin().getPosition();
xPoints[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
yPoints[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
final double diff = scale * 0.15;
if ( !verts2.add( edge.getOrigin() ) ) {
g.setColor( Color.ORANGE );
} else {
g.setColor( Color.BLACK );
}
g.drawRect( ( int ) ( ( centerX + point.getX() + highestX ) * scale ) - ( int ) diff + offsetX, ( int ) ( ( centerY - point.getY() ) * scale ) - ( int ) diff + offsetY, ( int ) ( diff * 2 ), ( int ) ( diff * 2 ) );
edge = edge.getNext();
g.setColor( Color.BLACK );
g.drawPolygon( x, y, points.size() );
g.drawPolygon( xOffset, yOffset, points.size() );
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
g.fillPolygon( filledX, filledY, points.size() );
++count;
}
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
g.fillPolygon( xPoints, yPoints, size );
g.setColor( Color.MAGENTA );
drawOnBoard( g, "Polygon count: " + data.polygons.size(), 0, -10 );
}
}
if ( chains != null ) {
int i = 0;
for ( Chain chain : chains ) {
Point p1 = chain.getOrigin().getPosition();
Point p2 = chain.getDest().getPosition();
if ( data.chains != null ) {
g.setColor( Color.RED );
g.drawLine(
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX + i,
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY + i,
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX + i,
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY + i
);
for ( final Chain chain : data.chains ) {
final Point p1 = chain.getStart();
final Point p2 = chain.getEnd();
g.drawLine(
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
);
}
// for ( HalfEdge e : chain.getLinks() ) {
// Point e1 = e.getOrigin().getPosition();
// Point e2 = e.getSym().getOrigin().getPosition();
// g.setColor( Color.BLUE );
// g.drawLine(
// ( int ) ( ( centerX + e1.getX() + 40 ) * scale ) + offsetX + i + 1,
// ( int ) ( ( centerY - e1.getY() ) * scale ) + offsetY + i + 1,
// ( int ) ( ( centerX + e2.getX() + 40 ) * scale ) + offsetX + i + 1,
// ( int ) ( ( centerY - e2.getY() ) * scale ) + offsetY + i + 1
// );
// }
// i += 2;
drawOnBoard( g, "Chain count: " + data.chains.size(), 0, -15 );
}
if ( data.selectedChains != null ) {
g.setColor( Color.BLUE );
for ( final Chain chain : data.selectedChains ) {
final Point p1 = chain.getStart();
final Point p2 = chain.getEnd();
g.drawLine(
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
);
}
drawOnBoard( g, "Minimum chain count: " + data.selectedChains.size(), 0, -20 );
}
}
java.awt.Point p = MouseInfo.getPointerInfo().getLocation();
java.awt.Point componentLocation = getLocation();
SwingUtilities.convertPointToScreen( componentLocation, this );
java.awt.Point componentLocation = getLocationOnScreen();
p = new java.awt.Point( p.x - componentLocation.x, p.y - componentLocation.y );
g.setColor( Color.RED );
g.setFont( new Font( Font.MONOSPACED, Font.BOLD, 30 ) );
g.drawString( "X: " + ( ( p.x - offsetX ) / scale - centerX ), 10, 30 );
g.drawString( "Y: " + ( - ( ( p.y - offsetY ) / scale - centerY ) ), 10, 60 );
}
private static class PlaneData {
Collection< Point > vertices;
Collection< Segment > edges;
Collection< Chain > chains;
Collection< Chain > selectedChains;
Collection< Polygon > polygons;
}
}