├── ## 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
12 |
13 | - Algorithm using the cross product - O(1)
14 |
15 | |
16 |
17 |
18 | |
19 |
20 |
21 | Any segments intersection
22 |
23 |
24 |
25 | - Sweeping line algorithm - O(n·lg(n))
26 |
- Naive algorithm - O(n2)
27 |
28 | |
29 |
30 |
31 | |
32 |
33 |
34 | Convex hull construction
35 |
36 |
37 |
38 | - Graham's scan - O(n·lg(n))
39 |
- Jarvis' march - O(n·h)
40 |
41 | |
42 |
43 |
44 | |
45 |
46 |
47 | Closest points pair
48 |
49 |
50 |
51 | - Divide&Conquer - O(n·lg(n))
52 |
- Naive algorithm - O(n2)
53 |
54 | |
55 |
56 |
57 | |
58 |
59 |
60 | Polygon triangulation
61 |
62 |
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 | |
69 |
70 |
71 | |
72 |
73 |
74 | Point set Delaunay triangulation
75 |
76 |
77 |
78 |
79 | - Randomized incremental construction - O(n·lg(n))
80 |
- Brute force edge flipping algorithm - O(n3)
81 |
82 | |
83 |
84 |
85 | |
86 |
87 |
88 |
89 |
90 | - 3D Terrain construction via VRML
91 |
92 | |
93 |
94 |
95 | |
96 |
97 |
98 |
99 | Halfplanes intersection
100 |
101 |
102 |
103 | - Incremental algorithm - O(n2)
104 |
105 | |
106 |
107 |
108 | |
109 |
110 |
111 | Voronoi diagram
112 |
113 |
114 |
115 | - Construction via Halfplanes intersection - O(n3)
116 |
117 | |
118 |
119 |
120 | |
121 |
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
--------------------------------------------------------------------------------