├── ## Demo ## ├── 1) Two segments intersection.jar ├── 2) Any segments intersection.jar ├── 3) Convex hull.jar ├── 3D terrain construction via VRML │ ├── README.markdown │ ├── Terrain.wrl │ ├── VRMLTerrainScript$1.class │ ├── VRMLTerrainScript$ProjectedPoint3D.class │ ├── VRMLTerrainScript.class │ └── ru │ │ └── dubov │ │ ├── delaunay │ │ ├── Delaunay$1.class │ │ ├── Delaunay.class │ │ ├── TriangulationDAG$Node.class │ │ └── TriangulationDAG.class │ │ └── primitives │ │ ├── Circle.class │ │ ├── Line.class │ │ ├── Point.class │ │ ├── Polygon.class │ │ ├── Segment.class │ │ ├── Triangle$1.class │ │ ├── Triangle$Side.class │ │ └── Triangle.class ├── 4) Closest points pair.jar ├── 5) Polygon triangulation.jar ├── 6) Point set Delaunay triangulation.jar ├── 7) Halfplanes intersection.jar ├── 8) Voronoi diagram.jar └── lib │ ├── HalfplanesIntersection.jar │ └── PrimitivesLibrary.jar ├── .gitignore ├── 1) Two segments intersection └── src │ └── ru │ └── dubov │ └── segmentsintersect │ ├── MainFrame.form │ ├── MainFrame.java │ └── SegmentsIntersect.java ├── 2) Any segments intersection ├── src │ └── ru │ │ └── dubov │ │ └── anysegmentsintersect │ │ ├── MainFrame.form │ │ ├── MainFrame.java │ │ └── SegmentsIntersect.java └── test │ └── ru │ └── dubov │ └── anysegmentsintersect │ └── test │ └── SegmentsIntersectTest.java ├── 3) Convex hull ├── src │ └── ru │ │ └── dubov │ │ └── convexhull │ │ ├── ConvexHull.java │ │ ├── MainFrame.form │ │ └── MainFrame.java └── test │ └── ru │ └── dubov │ └── convexhull │ └── test │ └── ConvexHullTest.java ├── 4) Closest points pair ├── src │ └── ru │ │ └── dubov │ │ └── closestpair │ │ ├── ClosestPair.java │ │ ├── MainFrame.form │ │ └── MainFrame.java └── test │ └── ru │ └── dubov │ └── closestpair │ └── test │ └── ClosestPairTest.java ├── 5) Polygon triangulation ├── src │ └── ru │ │ └── dubov │ │ └── polygontriangulation │ │ ├── DivideAndConquerAlgorithm.java │ │ ├── DoublyLinkedCyclicList.java │ │ ├── MainFrame.form │ │ ├── MainFrame.java │ │ ├── MonotonePartitioningAlgorithm.java │ │ ├── SegmentsIntersect.java │ │ ├── VanGoghAlgorithm.java │ │ └── VanGoghPolygon.java └── test │ └── ru │ └── dubov │ └── polygontriangulation │ └── test │ └── VanGoghAlgorithmTest.java ├── 6) Point set Delaunay triangulation ├── src │ └── ru │ │ └── dubov │ │ └── delaunay │ │ ├── Delaunay.java │ │ ├── MainFrame.form │ │ ├── MainFrame.java │ │ ├── PointsTriangulation.java │ │ ├── TriangulationDAG.java │ │ └── VRMLTerrainScript.java └── test │ ├── Analysis.xlsx │ └── ru │ └── dubov │ └── delaunay │ └── test │ ├── DelaunayTest.java │ └── TerrainTriangulationTest.java ├── 7) Halfplanes intersection └── src │ └── ru │ └── dubov │ └── halfplanes │ ├── ConvexRegion.java │ ├── HalfplanesIntersection.java │ ├── MainFrame.form │ └── MainFrame.java ├── 8) Voronoi diagram └── src │ └── ru │ └── dubov │ └── voronoidiagram │ ├── MainFrame.form │ ├── MainFrame.java │ └── VoronoiDiagram.java ├── Primitives Library ├── .gitignore └── src │ └── ru │ └── dubov │ └── primitives │ ├── Circle.java │ ├── Halfplane.java │ ├── Line.java │ ├── Point.java │ ├── Polygon.java │ ├── Segment.java │ └── Triangle.java └── README.markdown /## Demo ##/1) Two segments intersection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/1) Two segments intersection.jar -------------------------------------------------------------------------------- /## Demo ##/2) Any segments intersection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/2) Any segments intersection.jar -------------------------------------------------------------------------------- /## Demo ##/3) Convex hull.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3) Convex hull.jar -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/README.markdown: -------------------------------------------------------------------------------- 1 | ## VRML Terrain construction 2 | 3 | This demo visualizes the construction of a 3D terrain using the Delaunay triangulation of point projections onto the two dimensional plane. As the tool of visulatization, VRML is used. 4 | 5 | ## Required software 6 | 7 | To play the demo, you need to have a VRML player. The player can be installed either as a browser plugin or as a special tool. Among different VRML player implementations the Instant reality tool is the latest and the most developed one. 8 | 9 | ## Important notes 10 | 11 | NB: There should be only latin letters in the path to this folder. 12 | 13 | ## Illustrations 14 | 15 | -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/Terrain.wrl: -------------------------------------------------------------------------------- 1 | #VRML V2.0 utf8 2 | 3 | DEF Direct01 DirectionalLight 4 | { 5 | direction .642 -.514 -.569 6 | } 7 | 8 | DEF terrain IndexedFaceSet 9 | { 10 | solid FALSE # Each triangle will have two faces 11 | 12 | coord DEF facePoints Coordinate 13 | { 14 | #point [] 15 | } 16 | 17 | #coordIndex [] 18 | } 19 | 20 | DEF terrainLines IndexedLineSet 21 | { 22 | coord DEF linePoints Coordinate 23 | { 24 | #point [] 25 | } 26 | 27 | #coordIndex [] 28 | } 29 | 30 | 31 | 32 | DEF script Script 33 | { 34 | directOutput TRUE 35 | mustEvaluate TRUE 36 | 37 | eventOut MFVec3f point_changed # fields with "USE" can be used here alternatively 38 | eventOut MFInt32 coordIndex_changed # fields with "USE" can be used here alternatively 39 | 40 | url "VRMLTerrainScript.class" 41 | } 42 | 43 | ROUTE script.point_changed TO facePoints.set_point 44 | ROUTE script.point_changed TO linePoints.set_point 45 | ROUTE script.coordIndex_changed TO terrain.set_coordIndex 46 | ROUTE script.coordIndex_changed TO terrainLines.set_coordIndex 47 | -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/VRMLTerrainScript$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/VRMLTerrainScript$1.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/VRMLTerrainScript$ProjectedPoint3D.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/VRMLTerrainScript$ProjectedPoint3D.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/VRMLTerrainScript.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/VRMLTerrainScript.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/Delaunay$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/Delaunay$1.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/Delaunay.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/Delaunay.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/TriangulationDAG$Node.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/TriangulationDAG$Node.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/TriangulationDAG.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/delaunay/TriangulationDAG.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Circle.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Circle.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Line.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Line.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Point.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Point.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Polygon.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Polygon.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Segment.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Segment.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle$1.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle$Side.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle$Side.class -------------------------------------------------------------------------------- /## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/3D terrain construction via VRML/ru/dubov/primitives/Triangle.class -------------------------------------------------------------------------------- /## Demo ##/4) Closest points pair.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/4) Closest points pair.jar -------------------------------------------------------------------------------- /## Demo ##/5) Polygon triangulation.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/5) Polygon triangulation.jar -------------------------------------------------------------------------------- /## Demo ##/6) Point set Delaunay triangulation.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/6) Point set Delaunay triangulation.jar -------------------------------------------------------------------------------- /## Demo ##/7) Halfplanes intersection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/7) Halfplanes intersection.jar -------------------------------------------------------------------------------- /## Demo ##/8) Voronoi diagram.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/8) Voronoi diagram.jar -------------------------------------------------------------------------------- /## Demo ##/lib/HalfplanesIntersection.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/lib/HalfplanesIntersection.jar -------------------------------------------------------------------------------- /## Demo ##/lib/PrimitivesLibrary.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/## Demo ##/lib/PrimitivesLibrary.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # NetBeans specific # 2 | nbproject/ 3 | build/ 4 | nbbuild/ 5 | dist/ 6 | nbdist/ 7 | nbactions.xml 8 | nb-configuration.xml 9 | build.xml 10 | manifest.mf 11 | 12 | # Class Files # 13 | *.class 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.ear 19 | 20 | # Eclipse 21 | .classpath 22 | .project 23 | .settings/ 24 | 25 | # Intellij 26 | .idea/ 27 | *.iml 28 | *.iws 29 | 30 | # Mac 31 | .DS_Store 32 | 33 | # Maven 34 | log/ 35 | target/ 36 | 37 | -------------------------------------------------------------------------------- /1) Two segments intersection/src/ru/dubov/segmentsintersect/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 | -------------------------------------------------------------------------------- /1) Two segments intersection/src/ru/dubov/segmentsintersect/MainFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | /* 7 | * MainFrame.java 8 | * 9 | * Created on Jan 17, 2012, 9:38:52 AM 10 | */ 11 | package ru.dubov.segmentsintersect; 12 | 13 | import java.awt.Color; 14 | import java.awt.Graphics; 15 | import ru.dubov.primitives.Point; 16 | import ru.dubov.primitives.Segment; 17 | 18 | /** 19 | * 20 | * @author MSDubov 21 | */ 22 | public class MainFrame extends javax.swing.JFrame { 23 | 24 | /** Creates new form MainFrame */ 25 | public MainFrame() { 26 | initComponents(); 27 | setLocationRelativeTo(null); 28 | c = 0; 29 | } 30 | 31 | /** This method is called from within the constructor to 32 | * initialize the form. 33 | * WARNING: Do NOT modify this code. The content of this method is 34 | * always regenerated by the Form Editor. 35 | */ 36 | @SuppressWarnings("unchecked") 37 | // //GEN-BEGIN:initComponents 38 | private void initComponents() { 39 | 40 | jPanel1 = new javax.swing.JPanel() { 41 | @Override 42 | public void paintComponent(Graphics g) { 43 | g.clearRect(0, 0, this.getWidth(), this.getHeight()); 44 | g.setColor(Color.BLACK); 45 | 46 | if (c == 1) { 47 | g.drawLine((int)p1.getX(), (int)p1.getY(), (int)mouseX, (int)mouseY); 48 | } 49 | if (c >= 2) { 50 | g.drawLine((int)p1.getX(), (int)p1.getY(), (int)p2.getX(), (int)p2.getY()); 51 | } 52 | if (c == 3) { 53 | g.drawLine((int)p3.getX(), (int)p3.getY(), (int)mouseX, (int)mouseY); 54 | } 55 | if (c == 4) { 56 | g.drawLine((int)p3.getX(), (int)p3.getY(), (int)p4.getX(), (int)p4.getY()); 57 | 58 | if(intersection) g.setColor(Color.GREEN); 59 | else g.setColor(Color.RED); 60 | 61 | g.fillRect(getWidth() - 17, 2, 15, 15); 62 | } 63 | } 64 | }; 65 | 66 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 67 | setTitle("Segments intersection"); 68 | 69 | jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); 70 | jPanel1.addMouseListener(new java.awt.event.MouseAdapter() { 71 | public void mousePressed(java.awt.event.MouseEvent evt) { 72 | jPanel1MousePressed(evt); 73 | } 74 | }); 75 | jPanel1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { 76 | public void mouseMoved(java.awt.event.MouseEvent evt) { 77 | jPanel1MouseMoved(evt); 78 | } 79 | }); 80 | 81 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); 82 | jPanel1.setLayout(jPanel1Layout); 83 | jPanel1Layout.setHorizontalGroup( 84 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 85 | .addGap(0, 378, Short.MAX_VALUE) 86 | ); 87 | jPanel1Layout.setVerticalGroup( 88 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 89 | .addGap(0, 276, Short.MAX_VALUE) 90 | ); 91 | 92 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 93 | getContentPane().setLayout(layout); 94 | layout.setHorizontalGroup( 95 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 96 | .addGroup(layout.createSequentialGroup() 97 | .addContainerGap() 98 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 99 | .addContainerGap()) 100 | ); 101 | layout.setVerticalGroup( 102 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 103 | .addGroup(layout.createSequentialGroup() 104 | .addContainerGap() 105 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 106 | .addContainerGap()) 107 | ); 108 | 109 | pack(); 110 | }// //GEN-END:initComponents 111 | 112 | Point p1, p2, p3, p4; 113 | int c; 114 | boolean intersection = false; 115 | 116 | double mouseX, mouseY; 117 | private void jPanel1MouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jPanel1MouseMoved 118 | mouseX = evt.getX(); 119 | mouseY = evt.getY(); 120 | 121 | jPanel1.repaint(); 122 | }//GEN-LAST:event_jPanel1MouseMoved 123 | 124 | private void jPanel1MousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jPanel1MousePressed 125 | 126 | if (c == 4) c = 0; 127 | 128 | if (c == 0) { 129 | p1 = new Point(evt.getX(), evt.getY()); 130 | } else if (c == 1) { 131 | p2 = new Point(evt.getX(), evt.getY()); 132 | } else if (c == 2) { 133 | p3 = new Point(evt.getX(), evt.getY()); 134 | } else if (c == 3) { 135 | p4 = new Point(evt.getX(), evt.getY()); 136 | Segment s1 = new Segment(p1, p2); 137 | Segment s2 = new Segment(p3, p4); 138 | intersection = SegmentsIntersect.two(s1, s2); 139 | } 140 | 141 | c++; 142 | 143 | jPanel1.repaint(); 144 | }//GEN-LAST:event_jPanel1MousePressed 145 | 146 | /** 147 | * @param args the command line arguments 148 | */ 149 | public static void main(String args[]) { 150 | /* Set the Nimbus look and feel */ 151 | // 152 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. 153 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 154 | */ 155 | try { 156 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { 157 | if ("Nimbus".equals(info.getName())) { 158 | javax.swing.UIManager.setLookAndFeel(info.getClassName()); 159 | break; 160 | } 161 | } 162 | } catch (ClassNotFoundException ex) { 163 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 164 | } catch (InstantiationException ex) { 165 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 166 | } catch (IllegalAccessException ex) { 167 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 168 | } catch (javax.swing.UnsupportedLookAndFeelException ex) { 169 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 170 | } 171 | // 172 | 173 | /* Create and display the form */ 174 | java.awt.EventQueue.invokeLater(new Runnable() { 175 | 176 | public void run() { 177 | new MainFrame().setVisible(true); 178 | } 179 | }); 180 | } 181 | // Variables declaration - do not modify//GEN-BEGIN:variables 182 | private javax.swing.JPanel jPanel1; 183 | // End of variables declaration//GEN-END:variables 184 | } 185 | -------------------------------------------------------------------------------- /1) Two segments intersection/src/ru/dubov/segmentsintersect/SegmentsIntersect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package ru.dubov.segmentsintersect; 6 | 7 | 8 | import ru.dubov.primitives.Point; 9 | import ru.dubov.primitives.Segment; 10 | 11 | /** 12 | * Determines whether the two given segments intersect in O(1), 13 | * as described in "Introduction to Algorithms" by Cormen et al. 14 | * 15 | * @author Mikhail Dubov 16 | */ 17 | public class SegmentsIntersect { 18 | 19 | private static double direction(Point p0, Point p1, Point p2) { 20 | return ((p2.getX() - p0.getX()) * (p1.getY() - p0.getY()) - 21 | (p2.getY() - p0.getY()) * (p1.getX() - p0.getX())); 22 | } 23 | 24 | private static boolean onSegment(Point pi, Point pj, Point pk) { 25 | return (Math.min(pi.getX(), pj.getX()) <= pk.getX() && 26 | pk.getX() <= Math.max(pi.getX(), pj.getX()) && 27 | Math.min(pi.getY(), pj.getY()) <= pk.getY() && 28 | pk.getY() <= Math.max(pi.getY(), pj.getY())); 29 | } 30 | 31 | public static boolean two(Segment s1, Segment s2) { 32 | 33 | Point p1 = s1.getLeft(); 34 | Point p2 = s1.getRight(); 35 | Point p3 = s2.getLeft(); 36 | Point p4 = s2.getRight(); 37 | 38 | double d1 = direction(p3, p4, p1); 39 | double d2 = direction(p3, p4, p2); 40 | double d3 = direction(p1, p2, p3); 41 | double d4 = direction(p1, p2, p4); 42 | 43 | if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && 44 | ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) { 45 | return true; 46 | } else if (d1 == 0 && onSegment(p3, p4, p1)) { 47 | return true; 48 | } else if (d2 == 0 && onSegment(p3, p4, p2)) { 49 | return true; 50 | } else if (d3 == 0 && onSegment(p1, p2, p3)) { 51 | return true; 52 | } else if (d4 == 0 && onSegment(p1, p2, p4)) { 53 | return true; 54 | } else { 55 | return false; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /2) Any segments intersection/src/ru/dubov/anysegmentsintersect/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
182 | -------------------------------------------------------------------------------- /2) Any segments intersection/src/ru/dubov/anysegmentsintersect/SegmentsIntersect.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.anysegmentsintersect; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.TreeSet; 7 | import ru.dubov.primitives.Point; 8 | import ru.dubov.primitives.Segment; 9 | 10 | 11 | public class SegmentsIntersect { 12 | 13 | /** 14 | * Determines whether any two segments in the given set intersect 15 | * using the naive approach that runs in O(n^2). 16 | * 17 | * @param segments The set of segments 18 | * @return true, if any segments intersect, false otherwise 19 | */ 20 | public static boolean any_Naive(ArrayList segments) { 21 | for (int i = 0; i < segments.size() - 1; i++) { 22 | for (int j = i + 1; j < segments.size(); j++) { 23 | if (SegmentsIntersect.two(segments.get(i), segments.get(j))) { 24 | 25 | segm1 = segments.get(i); 26 | segm2 = segments.get(j); 27 | 28 | return true; 29 | } 30 | } 31 | } 32 | 33 | return false; 34 | } 35 | 36 | /** 37 | * Determines whether any two segments in the given set intersect 38 | * using the "sweeping line" algorithm that runs in O(n*lg(n)). 39 | * 40 | * @param segments The set of segments 41 | * @return true, if any segments intersect, false otherwise 42 | */ 43 | public static boolean any(ArrayList segments) { 44 | 45 | // A Red-Black tree that will support an ordered set of 46 | // segments with O(n*lg(n)) time complexity 47 | TreeSet segmentsTree = 48 | new TreeSet(segmentsComparator); 49 | 50 | // An array of segment endpoints, sorted by their X coordinate 51 | ArrayList points = new ArrayList(); 52 | for (Segment s : segments) { 53 | points.add(s.getLeft()); 54 | points.add(s.getRight()); 55 | } 56 | Collections.sort(points, PointsComparatorX); 57 | 58 | Segment pSegm; 59 | foundBoundaryCase = false; 60 | 61 | for (Point p : points) { // Проход по упорядоченному списку точек 62 | 63 | // Set the X coordinate that will be used to perform the comparison 64 | segmentsComparator.setX(p.getX()); 65 | 66 | pSegm = p.getSegment(); 67 | 68 | if (p.isLeft()) { 69 | 70 | segmentsTree.add(pSegm); // INSERT 71 | 72 | if ((segmentsTree.lower(pSegm) != null && 73 | SegmentsIntersect.two(segmentsTree.lower(pSegm), pSegm))) { 74 | 75 | segm1 = segmentsTree.lower(pSegm); 76 | segm2 = pSegm; 77 | 78 | return true; 79 | } 80 | 81 | if ((segmentsTree.higher(pSegm) != null && 82 | SegmentsIntersect.two(segmentsTree.higher(pSegm), pSegm))) { 83 | 84 | segm1 = segmentsTree.higher(pSegm); 85 | segm2 = pSegm; 86 | 87 | return true; 88 | } 89 | 90 | if(foundBoundaryCase) { 91 | return true; 92 | } 93 | 94 | } else { // p.isRight() 95 | 96 | if(segmentsTree.lower(pSegm) != null && 97 | segmentsTree.higher(pSegm) != null && 98 | SegmentsIntersect.two(segmentsTree.higher(pSegm), 99 | segmentsTree.lower(pSegm))) { 100 | 101 | segm1 = segmentsTree.higher(pSegm); 102 | segm2 = segmentsTree.lower(pSegm); 103 | 104 | return true; 105 | } 106 | 107 | segmentsTree.remove(pSegm); 108 | } 109 | 110 | } 111 | 112 | return false; 113 | } 114 | 115 | /** 116 | * Returns a pair of intersecting segments. 117 | * (to be called after the algorithm finished its work). 118 | * 119 | * @return ArrayList that contains the required pair. 120 | */ 121 | public static ArrayList intersectingSegments() { 122 | ArrayList result = new ArrayList(); 123 | 124 | result.add(segm1); 125 | result.add(segm2); 126 | 127 | return result; 128 | } 129 | 130 | /** 131 | * Determines whether the two segments intersect in O(1). 132 | * 133 | * @param s1 The first segment 134 | * @param s2 The second segment 135 | * @return true, if the segments intersect, false otherwise 136 | */ 137 | public static boolean two(Segment s1, Segment s2) { 138 | 139 | Point p1 = s1.getLeft(); 140 | Point p2 = s1.getRight(); 141 | Point p3 = s2.getLeft(); 142 | Point p4 = s2.getRight(); 143 | 144 | double d1 = direction(p3, p4, p1); 145 | double d2 = direction(p3, p4, p2); 146 | double d3 = direction(p1, p2, p3); 147 | double d4 = direction(p1, p2, p4); 148 | 149 | if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && 150 | ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) { 151 | return true; 152 | } else if (d1 == 0 && onSegment(p3, p4, p1)) { 153 | return true; 154 | } else if (d2 == 0 && onSegment(p3, p4, p2)) { 155 | return true; 156 | } else if (d3 == 0 && onSegment(p1, p2, p3)) { 157 | return true; 158 | } else if (d4 == 0 && onSegment(p1, p2, p4)) { 159 | return true; 160 | } else { 161 | return false; 162 | } 163 | } 164 | 165 | private static double direction(Point p0, Point p1, Point p2) { 166 | return ((p2.getX() - p0.getX()) * (p1.getY() - p0.getY()) - 167 | (p2.getY() - p0.getY()) * (p1.getX() - p0.getX())); 168 | } 169 | 170 | private static boolean onSegment(Point pi, Point pj, Point pk) { 171 | return (Math.min(pi.getX(), pj.getX()) <= pk.getX() && 172 | pk.getX() <= Math.max(pi.getX(), pj.getX()) && 173 | Math.min(pi.getY(), pj.getY()) <= pk.getY() && 174 | pk.getY() <= Math.max(pi.getY(), pj.getY())); 175 | } 176 | 177 | /** 178 | * Compares two segments in some X coordinate. 179 | */ 180 | static class SegmentsComparator implements Comparator { 181 | 182 | @Override 183 | public int compare(Segment s1, Segment s2) { 184 | 185 | // The case when the points are not comparable in the X coordinate 186 | if (x < s1.getLeft().getX() || x > s1.getRight().getX() || 187 | x < s2.getLeft().getX() || x > s2.getRight().getX()) { 188 | return 0; 189 | } 190 | 191 | // Compute the Y coordinates for the corresponding X coordinates 192 | double y1 = yForX(s1, x); 193 | double y2 = yForX(s2, x); 194 | 195 | if (Double.isNaN(y1)) { 196 | if(s1.getLeft().getY() >= y2 && s1.getRight().getY() <= y2) { 197 | // a boundary case 198 | segm1 = s1; 199 | segm2 = s2; 200 | foundBoundaryCase = true; 201 | } 202 | if (s1.getLeft().getY() < y2) { 203 | return -1; 204 | } else if (s1.getLeft().getY() > y2) { 205 | return 1; 206 | } else { 207 | return 0; 208 | } 209 | } else if (Double.isNaN(y2)) { 210 | if(s2.getLeft().getY() >= y1 && s2.getRight().getY() <= y1) { 211 | // a boundary case 212 | segm1 = s1; 213 | segm2 = s2; 214 | foundBoundaryCase = true; 215 | } 216 | if (s2.getLeft().getY() < y1) { 217 | return 1; 218 | } else if (s2.getLeft().getY() > y1) { 219 | return -1; 220 | } else { 221 | return 0; 222 | } 223 | } else if(y1 < y2) { 224 | return -1; 225 | } else if (y1 > y2) { 226 | return 1; 227 | } else { 228 | if(s1 != s2) { 229 | // a boundary case 230 | segm1 = s1; 231 | segm2 = s2; 232 | foundBoundaryCase = true; 233 | } 234 | return 0; 235 | } 236 | } 237 | 238 | /** 239 | * Sets the X coordinate to perform the comparison in. 240 | */ 241 | public void setX(double x) { 242 | this.x = x; 243 | } 244 | 245 | /** 246 | * Calculates the Y coordinate of a point on the 247 | * segment by its X coordinate. 248 | */ 249 | private double yForX(Segment s, double x) { 250 | return (s.getRight().getX()*s.getLeft().getY() - 251 | s.getLeft().getX()*s.getRight().getY() - 252 | x*(s.getLeft().getY() - s.getRight().getY())) / 253 | (s.getRight().getX() - s.getLeft().getX()); 254 | } 255 | 256 | private double x; 257 | } 258 | 259 | 260 | 261 | // Comparators initialization 262 | static { 263 | segmentsComparator = new SegmentsComparator(); 264 | 265 | PointsComparatorX = new Comparator() { 266 | 267 | public int compare(Point p1, Point p2) { 268 | if (p1.getX() < p2.getX()) { 269 | return -1; 270 | } else if (p1.getX() > p2.getX()) { 271 | return 1; 272 | } else { 273 | // If the X coordinates are the same, then 274 | // the left point should go before the right one 275 | if (p1.isLeft() && p2.isRight()) { 276 | return -1; 277 | } else if (p1.isRight() && p2.isLeft()) { 278 | return 1; 279 | } else { 280 | // If still the same, then sort by the Y coordinate 281 | if (p1.getY() < p2.getY()) { 282 | return -1; 283 | } else if (p1.getY() > p2.getY()) { 284 | return 1; 285 | } else { 286 | return 0; 287 | } 288 | } 289 | } 290 | } 291 | }; 292 | } 293 | 294 | // The sweeping line algorithm requires two comparators: 295 | // the one for comparing two segments in some X coordinate 296 | // (user in the RB-tree), and the other for the 297 | // initial points sorting "from the left to the right". 298 | private static SegmentsComparator segmentsComparator; 299 | private static Comparator PointsComparatorX; 300 | 301 | // Dealing with the boundary cases 302 | private static boolean foundBoundaryCase; 303 | 304 | // Here a pair of intersecting segments is stored 305 | private static Segment segm1, segm2; 306 | } -------------------------------------------------------------------------------- /2) Any segments intersection/test/ru/dubov/anysegmentsintersect/test/SegmentsIntersectTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.anysegmentsintersect.test; 2 | 3 | import ru.dubov.anysegmentsintersect.SegmentsIntersect; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | import junit.framework.TestCase; 7 | import ru.dubov.anysegmentsintersect.SegmentsIntersect; 8 | import ru.dubov.primitives.Point; 9 | import ru.dubov.primitives.Segment; 10 | 11 | public class SegmentsIntersectTest extends TestCase { 12 | 13 | public void testAnySegmentsIntersect_Prepared() { 14 | 15 | ArrayList segments; 16 | 17 | // Test case 1 18 | 19 | segments = new ArrayList(); 20 | 21 | segments.add(new Segment(new Point(0, 0), new Point(1, 0))); 22 | segments.add(new Segment(new Point(2, 0), new Point(3, 0))); 23 | segments.add(new Segment(new Point(4, 0), new Point(5, 0))); 24 | segments.add(new Segment(new Point(4.3, 1), new Point(4.7, -1))); 25 | segments.add(new Segment(new Point(6, 0), new Point(7, 0))); 26 | 27 | assertTrue(SegmentsIntersect.any(segments)); 28 | 29 | // Test case 2 30 | 31 | segments = new ArrayList(); 32 | 33 | segments.add(new Segment(new Point(0, 0), new Point(1, 0))); 34 | segments.add(new Segment(new Point(2, 0), new Point(3, 0))); 35 | segments.add(new Segment(new Point(4, 0), new Point(5, 0))); 36 | 37 | assertTrue(! SegmentsIntersect.any(segments)); 38 | } 39 | 40 | public void testAnySegmentsIntersect_Boundary() { 41 | 42 | ArrayList segments; 43 | 44 | // Test case 1 45 | 46 | segments = new ArrayList(); 47 | 48 | segments.add(new Segment(new Point(0, 0), new Point(2, 1))); 49 | segments.add(new Segment(new Point(2, 1), new Point(3, 0))); 50 | segments.add(new Segment(new Point(4, 0), new Point(5, 0))); 51 | 52 | assertTrue(SegmentsIntersect.any(segments)); 53 | 54 | // Test case 2 55 | 56 | segments = new ArrayList(); 57 | 58 | segments.add(new Segment(new Point(0, 0), new Point(2, 1))); 59 | segments.add(new Segment(new Point(2, 2), new Point(3, 0))); 60 | segments.add(new Segment(new Point(2, 2), new Point(5, 0))); 61 | 62 | assertTrue(SegmentsIntersect.any(segments)); 63 | } 64 | 65 | public void testAnySegmentsIntersect_Randomized() { 66 | 67 | Random rand = new Random(); 68 | 69 | for (int repeat = 0; repeat < 1000; repeat++) { 70 | for (int i = 2; i <= 20; i++) { 71 | ArrayList segments = new ArrayList(); 72 | 73 | for(int j = 0; j < i; j++) { 74 | segments.add(new Segment(new Point(rand.nextDouble()*100, rand.nextDouble()*100), 75 | new Point(rand.nextDouble()*100, rand.nextDouble()*100))); 76 | } 77 | 78 | assertTrue(SegmentsIntersect.any(segments) == 79 | SegmentsIntersect.any_Naive(segments)); 80 | } 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /3) Convex hull/src/ru/dubov/convexhull/ConvexHull.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.convexhull; 2 | 3 | import java.util.*; 4 | import ru.dubov.primitives.Point; 5 | import ru.dubov.primitives.Polygon; 6 | 7 | /** 8 | * Builds the convex hull of a given set of points 9 | * using different algorithms. 10 | * 11 | * @author Mikhail Dubov 12 | */ 13 | public class ConvexHull { 14 | 15 | /** 16 | * Builds the convex hull of a given set of points 17 | * using the Graham's scan algorithm - O(n*log(n)). 18 | */ 19 | public static Polygon Graham(ArrayList pointsList) { 20 | 21 | // Clone the list of points as it will be changed during the algorithm. 22 | // We assume that pointsList.size >= 3. 23 | 24 | ArrayList points = (ArrayList)pointsList.clone(); 25 | Stack result = new Stack(); 26 | 27 | // Get the lowest most left point - the first vertex of the convex hull - in O(n) 28 | 29 | Point p0 = getLowestPoint(points); 30 | result.push(p0); 31 | points.remove(p0); 32 | 33 | // Sort the rest of the points by their polar angle - in O(n*log(n)) 34 | 35 | Collections.sort(points, new PointComparator(p0)); 36 | 37 | // Add two more points to the stack before starting the Graham's scan 38 | 39 | Iterator iter = points.iterator(); 40 | 41 | Point p1 = iter.next(); 42 | result.push(p1); 43 | 44 | Point p2 = iter.next(); 45 | result.push(p2); 46 | 47 | // Traverse the sorted point list, determining which points 48 | // should go to the convex hull - in O(n) 49 | 50 | while (iter.hasNext()) { 51 | Point pi = iter.next(); 52 | Point top = result.elementAt(result.size() - 1); 53 | Point nextToTop = result.elementAt(result.size() - 2); 54 | 55 | while (! isLeftTurn(nextToTop, top, pi)) { 56 | result.pop(); 57 | 58 | // Update the "top" vertex 59 | top = nextToTop; 60 | nextToTop = result.elementAt(result.size() - 2); 61 | } 62 | 63 | result.push(pi); 64 | } 65 | 66 | // The convex hull has been constructed, now return the list 67 | // of its vertices in CCW order 68 | 69 | ArrayList polygon = new ArrayList(); 70 | while(! result.empty()) { 71 | polygon.add(result.pop()); 72 | } 73 | Collections.reverse(polygon); 74 | 75 | return new Polygon(polygon); 76 | } 77 | 78 | /** 79 | * Builds the convex hull of a given set of points 80 | * using the Jarvis' march algorithm - O(n*h), 81 | * where h is the number of vertises in the hull, 82 | * so the worst case is O(n^2). 83 | */ 84 | public static Polygon Jarvis(ArrayList pointsList) { 85 | 86 | // Clone the list of points as it will be changed during the algorithm. 87 | // We assume that pointsList.size >= 3. 88 | 89 | ArrayList points = (ArrayList)pointsList.clone(); 90 | ArrayList result = new ArrayList(); 91 | 92 | // Get the lowest most left point - the first vertex of the convex hull - in O(n) 93 | 94 | Point p0 = getLowestPoint(points); 95 | result.add(p0); 96 | points.remove(p0); 97 | 98 | // Get the last vertex of the convex hull - in O(n) 99 | 100 | Point pE = points.get(0); 101 | Point candidate; 102 | for(int i = 1; i < points.size(); i++) { 103 | candidate = points.get(i); 104 | if(crossProduct(p0, candidate, pE) < 0) { 105 | pE = candidate; 106 | } 107 | } 108 | 109 | // Construct the chain - O(h) steps... 110 | 111 | Point next = null; 112 | Point last = p0; 113 | do { 114 | // ... with each step scanning O(n) points, 115 | // so O(n*h) operations in total 116 | next = points.get(0); 117 | 118 | for(int i = 1; i < points.size(); i++) { 119 | candidate = points.get(i); 120 | if (crossProduct(last, candidate, next) > 0 || 121 | crossProduct(last, candidate, next) == 0 && 122 | last.dist(candidate) > last.dist(next)) { 123 | next = candidate; 124 | } 125 | } 126 | 127 | result.add(next); 128 | points.remove(next); 129 | last = next; 130 | 131 | } while(next != pE); 132 | 133 | // The convex hull has been constructed, now return the list 134 | // of its vertices in CCW order 135 | 136 | return new Polygon(result); 137 | } 138 | 139 | /** 140 | * Compares two points by their polar angles 141 | * using the cross product. 142 | */ 143 | static class PointComparator implements Comparator { 144 | 145 | private Point p0; 146 | 147 | public PointComparator(Point p0) { 148 | this.p0 = p0; 149 | } 150 | 151 | // Compare two points by their polar angle with respect to p0; 152 | // in case the angle is the same, the distance to p0 is used 153 | @Override 154 | public int compare(Point p1, Point p2) { 155 | 156 | double crossProduct = crossProduct(p0, p1, p2); 157 | 158 | if (crossProduct > 0) return -1; 159 | if (crossProduct < 0) return 1; 160 | 161 | // cross_product = 0 => the vectors are collinear, and the vertex 162 | // that lies further from p0 should be considered "larger" 163 | double d1 = p0.dist(p1); 164 | double d2 = p0.dist(p2); 165 | 166 | if (d1 < d2) return -1; 167 | if (d1 > d2) return 1; 168 | 169 | return 0; 170 | } 171 | } 172 | 173 | private static double crossProduct(Point p0, Point p1, Point p2) { 174 | return (p1.getX() - p0.getX()) * (p2.getY() - p0.getY()) - 175 | (p2.getX() - p0.getX()) * (p1.getY() - p0.getY()); 176 | } 177 | 178 | private static boolean isLeftTurn(Point p0, Point p1, Point p2) { 179 | return (crossProduct(p0, p1, p2) > 0); 180 | } 181 | 182 | private static Point getLowestPoint(ArrayList points) { 183 | Point result = points.get(0); 184 | 185 | Point candidate; 186 | for (int i = 1; i < points.size(); i++) { 187 | candidate = points.get(i); 188 | 189 | if (candidate.getY() < result.getY() || 190 | candidate.getY() == result.getY() && candidate.getX() < result.getX()) { 191 | result = candidate; 192 | } 193 | } 194 | 195 | return result; 196 | } 197 | 198 | private static void printPointsList(ArrayList points) { 199 | for (Point p : points) { 200 | System.out.println(p); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /3) Convex hull/src/ru/dubov/convexhull/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |
178 | -------------------------------------------------------------------------------- /3) Convex hull/src/ru/dubov/convexhull/MainFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MainFrame.java 3 | * 4 | * Created on Jan 25, 2012, 6:00:08 AM 5 | */ 6 | package ru.dubov.convexhull; 7 | 8 | import java.awt.Color; 9 | import java.awt.Graphics; 10 | import java.util.ArrayList; 11 | import ru.dubov.primitives.Point; 12 | import ru.dubov.primitives.Polygon; 13 | 14 | 15 | public class MainFrame extends javax.swing.JFrame { 16 | 17 | /** Creates new form MainFrame */ 18 | public MainFrame() { 19 | initComponents(); 20 | setLocationRelativeTo(null); 21 | points = new ArrayList(); 22 | state = 0; 23 | } 24 | 25 | /** This method is called from within the constructor to 26 | * initialize the form. 27 | * WARNING: Do NOT modify this code. The content of this method is 28 | * always regenerated by the Form Editor. 29 | */ 30 | @SuppressWarnings("unchecked") 31 | // //GEN-BEGIN:initComponents 32 | private void initComponents() { 33 | 34 | jPanel1 = new javax.swing.JPanel() { 35 | @Override 36 | public void paintComponent(Graphics g) { 37 | 38 | g.clearRect(0, 0, this.getWidth(), this.getHeight()); 39 | g.setColor(Color.BLACK); 40 | 41 | for(Point p : points) { 42 | g.fillOval((int)p.getX() - 3, 43 | (int)(getHeight() - p.getY() - 3), 5, 5); 44 | } 45 | 46 | if (state == 1) { // Рисуем оболочку 47 | Point cur, next; 48 | for(int i = 0; i < convexHull.size(); i++) { 49 | cur = convexHull.get(i); 50 | next = convexHull.get((i + 1) % convexHull.size()); 51 | g.drawLine((int)cur.getX(), (int)(getHeight() - cur.getY()), 52 | (int)next.getX(), (int)(getHeight() - next.getY())); 53 | } 54 | } 55 | } 56 | }; 57 | jPanel4 = new javax.swing.JPanel(); 58 | jPanel3 = new javax.swing.JPanel(); 59 | jButton3 = new javax.swing.JButton(); 60 | jButton4 = new javax.swing.JButton(); 61 | jPanel2 = new javax.swing.JPanel(); 62 | jButton1 = new javax.swing.JButton(); 63 | jButton2 = new javax.swing.JButton(); 64 | 65 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 66 | setTitle("Convex hull"); 67 | 68 | jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); 69 | jPanel1.addMouseListener(new java.awt.event.MouseAdapter() { 70 | public void mousePressed(java.awt.event.MouseEvent evt) { 71 | jPanel1MousePressed(evt); 72 | } 73 | }); 74 | 75 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); 76 | jPanel1.setLayout(jPanel1Layout); 77 | jPanel1Layout.setHorizontalGroup( 78 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 79 | .addGap(0, 553, Short.MAX_VALUE) 80 | ); 81 | jPanel1Layout.setVerticalGroup( 82 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 83 | .addGap(0, 283, Short.MAX_VALUE) 84 | ); 85 | 86 | jButton3.setText("Points only"); 87 | jButton3.addActionListener(new java.awt.event.ActionListener() { 88 | public void actionPerformed(java.awt.event.ActionEvent evt) { 89 | jButton3ActionPerformed(evt); 90 | } 91 | }); 92 | 93 | jButton4.setText("Clear"); 94 | jButton4.addActionListener(new java.awt.event.ActionListener() { 95 | public void actionPerformed(java.awt.event.ActionEvent evt) { 96 | jButton4ActionPerformed(evt); 97 | } 98 | }); 99 | 100 | javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); 101 | jPanel3.setLayout(jPanel3Layout); 102 | jPanel3Layout.setHorizontalGroup( 103 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 104 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup() 105 | .addContainerGap(24, Short.MAX_VALUE) 106 | .addComponent(jButton3) 107 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 108 | .addComponent(jButton4)) 109 | ); 110 | jPanel3Layout.setVerticalGroup( 111 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 112 | .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 113 | .addComponent(jButton4, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE) 114 | .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)) 115 | ); 116 | 117 | jButton1.setText("Graham's scan - O(n∙log(n))"); 118 | jButton1.addActionListener(new java.awt.event.ActionListener() { 119 | public void actionPerformed(java.awt.event.ActionEvent evt) { 120 | jButton1ActionPerformed(evt); 121 | } 122 | }); 123 | 124 | jButton2.setText("Jarvis's march - O(n∙h)"); 125 | jButton2.addActionListener(new java.awt.event.ActionListener() { 126 | public void actionPerformed(java.awt.event.ActionEvent evt) { 127 | jButton2ActionPerformed(evt); 128 | } 129 | }); 130 | 131 | javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); 132 | jPanel2.setLayout(jPanel2Layout); 133 | jPanel2Layout.setHorizontalGroup( 134 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 135 | .addGroup(jPanel2Layout.createSequentialGroup() 136 | .addComponent(jButton1) 137 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 138 | .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) 139 | .addContainerGap()) 140 | ); 141 | jPanel2Layout.setVerticalGroup( 142 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 143 | .addGroup(jPanel2Layout.createSequentialGroup() 144 | .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 145 | .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) 146 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)) 147 | .addContainerGap(1, Short.MAX_VALUE)) 148 | ); 149 | 150 | javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4); 151 | jPanel4.setLayout(jPanel4Layout); 152 | jPanel4Layout.setHorizontalGroup( 153 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 154 | .addGroup(jPanel4Layout.createSequentialGroup() 155 | .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 156 | .addGap(59, 59, 59) 157 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 158 | ); 159 | jPanel4Layout.setVerticalGroup( 160 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 161 | .addComponent(jPanel2, 0, 35, Short.MAX_VALUE) 162 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 163 | ); 164 | 165 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 166 | getContentPane().setLayout(layout); 167 | layout.setHorizontalGroup( 168 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 169 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 170 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 171 | ); 172 | layout.setVerticalGroup( 173 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 174 | .addGroup(layout.createSequentialGroup() 175 | .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 176 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 177 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 178 | ); 179 | 180 | pack(); 181 | }// //GEN-END:initComponents 182 | 183 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed 184 | if (points.size() < 3) return; 185 | 186 | convexHull = ConvexHull.Graham(points); 187 | 188 | state = 1; 189 | 190 | jPanel1.repaint(); 191 | }//GEN-LAST:event_jButton1ActionPerformed 192 | 193 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed 194 | if (points.size() < 3) return; 195 | 196 | convexHull = ConvexHull.Jarvis(points); 197 | 198 | state = 1; 199 | 200 | jPanel1.repaint(); 201 | }//GEN-LAST:event_jButton2ActionPerformed 202 | 203 | private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed 204 | state = 0; 205 | 206 | jPanel1.repaint(); 207 | }//GEN-LAST:event_jButton3ActionPerformed 208 | 209 | private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed 210 | points = new ArrayList(); 211 | state = 0; 212 | 213 | jPanel1.repaint(); 214 | }//GEN-LAST:event_jButton4ActionPerformed 215 | 216 | private void jPanel1MousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jPanel1MousePressed 217 | if (state == 0) { 218 | Point pi = new Point(evt.getX(), jPanel1.getHeight() - evt.getY()); 219 | points.add(pi); 220 | } 221 | 222 | jPanel1.repaint(); 223 | }//GEN-LAST:event_jPanel1MousePressed 224 | 225 | /** 226 | * @param args the command line arguments 227 | */ 228 | public static void main(String args[]) { 229 | /* Set the Nimbus look and feel */ 230 | // 231 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. 232 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 233 | */ 234 | try { 235 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { 236 | if ("Nimbus".equals(info.getName())) { 237 | javax.swing.UIManager.setLookAndFeel(info.getClassName()); 238 | break; 239 | } 240 | } 241 | } catch (ClassNotFoundException ex) { 242 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 243 | } catch (InstantiationException ex) { 244 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 245 | } catch (IllegalAccessException ex) { 246 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 247 | } catch (javax.swing.UnsupportedLookAndFeelException ex) { 248 | java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 249 | } 250 | // 251 | 252 | /* Create and display the form */ 253 | java.awt.EventQueue.invokeLater(new Runnable() { 254 | 255 | public void run() { 256 | new MainFrame().setVisible(true); 257 | } 258 | }); 259 | } 260 | // Variables declaration - do not modify//GEN-BEGIN:variables 261 | private javax.swing.JButton jButton1; 262 | private javax.swing.JButton jButton2; 263 | private javax.swing.JButton jButton3; 264 | private javax.swing.JButton jButton4; 265 | private javax.swing.JPanel jPanel1; 266 | private javax.swing.JPanel jPanel2; 267 | private javax.swing.JPanel jPanel3; 268 | private javax.swing.JPanel jPanel4; 269 | // End of variables declaration//GEN-END:variables 270 | 271 | private ArrayList points; 272 | private Polygon convexHull; 273 | private int state; 274 | } 275 | -------------------------------------------------------------------------------- /3) Convex hull/test/ru/dubov/convexhull/test/ConvexHullTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.convexhull.test; 2 | 3 | import ru.dubov.convexhull.ConvexHull; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | import junit.framework.TestCase; 7 | 8 | import ru.dubov.convexhull.ConvexHull; 9 | import ru.dubov.primitives.Point; 10 | import ru.dubov.primitives.Polygon; 11 | 12 | public class ConvexHullTest extends TestCase { 13 | 14 | private void checkConvexHull(ArrayList points) { 15 | 16 | // printPointsList(points); System.out.println(); 17 | 18 | Polygon CH_Graham = ConvexHull.Graham(points); 19 | Polygon CH_Jarvis = ConvexHull.Jarvis(points); 20 | 21 | //printPointsList(CH_Graham); System.out.println(); 22 | //printPointsList(CH_Jarvis); System.out.println(); 23 | //System.out.println("---------------------"); 24 | 25 | if (CH_Graham.size() != CH_Jarvis.size()) { 26 | fail("CH's are of different size!"); 27 | } 28 | 29 | for (int i = 0; i < CH_Graham.size(); i++) { 30 | if(! (CH_Graham.get(i)).equals(CH_Jarvis.get(i))) { 31 | fail("Different points: " + (CH_Graham.get(i)) + 32 | " in Graham and " + (CH_Jarvis.get(i)) + 33 | " in Jarvis."); 34 | } 35 | } 36 | 37 | assertTrue(true); 38 | } 39 | 40 | public void testConvexHull_Example() { 41 | 42 | ArrayList points = new ArrayList(); 43 | points.add(new Point(1, 0)); 44 | points.add(new Point(2, 7)); 45 | points.add(new Point(20, 10)); // !!! 46 | points.add(new Point(10, 10)); // Граничный случай 47 | points.add(new Point(0, 10)); // !!! 48 | points.add(new Point(1, 1)); 49 | points.add(new Point(-5, 5)); 50 | 51 | checkConvexHull(points); 52 | } 53 | 54 | public void testConvexHull_Randomized() { 55 | 56 | Random rand = new Random(); 57 | 58 | for (int n = 3; n <= 20; n++) { 59 | ArrayList points = new ArrayList(); 60 | 61 | for(int i = 0; i < n; i++) { 62 | points.add(new Point(rand.nextDouble()*100, 63 | rand.nextDouble()*100)); 64 | } 65 | 66 | checkConvexHull(points); 67 | } 68 | } 69 | 70 | private static void printPointsList(ArrayList points) { 71 | for (Point p : points) { 72 | System.out.println(p); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /4) Closest points pair/src/ru/dubov/closestpair/ClosestPair.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.closestpair; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | 7 | import ru.dubov.primitives.Point; 8 | 9 | /** 10 | * Finds the pair of closest points using different algorithms. 11 | * 12 | * @author Mikhail Dubov 13 | */ 14 | public class ClosestPair { 15 | 16 | /** 17 | * Finds the pair of closest points using a naive method in O(n^2). 18 | */ 19 | public static ArrayList Naive(ArrayList points) { 20 | Point p0 = points.get(0); 21 | Point p1 = points.get(1); 22 | 23 | for (int i = 0; i < points.size() - 1; i++) { 24 | for (int j = i + 1; j < points.size(); j++) { 25 | if(dist(points.get(i), points.get(j)) < dist(p0, p1)) { 26 | p0 = points.get(i); 27 | p1 = points.get(j); 28 | } 29 | } 30 | } 31 | 32 | ArrayList resultPair = new ArrayList(); 33 | resultPair.add(p0); 34 | resultPair.add(p1); 35 | 36 | return resultPair; 37 | } 38 | 39 | /** 40 | * Finds the pair of closest points using 41 | * a "divide and conquer" algorithm in O(n*log(n)). 42 | */ 43 | public static ArrayList Fast(ArrayList points) { 44 | ArrayList X = (ArrayList)points.clone(); 45 | ArrayList Y = (ArrayList)points.clone(); 46 | 47 | Collections.sort(X, new PointComparatorX()); 48 | Collections.sort(Y, new PointComparatorY()); 49 | 50 | return ClosestPair(X, Y); 51 | } 52 | 53 | /** 54 | * The recursive procedure for the "Fast" algorithm. 55 | */ 56 | private static ArrayList ClosestPair(ArrayList X, ArrayList Y) { 57 | 58 | ArrayList resultPair = new ArrayList(); 59 | 60 | // Recursion base case: |P| <= 3 61 | if (X.size() <= 1) { 62 | resultPair = null; 63 | } else if (X.size() == 2) { 64 | resultPair = X; 65 | } else if (X.size() == 3) { 66 | // |P| == 3 => Brute force 67 | double dist1 = dist(X.get(0), X.get(1)); 68 | double dist2 = dist(X.get(0), X.get(2)); 69 | double dist3 = dist(X.get(1), X.get(2)); 70 | 71 | if (dist2 < dist3) { 72 | if (dist1 < dist2) { 73 | resultPair.add(X.get(0)); 74 | resultPair.add(X.get(1)); 75 | } else { 76 | resultPair.add(X.get(0)); 77 | resultPair.add(X.get(2)); 78 | } 79 | } else { 80 | if (dist1 < dist3) { 81 | resultPair.add(X.get(0)); 82 | resultPair.add(X.get(1)); 83 | } else { 84 | resultPair.add(X.get(1)); 85 | resultPair.add(X.get(2)); 86 | } 87 | } 88 | } 89 | // Recursion step: divide & conquer 90 | else { 91 | double lX = (X.get(X.size() / 2)).getX(); 92 | 93 | // Divide ... 94 | 95 | ArrayList XL = new ArrayList(); 96 | ArrayList XR = new ArrayList(); 97 | 98 | for (int i = 0; i < X.size(); i++) { 99 | if ((X.get(i)).getX() <= lX) { 100 | XL.add(X.get(i)); 101 | } else { 102 | XR.add(X.get(i)); 103 | } 104 | } 105 | 106 | ArrayList YL = new ArrayList(); 107 | ArrayList YR = new ArrayList(); 108 | 109 | for (int i = 0; i < Y.size(); i++) { 110 | if ((Y.get(i)).getX() <= lX) { 111 | YL.add(Y.get(i)); 112 | } else { 113 | YR.add(Y.get(i)); 114 | } 115 | } 116 | 117 | // The degenerate case when all the points go to the left should be 118 | // treated with care. This happens e.g. when all the points have 119 | // the same X coordinate. 120 | // TODO(mikhaildubov): performance can degrade to O(n^2) here if 121 | // all the points are on the Y axis. 122 | if (XR.size() == 0) { 123 | // Temporary solution: just move the "rightmost" point from XL to XR 124 | Point repl = XL.get(XL.size()-1); 125 | 126 | XL.remove(repl); 127 | XR.add(repl); 128 | 129 | YL.remove(repl); 130 | YR.add(repl); 131 | } 132 | 133 | 134 | // ... Conquer ... 135 | 136 | ArrayList ClosestLeft = ClosestPair(XL, YL); 137 | ArrayList ClosestRight = ClosestPair(XR, YR); 138 | ArrayList ClosestBetween = ClosestBetweenSubsets(X, Y, 139 | Math.min(dist(ClosestLeft), dist(ClosestRight))); 140 | 141 | double distLeft = dist(ClosestLeft); 142 | double distRight = dist(ClosestRight); 143 | double distBetween = dist(ClosestBetween); 144 | 145 | // ... Combine. 146 | 147 | if (distLeft < distRight) { 148 | if (distBetween < distLeft) { 149 | resultPair = ClosestBetween; 150 | } else { 151 | resultPair = ClosestLeft; 152 | } 153 | } else { 154 | if (distBetween < distRight) { 155 | resultPair = ClosestBetween; 156 | } else { 157 | resultPair = ClosestRight; 158 | } 159 | } 160 | } 161 | 162 | return resultPair; 163 | } 164 | 165 | private static ArrayList ClosestBetweenSubsets(ArrayList X, ArrayList Y, double d) { 166 | double lX = (X.get(X.size() / 2)).getX(); 167 | 168 | // Populate the Y2 list 169 | ArrayList Y2 = new ArrayList(); 170 | for (int i = 0; i < Y.size(); i++) { 171 | if (Math.abs((Y.get(i)).getX() - lX) <= d) { 172 | Y2.add(Y.get(i)); 173 | } 174 | } 175 | 176 | // Iterate over Y2 in search of the nearest points 177 | if (Y2.size() < 2) { 178 | return null; 179 | } 180 | 181 | Point p1 = Y2.get(0); 182 | Point p2 = Y2.get(1); 183 | 184 | // It is enough to check only 7 points 185 | for (int i = 0; i < Y2.size() - 1; i++) { 186 | for (int j = i + 1; j <= Math.min(i + 8, Y2.size() - 1); j++) 187 | if (dist(Y2.get(i), Y2.get(j)) < dist(p1, p2)) { 188 | p1 = Y2.get(i); 189 | p2 = Y2.get(j); 190 | } 191 | } 192 | 193 | ArrayList resultPair = new ArrayList(); 194 | resultPair.add(p1); 195 | resultPair.add(p2); 196 | 197 | return resultPair; 198 | } 199 | 200 | public static double dist(Point p0, Point p1) { 201 | return Math.sqrt((p1.getX() - p0.getX()) * (p1.getX() - p0.getX()) + 202 | (p1.getY() - p0.getY()) * (p1.getY() - p0.getY())); 203 | } 204 | 205 | private static double dist(ArrayList pair) { 206 | if (pair == null || pair.size() != 2) { 207 | return Double.POSITIVE_INFINITY; 208 | } else { 209 | return Math.sqrt(((pair.get(1)).getX() - (pair.get(0)).getX()) * ((pair.get(1)).getX() - (pair.get(0)).getX()) + 210 | ((pair.get(1)).getY() - (pair.get(0)).getY()) * ((pair.get(1)).getY() - (pair.get(0)).getY())); 211 | } 212 | } 213 | 214 | 215 | static class PointComparatorX implements Comparator { 216 | 217 | public int compare(Point p1, Point p2) { 218 | 219 | if (p1.getX() < p2.getX()) return -1; 220 | if (p1.getX() > p2.getX()) return 1; 221 | return 0; 222 | } 223 | } 224 | 225 | static class PointComparatorY implements Comparator { 226 | 227 | @Override 228 | public int compare(Point p1, Point p2) { 229 | 230 | if (p1.getY() < p2.getY()) return -1; 231 | if (p1.getY() > p2.getY()) return 1; 232 | return 0; 233 | } 234 | } 235 | } -------------------------------------------------------------------------------- /4) Closest points pair/src/ru/dubov/closestpair/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
181 | -------------------------------------------------------------------------------- /4) Closest points pair/test/ru/dubov/closestpair/test/ClosestPairTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.closestpair.test; 2 | 3 | import ru.dubov.closestpair.ClosestPair; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | import junit.framework.TestCase; 7 | import ru.dubov.closestpair.ClosestPair; 8 | import ru.dubov.primitives.Point; 9 | 10 | 11 | public class ClosestPairTest extends TestCase { 12 | 13 | private void checkClosestPair(ArrayList points) { 14 | ArrayList resNaive = ClosestPair.Naive(points); 15 | ArrayList resFast = ClosestPair.Fast(points); 16 | 17 | if(resNaive.size() != 2) { 18 | fail("Naive method returns not a pair of points."); 19 | } 20 | if(resFast.size() != 2) { 21 | fail("Fast method returns not a pair of points."); 22 | } 23 | 24 | // Используем расстояния, а не конкретные точки: может быть несколько пар! 25 | if (ClosestPair.dist(resNaive.get(0), resNaive.get(1)) != 26 | ClosestPair.dist(resFast.get(0), resFast.get(1))) { 27 | fail("Pairs returned by the naive and fast methods are different."); 28 | } 29 | 30 | assertTrue(true); 31 | } 32 | 33 | 34 | public void testClosestPair_Example() { 35 | // Boundary case - X coordinates are equal 36 | ArrayList points = new ArrayList(); 37 | points.add(new Point(100, 100)); 38 | points.add(new Point(100, 102)); 39 | points.add(new Point(100, 103)); 40 | points.add(new Point(100, 105)); 41 | points.add(new Point(100, 107)); 42 | 43 | checkClosestPair(points); 44 | 45 | // Boundary case - Y coordinates are equal 46 | points = new ArrayList(); 47 | points.add(new Point(100, 100)); 48 | points.add(new Point(102, 100)); 49 | points.add(new Point(103, 100)); 50 | points.add(new Point(105, 100)); 51 | points.add(new Point(107, 100)); 52 | 53 | checkClosestPair(points); 54 | 55 | } 56 | 57 | public void testClosestPair_Randomised() { 58 | Random rand = new Random(); 59 | 60 | for (int n = 3; n <= 100; n++) { 61 | ArrayList points = new ArrayList(); 62 | 63 | for(int i = 0; i < n; i++) { 64 | points.add(new Point(rand.nextDouble()*100, 65 | rand.nextDouble()*100)); 66 | } 67 | 68 | checkClosestPair(points); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/DivideAndConquerAlgorithm.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | 3 | import java.util.ArrayList; 4 | import ru.dubov.primitives.Polygon; 5 | import ru.dubov.primitives.Triangle; 6 | 7 | public class DivideAndConquerAlgorithm { 8 | 9 | /** 10 | * Calculates the polygon triangulation 11 | * using primitive divide & conquer strategy. 12 | * 13 | * This implementation runs in O(n^4) worst case (!). 14 | * 15 | * @param p Polygon. 16 | * @return The array of triangles. 17 | */ 18 | public static ArrayList triangulate(Polygon p) { 19 | 20 | result = new ArrayList(); 21 | 22 | isClockwise = p.isClockwise(); 23 | 24 | divideAndConquer(p); 25 | 26 | return result; 27 | } 28 | 29 | private static void divideAndConquer(Polygon p) { 30 | 31 | if(p.size() < 3) { 32 | return; 33 | } 34 | if(p.size() == 3) { 35 | result.add(new Triangle(p.get(0), p.get(1), p.get(2))); 36 | return; 37 | } 38 | 39 | boolean diag = true; 40 | int start = 0, finish = 0; 41 | int prev = 0, next = 0; 42 | 43 | // Looking for a diagonal - O(n^3) 44 | outer: for(int i = 0; i < p.size()-1; i++) { 45 | for (int j = i+2; j < p.size(); j++) { 46 | 47 | if (i == j || Math.abs(i-j) == 1 || 48 | (i == p.size()-1 && j == 0) || 49 | (j == p.size()-1 && i == 0)) { 50 | continue; // not even a candidate to be a diagonal 51 | } 52 | 53 | start = i; 54 | finish = j; 55 | diag = true; 56 | 57 | // Test for intersections 58 | for (int k = 0; k < p.size(); k++) { 59 | 60 | if (i != k && j != k && i != (k+1)%p.size() && j != (k+1)%p.size() && 61 | SegmentsIntersect.segmentsIntersect(p.get(i), p.get(j), 62 | p.get(k), p.get((k+1)%p.size()))) { 63 | diag = false; break; 64 | } 65 | } 66 | 67 | 68 | // Test whether it is not an outer diagonal 69 | // See http://stackoverflow.com/questions/4380479/finding-the-diagonals-of-a-polygon 70 | 71 | prev = i - 1; 72 | if (prev < 0) prev += p.size(); 73 | next = (i + 1) % p.size(); 74 | 75 | if (p.isConvex(i)) { 76 | if (! (SegmentsIntersect.isRightTurn(p.get(i), p.get(j), p.get(next)) ^ isClockwise && 77 | SegmentsIntersect.isRightTurn(p.get(i), p.get(prev), p.get(j)) ^ isClockwise)) { 78 | diag = false; 79 | } 80 | } else { 81 | if (! (SegmentsIntersect.isLeftTurn(p.get(i), p.get(j), p.get(prev)) ^ isClockwise || 82 | SegmentsIntersect.isLeftTurn(p.get(i), p.get(next), p.get(j)) ^ isClockwise)) { 83 | diag = false; 84 | } 85 | } 86 | 87 | if (diag) { 88 | break outer; 89 | } 90 | } 91 | } 92 | 93 | 94 | divideAndConquer(p.subPolygon(start, finish)); 95 | divideAndConquer(p.subPolygon(finish, start)); 96 | } 97 | 98 | static boolean isClockwise; 99 | private static ArrayList result; 100 | } 101 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/DoublyLinkedCyclicList.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | 3 | /** 4 | * Doubly linked list structure 5 | * for O(n^2) Van Gogh implementation 6 | */ 7 | public class DoublyLinkedCyclicList { 8 | public DoublyLinkedCyclicList() { 9 | head = null; 10 | } 11 | 12 | public void insert(T value) { 13 | 14 | Node node = new Node(value); 15 | 16 | if (head == null) { 17 | head = node; 18 | 19 | head.next = head; 20 | head.prev = head; 21 | 22 | return; 23 | } 24 | 25 | head.prev.next = node; 26 | node.prev = head.prev; 27 | 28 | node.next = head; 29 | head.prev = node; 30 | 31 | head = node; 32 | } 33 | 34 | public void next() { 35 | head = head.next; 36 | } 37 | 38 | public void prev() { 39 | head = head.prev; 40 | } 41 | 42 | public Node head() { 43 | return head; 44 | } 45 | 46 | private Node head; 47 | 48 | public class Node { 49 | 50 | public Node() { 51 | next = prev = null; 52 | } 53 | 54 | public Node(T val) { 55 | next = prev = null; 56 | value = val; 57 | } 58 | 59 | public void delete() { 60 | 61 | if (this.prev != null) { 62 | this.prev.next = this.next; 63 | } 64 | if (this.next != null) { 65 | this.next.prev = this.prev; 66 | } 67 | 68 | if (head == this) { 69 | head = this.next; 70 | } 71 | } 72 | 73 | public Node next, prev; 74 | public T value; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/MonotonePartitioningAlgorithm.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.PriorityQueue; 6 | import java.util.TreeMap; 7 | import ru.dubov.primitives.*; 8 | 9 | public class MonotonePartitioningAlgorithm { 10 | 11 | /** 12 | * Calculates the polygon triangulation 13 | * using its partitioning into monotone pieces. 14 | * 15 | * This implementation runs in O(n*log(n)) time. 16 | * 17 | * See sections 3.2-3.3 in [deBerg] for details. 18 | * 19 | * @param p Polygon. 20 | * @return The array of triangles. 21 | */ 22 | /*public static ArrayList triangulate(Polygon p) { 23 | 24 | ArrayList monotonePartitioning = makeMonotone(p); 25 | 26 | ArrayList res = new ArrayList(); 27 | 28 | for (Polygon m : monotonePartitioning) { 29 | res.addAll(triangulateMonotonePolygon(m)); 30 | } 31 | 32 | return res; 33 | }*/ 34 | 35 | public static ArrayList makeMonotone(Polygon p) { 36 | 37 | // Algorithm requires the counterclockwise direction 38 | p.makeCounterClockwise(); 39 | 40 | // Constructing the priority queue 41 | PriorityQueue Q = new PriorityQueue 42 | (p.size(), new VertexComparator()); 43 | for (int i = 0; i < p.size(); i++) { 44 | Q.add(new Vertex(p, i)); 45 | } 46 | 47 | // Initializing the binary tree 48 | TreeMap T = new TreeMap(); 49 | 50 | // Partitioning as set of diagonals 51 | ArrayList D = new ArrayList(); 52 | 53 | // Handling vertices 54 | while (! Q.isEmpty()) { 55 | try { 56 | handleVertex(Q.poll(), T, D); 57 | } catch(Exception e) { 58 | System.out.print("!"); 59 | } 60 | } 61 | 62 | return D; 63 | } 64 | 65 | /*private static ArrayList triangulateMonotonePolygon(Polygon p) { 66 | 67 | }*/ 68 | 69 | private static void handleVertex(Vertex v_i, TreeMap T, 70 | ArrayList D) { 71 | 72 | int i = v_i.getIndex(); 73 | int i_1 = (i == 0) ? (v_i.getPolygon().size()-1) : (i-1); 74 | 75 | Edge e_i, e_j, e_i_1, temp; 76 | Vertex helper_e_i_1, helper_e_j; 77 | 78 | switch (v_i.getVertexType()) { 79 | 80 | case START: 81 | e_i = new Edge(v_i.getPolygon(), i); 82 | e_i.setHelper(v_i); 83 | T.put(i, e_i); 84 | 85 | break; 86 | 87 | case END: 88 | 89 | e_i_1 = T.get(i_1); 90 | 91 | helper_e_i_1 = e_i_1.getHelper(); 92 | if (helper_e_i_1.getVertexType() == VertexType.MERGE) { 93 | D.add(new Diagonal(i, helper_e_i_1.getIndex())); 94 | } 95 | 96 | //T.remove(i_1); 97 | 98 | break; 99 | 100 | case SPLIT: 101 | e_j = null; 102 | // TODO: Improve to O(log(n))! 103 | for (int j : T.keySet()) { 104 | temp = T.get(j); 105 | if (temp.intersectsWithSweepLine(v_i.getY()) && 106 | temp.liesOnTheLeftSideof(v_i)) { 107 | if (e_j == null) { 108 | e_j = temp; 109 | } else if (temp.liesOnTheRightSideof(e_j, v_i.getY())) { 110 | e_j = temp; 111 | } 112 | } 113 | } 114 | 115 | D.add(new Diagonal(i, e_j.getHelper().getIndex())); 116 | 117 | e_j.setHelper(v_i); 118 | 119 | e_i = new Edge(v_i.getPolygon(), i); 120 | T.put(i, e_i); 121 | e_i.setHelper(v_i); 122 | 123 | break; 124 | 125 | case MERGE: 126 | 127 | e_i_1 = T.get(i_1); 128 | helper_e_i_1 = e_i_1.getHelper(); 129 | 130 | if (helper_e_i_1.getVertexType() == VertexType.MERGE) { 131 | D.add(new Diagonal(i, helper_e_i_1.getIndex())); 132 | } 133 | 134 | //T.remove(i_1); 135 | 136 | e_j = null; 137 | // TODO: Improve to O(log(n))! 138 | for (int j : T.keySet()) { 139 | temp = T.get(j); 140 | if (temp.intersectsWithSweepLine(v_i.getY()) && 141 | temp.liesOnTheLeftSideof(v_i)) { 142 | if (e_j == null) { 143 | e_j = temp; 144 | } else if (temp.liesOnTheRightSideof(e_j, v_i.getY())) { 145 | e_j = temp; 146 | } 147 | } 148 | } 149 | 150 | helper_e_j = e_j.getHelper(); 151 | 152 | if (helper_e_j.getVertexType() == VertexType.MERGE) { 153 | D.add(new Diagonal(i, helper_e_j.getIndex())); 154 | } 155 | 156 | e_j.setHelper(v_i); 157 | 158 | break; 159 | 160 | case REGULAR: 161 | 162 | if (v_i.polygonInteriorLiesToTheRight()) { 163 | 164 | e_i_1 = T.get(i_1); 165 | helper_e_i_1 = e_i_1.getHelper(); 166 | 167 | if (helper_e_i_1.getVertexType() == VertexType.MERGE) { 168 | D.add(new Diagonal(i, helper_e_i_1.getIndex())); 169 | } 170 | 171 | //T.remove(i_1); 172 | 173 | e_i = new Edge(v_i.getPolygon(), i); 174 | T.put(i, e_i); 175 | e_i.setHelper(v_i); 176 | 177 | } else { 178 | 179 | e_j = null; 180 | // TODO: Improve to O(log(n))! 181 | for (int j : T.keySet()) { 182 | temp = T.get(j); 183 | if (temp.intersectsWithSweepLine(v_i.getY()) && 184 | temp.liesOnTheLeftSideof(v_i)) { 185 | if (e_j == null) { 186 | e_j = temp; 187 | } else if (temp.liesOnTheRightSideof(e_j, v_i.getY())) { 188 | e_j = temp; 189 | } 190 | } 191 | } 192 | 193 | helper_e_j = e_j.getHelper(); 194 | 195 | if (helper_e_j.getVertexType() == VertexType.MERGE) { 196 | D.add(new Diagonal(i, helper_e_j.getIndex())); 197 | } 198 | 199 | e_j.setHelper(v_i); 200 | } 201 | 202 | break; 203 | } 204 | } 205 | 206 | static enum VertexType { 207 | START, 208 | END, 209 | REGULAR, 210 | SPLIT, 211 | MERGE 212 | } 213 | 214 | static class Vertex { 215 | 216 | public Vertex(Polygon p, int i) { 217 | polygon = p; 218 | index = i; 219 | } 220 | 221 | public int getIndex() { 222 | return index; 223 | } 224 | 225 | public double getX() { 226 | return polygon.get(index).getX(); 227 | } 228 | 229 | public double getY() { 230 | return polygon.get(index).getY(); 231 | } 232 | 233 | public Polygon getPolygon() { 234 | return polygon; 235 | } 236 | 237 | public VertexType getVertexType() { 238 | 239 | Point pCur = polygon.get(index); 240 | Point pPrev = polygon.get(index == 0 ? polygon.size()-1 : index-1); 241 | Point pNext = polygon.get((index+1) % polygon.size()); 242 | 243 | if (pPrev.getY() < pCur.getY() && 244 | pNext.getY() < pCur.getY()) { 245 | if (polygon.isConvex(index)) { 246 | return VertexType.START; 247 | } else { 248 | return VertexType.SPLIT; 249 | } 250 | } else if (pPrev.getY() > pCur.getY() && 251 | pNext.getY() > pCur.getY()) { 252 | if (polygon.isConvex(index)) { 253 | return VertexType.END; 254 | } else { 255 | return VertexType.MERGE; 256 | } 257 | } else { 258 | return VertexType.REGULAR; 259 | } 260 | } 261 | 262 | public boolean polygonInteriorLiesToTheRight() { 263 | 264 | Point pCur = polygon.get(index); 265 | Point pPrev = polygon.get(index == 0 ? polygon.size()-1 : index-1); 266 | Point pNext = polygon.get((index+1) % polygon.size()); 267 | 268 | if (pPrev.getY() > pCur.getY() && 269 | pNext.getY() < pCur.getY()) { 270 | return true; 271 | } else { 272 | return false; 273 | } 274 | } 275 | 276 | private Polygon polygon; 277 | private int index; 278 | } 279 | 280 | static class Edge { 281 | 282 | public Edge(Polygon p, int i) { 283 | polygon = p; 284 | index = i; 285 | } 286 | 287 | public void setHelper(Vertex v) { 288 | helper = v; 289 | } 290 | 291 | public Vertex getHelper() { 292 | return helper; 293 | } 294 | 295 | public int getIndex() { 296 | return index; 297 | } 298 | 299 | public Vertex getA() { 300 | return new Vertex(polygon, index); 301 | } 302 | 303 | public Vertex getB() { 304 | return new Vertex(polygon, (index+1)%polygon.size()); 305 | } 306 | 307 | public boolean intersectsWithSweepLine(double sweepY) { 308 | return (sweepY >= getA().getY() && sweepY <= getB().getY()) || 309 | (sweepY >= getB().getY() && sweepY <= getA().getY()); 310 | } 311 | 312 | public Line getLine() { 313 | return new Line(polygon.get(index), 314 | polygon.get((index+1)%polygon.size())); 315 | } 316 | 317 | public boolean liesOnTheRightSideof(Edge e, double Y) { 318 | return this.getLine().XforY(Y) > e.getLine().XforY(Y); 319 | } 320 | 321 | public boolean liesOnTheLeftSideof(Vertex v) { 322 | return this.getLine().XforY(v.getY()) < v.getX(); 323 | } 324 | 325 | private Polygon polygon; 326 | private Vertex helper; 327 | private int index; 328 | } 329 | 330 | static class Diagonal { 331 | 332 | public Diagonal(int i1, int i2) { 333 | index1 = i1; 334 | index2 = i2; 335 | } 336 | 337 | public int getA() { 338 | return index1; 339 | } 340 | 341 | public int getB() { 342 | return index2; 343 | } 344 | 345 | int index1, index2; 346 | } 347 | 348 | static class VertexComparator implements Comparator { 349 | 350 | @Override 351 | public int compare(Vertex v1, Vertex v2) { 352 | 353 | if (v1.getY() > v2.getY() || 354 | v1.getY() == v2.getY() && 355 | v1.getX() > v2.getX()) 356 | return -1; 357 | 358 | else return 1; 359 | } 360 | } 361 | 362 | } 363 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/SegmentsIntersect.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | import ru.dubov.primitives.Point; 3 | 4 | public class SegmentsIntersect { 5 | 6 | private static double direction(Point p0, Point p1, Point p2) { 7 | return ((p2.getX() - p0.getX()) * (p1.getY() - p0.getY()) - 8 | (p2.getY() - p0.getY()) * (p1.getX() - p0.getX())); 9 | } 10 | 11 | private static double crossProduct(Point p0, Point p1, Point p2) { 12 | return (p1.getX() - p0.getX()) * (p2.getY() - p0.getY()) - 13 | (p2.getX() - p0.getX()) * (p1.getY() - p0.getY()); 14 | } 15 | 16 | public static boolean isLeftTurn(Point p0, Point p1, Point p2) { 17 | return (crossProduct(p0, p1, p2) > 0); 18 | } 19 | 20 | public static boolean isRightTurn(Point p0, Point p1, Point p2) { 21 | return (crossProduct(p0, p1, p2) < 0); 22 | } 23 | 24 | private static boolean onSegment(Point pi, Point pj, Point pk) { 25 | return (Math.min(pi.getX(), pj.getX()) <= pk.getX() && 26 | pk.getX() <= Math.max(pi.getX(), pj.getX()) && 27 | Math.min(pi.getY(), pj.getY()) <= pk.getY() && 28 | pk.getY() <= Math.max(pi.getY(), pj.getY())); 29 | } 30 | 31 | public static boolean segmentsIntersect(Point p1, Point p2, 32 | Point p3, Point p4) { 33 | double d1 = direction(p3, p4, p1); 34 | double d2 = direction(p3, p4, p2); 35 | double d3 = direction(p1, p2, p3); 36 | double d4 = direction(p1, p2, p4); 37 | 38 | if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && 39 | ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) { 40 | return true; 41 | } else if (d1 == 0 && onSegment(p3, p4, p1)) { 42 | return true; 43 | } else if (d2 == 0 && onSegment(p3, p4, p2)) { 44 | return true; 45 | } else if (d3 == 0 && onSegment(p1, p2, p3)) { 46 | return true; 47 | } else if (d4 == 0 && onSegment(p1, p2, p4)) { 48 | return true; 49 | } else { 50 | return false; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/VanGoghAlgorithm.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | 3 | import java.util.ArrayList; 4 | import ru.dubov.primitives.Point; 5 | import ru.dubov.primitives.Polygon; 6 | import ru.dubov.primitives.Triangle; 7 | 8 | 9 | public class VanGoghAlgorithm { 10 | 11 | /** 12 | * Calculates the Van Gogh (ear-clipping) 13 | * triangulation for the given polygon. 14 | * This implementation runs in O(n^2), 15 | * as the ears are stored at once in a 16 | * separate list, that updates each step. 17 | * 18 | * @param p Polygon. 19 | * @return The array of triangles. 20 | */ 21 | public static ArrayList fast(Polygon p) { 22 | 23 | ArrayList result = new ArrayList(); 24 | 25 | VanGoghPolygon ap = new VanGoghPolygon(p); 26 | 27 | for(int i = 0; i < p.size() - 2; i++) { 28 | result.add(ap.removeEar()); 29 | } 30 | 31 | return result; 32 | } 33 | 34 | /** 35 | * Calculates the Van Gogh (ear-clipping) 36 | * triangulation for the given polygon. 37 | * This implementation runs in O(n^3), 38 | * as the algorithm searches each step 39 | * for an ear, which takes O(n) time. 40 | * 41 | * There is another implementation 42 | * ensuring the O(n^2) time. 43 | * 44 | * @param p Polygon. 45 | * @return The array of triangles. 46 | */ 47 | public static ArrayList slow(Polygon p) { 48 | 49 | ArrayList result = new ArrayList(); 50 | 51 | boolean isClockwise = p.isClockwise(); 52 | 53 | while (p.size() > 3) { 54 | 55 | int li = -1; 56 | Point l, v, r; 57 | Triangle tr; 58 | boolean isEar; 59 | int tryings = 0; 60 | 61 | do { 62 | tryings ++; 63 | 64 | if (tryings >= p.size()) { 65 | // Endless loop; bad input 66 | return null; 67 | } 68 | 69 | li ++; 70 | l = p.get(li % p.size()); 71 | v = p.get((li+1) % p.size()); 72 | r = p.get((li+2) % p.size()); 73 | tr = new Triangle(l, v, r); 74 | 75 | isEar = Point.isLeftTurn(l, v, r) ^ isClockwise; 76 | 77 | if (isEar) { // Further analysis required 78 | for (int i = 0; i < p.size(); i++) { // (there shoud be 79 | if (tr.pointInside(p.get(i))) { // no points inside) 80 | isEar = false; 81 | break; 82 | } 83 | } 84 | } 85 | 86 | } while(! isEar); // Until we've discovered an ear 87 | 88 | // Guaranteed that we got an ear here 89 | 90 | result.add(tr); 91 | p.remove((li+1) % p.size()); 92 | 93 | } 94 | 95 | if (p.size() == 3) { // The last triangle 96 | result.add(new Triangle(p.get(0), p.get(1), p.get(2))); 97 | } 98 | 99 | return result; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /5) Polygon triangulation/src/ru/dubov/polygontriangulation/VanGoghPolygon.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation; 2 | import ru.dubov.primitives.Point; 3 | import ru.dubov.primitives.Polygon; 4 | import ru.dubov.primitives.Triangle; 5 | 6 | /** 7 | * An advanced version of polygon (with "ears") 8 | * for O(n^2) Van Gogh implementation. 9 | * The implementation involves using double linked 10 | * cyclic lists for storing the vertices. 11 | */ 12 | public class VanGoghPolygon { 13 | 14 | public VanGoghPolygon() { 15 | vertices = new DoublyLinkedCyclicList(); 16 | ears = new DoublyLinkedCyclicList.Node>(); 17 | } 18 | 19 | public VanGoghPolygon(Polygon p) { 20 | this(); 21 | 22 | for (int i = p.size()-1; i >= 0; i--) { 23 | vertices.insert(p.get(i)); 24 | } 25 | 26 | constructEarList(); 27 | } 28 | 29 | /*private void listVert() { 30 | DoublyLinkedCyclicList.Node cur = vertices.head(); 31 | System.out.println(); 32 | do { 33 | System.out.print(cur.value+" "); 34 | cur = cur.next; 35 | } while(cur != vertices.head()); 36 | System.out.println(); 37 | }*/ 38 | 39 | public Triangle removeEar() { 40 | 41 | DoublyLinkedCyclicList.Node adjLeft = ears.head().value.prev; 42 | DoublyLinkedCyclicList.Node adjRight = ears.head().value.next; 43 | 44 | Triangle ear = new Triangle(adjLeft.value, 45 | ears.head().value.value, adjRight.value); 46 | 47 | ears.head().value.delete(); // Delete in vertices 48 | ears.head().delete(); // Delete in ears 49 | 50 | if (isConvex(adjLeft, isClockwise)) { 51 | // not an ear anymore? 52 | if (adjLeft == ears.head().prev.value) { 53 | if (! isEar(ears.head().prev.value, isClockwise)) { 54 | ears.head().prev.delete(); 55 | } 56 | // new ear? 57 | } else if (isEar(adjLeft, isClockwise)) { 58 | ears.insert(adjLeft); 59 | ears.next(); 60 | } 61 | } 62 | 63 | // next has now become the head (after deleting) 64 | 65 | if (isConvex(ears.head().value, isClockwise)) { 66 | // not an ear anymore? 67 | if (adjRight == ears.head().value) { 68 | if (! isEar(ears.head().value, isClockwise)) { 69 | ears.head().delete(); 70 | } 71 | // new ear? 72 | } else if (isEar(adjRight, isClockwise)) { 73 | ears.insert(adjRight); 74 | } 75 | } 76 | 77 | return ear; 78 | } 79 | 80 | public final void constructEarList() { 81 | DoublyLinkedCyclicList.Node cur = vertices.head(); 82 | if (cur == null) return; 83 | 84 | isClockwise = isClockwise(); 85 | 86 | // Building the ear list for O(n^2) time. 87 | 88 | cur = vertices.head().prev; 89 | do { 90 | if (isEar(cur, isClockwise)) { 91 | ears.insert(cur); 92 | } 93 | cur = cur.prev; 94 | } while(cur != vertices.head().prev); 95 | } 96 | 97 | private boolean isConvex(DoublyLinkedCyclicList.Node node, boolean isClockwise) { 98 | return Point.isLeftTurn(node.prev.value, node.value, node.next.value) 99 | ^ isClockwise; 100 | } 101 | 102 | /*private boolean isReflex(DoublyLinkedList.Node node, boolean isClockwise) { 103 | return ! isConvex(node, isClockwise); 104 | }*/ 105 | 106 | /** 107 | * O(n) 108 | */ 109 | private boolean isEar(DoublyLinkedCyclicList.Node node, boolean isClockwise) { 110 | 111 | if (! isConvex(node, isClockwise)) { 112 | return false; 113 | } 114 | 115 | Triangle tr = new Triangle(node.prev.value, node.value, node.next.value); 116 | 117 | DoublyLinkedCyclicList.Node cur = vertices.head(); 118 | do { 119 | if (tr.pointInside(cur.value)) return false; 120 | cur = cur.next; 121 | } while(cur != vertices.head()); 122 | 123 | return true; 124 | } 125 | 126 | public boolean isClockwise() { 127 | double sum = 0; 128 | 129 | DoublyLinkedCyclicList.Node cur = vertices.head(); 130 | 131 | do { 132 | sum += (cur.next.value.getX() - cur.value.getX())* 133 | (cur.next.value.getY() + cur.value.getY()); 134 | 135 | cur = cur.next; 136 | } while(cur != vertices.head()); 137 | 138 | return (sum > 0); 139 | } 140 | 141 | private DoublyLinkedCyclicList vertices; 142 | private DoublyLinkedCyclicList.Node> ears; 143 | private boolean isClockwise; 144 | } 145 | -------------------------------------------------------------------------------- /5) Polygon triangulation/test/ru/dubov/polygontriangulation/test/VanGoghAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.polygontriangulation.test; 2 | 3 | import ru.dubov.polygontriangulation.VanGoghAlgorithm; 4 | import junit.framework.TestCase; 5 | import ru.dubov.primitives.Point; 6 | import ru.dubov.primitives.Polygon; 7 | 8 | public class VanGoghAlgorithmTest extends TestCase { 9 | 10 | public void testFast() { 11 | Polygon p = new Polygon(); 12 | p.add(new Point(0,0)); 13 | p.add(new Point(1,1)); 14 | p.add(new Point(2,0)); 15 | p.add(new Point(3,1)); 16 | p.add(new Point(4,0)); 17 | p.add(new Point(4,4)); 18 | p.add(new Point(0,4)); 19 | 20 | VanGoghAlgorithm.fast(p); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/src/ru/dubov/delaunay/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 |
190 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/src/ru/dubov/delaunay/PointsTriangulation.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.delaunay; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | import java.util.Stack; 8 | import ru.dubov.primitives.Point; 9 | import ru.dubov.primitives.Triangle; 10 | 11 | 12 | public class PointsTriangulation { 13 | 14 | /** 15 | * Computes some [bad] points triangulation to be used 16 | * in the brute force edge flipping algorithm. 17 | * 18 | * @return Triangulation 19 | */ 20 | public static ArrayList some(List points) { 21 | 22 | Point p0 = getLowestPoint(points); 23 | points.remove(p0); 24 | 25 | Collections.sort(points, new PointComparator(p0)); 26 | 27 | ArrayList result = new ArrayList(); 28 | 29 | Stack S = new Stack(); 30 | S.push(p0); 31 | S.push(points.get(0)); 32 | 33 | Point top, nextToTop; 34 | Triangle lastAdded = null, temp = null, 35 | lastAddedInStack = null, tempInStack = null; 36 | 37 | for (int i = 0; i < points.size(); i++) { 38 | 39 | if (i < points.size() - 1) { 40 | 41 | // Adding new "narrow" triangle with p0 as one of its vertices 42 | temp = new Triangle(p0, points.get(i), points.get(i+1)); 43 | 44 | // Linking the triangles (!) 45 | temp.link(lastAdded); 46 | 47 | result.add(temp); 48 | lastAdded = temp; 49 | } 50 | 51 | if (i > 0) { 52 | 53 | // using the idea of Graham's scan 54 | // to make the triangulation "convex" (!) 55 | 56 | top = S.peek(); 57 | nextToTop = S.elementAt(S.size() - 2); 58 | 59 | while (isRightTurn(nextToTop, top, points.get(i))) { 60 | 61 | tempInStack = new Triangle(nextToTop, top, points.get(i)); 62 | 63 | // Linking the triangles (!) 64 | tempInStack.link(lastAddedInStack); 65 | int k = 0; 66 | for (int j = result.size()-1; j >= 0; j--) { 67 | if (tempInStack.link(result.get(j))) { 68 | k++; 69 | } 70 | if (k == 2) { 71 | break; 72 | } 73 | } 74 | 75 | result.add(tempInStack); 76 | lastAddedInStack = tempInStack; 77 | 78 | S.pop(); 79 | 80 | top = nextToTop; 81 | nextToTop = S.elementAt(S.size() - 2); 82 | } 83 | 84 | S.push(points.get(i)); 85 | } 86 | } 87 | 88 | return result; 89 | } 90 | 91 | /** 92 | * Compares two points by their polar angles 93 | * using the cross product. 94 | */ 95 | static class PointComparator implements Comparator { 96 | 97 | private Point p0; 98 | 99 | public PointComparator(Point p0) { 100 | this.p0 = p0; 101 | } 102 | 103 | // Compare two points by their polar angle with respect to p0; 104 | // in case the angle is the same, the distance to p0 is used 105 | @Override 106 | public int compare(Point p1, Point p2) { 107 | 108 | double crossProduct = crossProduct(p0, p1, p2); 109 | 110 | if (crossProduct > 0) return -1; 111 | if (crossProduct < 0) return 1; 112 | 113 | // cross_product = 0 => the vectors are collinear, and the vertex 114 | // that lies further from p0 should be considered "larger" 115 | double d1 = p0.dist(p1); 116 | double d2 = p0.dist(p2); 117 | 118 | if (d1 < d2) return -1; 119 | if (d1 > d2) return 1; 120 | 121 | return 0; 122 | } 123 | } 124 | 125 | private static double crossProduct(Point p0, Point p1, Point p2) { 126 | return (p1.getX() - p0.getX()) * (p2.getY() - p0.getY()) - 127 | (p2.getX() - p0.getX()) * (p1.getY() - p0.getY()); 128 | } 129 | 130 | private static boolean isRightTurn(Point p0, Point p1, Point p2) { 131 | return (crossProduct(p0, p1, p2) < 0); 132 | } 133 | 134 | private static Point getLowestPoint(List points) { 135 | Point result = points.get(0); 136 | 137 | Point candidate; 138 | for (int i = 1; i < points.size(); i++) { 139 | candidate = points.get(i); 140 | 141 | if (candidate.getY() < result.getY() || 142 | candidate.getY() == result.getY() && candidate.getX() < result.getX()) { 143 | result = candidate; 144 | } 145 | } 146 | 147 | return result; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/src/ru/dubov/delaunay/TriangulationDAG.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.delaunay; 2 | 3 | import java.util.ArrayList; 4 | import ru.dubov.primitives.Point; 5 | import ru.dubov.primitives.Triangle; 6 | 7 | /** 8 | * Represents the directed acyclic graph data structure 9 | * used in the randomized incremental triangulation algorithm. 10 | * 11 | * @author Mikhail Dubov 12 | */ 13 | public class TriangulationDAG { 14 | 15 | public void init(Triangle t) { 16 | root = new Node(t); 17 | } 18 | 19 | public Node getRoot() { 20 | return root; 21 | } 22 | 23 | /** 24 | * Looks in the DAG for the smallest triangle 25 | * containing the given point. 26 | * 27 | * @param p The point 28 | * @return The smallest triangle that contains p 29 | */ 30 | public Triangle locate(Point p) { 31 | return _locate(root, p); 32 | } 33 | 34 | /** 35 | * Recursive procedure for locate(p). 36 | */ 37 | private Triangle _locate(Node n, Point p) { 38 | 39 | if (n.getChild(0) != null && 40 | n.getChild(0).getTriangle().pointInside(p, false)) { 41 | return _locate(n.getChild(0), p); 42 | 43 | } else if (n.getChild(1) != null && 44 | n.getChild(1).getTriangle().pointInside(p, false)) { 45 | return _locate(n.getChild(1), p); 46 | 47 | } else if (n.getChild(2) != null && 48 | n.getChild(2).getTriangle().pointInside(p, false)) { 49 | return _locate(n.getChild(2), p); 50 | 51 | } else { // Leaf 52 | return n.getTriangle(); 53 | } 54 | } 55 | 56 | /** 57 | * Traverses the DAG and collects the triangulation from its leafs. 58 | * 59 | * @return The set of triangles 60 | */ 61 | public ArrayList getTriangulation() { 62 | ArrayList res = new ArrayList(); 63 | traverseAndFill(root, res); 64 | return res; 65 | } 66 | 67 | /** 68 | * Recursive procedure for getTriangulation(). 69 | */ 70 | private void traverseAndFill(Node n, ArrayList t) { 71 | 72 | boolean isLeaf = true; 73 | 74 | // to visit vertices no more than once 75 | n.setVisited(true); 76 | 77 | if (n.getChild(0) != null) { 78 | isLeaf = false; 79 | if (! n.getChild(0).getVisited()) { 80 | traverseAndFill(n.getChild(0), t); 81 | } 82 | } 83 | if (n.getChild(1) != null) { 84 | isLeaf = false; 85 | if (! n.getChild(1).getVisited()) { 86 | traverseAndFill(n.getChild(1), t); 87 | } 88 | } 89 | if (n.getChild(2) != null) { 90 | isLeaf = false; 91 | if (! n.getChild(2).getVisited()) { 92 | traverseAndFill(n.getChild(2), t); 93 | } 94 | } 95 | 96 | if (isLeaf) { 97 | t.add(n.getTriangle()); 98 | } 99 | } 100 | 101 | private Node root; 102 | 103 | 104 | 105 | /** 106 | * Represents a node of the DAG. 107 | */ 108 | public static class Node { 109 | 110 | public Node(Triangle t) { 111 | triangle = t; 112 | triangle.setTag(this); 113 | visited = false; 114 | } 115 | 116 | public void addChild(Triangle t) { 117 | addChild(new Node(t)); 118 | } 119 | 120 | public void addChild(Node n) { 121 | if (ch0 == null) { 122 | ch0 = n; 123 | ch0.addParent(this); 124 | } else if (ch1 == null) { 125 | ch1 = n; 126 | ch1.addParent(this); 127 | } else if (ch2 == null) { 128 | ch2 = n; 129 | ch2.addParent(this); 130 | } 131 | } 132 | 133 | public void addParent(Node n) { 134 | if (parent0 == null) { 135 | parent0 = n; 136 | } else if (parent1 == null) { 137 | parent1 = n; 138 | } 139 | } 140 | 141 | public void removeChild(Triangle t) { 142 | 143 | if (t.equals(ch0.triangle)) { 144 | ch0.removeParent(this); 145 | ch0 = null; 146 | } else if (t.equals(ch1.triangle)) { 147 | ch1.removeParent(this); 148 | ch1 = null; 149 | } else if (t.equals(ch2.triangle)) { 150 | ch2.removeParent(this); 151 | ch2 = null; 152 | } 153 | } 154 | 155 | public void removeChild(Node n) { 156 | 157 | if (n == ch0) { 158 | ch0.removeParent(this); 159 | ch0 = null; 160 | } else if (n == ch1) { 161 | ch1.removeParent(this); 162 | ch1 = null; 163 | } else if (n == ch2) { 164 | ch2.removeParent(this); 165 | ch2 = null; 166 | } 167 | } 168 | 169 | public void removeParent(Node n) { 170 | 171 | if (parent0 == n) { 172 | parent0 = null; 173 | } else if (parent1 == n) { 174 | parent1 = null; 175 | } 176 | } 177 | 178 | public void removeChildren() { 179 | ch0 = null; 180 | ch1 = null; 181 | ch2 = null; 182 | } 183 | 184 | public boolean hasChildren() { 185 | if (ch0 != null) { 186 | return true; 187 | } 188 | if (ch1 != null) { 189 | return true; 190 | } 191 | if (ch2 != null) { 192 | return true; 193 | } 194 | return false; 195 | } 196 | 197 | public int getChildrenCount() { 198 | 199 | int res = 0; 200 | 201 | if (ch0 != null) { 202 | res++; 203 | } 204 | if (ch1 != null) { 205 | res++; 206 | } 207 | if (ch2 != null) { 208 | res++; 209 | } 210 | 211 | return res; 212 | } 213 | 214 | public int getParentsCount() { 215 | 216 | int res = 0; 217 | 218 | if (parent0 != null) { 219 | res++; 220 | } 221 | if (parent1 != null) { 222 | res++; 223 | } 224 | 225 | return res; 226 | } 227 | 228 | public Triangle getTriangle() { 229 | return triangle; 230 | } 231 | 232 | public Node getChild(int i) { 233 | switch(i) { 234 | case 0: return ch0; 235 | case 1: return ch1; 236 | case 2: return ch2; 237 | default: return null; 238 | } 239 | } 240 | 241 | public Node getParent(int i) { 242 | switch(i) { 243 | case 0: return parent0; 244 | case 1: return parent1; 245 | default: return null; 246 | } 247 | } 248 | 249 | public boolean getVisited() { 250 | return visited; 251 | } 252 | 253 | public void setVisited(boolean v) { 254 | visited = v; 255 | } 256 | 257 | private Triangle triangle; 258 | private Node parent0, parent1; 259 | private Node ch0, ch1, ch2; 260 | private boolean visited; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/src/ru/dubov/delaunay/VRMLTerrainScript.java: -------------------------------------------------------------------------------- 1 | import java.awt.BorderLayout; 2 | import java.awt.event.ActionEvent; 3 | import java.awt.event.ActionListener; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | import vrml.node.Script; 7 | import javax.swing.*; 8 | import ru.dubov.primitives.Triangle; 9 | import ru.dubov.primitives.Point; 10 | import ru.dubov.delaunay.*; 11 | 12 | /** 13 | * A script class for a VRML scene 14 | * that visualizes the terrain construction 15 | * using Delaunay triangulation. 16 | * 17 | * @author Mikhail Dubov 18 | */ 19 | public class VRMLTerrainScript extends Script { 20 | 21 | @Override 22 | public void initialize() { 23 | 24 | // VRML events 25 | 26 | point_changed = (vrml.field.MFVec3f)getEventOut("point_changed"); 27 | coordIndex_changed = (vrml.field.MFInt32)getEventOut("coordIndex_changed"); 28 | 29 | // Helper frame 30 | 31 | frame = new JFrame("Terrain via Delaunay"); 32 | frame.setResizable(false); 33 | 34 | final JSpinner pointCount = new JSpinner(); 35 | pointCount.setValue(20); 36 | pointCount.setLocation(80, 80); 37 | frame.getContentPane().add(pointCount, BorderLayout.CENTER); 38 | 39 | JButton generate = new JButton(); 40 | generate.setText("Generate"); 41 | generate.setLocation(60, 150); 42 | 43 | generate.addActionListener(new ActionListener() { 44 | 45 | @Override 46 | public void actionPerformed(ActionEvent e) { 47 | 48 | // Generate points 49 | Random rand = new Random(); 50 | points = new ArrayList(); 51 | 52 | points.add(new ProjectedPoint3D(-3, -3, 0)); 53 | points.add(new ProjectedPoint3D(-3, 3, 0)); 54 | points.add(new ProjectedPoint3D(3, -3, 0)); 55 | points.add(new ProjectedPoint3D(3, 3, 0)); 56 | 57 | for (int i = 0; i < (Integer)pointCount.getValue(); i++) { 58 | points.add(new ProjectedPoint3D(rand.nextDouble()*6-3, 59 | rand.nextDouble()*6-3, rand.nextDouble()*0.5-0.25)); 60 | } 61 | 62 | // Triangulate (Delaunay) 63 | triangulation = Delaunay.randomizedIncremental(points); 64 | 65 | // Draw (IndexedLineSet) 66 | float[] pointCoord = new float[triangulation.size()*9]; 67 | int[] pointInd = new int[triangulation.size()*4]; 68 | 69 | for(int i = 0; i < triangulation.size(); i++) { 70 | pointCoord[i*9] = (float)((ProjectedPoint3D)triangulation.get(i).getA()).getX(); 71 | pointCoord[i*9+1] = (float)((ProjectedPoint3D)triangulation.get(i).getA()).getY(); 72 | pointCoord[i*9+2] = (float)((ProjectedPoint3D)triangulation.get(i).getA()).getZ(); 73 | pointCoord[i*9+3] = (float)((ProjectedPoint3D)triangulation.get(i).getB()).getX(); 74 | pointCoord[i*9+4] = (float)((ProjectedPoint3D)triangulation.get(i).getB()).getY(); 75 | pointCoord[i*9+5] = (float)((ProjectedPoint3D)triangulation.get(i).getB()).getZ(); 76 | pointCoord[i*9+6] = (float)((ProjectedPoint3D)triangulation.get(i).getC()).getX(); 77 | pointCoord[i*9+7] = (float)((ProjectedPoint3D)triangulation.get(i).getC()).getY(); 78 | pointCoord[i*9+8] = (float)((ProjectedPoint3D)triangulation.get(i).getC()).getZ(); 79 | 80 | pointInd[i*4] = i*3; 81 | pointInd[i*4+1] = i*3+1; 82 | pointInd[i*4+2] = i*3+2; 83 | pointInd[i*4+3] = -1; 84 | } 85 | 86 | point_changed.setValue(pointCoord); 87 | coordIndex_changed.setValue(pointInd); 88 | } 89 | 90 | private ArrayList points; 91 | private ArrayList triangulation; 92 | }); 93 | 94 | frame.getContentPane().add(generate, BorderLayout.SOUTH); 95 | 96 | frame.pack(); 97 | frame.setLocationRelativeTo(frame.getRootPane()); 98 | frame.setSize(240, 100); 99 | frame.setVisible(true); 100 | } 101 | 102 | @Override 103 | public void shutdown() { 104 | frame.setVisible(false); 105 | frame.dispose(); 106 | } 107 | 108 | 109 | static class ProjectedPoint3D extends Point { 110 | 111 | public ProjectedPoint3D(double x, double y, double z) { 112 | super(x, y); 113 | this.z = z; 114 | } 115 | 116 | public double getZ() { 117 | return z; 118 | } 119 | 120 | final protected double z; 121 | } 122 | 123 | private JFrame frame; 124 | 125 | private vrml.field.MFVec3f point_changed; 126 | private vrml.field.MFInt32 coordIndex_changed; 127 | } 128 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/test/Analysis.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikhaildubov/computational-geometry/179cb93662d14716e74416e8629e1ae2cf587e28/6) Point set Delaunay triangulation/test/Analysis.xlsx -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/test/ru/dubov/delaunay/test/DelaunayTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.delaunay.test; 2 | 3 | import ru.dubov.delaunay.Delaunay; 4 | import java.util.ArrayList; 5 | import java.util.Calendar; 6 | import java.util.Random; 7 | import junit.framework.TestCase; 8 | import ru.dubov.primitives.*; 9 | 10 | /** 11 | * @author Mikhail Dubov 12 | */ 13 | public class DelaunayTest extends TestCase { 14 | 15 | public DelaunayTest(String testName) { 16 | super(testName); 17 | } 18 | 19 | /** 20 | * Test of randomizedIncremental algorithm, of class Delaunay. 21 | * Test the boundary case: a point falls on the line 22 | */ 23 | public void testRandomizedIncrementalBoundaryCase() { 24 | 25 | ArrayList points = new ArrayList(); 26 | points.add(new Point(1, 1)); 27 | points.add(new Point(2, 2)); 28 | points.add(new Point(2, 0)); 29 | points.add(new Point(3, 1)); 30 | points.add(new Point(2, 1)); // Falls on the line 31 | 32 | ArrayList triang = Delaunay.randomizedIncremental(points); 33 | 34 | // NB: Should be tested in the algorithm version 35 | // that uses no random shuffling 36 | 37 | for (Triangle t : triang) { 38 | System.out.println("("+t.getA()+", "+t.getB()+", "+t.getC()+")"); 39 | } 40 | 41 | assertTrue(true); 42 | } 43 | 44 | 45 | public void testTimeBruteForce() { 46 | 47 | Random rand = new Random(); 48 | 49 | for (int i = 20; i <= 5000; i += 20) { 50 | 51 | ArrayList pts = new ArrayList(); 52 | 53 | for(int j = 0; j < i; j++) { 54 | pts.add(new Point(rand.nextDouble()*10, rand.nextDouble()*10)); 55 | } 56 | 57 | double start = Calendar.getInstance().getTimeInMillis(); 58 | 59 | Delaunay.bruteForce(pts); 60 | 61 | System.out.println(Calendar.getInstance().getTimeInMillis()-start); 62 | } 63 | 64 | } 65 | 66 | public void testTimeRandomized() { 67 | 68 | Random rand = new Random(); 69 | 70 | for (int i = 5020; i <= 8000; i += 20) { 71 | 72 | ArrayList pts = new ArrayList(); 73 | 74 | for(int j = 0; j < i; j++) { 75 | pts.add(new Point(rand.nextDouble()*10, rand.nextDouble()*10)); 76 | } 77 | 78 | double start = Calendar.getInstance().getTimeInMillis(); 79 | 80 | Delaunay.randomizedIncremental(pts); 81 | 82 | System.out.println(Calendar.getInstance().getTimeInMillis()-start); 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /6) Point set Delaunay triangulation/test/ru/dubov/delaunay/test/TerrainTriangulationTest.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.delaunay.test; 2 | 3 | import ru.dubov.delaunay.Delaunay; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | import junit.framework.TestCase; 7 | import ru.dubov.primitives.*; 8 | 9 | /** 10 | * 11 | * @author MSDubov 12 | */ 13 | public class TerrainTriangulationTest extends TestCase { 14 | 15 | public TerrainTriangulationTest(String testName) { 16 | super(testName); 17 | } 18 | 19 | 20 | 21 | public void testVRMLTerrainTriangulation() { 22 | Random rand = new Random(); 23 | ArrayList points = new ArrayList(); 24 | 25 | points.add(new Point(-3, -3)); 26 | points.add(new Point(-3, 3)); 27 | points.add(new Point(3, -3)); 28 | points.add(new Point(3, 3)); 29 | 30 | for (int i = 0; i < 1000; i++) { 31 | points.add(new Point(rand.nextDouble()*6-3, rand.nextDouble()*6-3)); 32 | } 33 | 34 | // Triangulate (Delaunay) 35 | ArrayList triangulation = Delaunay.randomizedIncremental(points); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /8) Voronoi diagram/src/ru/dubov/voronoidiagram/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
179 | -------------------------------------------------------------------------------- /8) Voronoi diagram/src/ru/dubov/voronoidiagram/VoronoiDiagram.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.voronoidiagram; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import ru.dubov.halfplanes.HalfplanesIntersection; 6 | import ru.dubov.primitives.*; 7 | 8 | /** 9 | * Computes Voronoi diagrams for point sets 10 | * using different algorithms. 11 | * 12 | * @author Mikhail Dubov 13 | */ 14 | public class VoronoiDiagram { 15 | 16 | /** 17 | * Computes the Voronoi diagram for the given point set 18 | * in a straightforward way using halfplanes intersection. 19 | * 20 | * Runs in O(n^3) (improvable to O(n^2*log(n)), 21 | * depends on halfplanes intersection algorithm complexity). 22 | * 23 | * @param points Point set 24 | * @return Voronoi diagram for p 25 | */ 26 | public static ArrayList viaHalfplanesIntersection(List points) { 27 | 28 | ArrayList res = new ArrayList(); 29 | 30 | ArrayList halfplanes; 31 | Segment p1p2; 32 | Line p1p2perp; 33 | 34 | // Calculating Voronoi cells for each point 35 | for (Point p1 : points) { 36 | 37 | // Initializing halfplanes set with the bounding ones 38 | halfplanes = Halfplane.boundingRectangle(-10000, 10000, -10000, 10000); 39 | 40 | // Getting halfplanes 41 | for (Point p2 : points) { 42 | 43 | if (! p1.equals(p2)) { 44 | 45 | p1p2 = new Segment(p1, p2); 46 | p1p2perp = p1p2.getLine().getPerpendicularLine 47 | (p1p2.getBisectionalPoint()); 48 | 49 | if (p2.getX() < p1.getX() || 50 | p2.getX() == p1.getX() && 51 | p2.getY() > p1.getY()) { 52 | halfplanes.add(new Halfplane(p1p2perp, true)); 53 | } else { 54 | halfplanes.add(new Halfplane(p1p2perp, false)); 55 | } 56 | } 57 | } 58 | 59 | // The halfplanes intersection is a single Voronoi cell 60 | res.add(HalfplanesIntersection.intersectHalfplanes_Naive(halfplanes)); 61 | } 62 | 63 | return res; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Primitives Library/.gitignore: -------------------------------------------------------------------------------- 1 | *.class -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Circle.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | /** 4 | * Represents a circle. 5 | * 6 | * @author Mikhail Dubov 7 | */ 8 | public class Circle { 9 | 10 | /** 11 | * Initializes a circle by its center and radius. 12 | * 13 | * @param center The center of the circle 14 | * @param radius The radius of the circle, must be positive 15 | */ 16 | public Circle(Point center, double radius) { 17 | this.center = center; 18 | this.radius = radius; 19 | } 20 | 21 | /** 22 | * Initializes a circle by three different 23 | * points lying on its boundary. 24 | * 25 | * @param a A point that lies on the circle boundary 26 | * @param b A point that lies on the circle boundary 27 | * @param c A point that lies on the circle boundary 28 | */ 29 | public Circle(Point a, Point b, Point c) { 30 | Line l1 = new Line(2*a.getX() - 2*b.getX(), 31 | 2*a.getY() - 2*b.getY(), 32 | b.getX()*b.getX() - a.getX()*a.getX() + 33 | b.getY()*b.getY() - a.getY()*a.getY()); 34 | Line l2 = new Line(2*a.getX() - 2*c.getX(), 35 | 2*a.getY() - 2*c.getY(), 36 | c.getX()*c.getX() - a.getX()*a.getX() + 37 | c.getY()*c.getY() - a.getY()*a.getY()); 38 | 39 | // TODO: throw an Exception? 40 | center = Line.intersection(l1, l2); // may be NaP (if a, b, c are collinear). 41 | radius = center.dist(a); 42 | } 43 | 44 | /** 45 | * Returns the circle center. 46 | * 47 | * @return The center point 48 | */ 49 | public Point getCenter() { 50 | return center; 51 | } 52 | 53 | /** 54 | * Returns the circle radius. 55 | * 56 | * @return The radius 57 | */ 58 | public double getRadius() { 59 | return radius; 60 | } 61 | 62 | /** 63 | * Determines whether the given point lies inside the circle. 64 | * 65 | * @param p The point 66 | * @return true, if the point is inside, false otherwise 67 | */ 68 | public boolean isInside(Point p) { 69 | return center.dist(p) < radius; 70 | } 71 | 72 | /** 73 | * Determines whether the given point lies outside the circle. 74 | * 75 | * @param p The point 76 | * @return true, if the point is outside, false otherwise 77 | */ 78 | public boolean isOutside(Point p) { 79 | return center.dist(p) > radius; 80 | } 81 | 82 | private Point center; 83 | private double radius; 84 | } 85 | -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Halfplane.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Represents a halfplane by means of the boundary line 7 | * ax + by = c, where one accepts that 8 | * for the halfplane ax + by + c <= 0 holds. 9 | * 10 | * @author Mikhail Dubov 11 | */ 12 | public class Halfplane { 13 | 14 | /** 15 | * Initializes a halfplane by its boundary line 16 | * and by the side where the halfplane lies. 17 | * 18 | * @param line The boundary line 19 | * @param rightSide true, if the halfplanes lies on the right side 20 | * of the line, false otherwise 21 | */ 22 | public Halfplane(Line line, boolean rightSide) { 23 | this.line = line; 24 | this.rightSide = rightSide; 25 | } 26 | 27 | /** 28 | * Returns the boundary line for the halfplane. 29 | * 30 | * @return The boundary line 31 | */ 32 | public Line getLine() { 33 | return line; 34 | } 35 | 36 | /** 37 | * Determines whether the boundary line 38 | * lies on the left side of the halfplane. 39 | * 40 | * @return true, if the boundary line is on the left, flase otherwise 41 | */ 42 | public boolean isLeftBoundary() { 43 | return rightSide; 44 | } 45 | 46 | /** 47 | * Determines whether the boundary line 48 | * lies on the right side of the halfplane. 49 | * 50 | * @return true, if the boundary line is on the right, flase otherwise 51 | */ 52 | public boolean isRightBoundary() { 53 | return ! rightSide; 54 | } 55 | 56 | 57 | 58 | /** 59 | * Determines whether the point lies 60 | * in the halfplane. 61 | * 62 | * @param p The point 63 | * @return true, if the point lies in the left halfplane, false otherwise 64 | */ 65 | public boolean includes(Point p) { 66 | 67 | if (isLeftBoundary()) { 68 | 69 | if (line.isHorizontal()) { // == upper boundary 70 | return (p.getY() < line.YforX(0)); 71 | } else { 72 | return (p.getX() > line.XforY(p.getY())); 73 | } 74 | 75 | } else { 76 | 77 | if (line.isHorizontal()) { // == lower boundary 78 | return (p.getY() > line.YforX(0)); 79 | } else { 80 | return (p.getX() < line.XforY(p.getY())); 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * Initializes a set of halfplanes with a bounding rectangle. 87 | * 88 | * @param x1 left X coordinate 89 | * @param x2 right X coordinate 90 | * @param y1 bottom Y coordinate 91 | * @param y2 upper Y coordinate 92 | * @return Set of bounding halfplanes 93 | */ 94 | public static ArrayList boundingRectangle(double x1, double x2, 95 | double y1, double y2) { 96 | 97 | ArrayList res = new ArrayList(); 98 | 99 | if (x1 > x2) { 100 | double temp = x1; 101 | x1 = x2; 102 | x2 = temp; 103 | } 104 | 105 | if (y1 > y2) { 106 | double temp = y1; 107 | y1 = y2; 108 | y2 = temp; 109 | } 110 | 111 | res.add(new Halfplane(new Line(new Point(x1, 0), new Point(x1, 1)), true)); 112 | res.add(new Halfplane(new Line(new Point(0, y2), new Point(1, y2)), true)); 113 | res.add(new Halfplane(new Line(new Point(x2, 0), new Point(x2, 1)), false)); 114 | res.add(new Halfplane(new Line(new Point(0, y1), new Point(1, y1)), false)); 115 | 116 | return res; 117 | } 118 | 119 | private Line line; 120 | private boolean rightSide; 121 | } 122 | -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Line.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | /** 4 | * Represents a line by means of three coefficients 5 | * a, b and c, where ax + by + c = 0 holds. 6 | * 7 | * @author Mikhail Dubov 8 | */ 9 | public class Line { 10 | 11 | /** 12 | * Initializes a line by the three coefficients 13 | * a, b and c, where ax + by + c = 0 holds. 14 | * @param a The a coefficient 15 | * @param b The b coefficient 16 | * @param c The c coefficient 17 | */ 18 | public Line(double a, double b, double c) { 19 | this.a = a; 20 | this.b = b; 21 | this.c = c; 22 | } 23 | 24 | /** 25 | * Initializes a line by two different points lying on it. 26 | * 27 | * @param p1 The first point 28 | * @param p2 The second point 29 | */ 30 | public Line(Point p1, Point p2) { 31 | this.a = p1.getY() - p2.getY(); 32 | this.b = p2.getX() - p1.getX(); 33 | this.c = p1.getX()*p2.getY() - p2.getX()*p1.getY(); 34 | } 35 | 36 | /** 37 | * Initializes a line by a segment lying on it. 38 | * 39 | * @param s The segment 40 | */ 41 | public Line(Segment s) { 42 | this(s.getLeft(), s.getRight()); 43 | } 44 | 45 | /** 46 | * Returns the a coefficient for the line, 47 | * where ax + by + c = 0 holds. 48 | * 49 | * @return The a coefficient 50 | */ 51 | public double getA() { 52 | return a; 53 | } 54 | /** 55 | * Returns the b coefficient for the line, 56 | * where ax + by + c = 0 holds. 57 | * 58 | * @return The b coefficient 59 | */ 60 | 61 | public double getB() { 62 | return b; 63 | } 64 | 65 | /** 66 | * Returns the c coefficient for the line, 67 | * where ax + by + c = 0 holds. 68 | * 69 | * @return The c coefficient 70 | */ 71 | public double getC() { 72 | return c; 73 | } 74 | 75 | /** 76 | * Calculates the X coordinate of a point on the line 77 | * by its Y coordinate. 78 | * 79 | * @param y The Y coordinate 80 | * @return The X coordinate 81 | */ 82 | public double XforY(double y) { 83 | return (-c - b*y)/a; 84 | } 85 | 86 | /** 87 | * Calculates the Y coordinate of a point on the line 88 | * by its X coordinate. 89 | * 90 | * @param x The X coordinate 91 | * @return The Y coordinate 92 | */ 93 | public double YforX(double x) { 94 | return (-c - a*x)/b; 95 | } 96 | 97 | /** 98 | * Determines whether the point lies on the line. 99 | * 100 | * @param p The Point 101 | * @return true if the point lies on the line, false otherwise 102 | */ 103 | public boolean pointOnLine(Point p) { 104 | return (a*p.getX() + b*p.getY() + c == 0); 105 | } 106 | 107 | /** 108 | * Determines whether the line is ascending 109 | * (that is, makes an angle with the positive 110 | * direction of the X axis that lies in (0, pi/2). 111 | * 112 | * @return true, if the line is ascending, false otherwise 113 | */ 114 | public boolean isAscending() { 115 | return (b == 0 || (-a/b) >= 0); 116 | } 117 | 118 | /** 119 | * Determines whether the line is vertical 120 | * (that is, makes an angle with the positive 121 | * direction of the X axis that is equal to pi/2. 122 | * 123 | * @return true, if the line is vertical, false otherwise 124 | */ 125 | public boolean isVertical() { 126 | return (b == 0 && a != 0); 127 | } 128 | 129 | /** 130 | * Determines whether the line is descending 131 | * (that is, makes an angle with the positive 132 | * direction of the X axis that lies in (pi/2, pi). 133 | * 134 | * @return true, if the line is descending, false otherwise 135 | */ 136 | public boolean isDescending() { 137 | return (b == 0 || (-a/b) < 0); 138 | } 139 | 140 | /** 141 | * Determines whether the line is horizontal 142 | * (that is, makes an angle with the positive 143 | * direction of the X axis that is equal to pi. 144 | * 145 | * @return true, if the line is horizontal, false otherwise 146 | */ 147 | public boolean isHorizontal() { 148 | return (a == 0 && b != 0); 149 | } 150 | 151 | /** 152 | * Determines whether the line is undefined 153 | * (e.g. two equal points were passed to the constructor). 154 | * 155 | * @return true, if the line is undefined, false otherwise 156 | */ 157 | public boolean isUndefined() { 158 | return (a == 0 && b == 0 && c == 0); 159 | } 160 | 161 | /** 162 | * Calculates the angle that the line makes 163 | * with the positive direction of the X axis. 164 | * 165 | * @return The angle in radians 166 | */ 167 | public double getAngle() { 168 | if (isVertical()) return Math.PI/2; 169 | 170 | double atan = Math.atan(-a/b); 171 | if (atan < 0) atan += Math.PI; 172 | 173 | return atan; 174 | } 175 | 176 | /** 177 | * Calculates the perpendicular line that 178 | * passes through the given point. 179 | * 180 | * @param p Point that the perpendicular passes through 181 | * @return The perpendicular 182 | */ 183 | public Line getPerpendicularLine(Point p) { 184 | return new Line(this.b, -this.a, -this.b*p.getX() + this.a*p.getY()); 185 | } 186 | 187 | /** 188 | * Determines whether the point lies 189 | * on the left side of the line. 190 | * 191 | * @param p The point 192 | * @return true, if the point lies in the left halfplane, false otherwise 193 | */ 194 | public boolean isLeftPoint(Point p) { 195 | return p.getX() < XforY(p.getY()); 196 | } 197 | 198 | /** 199 | * Determines whether the point lies 200 | * on the right side of the line. 201 | * 202 | * @param p The point 203 | * @return true, if the point lies in the right halfplane, false otherwise 204 | */ 205 | public boolean isRightPoint(Point p) { 206 | return p.getX() > XforY(p.getY()); 207 | } 208 | 209 | /** 210 | * Calculates the intersection of two lines. 211 | * 212 | * @param l1 The first line 213 | * @param l2 The second line 214 | * @return The intersection point or Point.NaP, if there is no intersection 215 | */ 216 | public static Point intersection(Line l1, Line l2) { 217 | if (l1.b != 0) { 218 | double f = l2.a - l2.b*l1.a/l1.b; 219 | if (f == 0) { 220 | return Point.NaP; 221 | } else { 222 | double x = (-l2.c + l2.b*l1.c/l1.b) / f; 223 | double y = (-l1.c - l1.a*x) / l1.b; 224 | 225 | return new Point(x, y); 226 | } 227 | } else { 228 | if (l1.a == 0) { 229 | return Point.NaP; 230 | } else { 231 | double f = l2.b - l2.a*l1.b/l1.a; 232 | if (f == 0) { 233 | return Point.NaP; 234 | } else { 235 | double y = (-l2.c + l2.a*l1.c/l1.a) / f; 236 | double x = (-l1.c - l1.b*y) / l1.a; 237 | 238 | return new Point(x, y); 239 | } 240 | } 241 | } 242 | } 243 | 244 | @Override 245 | public String toString() { 246 | return a + "*x + " + b + "*y + " + c + " = 0.0"; 247 | } 248 | 249 | private double a, b, c; 250 | } 251 | -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Point.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | /** 4 | * Represents a point in two dimensional space. 5 | * 6 | * @author Mikhail Dubov 7 | */ 8 | public class Point { 9 | 10 | /** 11 | * Initializes a point by its coordinates. 12 | * 13 | * @param x The X coordinate. 14 | * @param y The Y coordinate. 15 | */ 16 | public Point(double x, double y) { 17 | this.x = x; 18 | this.y = y; 19 | } 20 | 21 | /** 22 | * Attaches a segment to the point. 23 | * 24 | * @param segment The attached segment 25 | */ 26 | public void setSegment(Segment segment) { 27 | this.segment = segment; 28 | } 29 | 30 | /** 31 | * Returns the X coordinate of the point. 32 | * 33 | * @return The X coordinate 34 | */ 35 | public double getX() { 36 | return x; 37 | } 38 | 39 | /** 40 | * Returns the Y coordinate of the point. 41 | * 42 | * @return The Y coordinate 43 | */ 44 | public double getY() { 45 | return y; 46 | } 47 | 48 | /** 49 | * Returns the segment attached to the point. 50 | * @return The attached segment 51 | */ 52 | public Segment getSegment() { 53 | return segment; 54 | } 55 | 56 | /** 57 | * Calculates the cross product 58 | * of two vectors (p0, p1) and (p0, p2) 59 | * defined by three points p0, p1 and p2. 60 | * 61 | * @param p0 The first point 62 | * @param p1 The second point 63 | * @param p2 The third point 64 | * @return The cross product 65 | */ 66 | private static double crossProduct(Point p0, Point p1, Point p2) { 67 | return (p1.getX() - p0.getX()) * (p2.getY() - p0.getY()) - 68 | (p2.getX() - p0.getX()) * (p1.getY() - p0.getY()); 69 | } 70 | 71 | /** 72 | * Calculates the remoteness of another point. 73 | * 74 | * @param p0 The point 75 | * @return The distance between the current point and the given point 76 | */ 77 | public double dist(Point p0) { 78 | return Math.sqrt((this.getX() - p0.getX()) * (this.getX() - p0.getX()) + 79 | (this.getY() - p0.getY()) * (this.getY() - p0.getY())); 80 | } 81 | 82 | /** 83 | * Determines whether two vectors (p0, p1) and (p1, p2) 84 | * defined by three points p0, p1 and p2 make a left turn. 85 | * 86 | * @param p0 The first point 87 | * @param p1 The second point 88 | * @param p2 The third point 89 | * @return true, if the turn is left, false otherwise 90 | */ 91 | public static boolean isLeftTurn(Point p0, Point p1, Point p2) { 92 | return (crossProduct(p0, p1, p2) > 0); 93 | } 94 | 95 | /** 96 | * Determines whether two vectors (p0, p1) and (p1, p2) 97 | * defined by three points p0, p1 and p2 make a right turn. 98 | * 99 | * @param p0 The first point 100 | * @param p1 The second point 101 | * @param p2 The third point 102 | * @return true, if the turn is right, false otherwise 103 | */ 104 | public static boolean isRightTurn(Point p0, Point p1, Point p2) { 105 | return (crossProduct(p0, p1, p2) < 0); 106 | } 107 | 108 | @Override 109 | public boolean equals(Object o2) { 110 | 111 | if (! (o2 instanceof Point)) { 112 | return false; 113 | } 114 | 115 | Point p2 = (Point) o2; 116 | 117 | return (this.getX() == p2.getX() && this.getY() == p2.getY()); 118 | } 119 | 120 | @Override 121 | public String toString() { 122 | return ("(" + this.getX() + ", " + this.getY() + ")"); 123 | } 124 | 125 | /** 126 | * Determines whether the point is the left vertex of the attached segment. 127 | * 128 | * @return true, if the point is the left vertex, false otherwise 129 | */ 130 | public boolean isLeft() { 131 | return (segment != null && this.equals(segment.getLeft())); 132 | } 133 | 134 | /** 135 | * Determines whether the point is the right vertex of the attached segment. 136 | * 137 | * @return true, if the point is the right vertex, false otherwise 138 | */ 139 | public boolean isRight() { 140 | return (segment != null && this.equals(segment.getRight())); 141 | } 142 | 143 | /** 144 | * Determines whether the point is equal to Point.NaP (not a point). 145 | * 146 | * @return true, if the point is NaP, false otherwise 147 | */ 148 | public boolean isNaP() { 149 | return x == Double.NaN || y == Double.NaN; 150 | } 151 | 152 | /** 153 | * The "not a point" object. 154 | */ 155 | public final static Point NaP = new Point(Double.NaN, Double.NaN); 156 | 157 | final private double x; 158 | final private double y; 159 | 160 | private Segment segment; 161 | } 162 | -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Polygon.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Stack; 5 | 6 | /** 7 | * Represents a polygon. 8 | * 9 | * @author Mikhail Dubov 10 | */ 11 | public class Polygon implements Cloneable { 12 | 13 | /** 14 | * Initializes an empty polygon. 15 | */ 16 | public Polygon() { 17 | vertices = new ArrayList(); 18 | } 19 | 20 | /** 21 | * Initializes a polygon by the list of its vertices 22 | * (either in clockwise or in counterclockwise order). 23 | * 24 | * @param vertices The list of polygon vertices 25 | */ 26 | public Polygon(ArrayList vertices) { 27 | this.vertices = (ArrayList)vertices.clone(); 28 | } 29 | 30 | /** 31 | * Adds a point to the list of vertices (as the last one). 32 | * 33 | * @param p The added vertex 34 | */ 35 | public void add(Point p) { 36 | if (p != null) { 37 | vertices.add(p); 38 | } 39 | } 40 | 41 | /** 42 | * Clones the polygon. 43 | * 44 | * @return The clone 45 | */ 46 | public Object clone() { 47 | 48 | Polygon copy = new Polygon(); 49 | 50 | copy.vertices = (ArrayList)this.vertices.clone(); 51 | 52 | return copy; 53 | } 54 | 55 | /** 56 | * Creates a subpolygon of the current polygon. 57 | * 58 | * @param start The index of the new "first" vertex in the list of vertices 59 | * @param end The index of the new "last" vertex in the list of vertices 60 | * @return The subpolygon 61 | */ 62 | public Polygon subPolygon(int start, int end) { 63 | Polygon p = new Polygon(); 64 | if (start < end) { 65 | for(int i = start; i <= end; i++) { 66 | p.add(get(i)); 67 | } 68 | } else if (start > end) { 69 | for(int i = start; i < size(); i++) { 70 | p.add(get(i)); 71 | } 72 | for(int i = 0; i <= end; i++) { 73 | p.add(get(i)); 74 | } 75 | } 76 | 77 | p.isClockwise = this.isClockwise; 78 | return p; 79 | } 80 | 81 | /** 82 | * Removes a point from the list of vertices. 83 | * 84 | * @param i The index of the removed point. 85 | */ 86 | public void remove(int i) { 87 | if (i >= 0 && i < size()) { 88 | vertices.remove(i); 89 | } 90 | } 91 | 92 | /** 93 | * Returns the number of vertices in the polygon. 94 | * 95 | * @return The number of vertices 96 | */ 97 | public int size() { 98 | return vertices.size(); 99 | } 100 | 101 | /** 102 | * Determines whether the list of polygon vertices is empty. 103 | * 104 | * @return true, if there are no vertices in the list, false otherwise 105 | */ 106 | public boolean isEmpty() { 107 | return vertices.isEmpty(); 108 | } 109 | 110 | /** 111 | * Returns the ith vertex of the polygon. 112 | * 113 | * @param i The index of the vertex 114 | * @return The vertex 115 | */ 116 | public Point get(int i) { 117 | if (i >= 0 && i < size()) { 118 | return vertices.get(i); 119 | } else { 120 | return null; 121 | } 122 | } 123 | 124 | /** 125 | * Determines whether the vertex is convex 126 | * (and makes an "ear" of the polygon). 127 | * 128 | * @param i The index of the vertex 129 | * @return true, if the vertex is convex, false otherwise 130 | */ 131 | public boolean isConvex(int i) { 132 | int prev = i - 1; 133 | if (prev < 0) prev += size(); 134 | int next = (i + 1) % size(); 135 | 136 | return (Point.isLeftTurn(get(prev), get(i), get(next)) 137 | ^ isClockwise); 138 | } 139 | 140 | /** 141 | * Determines whether the polygon is convex. 142 | * 143 | * @return true, if the polygon is convex, false otherwise 144 | */ 145 | public boolean isConvex() { 146 | 147 | for (int i = 0; i < size(); i++) { 148 | if (! isConvex(i)) { 149 | return false; 150 | } 151 | } 152 | 153 | return true; 154 | } 155 | 156 | /** 157 | * Makes the current polygon to be set in counterclockwise order. 158 | */ 159 | public void makeCounterClockwise() { 160 | 161 | if (isClockwise()) { 162 | Point temp; 163 | for (int i = 1; i <= vertices.size() / 2; i++) { 164 | temp = vertices.get(i); 165 | vertices.set(i, vertices.get(vertices.size()-i)); 166 | vertices.set(vertices.size()-i, temp); 167 | } 168 | } 169 | isClockwise = false; 170 | } 171 | 172 | /** 173 | * Determines, whether the order of vertices in the list is clockwise. 174 | * 175 | * @return true, if the order is clockwise, false if counterclockwise 176 | */ 177 | public boolean isClockwise() { 178 | double sum = 0; 179 | 180 | for (int i = 0; i < size(); i++) { 181 | sum += (vertices.get((i+1)%size()).getX() - vertices.get(i).getX())* 182 | (vertices.get((i+1)%size()).getY() + vertices.get(i).getY()); 183 | } 184 | 185 | isClockwise = (sum > 0); 186 | return isClockwise; 187 | } 188 | 189 | private ArrayList vertices; 190 | private boolean isClockwise; 191 | } 192 | -------------------------------------------------------------------------------- /Primitives Library/src/ru/dubov/primitives/Segment.java: -------------------------------------------------------------------------------- 1 | package ru.dubov.primitives; 2 | 3 | /** 4 | * Represents a segment. 5 | * 6 | * @author Mikhail Dubov 7 | */ 8 | public class Segment { 9 | 10 | /** 11 | * Initializes a segment by its vertices. 12 | * 13 | * @param left The left vertex 14 | * @param right The right vertex 15 | */ 16 | public Segment(Point left, Point right) { 17 | 18 | if(left.getX() > right.getX()) { 19 | Point temp = right; 20 | right = left; 21 | left = temp; 22 | } 23 | 24 | left.setSegment(this); 25 | right.setSegment(this); 26 | 27 | this.left = left; 28 | this.right = right; 29 | } 30 | 31 | /** 32 | * Returns the left vertex of the segment. 33 | * 34 | * @return The left vertex 35 | */ 36 | public Point getLeft() { 37 | return left; 38 | } 39 | 40 | /** 41 | * Returns the right vertex of the segment. 42 | * 43 | * @return The right vertex 44 | */ 45 | public Point getRight() { 46 | return right; 47 | } 48 | 49 | /** 50 | * Returns the bisectional point of the segment. 51 | * 52 | * @return The bisectional point 53 | */ 54 | public Point getBisectionalPoint() { 55 | return new Point((left.getX() + right.getX()) / 2, 56 | (left.getY() + right.getY()) / 2); 57 | } 58 | 59 | /** 60 | * Returns the line the segment lies on. 61 | */ 62 | public Line getLine() { 63 | return new Line(left, right); 64 | } 65 | 66 | 67 | @Override 68 | public boolean equals(Object o2) { 69 | 70 | if (! (o2 instanceof Segment)) { 71 | return false; 72 | } 73 | 74 | Segment s2 = (Segment) o2; 75 | 76 | return ((this.getLeft().equals(s2.getLeft()) && 77 | this.getRight().equals(s2.getRight())) || 78 | (this.getLeft().equals(s2.getRight()) && 79 | this.getRight().equals(s2.getLeft()))); 80 | } 81 | 82 | final private Point left; 83 | final private Point right; 84 | } 85 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 |

Computational geometry in Java

2 | 3 | The project contains both implementations and visualization tools for basic computational geometry algorithms in two-dimensional space. These algorithms are implemented in Java programming language and are visualized using the Swing libraries.

4 | 5 |

List of implemented algorithms:

6 | 7 | 8 | 9 |

Two segments intersection

10 | 11 | 16 | 19 |
12 |
    13 |
  • Algorithm using the cross product - O(1) 14 |
15 |
17 | 18 |

20 | 21 |

Any segments intersection

22 | 23 | 29 | 32 |
24 |
    25 |
  • Sweeping line algorithm - O(n·lg(n)) 26 |
  • Naive algorithm - O(n2) 27 |
28 |
30 | 31 |

33 | 34 |

Convex hull construction

35 | 36 | 42 | 45 |
37 |
    38 |
  • Graham's scan - O(n·lg(n)) 39 |
  • Jarvis' march - O(n·h) 40 |
41 |
43 | 44 |

46 | 47 |

Closest points pair

48 | 49 | 55 | 58 |
50 |
    51 |
  • Divide&Conquer - O(n·lg(n)) 52 |
  • Naive algorithm - O(n2) 53 |
54 |
56 | 57 |

59 | 60 |

Polygon triangulation

61 | 62 | 69 | 72 |
63 |
    64 |
  • "Ear clipping" (Van Gogh) algorithm (improved) - O(n2) 65 |
  • "Ear clipping" (Van Gogh) algorithm (naive) - O(n3) 66 |
  • Primitive Divide&Conquer algorithm - O(n4) 67 |
68 |
70 | 71 |

73 | 74 |

Point set Delaunay triangulation

75 | 76 | 77 | 83 | 86 | 87 | 88 | 93 | 96 | 97 |
78 |
    79 |
  • Randomized incremental construction - O(n·lg(n)) 80 |
  • Brute force edge flipping algorithm - O(n3) 81 |
82 |
84 | 85 |
89 |
    90 |
  • 3D Terrain construction via VRML 91 |
92 |
94 | 95 |

98 | 99 |

Halfplanes intersection

100 | 101 | 106 | 109 |
102 |
    103 |
  • Incremental algorithm - O(n2) 104 |
105 |
107 | 108 |

110 | 111 |

Voronoi diagram

112 | 113 | 118 | 121 |
114 |
    115 |
  • Construction via Halfplanes intersection - O(n3) 116 |
117 |
119 | 120 |



122 | 123 |

Reference books:

124 | 125 | 126 | 127 | 128 | 129 | 130 |
131 | 132 | * __"Introduction to Algorithms"__ by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein 133 | * __"Computational Geometry: Algorithms and Applications"__ by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars 134 | * __"The Algorithm Design Manual"__ by Steven S. Skiena 135 | * __"Programming Challenges"__ by Steven S. Skiena and Miguel Revilla 136 | * __"Axioms and hulls"__ by Donald E. Knuth --------------------------------------------------------------------------------