Properly determine the insertion edge when triangulation to avoid having to sort each vertex later Synchronize the plane viewer to prevent incorrect index saving when switching planes too quickly Use the shape instead of collision shape for summoned physics objects
998 lines
38 KiB
Java
998 lines
38 KiB
Java
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.Color;
|
|
import java.awt.Dimension;
|
|
import java.awt.FlowLayout;
|
|
import java.awt.Font;
|
|
import java.awt.Graphics;
|
|
import java.awt.MouseInfo;
|
|
import java.awt.event.ComponentAdapter;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseWheelEvent;
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileReader;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
import javax.swing.JButton;
|
|
import javax.swing.JComboBox;
|
|
import javax.swing.JFrame;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.SwingConstants;
|
|
import javax.swing.SwingUtilities;
|
|
|
|
import org.bukkit.util.Vector;
|
|
|
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Chain;
|
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
|
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.region.simple.RegionRuleWinding;
|
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple.GluWindingRule;
|
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet;
|
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder;
|
|
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane;
|
|
|
|
public class MeshingTest2 extends JPanel {
|
|
private static final File BASE = new File( System.getProperty( "user.dir" ) );
|
|
private static final File CHUNK_DIR = new File( BASE, "chunks" );
|
|
|
|
JFrame f;
|
|
|
|
// private int windowWidth = 1200;
|
|
// private int windowHeight = 1000;
|
|
|
|
private int windowWidth = 1200;
|
|
private int windowHeight = 800;
|
|
|
|
// private int centerX = 600;
|
|
// private int centerY = 400;
|
|
|
|
private int offsetX = windowWidth >> 1;
|
|
private int offsetY = windowHeight >> 1;
|
|
|
|
private double centerX = 0;
|
|
private double centerY = 0;
|
|
|
|
private double scale = 8;
|
|
|
|
private int stringBoardX = -100;
|
|
private int stringBoardY = 20;
|
|
|
|
final private List< Plane > planes;
|
|
final private PlaneData[] data;
|
|
int index = -1;
|
|
|
|
private JLabel statusBar;
|
|
private JComboBox< String > selectionBox;
|
|
|
|
private ReentrantLock lock = new ReentrantLock();
|
|
|
|
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< Plane > planes = mesh( file );
|
|
|
|
// Plane draw = null;
|
|
|
|
// System.out.println( "Planes: " + planes.size() );
|
|
// int planeIndex = 0;
|
|
// for ( planeIndex = 0; planeIndex < planes.size(); ++planeIndex ) {
|
|
// if ( planes.get( planeIndex ).polygons.size() > 100 ) {
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// 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 );
|
|
// }
|
|
|
|
// final PlaneData data = process( draw );
|
|
// final Collection< Polygon > polys = triangulate( draw );
|
|
// final Collection< Polygon > polys = test();
|
|
// System.out.println( "Polygon count: " + data.polygons.size() );
|
|
|
|
// final int startingIndex = planeIndex;
|
|
// SwingUtilities.invokeLater( new Runnable() {
|
|
// @Override
|
|
// public void run() {
|
|
// new MeshingTest2( planes ).setIndex( startingIndex ).start();;
|
|
// }
|
|
// } );
|
|
// break;
|
|
// }
|
|
|
|
final List< Plane > masterPlanes = new ArrayList< Plane >();
|
|
System.out.println( "Chunk files: " + CHUNK_DIR.listFiles().length );
|
|
int limit = 1;
|
|
for ( File file : CHUNK_DIR.listFiles() ) {
|
|
masterPlanes.addAll( mesh( file ) );
|
|
if ( --limit <= 0 ) {
|
|
break;
|
|
} else {
|
|
System.out.println( "Remaining: " + limit );
|
|
}
|
|
}
|
|
|
|
// final List< Plane > planes = mesh( new File( CHUNK_DIR, "world,-1,-1" ) );
|
|
// masterPlanes.add( planes.get( 20 ) );
|
|
// masterPlanes.addAll( planes );
|
|
|
|
// masterPlanes.add( getTestPlane() );
|
|
|
|
SwingUtilities.invokeLater( new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
new MeshingTest2( masterPlanes ).start();
|
|
}
|
|
} );
|
|
} else {
|
|
System.err.println( "No such directory exists: " + CHUNK_DIR );
|
|
}
|
|
}
|
|
|
|
private static List< Plane > mesh( File file ) {
|
|
String name = file.getName();
|
|
String[] split = name.split( "," );
|
|
ChunkLocation location = new ChunkLocation( split[ 0 ], Integer.parseInt( split[ 1 ] ), Integer.parseInt( split[ 2 ] ) );
|
|
|
|
System.out.println( "Parsing chunk " + location );
|
|
|
|
// First parse the file to get a list of all bounding boxes that we can use
|
|
List< AABB > boxes = new ArrayList< AABB >();
|
|
try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) ) {
|
|
String line;
|
|
while ( ( line = reader.readLine() ) != null ) {
|
|
if ( !line.isEmpty() ) {
|
|
String[] values = line.split( "," );
|
|
double minX = Double.parseDouble( values[ 0 ] );
|
|
double minY = Double.parseDouble( values[ 1 ] );
|
|
double minZ = Double.parseDouble( values[ 2 ] );
|
|
double maxX = Double.parseDouble( values[ 3 ] );
|
|
double maxY = Double.parseDouble( values[ 4 ] );
|
|
double maxZ = Double.parseDouble( values[ 5 ] );
|
|
boxes.add( new AABB( minX, minY, minZ, maxX, maxY, maxZ ) );
|
|
}
|
|
}
|
|
} catch ( FileNotFoundException e ) {
|
|
e.printStackTrace();
|
|
return null;
|
|
} catch ( IOException e ) {
|
|
e.printStackTrace();
|
|
return null;
|
|
}
|
|
|
|
System.out.println( "Found " + boxes.size() + " boxes" );
|
|
// Now that we have a bunch of bounding boxes, do whatever
|
|
|
|
MeshBuilder builder = new MeshBuilder();
|
|
for ( AABB box : boxes ) {
|
|
for ( Facet facet : generateFacetsFor( box ) ) {
|
|
builder.addFacet( facet );
|
|
}
|
|
}
|
|
|
|
return builder.planes;
|
|
}
|
|
|
|
public static class PolygonException extends RuntimeException {
|
|
Plane plane;
|
|
|
|
PolygonException( Plane plane ) {
|
|
this.plane = plane;
|
|
}
|
|
|
|
Plane getPlane() {
|
|
return plane;
|
|
}
|
|
}
|
|
|
|
private static void test( final Collection< Plane > planes ) {
|
|
final long processAllStart = System.currentTimeMillis();
|
|
planes.parallelStream().forEach( plane -> {
|
|
try {
|
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
|
|
|
for ( Polygon poly : plane.polygons ) {
|
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
|
}
|
|
long start = System.currentTimeMillis();
|
|
mesh.simplify();
|
|
mesh.generateRegions();
|
|
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 ) {
|
|
e.printStackTrace();
|
|
throw new PolygonException( plane );
|
|
}
|
|
} );
|
|
final long processAllEnd = System.currentTimeMillis();
|
|
System.out.println( "Took " + ( processAllEnd - processAllStart ) + "ms to process " + planes.size() + " planes" );
|
|
}
|
|
|
|
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() );
|
|
|
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
|
for ( Polygon poly : plane.polygons ) {
|
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
|
}
|
|
|
|
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
|
System.out.println( "Initial edge count: " + ( mesh.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.getEdgeCount() / 2 ) );
|
|
|
|
mesh.generateRegions();
|
|
long end = System.currentTimeMillis();
|
|
|
|
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;
|
|
|
|
// final List< Chain > sorted = new ArrayList< Chain >( chains );
|
|
// Collections.sort( sorted, ( a, b ) -> {
|
|
// final double diff = a.getStart().getX() - b.getStart().getX();
|
|
// if ( Math.abs( diff ) < 1e-7 ) {
|
|
// return Double.compare( a.getStart().getY(), b.getStart().getY() );
|
|
// }
|
|
// return Double.compare( diff, 0 );
|
|
// } );
|
|
// for ( Chain chain : sorted ) {
|
|
// System.out.println( "c: " + chain.getPoints() );
|
|
// }
|
|
//
|
|
// final Set< Point > points = new HashSet< Point >();
|
|
// for ( Chain chain : chains ) {
|
|
// for ( int i = 1; i < chain.getPoints().size() - 1; ++i ) {
|
|
// if ( !points.add( chain.getPoints().get( i ) ) ) {
|
|
// System.out.println( "Intersection at " + chain.getPoints().get( i ) );
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
@Override
|
|
public void onPartitionEvent( Collection< Polygon > polygons ) {
|
|
data.polygons = polygons;
|
|
}
|
|
} );
|
|
|
|
final Collection< Polygon > triangles = mesh.mesh();
|
|
if ( !triangles.isEmpty() ) {
|
|
data.polygons = triangles;
|
|
}
|
|
|
|
// final List< Point > points = new ArrayList< Point >( data.vertices );
|
|
// Collections.sort( points, ( a, b ) -> {
|
|
// final int x = Double.compare( a.getX(), b.getX() );
|
|
// if ( x == 0 ) {
|
|
// return Double.compare( a.getY(), b.getY() );
|
|
// }
|
|
// return x;
|
|
// } );
|
|
// for ( Point v : points ) {
|
|
// System.out.println( v.getX() + ", " + v.getY() );
|
|
// }
|
|
|
|
System.out.println( "Took " + ( end - start ) + "ms" );
|
|
return data;
|
|
} else {
|
|
System.out.println( "No data!" );
|
|
return null;
|
|
}
|
|
}
|
|
|
|
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 );
|
|
System.out.println( "Size:\t" + plane.polygons.size() );
|
|
|
|
Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
|
for ( Polygon poly : plane.polygons ) {
|
|
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
|
}
|
|
|
|
System.out.println( "Initial vertex count: " + mesh.getVertices().size() );
|
|
System.out.println( "Initial edge count: " + ( mesh.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.getEdgeCount() / 2 ) );
|
|
|
|
mesh.generateRegions();
|
|
|
|
System.out.println( "After generating regions vertex count: " + mesh.getVertices().size() );
|
|
System.out.println( "After generating regions edge count: " + ( mesh.getEdgeCount() / 2 ) );
|
|
|
|
// Make sure this works
|
|
mesh = mesh.copyOf();
|
|
|
|
Collection< Polygon > polys = new HashSet< Polygon >();
|
|
|
|
try {
|
|
polys = mesh.mesh();
|
|
} catch ( IllegalStateException e ) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
long end = System.currentTimeMillis();
|
|
System.out.println( "Took " + ( end - start ) + "ms" );
|
|
|
|
return polys;
|
|
} else {
|
|
System.out.println( "No data!" );
|
|
return Collections.emptySet();
|
|
}
|
|
}
|
|
|
|
private static PlaneData test() {
|
|
final PlaneData data = new PlaneData();
|
|
final Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
|
mesh.addHandler( new MeshEventHandler() {
|
|
@Override
|
|
public void onChainGenerationEvent( Collection< Chain > chains ) {
|
|
data.chains = chains;
|
|
}
|
|
|
|
@Override
|
|
public void onPreChainMergeEvent( Collection< Chain > chains ) {
|
|
data.selectedChains = chains;
|
|
|
|
for ( Chain chain : chains ) {
|
|
System.out.println( "c: " + chain.getPoints() );
|
|
}
|
|
}
|
|
} );
|
|
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( -1, 0 ),
|
|
// new Point( 1, -2 ),
|
|
// new Point( 1, -1 ),
|
|
// new Point( 2, -1 ),
|
|
// new Point( 1, 0 ),
|
|
// new Point( 1, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 0, 0 ),
|
|
// new Point( 5, 0 ),
|
|
// new Point( 5, 1 ),
|
|
// new Point( 4, 1 ),
|
|
// new Point( 4, 2 ),
|
|
// new Point( 3, 2 ),
|
|
// new Point( 3, 3 ),
|
|
// new Point( 2, 3 ),
|
|
// new Point( 2, 4 ),
|
|
// new Point( 0, 4 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 0, 0 ),
|
|
// new Point( 2, 0 ),
|
|
// new Point( 2, 1 ),
|
|
// new Point( 1, 1 ),
|
|
// new Point( 1, 2 ),
|
|
// new Point( 2, 2 ),
|
|
// new Point( 2, 4 ),
|
|
// new Point( 1, 4 ),
|
|
// new Point( 1, 3 ),
|
|
// new Point( 0, 3 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 0, 0 ),
|
|
// new Point( 3, 0 ),
|
|
// new Point( 3, 1 ),
|
|
// new Point( 4, 1 ),
|
|
// new Point( 4, 2 ),
|
|
// new Point( 2, 2 ),
|
|
// new Point( 2, 1 ),
|
|
// new Point( 1, 1 ),
|
|
// new Point( 0, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 0, 0 ),
|
|
// new Point( 2, 0 ),
|
|
// new Point( 2, 1 ),
|
|
// new Point( 3, 1 ),
|
|
// new Point( 3, 2 ),
|
|
// new Point( 2, 2 ),
|
|
// new Point( 2, 5 ),
|
|
// new Point( 1, 5 ),
|
|
// new Point( 1, 7 ),
|
|
// new Point( 0, 7 ),
|
|
// new Point( 0, 4 ),
|
|
// new Point( 1, 4 ),
|
|
// new Point( 1, 1 ),
|
|
// new Point( 0, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 1, 0 ),
|
|
// new Point( 6, 0 ),
|
|
// new Point( 6, 1 ),
|
|
// new Point( 4, 1 ),
|
|
// new Point( 4, 2 ),
|
|
// new Point( 2, 2 ),
|
|
// new Point( 2, 3 ),
|
|
// new Point( 0, 3 ),
|
|
// new Point( 0, 1 ),
|
|
// new Point( 1, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 3, 1 ),
|
|
// new Point( 3, 0 ),
|
|
// new Point( 10, 0 ),
|
|
// new Point( 10, 6 ),
|
|
// new Point( 9, 6 ),
|
|
// new Point( 9, 4 ),
|
|
// new Point( 8, 4 ),
|
|
// new Point( 8, 3 ),
|
|
// new Point( 5, 3 ),
|
|
// new Point( 5, 2 ),
|
|
// new Point( 0, 2 ),
|
|
// new Point( 0, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 7, 2 ),
|
|
// new Point( 9, 2 ),
|
|
// new Point( 9, 3 ),
|
|
// new Point( 7, 3 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 0, 0 ),
|
|
// new Point( 6, 1 ),
|
|
// new Point( 7, 2 ),
|
|
// new Point( 1, 1 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 2, 2 ),
|
|
// new Point( 8, 3 ),
|
|
// new Point( 9, 4 ),
|
|
// new Point( 3, 3 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 2.816104467407629, -3.7472939407853763 ),
|
|
// new Point( 2.815383313894849, -2.9972942874937156 ),
|
|
// new Point( 2.0028836894955497, -2.9980755371325607 ),
|
|
// new Point( 2.0036048430083304, -3.7480751904242213 )
|
|
// ) ), RegionRuleWinding.CLOCKWISE );
|
|
// mesh.addPolygon( new Polygon( Arrays.asList(
|
|
// new Point( 2.8144217758778085, -1.9972947497715015 ),
|
|
// new Point( 2.0019221514785093, -1.998075999410347 ),
|
|
// new Point( 2.00264330499129, -2.748075652702007 ),
|
|
// 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 );
|
|
|
|
data.polygons = mesh.mesh();
|
|
data.vertices = mesh.getVertices();
|
|
data.edges = mesh.getEdges();
|
|
|
|
return data;
|
|
}
|
|
|
|
private static Plane getTestPlane() {
|
|
final Plane plane = new Plane();
|
|
|
|
// plane.polygons.add( new Polygon( Arrays.asList(
|
|
// new Point( 1, 0 ),
|
|
// new Point( 2, 1 ),
|
|
// new Point( 3, 0 ),
|
|
// new Point( 4, 1 ),
|
|
// new Point( 3, 2 ),
|
|
// new Point( 4, 3 ),
|
|
// new Point( 3, 4 ),
|
|
// new Point( 2, 3 ),
|
|
// new Point( 1, 4 ),
|
|
// new Point( 0, 3 ),
|
|
// new Point( 1, 2 ),
|
|
// new Point( 0, 1 )
|
|
plane.polygons.add( new Polygon( Arrays.asList(
|
|
new Point( 1, 0 ),
|
|
new Point( 2, 1 ),
|
|
new Point( 3, 0 ),
|
|
new Point( 4, 1 ),
|
|
new Point( 3, 2 ),
|
|
new Point( 4, 3 ),
|
|
new Point( 3, 4 ),
|
|
new Point( 2, 3 ),
|
|
new Point( 1, 4 ),
|
|
new Point( 0, 3 ),
|
|
new Point( 1, 2 ),
|
|
new Point( 0, 1 )
|
|
) ) );
|
|
|
|
return plane;
|
|
}
|
|
|
|
private static List< Facet > generateFacetsFor( AABB box ) {
|
|
List< Facet > facets = new ArrayList< Facet >();
|
|
|
|
Vector p1 = new Vector( box.xmin, box.ymin, box.zmin );
|
|
Vector p2 = new Vector( box.xmin, box.ymin, box.zmax );
|
|
Vector p3 = new Vector( box.xmin, box.ymax, box.zmin );
|
|
Vector p4 = new Vector( box.xmin, box.ymax, box.zmax );
|
|
Vector p5 = new Vector( box.xmax, box.ymin, box.zmin );
|
|
Vector p6 = new Vector( box.xmax, box.ymin, box.zmax );
|
|
Vector p7 = new Vector( box.xmax, box.ymax, box.zmin );
|
|
Vector p8 = new Vector( box.xmax, box.ymax, box.zmax );
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p1 );
|
|
facet.points.add( p2 );
|
|
facet.points.add( p4 );
|
|
facet.points.add( p3 );
|
|
facet.normal = new Vector( -1, 0, 0 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p5 );
|
|
facet.points.add( p6 );
|
|
facet.points.add( p8 );
|
|
facet.points.add( p7 );
|
|
facet.normal = new Vector( 1, 0, 0 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p1 );
|
|
facet.points.add( p2 );
|
|
facet.points.add( p6 );
|
|
facet.points.add( p5 );
|
|
facet.normal = new Vector( 0, -1, 0 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p3 );
|
|
facet.points.add( p4 );
|
|
facet.points.add( p8 );
|
|
facet.points.add( p7 );
|
|
facet.normal = new Vector( 0, 1, 0 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p1 );
|
|
facet.points.add( p3 );
|
|
facet.points.add( p7 );
|
|
facet.points.add( p5 );
|
|
facet.normal = new Vector( 0, 0, -1 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
{
|
|
Facet facet = new Facet();
|
|
facet.points.add( p2 );
|
|
facet.points.add( p4 );
|
|
facet.points.add( p8 );
|
|
facet.points.add( p6 );
|
|
facet.normal = new Vector( 0, 0, 1 );
|
|
facets.add( facet );
|
|
}
|
|
|
|
return facets;
|
|
}
|
|
|
|
static class AABB {
|
|
double xmin, ymin, zmin;
|
|
double xmax, ymax, zmax;
|
|
|
|
AABB( double xmin, double ymin, double zmin, double xmax, double ymax, double zmax ) {
|
|
this.xmin = xmin;
|
|
this.ymin = ymin;
|
|
this.zmin = zmin;
|
|
|
|
this.xmax = xmax;
|
|
this.ymax = ymax;
|
|
this.zmax = zmax;
|
|
}
|
|
|
|
double getVolume() {
|
|
return ( xmax - xmin ) * ( ymax - ymin ) * ( zmax - zmin );
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return String.format( "(%f, %f, %f) -> (%f, %f, %f)", xmin, ymin, zmin, xmax, ymax, zmax );
|
|
}
|
|
}
|
|
|
|
public MeshingTest2( List< Plane > data ) {
|
|
this.planes = data;
|
|
this.data = new PlaneData[ data.size() ];
|
|
}
|
|
|
|
private MeshingTest2 setIndex( final int index ) {
|
|
final int planeCount = planes.size();
|
|
final int newIndex = ( ( ( index % planeCount ) + planeCount ) % planeCount );
|
|
lock.lock();
|
|
if ( this.index != newIndex ) {
|
|
this.index = newIndex;
|
|
|
|
if ( selectionBox != null ) {
|
|
selectionBox.setSelectedIndex( newIndex );
|
|
}
|
|
|
|
new Thread( () -> { getData( this.index, this::repaint ); } ).start();
|
|
}
|
|
lock.unlock();
|
|
|
|
return this;
|
|
}
|
|
|
|
private PlaneData getData( final int index, final Runnable callback ) {
|
|
if ( data[ index ] != null ) {
|
|
callback.run();
|
|
return data[ index ];
|
|
} else {
|
|
setStatus( "Loading..." );
|
|
final PlaneData data = process( planes.set( index, null ) );
|
|
setStatus( "Done!" );
|
|
this.data[ index ] = data;
|
|
callback.run();
|
|
return data;
|
|
}
|
|
}
|
|
|
|
private void setStatus( final String status ) {
|
|
if ( statusBar != null ) {
|
|
statusBar.setText( status );
|
|
}
|
|
}
|
|
|
|
private MeshingTest2 preMesh() {
|
|
for ( int i = 0; i < planes.size(); ++i ) {
|
|
this.data[ i ] = process( planes.set( i, null ) );
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private void start() {
|
|
if ( index == -1 ) {
|
|
setIndex( 0 );
|
|
}
|
|
|
|
f = new JFrame( "Drawing Board" );
|
|
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
|
|
|
final MouseAdapter adapter = new MouseAdapter() {
|
|
private boolean dragging = false;
|
|
private double initX, initY;
|
|
private int dragX, dragY;
|
|
private double scroll = Math.log( scale );
|
|
|
|
@Override
|
|
public void mousePressed( MouseEvent e ) {
|
|
dragging = true;
|
|
|
|
initX = centerX;
|
|
initY = centerY;
|
|
|
|
dragX = e.getXOnScreen();
|
|
dragY = e.getYOnScreen();
|
|
}
|
|
|
|
@Override
|
|
public void mouseReleased( MouseEvent e ) {
|
|
dragging = false;
|
|
}
|
|
|
|
@Override
|
|
public void mouseDragged( MouseEvent e ) {
|
|
if ( dragging ) {
|
|
centerX = initX + ( ( e.getXOnScreen() - dragX ) / scale );
|
|
centerY = initY + ( ( e.getYOnScreen() - dragY ) / scale );
|
|
|
|
repaint();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseWheelMoved( MouseWheelEvent e ) {
|
|
if ( e.getWheelRotation() < 0 ) {
|
|
scroll += e.isControlDown() ? 0.1 : 0.5;
|
|
} else {
|
|
scroll -= e.isControlDown() ? 0.1 : 0.5;
|
|
}
|
|
scale = Math.exp( scroll );
|
|
|
|
repaint();
|
|
}
|
|
|
|
@Override
|
|
public void mouseMoved( MouseEvent e ) {
|
|
super.mouseMoved( e );
|
|
|
|
repaint();
|
|
}
|
|
};
|
|
|
|
addMouseListener( adapter );
|
|
addMouseMotionListener( adapter );
|
|
addMouseWheelListener( adapter );
|
|
|
|
f.addComponentListener( new ComponentAdapter() {
|
|
public void componentResized( ComponentEvent e ) {
|
|
offsetX = getWidth() >> 1;
|
|
offsetY = getHeight() >> 1;
|
|
};
|
|
} );
|
|
|
|
final JPanel taskbar = new JPanel();
|
|
taskbar.setLayout( new FlowLayout() );
|
|
|
|
{
|
|
final JComboBox< String > data = new JComboBox< String >();
|
|
for ( int i = 0; i < planes.size(); ++i ) {
|
|
data.addItem( "Plane " + i );
|
|
}
|
|
data.addActionListener( e -> {
|
|
setIndex( data.getSelectedIndex() );
|
|
} );
|
|
taskbar.add( data );
|
|
|
|
selectionBox = data;
|
|
}
|
|
|
|
{
|
|
final JLabel label = new JLabel();
|
|
label.setPreferredSize( new Dimension( 100, 16 ) );
|
|
label.setHorizontalAlignment( SwingConstants.LEFT );
|
|
taskbar.add( label );
|
|
|
|
statusBar = label;
|
|
}
|
|
|
|
{
|
|
final JButton prev = new JButton( "Previous" );
|
|
prev.addActionListener( e -> { setIndex( index - 1 ); } );
|
|
taskbar.add( prev );
|
|
}
|
|
|
|
|
|
{
|
|
final JButton prev = new JButton( "Next" );
|
|
prev.addActionListener( e -> { setIndex( index + 1 ); } );
|
|
taskbar.add( prev );
|
|
}
|
|
|
|
f.add( taskbar, BorderLayout.NORTH );
|
|
|
|
f.add( this );
|
|
|
|
f.setSize( windowWidth, windowHeight );
|
|
f.setVisible( true );
|
|
f.setResizable( true );
|
|
|
|
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
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 );
|
|
|
|
final PlaneData data = this.data[ index ];
|
|
|
|
g.setColor( new Color( 200, 200, 200 ) );
|
|
g.drawLine( ( int ) ( scale * centerX ) + offsetX, 0, ( int ) ( scale * centerX ) + offsetX, getHeight() );
|
|
g.drawLine( 0, ( int ) ( scale * centerY ) + offsetY, getWidth(), ( int ) ( scale * centerY ) + offsetY );
|
|
g.setColor( Color.BLACK );
|
|
|
|
if ( data != null ) {
|
|
int highestX = 40;
|
|
if ( !data.edges.isEmpty() ) {
|
|
Collection< Point > points = data.vertices;
|
|
|
|
if ( !data.polygons.isEmpty() ) {
|
|
highestX = ( int ) ( points.parallelStream()
|
|
.max( ( a, b ) -> { return Double.compare( a.getX(), b.getX() ); } ).get().getX() + 40.5 );
|
|
} else {
|
|
highestX = 0;
|
|
}
|
|
|
|
g.setColor( Color.BLACK );
|
|
for ( final Segment segment : data.edges ) {
|
|
Point p1 = segment.getStart();
|
|
Point p2 = segment.getEnd();
|
|
g.drawLine(
|
|
( int ) ( ( centerX + p1.getX() + highestX ) * scale ) + offsetX,
|
|
( int ) ( ( centerY - p1.getY() ) * scale ) + offsetY,
|
|
( int ) ( ( centerX + p2.getX() + highestX ) * scale ) + offsetX,
|
|
( int ) ( ( centerY - p2.getY() ) * scale ) + offsetY
|
|
);
|
|
}
|
|
|
|
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 ) );
|
|
}
|
|
}
|
|
|
|
{
|
|
int count = 2;
|
|
Random random = new Random( hashCode() );
|
|
|
|
final int spacing = 25;
|
|
|
|
for ( final Polygon p : data.polygons ) {
|
|
final List< Point > points = p.getPoints();
|
|
final int[] x = new int[ points.size() ];
|
|
final int[] y = new int[ points.size() ];
|
|
final int[] xOffset = new int[ points.size() ];
|
|
final int[] yOffset = new int[ points.size() ];
|
|
final int[] filledX = new int[ points.size() ];
|
|
final int[] filledY = new int[ points.size() ];
|
|
for ( int i = 0; i < points.size(); ++i ) {
|
|
final Point point = points.get( i );
|
|
x[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
|
y[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
|
xOffset[ i ] = ( int ) ( ( centerX + point.getX() + count * spacing ) * scale ) + offsetX;
|
|
yOffset[ i ] = ( int ) ( ( centerY - point.getY() - 150 ) * scale ) + offsetY;
|
|
filledX[ i ] = ( int ) ( ( centerX + point.getX() ) * scale ) + offsetX;
|
|
filledY[ i ] = ( int ) ( ( centerY - point.getY() ) * scale ) + offsetY;
|
|
}
|
|
|
|
g.setColor( Color.BLACK );
|
|
g.drawPolygon( x, y, points.size() );
|
|
g.drawPolygon( xOffset, yOffset, points.size() );
|
|
g.setColor( new Color( random.nextInt( 0x1000000 ) ) );
|
|
g.fillPolygon( filledX, filledY, points.size() );
|
|
++count;
|
|
}
|
|
}
|
|
|
|
{
|
|
g.setColor( Color.RED );
|
|
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
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
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
|
|
);
|
|
}
|
|
}
|
|
|
|
g.setColor( Color.BLACK );
|
|
drawOnBoard( g, "Plane index: " + index, 0, 10 );
|
|
g.setColor( Color.CYAN );
|
|
drawOnBoard( g, "Edge count: " + data.edges.size(), 0, 0 );
|
|
g.setColor( Color.GREEN );
|
|
drawOnBoard( g, "Vertex count: " + data.vertices.size(), 0, -5 );
|
|
g.setColor( Color.MAGENTA );
|
|
drawOnBoard( g, "Triangle count: " + data.polygons.size(), 0, -10 );
|
|
g.setColor( Color.RED );
|
|
drawOnBoard( g, "Total chain count: " + data.chains.size(), 0, -15 );
|
|
g.setColor( Color.BLUE );
|
|
drawOnBoard( g, "Optimal chain count: " + data.selectedChains.size(), 0, -20 );
|
|
}
|
|
|
|
java.awt.Point p = MouseInfo.getPointerInfo().getLocation();
|
|
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 = new HashSet< Point >();
|
|
Collection< Segment > edges = new HashSet< Segment >();
|
|
Collection< Chain > chains = new HashSet< Chain >();
|
|
Collection< Chain > selectedChains = new HashSet< Chain >();
|
|
Collection< Polygon > polygons = new HashSet< Polygon >();
|
|
}
|
|
} |