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 >(); } }