Thread: [Geom4j-developer] SF.net SVN: geom4j:[13] trunk/src/net/sourceforge/geom4j
Status: Pre-Alpha
Brought to you by:
skhenkin
From: <skh...@us...> - 2009-12-01 23:05:27
|
Revision: 13 http://geom4j.svn.sourceforge.net/geom4j/?rev=13&view=rev Author: skhenkin Date: 2009-12-01 23:05:11 +0000 (Tue, 01 Dec 2009) Log Message: ----------- Binary search tree implemented. Tests for it added. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/Vector.java trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java trunk/src/net/sourceforge/geom4j/util/SearchTree.java Added Paths: ----------- trunk/src/net/sourceforge/geom4j/util/BinarySearchTreeTest.java Modified: trunk/src/net/sourceforge/geom4j/Vector.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Vector.java 2009-11-30 23:05:54 UTC (rev 12) +++ trunk/src/net/sourceforge/geom4j/Vector.java 2009-12-01 23:05:11 UTC (rev 13) @@ -15,7 +15,6 @@ package net.sourceforge.geom4j; -import java.util.Iterator; /** * A vector on a plane. Modified: trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java 2009-11-30 23:05:54 UTC (rev 12) +++ trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java 2009-12-01 23:05:11 UTC (rev 13) @@ -16,6 +16,8 @@ package net.sourceforge.geom4j.util; import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; /** * Binary search tree. @@ -76,7 +78,7 @@ protected Node findNode(E e) { Node node = root; while (node != null) { - int c = compare(node.value, e); + int c = compare(e, node.value); if (c == 0) return node; else if (c < 0) @@ -99,9 +101,10 @@ } Node node = root; Node parent; + int c; do { parent = node; - int c = compare(node.value, e); + c = compare(e, node.value); if (c == 0) { return false; } else if (c < 0) { @@ -110,7 +113,7 @@ node = node.right; } } while (node != null); - if (node == parent.left) { + if (c < 0) { parent.left = new Node(e, parent); } else { parent.right = new Node(e, parent); @@ -128,7 +131,7 @@ return false; } do { // node != null - int c = compare(node.value, e); + int c = compare(e, node.value); if (c == 0) { removeNode(node); return true; @@ -143,14 +146,50 @@ } private void removeNode(Node node) { - // TODO: !!! - if (node.left == null && node.right == null) { - //detachNode(parent, node); - } else if (node.left == null){ - node = node.right; + if (node.left == null && node.right == null) { // remove node + updateNodeReferences(node, null); + } else if (node.left == null){ // replace node with its single right child + updateNodeReferences(node, node.right); + } else if (node.right == null){ // replace node with its single left child + updateNodeReferences(node, node.left); + } else { + /* We can either replace the node with the biggest element of its + * left subtree or with the smallest element of its right subtree. + * We randomize the selection of this element to prevent tree + * degeneration into list + */ + boolean pseudoRandomChoice = (node.hashCode() % 2 == 0); + if (pseudoRandomChoice) { + // replace node value with its in-order predecessor's value + Node predecessor = previous(node); + node.value = predecessor.value; + // remove predecessor (it can have at most one child) + removeNode(predecessor); + } else { + // replace node value with its in-order successor's value + Node successor = next(node); + node.value = successor.value; + // remove successor (it can have at most one child) + removeNode(successor); + } } } + private void updateNodeReferences(Node node, Node replacement) { + if (node.parent == null) { // node == root + root = replacement; + } else { + if (node.parent.left == node) { + node.parent.left = replacement; + } else { + node.parent.right = replacement; + } + } + if (replacement != null) { + replacement.parent = node.parent; + } + } + @SuppressWarnings("unchecked") private int compare(E e1, E e2) { if (comparator == null) @@ -225,6 +264,29 @@ return parent; } - // TODO: create unit test; one of the ideas: build random tree, - // traverse its elements with next() and compare with the sorted set + /** Get ordered list of all tree elements */ + public List<E> toList() { + List<E> list = new LinkedList<E>(); + Node n = first(root); + while (n != null) { + list.add(n.value); + n = next(n); + } + return list; + } + + @Override + public String toString() { + return toString(root); + } + + public String toString(Node node) { + if (node == null) + return "*"; + return new StringBuilder("[").append(node.value).append(" ") + .append(toString(node.left)).append(" ").append(toString(node.right)) + .append("]").toString(); + } + + // TODO: add iterator } Added: trunk/src/net/sourceforge/geom4j/util/BinarySearchTreeTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinarySearchTreeTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/BinarySearchTreeTest.java 2009-12-01 23:05:11 UTC (rev 13) @@ -0,0 +1,100 @@ +package net.sourceforge.geom4j.util; + +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +public class BinarySearchTreeTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public void testSmallTree() { + SearchTree<Integer> t = createBinaryTree( + new Integer[] {7, 11, 5, 17, 6, 3, 9, 10, 4}); + assertEquals(3, t.first()); + assertEquals(17, t.last()); + assertEquals(7, t.next(6)); + assertEquals(10, t.previous(11)); + assertTrue(listsEqual(new Integer[] {3, 4, 5, 6, 7, 9, 10, 11, 17}, + t.toList())); + assertTrue(t.contains(7)); + t.remove(7); + assertFalse(t.contains(7)); + assertTrue(listsEqual(new Integer[] {3, 4, 5, 6, 9, 10, 11, 17}, + t.toList())); + assertTrue(t.contains(9)); + t.remove(9); + assertFalse(t.contains(9)); + assertTrue(listsEqual(new Integer[] {3, 4, 5, 6, 10, 11, 17}, + t.toList())); + assertTrue(t.contains(5)); + t.remove(5); + assertFalse(t.contains(5)); + assertTrue(listsEqual(new Integer[] {3, 4, 6, 10, 11, 17}, + t.toList())); + assertTrue(t.contains(17)); + t.remove(17); + assertFalse(t.contains(17)); + assertTrue(listsEqual(new Integer[] {3, 4, 6, 10, 11}, + t.toList())); + } + + @Test + public void testRandomTree() { + Integer[] values = createRandomUniqueArray(300, 1000); + SearchTree<Integer> t = createBinaryTree(values); + Arrays.sort(values); + assertTrue(listsEqual(values, t.toList())); + } + + // TODO: test removal + + private boolean listsEqual(Integer[] values, List<Integer> list) { + if (values.length != list.size()) + return false; + int i = 0; + for (Integer v: list) { + if (!v.equals(values[i++])) + return false; + } + return true; + } + + private Integer[] createRandomUniqueArray(int size, int maxValue) { + Random r = new Random(); + Set<Integer> used = new HashSet<Integer>(); + Integer[] values = new Integer[size]; + for (int i = 0; i < size; i++) { + Integer v; + do { + v = r.nextInt(maxValue); + } while (used.contains(v)); + values[i] = v; + used.add(v); + } + return values; + } + + private SearchTree<Integer> createBinaryTree(Integer[] values) { + SearchTree<Integer> tree = new BinarySearchTree<Integer>(); + populateTree(tree, values); + return tree; + } + + private void populateTree(SearchTree<Integer> tree, Integer[] values) { + for (int i: values) { + tree.insert(i); + } + } + +} Modified: trunk/src/net/sourceforge/geom4j/util/SearchTree.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/SearchTree.java 2009-11-30 23:05:54 UTC (rev 12) +++ trunk/src/net/sourceforge/geom4j/util/SearchTree.java 2009-12-01 23:05:11 UTC (rev 13) @@ -15,6 +15,8 @@ package net.sourceforge.geom4j.util; +import java.util.List; + /** * General purpose search tree. * Supports operations of inserting, searching and removing for @@ -50,4 +52,7 @@ /** Get previous (in-order predecessor) element for the given element */ E previous(E e); + + /** Get ordered list of all tree elements */ + List<E> toList(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2009-12-05 02:38:19
|
Revision: 15 http://geom4j.svn.sourceforge.net/geom4j/?rev=15&view=rev Author: skhenkin Date: 2009-12-05 02:38:09 +0000 (Sat, 05 Dec 2009) Log Message: ----------- Bentley-Ottmann line segments intersection algorithm implemented using priority queues based on binary trees. Tests added and refactored. Performance tested. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/Config.java trunk/src/net/sourceforge/geom4j/Point.java trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java trunk/src/net/sourceforge/geom4j/SegmentSet.java trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueue.java trunk/src/net/sourceforge/geom4j/util/SearchTree.java trunk/src/net/sourceforge/geom4j/util/SmartQueue.java Added Paths: ----------- trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueueTest.java trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueueTest.java trunk/src/net/sourceforge/geom4j/util/SmartQueueTestBase.java Removed Paths: ------------- trunk/src/net/sourceforge/geom4j/util/SmartQueueTest.java Modified: trunk/src/net/sourceforge/geom4j/Config.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Config.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/Config.java 2009-12-05 02:38:09 UTC (rev 15) @@ -56,4 +56,16 @@ return lessThanOrEqual(a, b) && lessThanOrEqual(a, x) && lessThanOrEqual(x, b) || lessThanOrEqual(b, a) && lessThanOrEqual(x, a) && lessThanOrEqual(b, x); } + + public static boolean isPositive(double x) { + return x > precision; + } + + public static boolean isNegative(double x) { + return x < -precision; + } + + public static double roundValue(double x) { + return Math.round(x / precision) * precision; + } } Modified: trunk/src/net/sourceforge/geom4j/Point.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Point.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/Point.java 2009-12-05 02:38:09 UTC (rev 15) @@ -33,6 +33,8 @@ public double getX() { return x; } + + public double getY() { return y; @@ -49,12 +51,20 @@ @Override public int hashCode() { - long bits = java.lang.Double.doubleToLongBits(getX()); - bits ^= java.lang.Double.doubleToLongBits(getY()) * 31; + long bits = java.lang.Double.doubleToLongBits(getRoundedX()); + bits ^= java.lang.Double.doubleToLongBits(getRoundedY()) * 31; return (((int) bits) ^ ((int) (bits >> 32))); } + private double getRoundedY() { + return Config.roundValue(x); + } + + private double getRoundedX() { + return Config.roundValue(y); + } + @Override public String toString() { return "(" + x + ", " + y + ")"; Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java 2009-12-05 02:38:09 UTC (rev 15) @@ -15,10 +15,11 @@ package net.sourceforge.geom4j; -import java.util.Collections; +import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import net.sourceforge.geom4j.util.BinaryTreeBasedSmartQueue; import net.sourceforge.geom4j.util.ListBasedSmartQueue; import net.sourceforge.geom4j.util.SmartQueue; @@ -69,16 +70,25 @@ * http://www.softsurfer.com/Archive/algorithm_0108/algorithm_0108.htm * http://www.cs.wustl.edu/~pless/506/l4.html */ - static SegmentIntersectionAlgorithm BENTLEY_OTTMANN = new SegmentIntersectionAlgorithm() { + static SegmentIntersectionAlgorithm BENTLEY_OTTMANN = new BentleyOttmannSegmentIntersectionAlgorithm(); + + class BentleyOttmannSegmentIntersectionAlgorithm implements SegmentIntersectionAlgorithm { + SweepLineComparator sweepLineComparator = new SweepLineComparator(); + EventComparator eventComparator = new EventComparator(); + @Override public PointSet run(SegmentSet segments) { // initialization SmartQueue<Event> eventQueue = initializeEventQueue(segments); - SmartQueue<SweepLineSegment> sweepLine = new ListBasedSmartQueue<SweepLineSegment>(); + //SmartQueue<SweepLineSegment> sweepLine + // = new ListBasedSmartQueue<SweepLineSegment>(sweepLineComparator); + SmartQueue<SweepLineSegment> sweepLine = + new BinaryTreeBasedSmartQueue<SweepLineSegment>(sweepLineComparator); PointSet intersections = new PointSet(); // process all events in the queue while (!eventQueue.isEmpty()) { Event e = eventQueue.poll(); + sweepLineComparator.setCurrentPoint(e.getPoint()); e.execute(eventQueue, sweepLine, intersections); } // return found intersections @@ -91,255 +101,266 @@ events.add(new LeftEndpointEvent(new SweepLineSegment(s))); events.add(new RightEndpointEvent(new SweepLineSegment(s))); } - Collections.sort(events); - SmartQueue<Event> eventQueue = new ListBasedSmartQueue<Event>(); + //SmartQueue<Event> eventQueue = new ListBasedSmartQueue<Event>(eventComparator); + SmartQueue<Event> eventQueue = new BinaryTreeBasedSmartQueue<Event>(eventComparator); for (Event e : events) { eventQueue.insert(e); } return eventQueue; } - }; + public void setCompareBeforeSweepLine(boolean compareBefore) { + sweepLineComparator.setBefore(compareBefore); + } - /* - * Helper classes for Bentley-Ottmann algorithm - */ + /* + * Helper classes for Bentley-Ottmann algorithm + */ - /** - * Sweep line event. Events are stored in the event queue which is a - * priority queue which doesn't store duplicate elements. - * - * @author Sergey Khenkin - */ - abstract class Event implements Comparable<Event> { - /** Segment left endpoint event type */ - public static final int LEFT_ENDPOINT = 0; - /** Segment intersection event type */ - public static final int INTERSECTION = 1; - /** Segment right endpoint event type */ - public static final int RIGHT_ENDPOINT = 2; - - /** Execute event-specific actions */ - public abstract void execute(SmartQueue<Event> eventQueue, - SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet); - /** - * Events are sorted by x coordinate asc, then by y coordinate asc, then - * by type asc. Sorting by type is important as left endpoints should be - * processed before intersections before right endpoints in case of the - * points with equal coordinates. + * Sweep line event. Events are stored in the event queue which is a + * priority queue which doesn't store duplicate elements. + * + * @author Sergey Khenkin */ - @Override - public int compareTo(Event e) { - Point p1 = getPoint(); - Point p2 = e.getPoint(); - if (p1.getX() < p2.getX()) { - return -1; - } else if (p1.getX() > p2.getX()) { - return 1; - } else { - if (p1.getY() < p2.getY()) { - return -1; - } else if (p1.getY() > p2.getY()) { - return 1; - } else { - if (getType() < e.getType()) { - return -1; - } else if (getType() > e.getType()) { - return 1; - } else { - return 0; - } - } - } - } + abstract class Event { + /** Segment left endpoint event type */ + public static final int LEFT_ENDPOINT = 0; + /** Segment intersection event type */ + public static final int INTERSECTION = 1; + /** Segment right endpoint event type */ + public static final int RIGHT_ENDPOINT = 2; - public abstract int getType(); + /** Execute event-specific actions */ + public abstract void execute(SmartQueue<Event> eventQueue, + SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet); - /** Get the point associated with the event */ - public abstract Point getPoint(); + public abstract int getType(); - /** - * Add intersection event if segments intersect and intersection point - * is to the right of the point being currently processed. It is - * important not to add intersections at the points to the left of - * currently processed one because we can end up in an infinite loop - * (moreover we never get back in the algorithm). - */ - protected static void addIntersectionEvent( - SmartQueue<Event> eventQueue, SweepLineSegment seg1, - SweepLineSegment seg2, Point curPoint) { - if (seg1 != null && seg2 != null) { - for (Point p : seg1.getSegment().intersects(seg2.getSegment()) - .getPoints()) { - if (p.getX() >= curPoint.getX()) { - eventQueue.insert(new IntersectionEvent(p, seg1, seg2)); + /** Get the point associated with the event */ + public abstract Point getPoint(); + + /** + * Add intersection event if segments intersect and intersection point + * is to the right of the point being currently processed. It is + * important not to add intersections at the points to the left of + * currently processed one because we can end up in an infinite loop + * (moreover we never get back in the algorithm). + */ + protected void addIntersectionEvent( + SmartQueue<Event> eventQueue, SweepLineSegment seg1, + SweepLineSegment seg2, Point curPoint) { + if (seg1 != null && seg2 != null) { + for (Point p : seg1.getSegment().intersects(seg2.getSegment()) + .getPoints()) { + if (p.getX() >= curPoint.getX()) { + eventQueue.insert(new IntersectionEvent(p, seg1, seg2)); + } } } } } - } - /** - * Left endpoint event. Left end of a segment is reached. - */ - class LeftEndpointEvent extends Event { - SweepLineSegment sweepLineSegment; - - public LeftEndpointEvent(SweepLineSegment sweepLineSegment) { - this.sweepLineSegment = sweepLineSegment; - } - /** - * On reaching left endpoint of a segment we insert this segment into - * the sweep line and add intersections of it with its nearest neighbors - * from top and bottom if they exist. + * Left endpoint event. Left end of a segment is reached. */ - @Override - public void execute(SmartQueue<Event> eventQueue, - SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { - sweepLine.insert(sweepLineSegment); - SweepLineSegment segBefore = sweepLine.prev(sweepLineSegment); - SweepLineSegment segAfter = sweepLine.next(sweepLineSegment); - addIntersectionEvent(eventQueue, segAfter, sweepLineSegment, - sweepLineSegment.getLeftPoint()); - addIntersectionEvent(eventQueue, sweepLineSegment, segBefore, - sweepLineSegment.getLeftPoint()); - } + class LeftEndpointEvent extends Event { + SweepLineSegment sweepLineSegment; - @Override - public Point getPoint() { - return sweepLineSegment.getLeftPoint(); - } + public LeftEndpointEvent(SweepLineSegment sweepLineSegment) { + this.sweepLineSegment = sweepLineSegment; + } - @Override - public int getType() { - return LEFT_ENDPOINT; - } + /** + * On reaching left endpoint of a segment we insert this segment into + * the sweep line and add intersections of it with its nearest neighbors + * from top and bottom if they exist. + */ + @Override + public void execute(SmartQueue<Event> eventQueue, + SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { + sweepLineComparator.setBefore(true); + sweepLine.insert(sweepLineSegment); + SweepLineSegment segBefore = sweepLine.prev(sweepLineSegment); + SweepLineSegment segAfter = sweepLine.next(sweepLineSegment); + sweepLineComparator.setBefore(false); + addIntersectionEvent(eventQueue, segAfter, sweepLineSegment, + sweepLineSegment.getLeftPoint()); + addIntersectionEvent(eventQueue, sweepLineSegment, segBefore, + sweepLineSegment.getLeftPoint()); + } - public String toString() { - return "<" + getType() + ", " + getPoint() + ", " - + sweepLineSegment + ">"; - } + @Override + public Point getPoint() { + return sweepLineSegment.getLeftPoint(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof LeftEndpointEvent) { - LeftEndpointEvent lee = (LeftEndpointEvent) obj; - return sweepLineSegment.equals(lee.sweepLineSegment); + @Override + public int getType() { + return LEFT_ENDPOINT; } - return false; - } - } - /** - * Right endpoint event. Right end of a segment is reached. - */ - class RightEndpointEvent extends Event { - SweepLineSegment sweepLineSegment; + public String toString() { + return "<" + getType() + ", " + getPoint() + ", " + + sweepLineSegment + ">"; + } - public RightEndpointEvent(SweepLineSegment sweepLineSegment) { - this.sweepLineSegment = sweepLineSegment; + @Override + public boolean equals(Object obj) { + if (obj instanceof LeftEndpointEvent) { + LeftEndpointEvent lee = (LeftEndpointEvent) obj; + return sweepLineSegment.equals(lee.sweepLineSegment); + } + return false; + } } /** - * On reaching right endpoint of a segment we check for intersection of - * segment's neighbors from top and bottom on the sweep line and add a - * new intersection event when appropriate. After that just remove the - * segment from the sweep line. + * Right endpoint event. Right end of a segment is reached. */ - @Override - public void execute(SmartQueue<Event> eventQueue, - SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { - SweepLineSegment segBefore = sweepLine.prev(sweepLineSegment); - SweepLineSegment segAfter = sweepLine.next(sweepLineSegment); - addIntersectionEvent(eventQueue, segAfter, segBefore, - sweepLineSegment.getRightPoint()); - sweepLine.remove(sweepLineSegment); - } + class RightEndpointEvent extends Event { + SweepLineSegment sweepLineSegment; - @Override - public Point getPoint() { - return sweepLineSegment.getRightPoint(); - } + public RightEndpointEvent(SweepLineSegment sweepLineSegment) { + this.sweepLineSegment = sweepLineSegment; + } - @Override - public int getType() { - return RIGHT_ENDPOINT; - } + /** + * On reaching right endpoint of a segment we check for intersection of + * segment's neighbors from top and bottom on the sweep line and add a + * new intersection event when appropriate. After that just remove the + * segment from the sweep line. + */ + @Override + public void execute(SmartQueue<Event> eventQueue, + SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { + SweepLineSegment segBefore = sweepLine.prev(sweepLineSegment); + SweepLineSegment segAfter = sweepLine.next(sweepLineSegment); + addIntersectionEvent(eventQueue, segAfter, segBefore, + sweepLineSegment.getRightPoint()); + sweepLine.remove(sweepLineSegment); + } - public String toString() { - return "<" + getType() + ", " + getPoint() + ", " - + sweepLineSegment + ">"; - } + @Override + public Point getPoint() { + return sweepLineSegment.getRightPoint(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof RightEndpointEvent) { - RightEndpointEvent ree = (RightEndpointEvent) obj; - return sweepLineSegment.equals(ree.sweepLineSegment); + @Override + public int getType() { + return RIGHT_ENDPOINT; } - return false; - } - } + public String toString() { + return "<" + getType() + ", " + getPoint() + ", " + + sweepLineSegment + ">"; + } - /** - * Intersection event. Intersection point of two segments is reached. - */ - class IntersectionEvent extends Event { - private Point p; - private SweepLineSegment sAbove; - private SweepLineSegment sBelow; + @Override + public boolean equals(Object obj) { + if (obj instanceof RightEndpointEvent) { + RightEndpointEvent ree = (RightEndpointEvent) obj; + return sweepLineSegment.equals(ree.sweepLineSegment); + } + return false; + } - public IntersectionEvent(Point p, SweepLineSegment sAbove, - SweepLineSegment sBelow) { - this.p = p; - this.sAbove = sAbove; - this.sBelow = sBelow; } /** - * On reaching intersection point of two segments on the sweep line add - * this point to the list of intersections, then swap the intersecting - * segments on the sweep line and check if they now intersect with their - * new neighbors. If so add new intersection points accordingly. + * Intersection event. Intersection point of two segments is reached. */ - @Override - public void execute(SmartQueue<Event> eventQueue, - SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { - intersectionSet.add(p); - sweepLine.swap(sAbove, sBelow); - addIntersectionEvent(eventQueue, sAbove, sweepLine.prev(sAbove), p); - addIntersectionEvent(eventQueue, sweepLine.next(sBelow), sBelow, p); - } + class IntersectionEvent extends Event { + private Point p; + private SweepLineSegment sAbove; + private SweepLineSegment sBelow; - @Override - public Point getPoint() { - return p; - } + public IntersectionEvent(Point p, SweepLineSegment sAbove, + SweepLineSegment sBelow) { + this.p = p; + this.sAbove = sAbove; + this.sBelow = sBelow; + } - @Override - public int getType() { - return INTERSECTION; - } + /** + * On reaching intersection point of two segments on the sweep line add + * this point to the list of intersections, then swap the intersecting + * segments on the sweep line and check if they now intersect with their + * new neighbors. If so add new intersection points accordingly. + */ + @Override + public void execute(SmartQueue<Event> eventQueue, + SmartQueue<SweepLineSegment> sweepLine, PointSet intersectionSet) { + intersectionSet.add(p); + sweepLineComparator.setBefore(true); + sweepLine.swap(sAbove, sBelow); + sweepLineComparator.setBefore(false); + addIntersectionEvent(eventQueue, sAbove, sweepLine.prev(sAbove), p); + addIntersectionEvent(eventQueue, sweepLine.next(sBelow), sBelow, p); + } - public String toString() { - return "<" + getType() + ", " + getPoint() + ", " + sAbove + ", " - + sBelow + ">"; + @Override + public Point getPoint() { + return p; + } + + @Override + public int getType() { + return INTERSECTION; + } + + public String toString() { + return "<" + getType() + ", " + getPoint() + ", " + sAbove + ", " + + sBelow + ">"; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IntersectionEvent) { + IntersectionEvent ie = (IntersectionEvent) obj; + return p.equals(ie.p) && sAbove.equals(ie.sAbove) + && sBelow.equals(ie.sBelow); + } + return false; + } } - @Override - public boolean equals(Object obj) { - if (obj instanceof IntersectionEvent) { - IntersectionEvent ie = (IntersectionEvent) obj; - return p.equals(ie.p) && sAbove.equals(ie.sAbove) - && sBelow.equals(ie.sBelow); + public class EventComparator implements Comparator<Event> { + + /** + * Events are sorted by x coordinate asc, then by y coordinate asc, then + * by type asc. Sorting by type is important as left endpoints should be + * processed before intersections before right endpoints in case of the + * points with equal coordinates. + */ + @Override + public int compare(Event e1, Event e2) { + Point p1 = e1.getPoint(); + Point p2 = e2.getPoint(); + if (Config.lessThan(p1.getX(), p2.getX())) { + return -1; + } else if (Config.lessThan(p2.getX(), p1.getX())) { + return 1; + } else { + if (Config.lessThan(p1.getY(), p2.getY())) { + return -1; + } else if (Config.lessThan(p2.getY(), p1.getY())) { + return 1; + } else { + if (e1.getType() < e2.getType()) { + return -1; + } else if (e1.getType() > e2.getType()) { + return 1; + } else { + return e1.equals(e2) ? 0 : -1; + } + } + } } - return false; } - } + }; + /** * A wrapper class for a segment which sets segment ordering on the sweep * line. @@ -448,4 +469,100 @@ } + /** Comparator for segments stored in sweep line */ + class SweepLineComparator implements Comparator<SweepLineSegment> { + private Point currentPoint; + private boolean before = false; + + /** + * Segments are sorted from top to bottom by the y coordinate of their + * intersection with the sweep line. This order is kept during sweep + * line movement by appropriate swaps of the intersecting segments. + */ + @Override + public int compare(SweepLineSegment s1, SweepLineSegment s2) { + Point p1 = s1.getLeftPoint(); + Point p2 = s2.getLeftPoint(); + // p1 is always to the left of p2 or on the same vertical + Point q1 = s1.getRightPoint(); + Point q2 = s2.getRightPoint(); + int pos = compareVerticals(p1, q1, p2, q2); + if (pos != 0) { + return pos; + } else { + int c = compareSegmentsAtIntersectionPoint(p1, q1, p2, q2); + return c; + } + } + + private int compareSegmentsAtIntersectionPoint(Point p1, Point q1, Point p2, Point q2) { + Vector v1 = new Vector(p1, q1); + Vector v2 = new Vector(p2, q2); + double d = v1.crossProduct(v2); + if (before) d = -d; + if (Config.isPositive(d)) { + return -1; + } else if (Config.isNegative(d)) { + return 1; + } else { + return 0; + } + } + + /** + * Determines how we compare segments crossing the sweep line + * at the same point: which is below before the sweep line + * (to the left of it) or after (to the right) + */ + public void setBefore(boolean flag) { + this.before = flag; + } + + /** + * Check if segment [p1,q1] crosses vertical that passes through + * point (x,0) segment below [p2,q2] + * Works for x: p1.x<=cp.x<=q1.x, p2.x<=cp.x<=q2.x + */ + private int compareVerticals(Point p1, Point q1, Point p2, Point q2) { + double y1 = calculateVertical(p1, q1); + double y2 = calculateVertical(p2, q2); + if (Config.lessThan(y1, y2)) return -1; + else if (Config.lessThan(y2, y1)) return 1; + else return 0; + } + + private double calculateVertical(Point p, Point q) { + if (Config.equal(p.getX(), q.getX())) { + return currentPoint.getY(); + } else { + return (currentPoint.getX() - p.getX()) * (q.getY() - p.getY()) + / (q.getX() - p.getX()) + p.getY(); + } + } + + /** + * Check if p is above the segment [leftPoint, rightPoint]. + * + * @return -1 if p is above the segment 1 if p is below the segment 0 if + * p lies on the same line with segment endpoints + */ + public int relativePosition(Point p, Point leftPoint, Point rightPoint) { + Vector v1 = new Vector(leftPoint, rightPoint); + Vector v2 = new Vector(leftPoint, p); + double d = v1.crossProduct(v2); + if (d > 0) { // p2 is above [p1, q1] + return -1; + } else if (d < 0) { // p2 is below [p1, q1] + return 1; + } else { // p2 lies on (p1, q1) + return 0; + } + } + + public void setCurrentPoint(Point currentPoint) { + this.currentPoint = currentPoint; + } + } + } + Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-05 02:38:09 UTC (rev 15) @@ -28,7 +28,7 @@ Polygon p = new Polygon() .addVertex(new Point(1, 1)) .addVertex(new Point(1, 2)) - //.addVertex(new Point(2, 3)) + .addVertex(new Point(2, 3)) .addVertex(new Point(3, 2)) .addVertex(new Point(3, 1)); ss2 = p.getSegmentSet(); @@ -141,5 +141,27 @@ assertEquals(naiveSet, boSet); } - + @Test + public void testBentleyOttmannPerformance() { + // a number of segments in the set + int n = 1000; + /* chaining list of segments of this shape: + * \/\/\/\/\/ + * /\/\/\/\/\ + * + * number of intersection is approximately 1.5*n + */ + SegmentSet ss = new SegmentSet(); + for (int i = 0; i < n/2; i++) { + ss.add(new Segment(new Point(i, 0), new Point(i+1, 1))); + ss.add(new Segment(new Point(i, 1), new Point(i+1, 0))); + } + //System.out.println(System.currentTimeMillis()); + PointSet naiveSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.NAIVE); + //System.out.println(System.currentTimeMillis()); + PointSet boSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.BENTLEY_OTTMANN); + //System.out.println(System.currentTimeMillis()); + //System.out.println(naiveSet.size()); + assertEquals(naiveSet, boSet); + } } Modified: trunk/src/net/sourceforge/geom4j/SegmentSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-05 02:38:09 UTC (rev 15) @@ -33,11 +33,13 @@ /** * Get all intersections between the segments of this set - * using naive algorithm - O(n*n). + * using Bentley-Ottmann algorithm - O((n+k) log n), + * where n is the number of segments in the set and k is + * the number of intersections. * @return set of intersection points */ public PointSet getAllIntersectionPoints() { - return getAllIntersectionPoints(SegmentIntersectionAlgorithm.NAIVE); + return getAllIntersectionPoints(SegmentIntersectionAlgorithm.BENTLEY_OTTMANN); } } Modified: trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/util/BinarySearchTree.java 2009-12-05 02:38:09 UTC (rev 15) @@ -54,13 +54,13 @@ } /** Root of the tree */ - private Node root = null; + protected Node root = null; /** * Comparator for tree node values. * If it is null the elements should implement Comparable. */ - private Comparator<? super E> comparator = null; + protected Comparator<? super E> comparator = null; /** Create an empty tree of comparable elements */ public BinarySearchTree() { @@ -147,7 +147,7 @@ return false; } - private void removeNode(Node node) { + protected void removeNode(Node node) { if (node.left == null && node.right == null) { // remove node updateNodeReferences(node, null); } else if (node.left == null){ // replace node with its single right child @@ -364,4 +364,10 @@ } } + + /** Check if tree contains elements */ + @Override + public boolean isEmpty() { + return root == null; + } } Added: trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java 2009-12-05 02:38:09 UTC (rev 15) @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import java.util.Comparator; + + +/** + * "Smarter" implementation of SmartQueue based on a binary search tree. + * It takes on average O(log n) time to insert/remove/swap + * and O(1) to get prev/next elements. + * There's still a room for performance improvements using some kind + * of self-balancing trees (like AVL-trees or Red-Black trees). + * + * @author Sergey Khenkin + */ +public class BinaryTreeBasedSmartQueue<E> implements SmartQueue<E> { + class CustomBinarySearchTree extends BinarySearchTree<E> { + + public CustomBinarySearchTree() { + super(); + } + + public CustomBinarySearchTree(Comparator<? super E> comparator) { + super(comparator); + } + + public E poll() { + Node n = firstNode(root); + if (n == null) + return null; + removeNode(n); + return n.value; + } + + public void swap(E e1, E e2) { + Node n1 = findNode(e1); + Node n2 = findNode(e2); + if (n1 != null && n2 != null) { + n1.value = e2; + n2.value = e1; + } + } + + } + /** Tree that stores queue elements */ + private CustomBinarySearchTree tree = new CustomBinarySearchTree(); + + public BinaryTreeBasedSmartQueue() { + tree = new CustomBinarySearchTree(); + } + + public BinaryTreeBasedSmartQueue(Comparator<E> comparator) { + tree = new CustomBinarySearchTree(comparator); + } + + @Override + public void insert(E e) { + tree.insert(e); + } + + @Override + public E next(E e) { + return tree.next(e); + } + + @Override + public E poll() { + return tree.poll(); + } + + @Override + public E prev(E e) { + return tree.previous(e); + } + + @Override + public E remove(E e) { + return tree.remove(e) ? e : null; + } + + @Override + public boolean isEmpty() { + return tree.isEmpty(); + } + + @Override + public void swap(E e1, E e2) { + tree.swap(e1, e2); + } + + public String toString() { + return tree.toString(); + } +} + + Added: trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueueTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueueTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueueTest.java 2009-12-05 02:38:09 UTC (rev 15) @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class BinaryTreeBasedSmartQueueTest extends SmartQueueTestBase { + @Override + protected SmartQueue<String> getQueueInstance() { + return new BinaryTreeBasedSmartQueue<String>(); + } + + @Test + public void testSwap() { + // actually swapping has not much sense for the search tree + SmartQueue<String> q = createQueue(); + q.swap("New York", "Brussels"); + assertEquals("New York", q.poll()); + } +} Modified: trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueue.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueue.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueue.java 2009-12-05 02:38:09 UTC (rev 15) @@ -15,30 +15,40 @@ package net.sourceforge.geom4j.util; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** - * Dummy implementation of SmartQueue based on linked list. + * Dummy implementation of SmartQueue based on a linked list. * It actually takes O(n) time to insert/remove/swap * or get prev/next elements. * So this is just a temporary implementation until a more - * efficient one of time O(n log n) is ready. + * efficient one of time O(log n) is ready. * * @author Sergey Khenkin */ -public class ListBasedSmartQueue<E extends Comparable<E>> implements SmartQueue<E> { +public class ListBasedSmartQueue<E> implements SmartQueue<E> { /** List that stores queue elements */ private List<E> list = new LinkedList<E>(); + protected Comparator<E> comparator = null; + + public ListBasedSmartQueue() { + } + + public ListBasedSmartQueue(Comparator<E> comparator) { + this.comparator = comparator; + } + @Override public void insert(E e) { int i = 0; while (i < list.size()) { - if (list.get(i).compareTo(e) > 0) { + if (compare(list.get(i), e) > 0) { break; - } else if (list.get(i).compareTo(e) == 0) { + } else if (compare(list.get(i), e) == 0) { if (list.get(i).equals(e)) { return; // don't add duplicate } else { @@ -105,6 +115,14 @@ return list.toString(); } + @SuppressWarnings("unchecked") + protected int compare(E e1, E e2) { + if (comparator == null) { + return ((Comparable<? super E>) e1).compareTo(e2); + } else { + return comparator.compare(e1, e2); + } + } } Added: trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueueTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueueTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/ListBasedSmartQueueTest.java 2009-12-05 02:38:09 UTC (rev 15) @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class ListBasedSmartQueueTest extends SmartQueueTestBase { + @Override + protected SmartQueue<String> getQueueInstance() { + return new ListBasedSmartQueue<String>(); + } + + @Test + public void testSwap() { + SmartQueue<String> q = createQueue(); + q.swap("New York", "Tel Aviv"); + q.swap("San Francisco", "Paris"); + + assertEquals("San Francisco", q.next("Tel Aviv")); + assertEquals("Sydney", q.prev("New York")); + assertEquals("London", q.next("Brussels")); + + assertEquals("Brussels", q.poll()); + assertEquals("London", q.poll()); + assertEquals("Tel Aviv", q.poll()); + assertEquals("San Francisco", q.poll()); + assertEquals("Paris", q.poll()); + assertEquals("Sydney", q.poll()); + assertEquals("New York", q.poll()); + assertEquals("Toronto", q.poll()); + assertEquals("Zurich", q.poll()); + assertTrue(q.isEmpty()); + } + +} Modified: trunk/src/net/sourceforge/geom4j/util/SearchTree.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/SearchTree.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/util/SearchTree.java 2009-12-05 02:38:09 UTC (rev 15) @@ -65,4 +65,7 @@ /** Get the number of elements in the tree */ int size(); + + /** Check if tree contains elements */ + boolean isEmpty(); } Modified: trunk/src/net/sourceforge/geom4j/util/SmartQueue.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/SmartQueue.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/util/SmartQueue.java 2009-12-05 02:38:09 UTC (rev 15) @@ -28,7 +28,7 @@ * * @author Sergey Khenkin */ -public interface SmartQueue<E extends Comparable<E>> { +public interface SmartQueue<E> { /** Insert an element */ void insert(E e); /** Remove an element */ Deleted: trunk/src/net/sourceforge/geom4j/util/SmartQueueTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/SmartQueueTest.java 2009-12-02 20:28:03 UTC (rev 14) +++ trunk/src/net/sourceforge/geom4j/util/SmartQueueTest.java 2009-12-05 02:38:09 UTC (rev 15) @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2009 Sergey Khenkin. All rights reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.sourceforge.geom4j.util; - -import static org.junit.Assert.*; - -import org.junit.Before; -import org.junit.Test; - -public class SmartQueueTest { - - @Before - public void setUp() throws Exception { - } - - private static SmartQueue<String> createQueue() { - SmartQueue<String> q = new ListBasedSmartQueue<String>(); - q.insert("New York"); - q.insert("Toronto"); - q.insert("Tel Aviv"); - q.insert("London"); - q.insert("Zurich"); - q.insert("Paris"); - q.insert("Sydney"); - q.insert("San Francisco"); - q.insert("Brussels"); - return q; - } - - @Test - public void testPollInsertAndIsEmpty() { - SmartQueue<String> q = createQueue(); - assertFalse(q.isEmpty()); - assertEquals("Brussels", q.poll()); - assertEquals("London", q.poll()); - assertEquals("New York", q.poll()); - assertEquals("Paris", q.poll()); - assertEquals("San Francisco", q.poll()); - assertEquals("Sydney", q.poll()); - assertEquals("Tel Aviv", q.poll()); - assertEquals("Toronto", q.poll()); - assertEquals("Zurich", q.poll()); - assertTrue(q.isEmpty()); - } - - @Test - public void testRemove() { - SmartQueue<String> q = createQueue(); - assertEquals("Toronto", q.remove("Toronto")); - assertEquals("Zurich", q.remove("Zurich")); - assertEquals("London", q.remove("London")); - assertEquals("San Francisco", q.remove("San Francisco")); - assertEquals("Brussels", q.poll()); - assertEquals("New York", q.poll()); - assertEquals("Paris", q.poll()); - assertEquals("Sydney", q.poll()); - assertEquals("Tel Aviv", q.poll()); - assertTrue(q.isEmpty()); - } - - @Test - public void testNexAndPrev() { - SmartQueue<String> q = createQueue(); - assertEquals("Toronto", q.next("Tel Aviv")); - assertEquals("Sydney", q.prev("Tel Aviv")); - assertEquals("Brussels", q.prev("London")); - assertEquals("Zurich", q.next("Toronto")); - assertNull(q.prev("Brussels")); - assertNull(q.next("Zurich")); - } - - @Test - public void testSwap() { - SmartQueue<String> q = createQueue(); - q.swap("New York", "Tel Aviv"); - q.swap("San Francisco", "Paris"); - - assertEquals("San Francisco", q.next("Tel Aviv")); - assertEquals("Sydney", q.prev("New York")); - assertEquals("London", q.next("Brussels")); - - assertEquals("Brussels", q.poll()); - assertEquals("London", q.poll()); - assertEquals("Tel Aviv", q.poll()); - assertEquals("San Francisco", q.poll()); - assertEquals("Paris", q.poll()); - assertEquals("Sydney", q.poll()); - assertEquals("New York", q.poll()); - assertEquals("Toronto", q.poll()); - assertEquals("Zurich", q.poll()); - assertTrue(q.isEmpty()); - } - -} Copied: trunk/src/net/sourceforge/geom4j/util/SmartQueueTestBase.java (from rev 12, trunk/src/net/sourceforge/geom4j/util/SmartQueueTest.java) =================================================================== --- trunk/src/net/sourceforge/geom4j/util/SmartQueueTestBase.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/SmartQueueTestBase.java 2009-12-05 02:38:09 UTC (rev 15) @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public abstract class SmartQueueTestBase { + + protected SmartQueue<String> createQueue() { + SmartQueue<String> q = getQueueInstance(); + q.insert("New York"); + q.insert("Toronto"); + q.insert("Tel Aviv"); + q.insert("London"); + q.insert("Zurich"); + q.insert("Paris"); + q.insert("Sydney"); + q.insert("San Francisco"); + q.insert("Brussels"); + return q; + } + + protected abstract SmartQueue<String> getQueueInstance(); + + @Test + public void testPollInsertAndIsEmpty() { + SmartQueue<String> q = createQueue(); + assertFalse(q.isEmpty()); + assertEquals("Brussels", q.poll()); + assertEquals("London", q.poll()); + assertEquals("New York", q.poll()); + assertEquals("Paris", q.poll()); + assertEquals("San Francisco", q.poll()); + assertEquals("Sydney", q.poll()); + assertEquals("Tel Aviv", q.poll()); + assertEquals("Toronto", q.poll()); + assertEquals("Zurich", q.poll()); + assertTrue(q.isEmpty()); + } + + @Test + public void testRemove() { + SmartQueue<String> q = createQueue(); + assertEquals("Toronto", q.remove("Toronto")); + assertEquals("Zurich", q.remove("Zurich")); + assertEquals("London", q.remove("London")); + assertEquals("San Francisco", q.remove("San Francisco")); + assertEquals("Brussels", q.poll()); + assertEquals("New York", q.poll()); + assertEquals("Paris", q.poll()); + assertEquals("Sydney", q.poll()); + assertEquals("Tel Aviv", q.poll()); + assertTrue(q.isEmpty()); + } + + @Test + public void testNexAndPrev() { + SmartQueue<String> q = createQueue(); + assertEquals("Toronto", q.next("Tel Aviv")); + assertEquals("Sydney", q.prev("Tel Aviv")); + assertEquals("Brussels", q.prev("London")); + assertEquals("Zurich", q.next("Toronto")); + assertNull(q.prev("Brussels")); + assertNull(q.next("Zurich")); + } + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2009-12-14 23:44:49
|
Revision: 20 http://geom4j.svn.sourceforge.net/geom4j/?rev=20&view=rev Author: skhenkin Date: 2009-12-14 23:44:34 +0000 (Mon, 14 Dec 2009) Log Message: ----------- BinaryTreeBasedSmartQueue switched to AVLTree instead of a simple BinarySearchTree Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java trunk/src/net/sourceforge/geom4j/util/AVLTree.java trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-14 22:57:47 UTC (rev 19) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-14 23:44:34 UTC (rev 20) @@ -97,7 +97,7 @@ double X1 = 0, X2 = 1000, Y1 = 0, Y2 = 1000; SegmentSet ss = new SegmentSet(); Random r = new Random(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 300; i++) { double x1 = r.nextDouble()*(X2-X1)+X1; double y1 = r.nextDouble()*(Y2-Y1)+Y1; double x2 = r.nextDouble()*(X2-X1)+X1; Modified: trunk/src/net/sourceforge/geom4j/util/AVLTree.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/AVLTree.java 2009-12-14 22:57:47 UTC (rev 19) +++ trunk/src/net/sourceforge/geom4j/util/AVLTree.java 2009-12-14 23:44:34 UTC (rev 20) @@ -28,7 +28,11 @@ * are performed with the complexity of O(log n), * where n is the number of elements in the tree. * This is achieved by keeping the tree balanced (for each node - * heights of its left and right subtrees differ at most by 1). + * heights of its left and right subtrees differ at most by 1). + * This balance is restored when needed using tree transformations + * called "rotations". + * See http://sky.fit.qut.edu.au/~maire/avl/System/AVLTree.html for more + * information and nice graphical explanation of rotations. * * @param <E> type of values stored in the tree * Modified: trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java 2009-12-14 22:57:47 UTC (rev 19) +++ trunk/src/net/sourceforge/geom4j/util/BinaryTreeBasedSmartQueue.java 2009-12-14 23:44:34 UTC (rev 20) @@ -18,11 +18,9 @@ import java.util.Comparator; /** - * "Smarter" implementation of SmartQueue based on a binary search tree. - * It takes on average O(log n) time to insert/remove/swap - * or get prev/next elements. - * There's still a room for performance improvements using some kind - * of self-balancing trees (like AVL-trees or Red-Black trees). + * "Smarter" implementation of SmartQueue based on a self-balancing + * binary search tree (AVL tree). + * It takes O(log n) time to insert/remove/swap or get prev/next elements. * * Actually this is a binary search tree with two added operations: * poll() - for extracting the first tree element; @@ -30,7 +28,7 @@ * * @author Sergey Khenkin */ -public class BinaryTreeBasedSmartQueue<E> extends BinarySearchTree<E> +public class BinaryTreeBasedSmartQueue<E> extends AVLTree<E> implements SmartQueue<E> { public BinaryTreeBasedSmartQueue() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2009-12-18 15:30:23
|
Revision: 21 http://geom4j.svn.sourceforge.net/geom4j/?rev=21&view=rev Author: skhenkin Date: 2009-12-18 15:30:14 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Equality performance check improved Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/CompoundFigure.java trunk/src/net/sourceforge/geom4j/EmptyFigure.java trunk/src/net/sourceforge/geom4j/Line.java trunk/src/net/sourceforge/geom4j/Point.java trunk/src/net/sourceforge/geom4j/PointPair.java trunk/src/net/sourceforge/geom4j/PointSet.java trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/Segment.java Modified: trunk/src/net/sourceforge/geom4j/CompoundFigure.java =================================================================== --- trunk/src/net/sourceforge/geom4j/CompoundFigure.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/CompoundFigure.java 2009-12-18 15:30:14 UTC (rev 21) @@ -79,11 +79,14 @@ @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj instanceof CompoundFigure) { CompoundFigure<?> cf = (CompoundFigure<?>) obj; return contains(cf) && cf.contains(this); } - return super.equals(obj); + return false; } Modified: trunk/src/net/sourceforge/geom4j/EmptyFigure.java =================================================================== --- trunk/src/net/sourceforge/geom4j/EmptyFigure.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/EmptyFigure.java 2009-12-18 15:30:14 UTC (rev 21) @@ -34,10 +34,7 @@ @Override public boolean equals(Object obj) { - if (obj instanceof EmptyFigure) { - return true; - } - return false; + return obj instanceof EmptyFigure; } @Override Modified: trunk/src/net/sourceforge/geom4j/Line.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Line.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/Line.java 2009-12-18 15:30:14 UTC (rev 21) @@ -53,7 +53,7 @@ Line line = (Line) obj; return contains(line.getP1()) && contains(line.getP2()); } - return super.equals(obj); + return false; } @Override Modified: trunk/src/net/sourceforge/geom4j/Point.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Point.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/Point.java 2009-12-18 15:30:14 UTC (rev 21) @@ -46,7 +46,7 @@ Point p = (Point) obj; return Config.equal(x, p.x) && Config.equal(y, p.y); } - return super.equals(obj); + return false; } @Override Modified: trunk/src/net/sourceforge/geom4j/PointPair.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointPair.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/PointPair.java 2009-12-18 15:30:14 UTC (rev 21) @@ -53,7 +53,7 @@ return firstPoint.equals(pp.firstPoint) && secondPoint.equals(pp.secondPoint) || firstPoint.equals(pp.secondPoint) && secondPoint.equals(pp.firstPoint); } - return super.equals(obj); + return false; } public double distance() { Modified: trunk/src/net/sourceforge/geom4j/PointSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointSet.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/PointSet.java 2009-12-18 15:30:14 UTC (rev 21) @@ -33,6 +33,9 @@ */ @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj instanceof PointSet) { return figures.equals(((PointSet) obj).figures); } Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2009-12-18 15:30:14 UTC (rev 21) @@ -65,11 +65,14 @@ @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj instanceof Polygon) { Polygon p = (Polygon) obj; return getSegmentSet().equals(p.getSegmentSet()); } - return super.equals(obj); + return false; } @Override Modified: trunk/src/net/sourceforge/geom4j/Segment.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Segment.java 2009-12-14 23:44:34 UTC (rev 20) +++ trunk/src/net/sourceforge/geom4j/Segment.java 2009-12-18 15:30:14 UTC (rev 21) @@ -40,12 +40,15 @@ @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } if (obj instanceof Segment) { Segment s = (Segment) obj; return startPoint.equals(s.startPoint) && endPoint.equals(s.endPoint) || startPoint.equals(s.endPoint) && endPoint.equals(s.startPoint); } - return super.equals(obj); + return false; } @Override This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2009-12-18 18:17:04
|
Revision: 22 http://geom4j.svn.sourceforge.net/geom4j/?rev=22&view=rev Author: skhenkin Date: 2009-12-18 18:16:58 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Triangle class added. Triangulation algorithm infrastructure created. Annoying bug in hashCode() for Segment and PointPair fixed. Bug in Line.hashCode() to be fixed. Varargs introduced in Polygon and PointSet constructors. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/CompoundFigure.java trunk/src/net/sourceforge/geom4j/DistanceTest.java trunk/src/net/sourceforge/geom4j/Line.java trunk/src/net/sourceforge/geom4j/PointPair.java trunk/src/net/sourceforge/geom4j/PointSet.java trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/Segment.java trunk/src/net/sourceforge/geom4j/SegmentSet.java trunk/src/net/sourceforge/geom4j/demo/Demo.java Added Paths: ----------- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java trunk/src/net/sourceforge/geom4j/Triangle.java trunk/src/net/sourceforge/geom4j/TriangleTest.java trunk/src/net/sourceforge/geom4j/Triangulation.java trunk/src/net/sourceforge/geom4j/TriangulationTest.java Modified: trunk/src/net/sourceforge/geom4j/CompoundFigure.java =================================================================== --- trunk/src/net/sourceforge/geom4j/CompoundFigure.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/CompoundFigure.java 2009-12-18 18:16:58 UTC (rev 22) @@ -44,6 +44,10 @@ } return this; } + + public boolean remove(F f) { + return figures.remove(f); + } public CompoundFigure<F> add(CompoundFigure<F> f) { // TODO: think of avoiding duplicate sets of points @@ -51,6 +55,11 @@ return this; } + public CompoundFigure<F> addAll(Collection<F> figures) { + this.figures.addAll(figures); + return this; + } + public int size() { return figures.size(); } Modified: trunk/src/net/sourceforge/geom4j/DistanceTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/DistanceTest.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/DistanceTest.java 2009-12-18 18:16:58 UTC (rev 22) @@ -86,6 +86,7 @@ @Test public void testPolygonDistance() { + // TODO: refactor this and other tests using varargs Figure p = new Point(10, 20); Figure square = new Polygon() .addVertex(new Point(0, 0)) Modified: trunk/src/net/sourceforge/geom4j/Line.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Line.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/Line.java 2009-12-18 18:16:58 UTC (rev 22) @@ -42,6 +42,7 @@ public int hashCode() { final int prime = 31; int result = 1; + // TODO: !!! this is completely wrong result = prime * result + p1.hashCode(); result = prime * result + p2.hashCode(); return result; Modified: trunk/src/net/sourceforge/geom4j/PointPair.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointPair.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/PointPair.java 2009-12-18 18:16:58 UTC (rev 22) @@ -39,11 +39,7 @@ @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + firstPoint.hashCode(); - result = prime * result + secondPoint.hashCode(); - return result; + return firstPoint.hashCode() + secondPoint.hashCode(); } @Override Modified: trunk/src/net/sourceforge/geom4j/PointSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointSet.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/PointSet.java 2009-12-18 18:16:58 UTC (rev 22) @@ -15,6 +15,7 @@ package net.sourceforge.geom4j; +import java.util.Arrays; import java.util.Collection; public class PointSet extends CompoundFigure<Point> { @@ -28,6 +29,10 @@ super(points); } + public PointSet(Point... points) { + this(Arrays.asList(points)); + } + /** * CompoundFigure's equality is overridden for better performance. */ @@ -47,8 +52,6 @@ return (PointSet) super.add(f); } - - /** * Get a convex hull (convex envelope) for a point set using some algorithm. * Different algorithms can be used for convex hull construction. Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2009-12-18 18:16:58 UTC (rev 22) @@ -27,11 +27,19 @@ public class Polygon extends BasicFigure { // TODO: think of making this class also immutable - private List<Point> vertices = new ArrayList<Point>(); + protected List<Point> vertices; public Polygon() { + this.vertices = new ArrayList<Point>(); } + public Polygon(Point... vertices) { + this.vertices = new ArrayList<Point>(vertices.length); + for (Point vertex: vertices) { + this.vertices.add(vertex); + } + } + public Polygon addVertex(Point p) { vertices.add(p); return this; @@ -58,6 +66,12 @@ return perimeter; } + /** + * Hash code of a polygon is defined as the hash code + * of the segment set of which it consists. + * This implies equal hash codes for equal polygons. + * Hash code based on vertices only is not enough! + */ @Override public int hashCode() { return getSegmentSet().hashCode(); @@ -238,4 +252,18 @@ intersections.remove(getVertices()); // we don't count vertices return intersections.isEmpty(); } + + /** + * Create polygon triangulation + */ + public Triangulation triangulate() { + return triangulate(PolygonTriangulationAlgorithm.NAIVE); + } + + /** + * Create polygon triangulation using the specified algorithm + */ + public Triangulation triangulate(PolygonTriangulationAlgorithm alg) { + return alg.run(this); + } } Added: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +/** + * A polygon triangulation algorithm. + * + * @author Sergey Khenkin + */ + +public interface PolygonTriangulationAlgorithm { + + /** + * Execute the algorithm and find the triangulation, + * i.e. a set of triangles + */ + Triangulation run(Polygon polygon); + + + /** + * Naive algorithm + */ + static PolygonTriangulationAlgorithm NAIVE = new PolygonTriangulationAlgorithm() { + @Override + public Triangulation run(Polygon polygon) { + Triangulation triangulation = new Triangulation(); + int n = polygon.getVertexCount(); + for (int i = 1; i < n-1; i++) { + triangulation.add(new Triangle(polygon.getVertex(0), + polygon.getVertex(i), + polygon.getVertex(i+1))); + } + return triangulation; + } + }; + +} Added: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class PolygonTriangulationAlgorithmTest { + + @Test + public void testNaive() { + Polygon polygon = new Polygon(new Point(0, 0), new Point(100, 0), + new Point(100, 100), new Point(0, 100)); + Triangulation triangulation = polygon.triangulate(); + assertTrue(triangulation.fits(polygon)); + } + +} Modified: trunk/src/net/sourceforge/geom4j/Segment.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Segment.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/Segment.java 2009-12-18 18:16:58 UTC (rev 22) @@ -55,8 +55,8 @@ public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + endPoint.hashCode(); - result = prime * result + startPoint.hashCode(); + result = prime * result + getLeftPoint().hashCode(); + result = prime * result + getRightPoint().hashCode(); return result; } Modified: trunk/src/net/sourceforge/geom4j/SegmentSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-18 18:16:58 UTC (rev 22) @@ -15,6 +15,11 @@ package net.sourceforge.geom4j; +/** + * A set of segments + * + * @author Sergey Khenkin + */ public class SegmentSet extends CompoundFigure<Segment> { @Override Added: trunk/src/net/sourceforge/geom4j/Triangle.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Triangle.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/Triangle.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +/** + * Triangle figure. + * Implemented as a specialized variation of a polygon. + * + * @author Sergey Khenkin + */ +public class Triangle extends Polygon { + public Triangle(Point a, Point b, Point c) { + vertices.add(a); + vertices.add(b); + vertices.add(c); + } + + +} Added: trunk/src/net/sourceforge/geom4j/TriangleTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/TriangleTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/TriangleTest.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TriangleTest { + @Test + public void testTriangleConstruction() { + Point p1 = new Point(50, 30); + Point p2 = new Point(-17, 45); + Point p3 = new Point(93, -41); + Polygon p = new Polygon(p1, p2, p3); + Triangle t = new Triangle(p1, p2, p3); + assertEquals(p, t); + } +} Added: trunk/src/net/sourceforge/geom4j/Triangulation.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Triangulation.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/Triangulation.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +/** + * A polygon triangulation, actually a set of triangles. + * + * @author Sergey Khenkin + */ +public class Triangulation extends CompoundFigure<Triangle> { + /** + * Test if this triangulation fits the polygon specified, + * i.e. if this is a valid triangulation for this polygon + */ + public boolean fits(Polygon polygon) { + /* + * We use a sequence of tests to check validity: + * 1. Number of triangles is less than number of polygon vertices by 2. + * 2. The vertices of triangulation and polygon match. + * 3. Every segment in triangulation comes at most twice. + * 4. Segments that come once form the polygon. + * 5. Other segments don't intersect in points other than polygon vertices. + */ + // 1 + if (size() != polygon.getVertexCount() - 2) { + return false; + } + // 2 + if (!getVertices().equals(new PointSet(polygon.getVertices()))) { + return false; + } + SegmentSet singles = new SegmentSet(); + SegmentSet doubles = new SegmentSet(); + for (Triangle triangle: figures) { + for (Segment segment: triangle.getSegmentSet()) { + // 3 + if (doubles.contains(segment)) { + return false; // a segment comes for the third time + } + if (singles.contains(segment)) { + singles.remove(segment); + doubles.add(segment); + } else { + singles.add(segment); + } + } + } + // 4 + if (!singles.equals(polygon.getSegmentSet())) { + return false; + } + // 5 + if (!getPoints().contains(doubles.getAllIntersectionPoints())) { + return false; + } + return true; + } + + public PointSet getVertices() { + PointSet vertices = new PointSet(); + for (Triangle triangle: figures) { + vertices.addAll(triangle.getVertices()); + } + return vertices; + } + + +} Added: trunk/src/net/sourceforge/geom4j/TriangulationTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/TriangulationTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2009-12-18 18:16:58 UTC (rev 22) @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TriangulationTest { + @Test + public void testTriangulationVertices() { + Point[] points = new Point[] { + new Point(10, 0), + new Point(0, 40), + new Point(60, 70), + new Point(30, 10) + }; + Polygon polygon = new Polygon(); + for (Point point: points) { + polygon.addVertex(point); + } + assertEquals(new PointSet(points), + polygon.triangulate().getVertices()); + } + + @Test + public void testFitting() { + Polygon polygon = new Polygon(new Point[] { + new Point(10, 0), + new Point(0, 40), + new Point(60, 70), + new Point(30, 10) + }); + Triangulation triangulation = new Triangulation(); + triangulation.add(new Triangle(new Point(0, 40), new Point(30, 10), new Point(10, 0))) + .add(new Triangle(new Point(30, 10), new Point(0, 40), new Point(60, 70))); + assertTrue(triangulation.fits(polygon)); + } +} Modified: trunk/src/net/sourceforge/geom4j/demo/Demo.java =================================================================== --- trunk/src/net/sourceforge/geom4j/demo/Demo.java 2009-12-18 15:30:14 UTC (rev 21) +++ trunk/src/net/sourceforge/geom4j/demo/Demo.java 2009-12-18 18:16:58 UTC (rev 22) @@ -26,6 +26,7 @@ import net.sourceforge.geom4j.Segment; import net.sourceforge.geom4j.SegmentIntersectionAlgorithm; import net.sourceforge.geom4j.SegmentSet; +import net.sourceforge.geom4j.Triangulation; /** * Demo class for the library. @@ -47,6 +48,7 @@ segmentIntersectionDemo(); simplePolygonDemo(); contentDemo(); + triangulationDemo(); } /** @@ -213,4 +215,20 @@ System.out.println("Square " + square + " contains segment " + segment); } } + + /** + * Demonstrate polygon triangulation + */ + private static void triangulationDemo() { + Polygon polygon = new Polygon() + .addVertex(new Point(0, 0)) + .addVertex(new Point(100, 0)) + .addVertex(new Point(100, 100)) + .addVertex(new Point(50, 100)) + .addVertex(new Point(0, 50)); + Triangulation triangulation = polygon.triangulate(); + System.out.println(String.format("Triangulation of the polygon %s is %s", + polygon, triangulation)); + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2009-12-18 22:27:36
|
Revision: 23 http://geom4j.svn.sourceforge.net/geom4j/?rev=23&view=rev Author: skhenkin Date: 2009-12-18 22:27:28 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Tests refactored to use varargs constructors. Line equality and hashCode tests fixed. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/ClosestPairOfPointsAlgorithmTest.java trunk/src/net/sourceforge/geom4j/Config.java trunk/src/net/sourceforge/geom4j/DistanceTest.java trunk/src/net/sourceforge/geom4j/IntersectionTest.java trunk/src/net/sourceforge/geom4j/Line.java trunk/src/net/sourceforge/geom4j/LineTest.java trunk/src/net/sourceforge/geom4j/PointPairTest.java trunk/src/net/sourceforge/geom4j/PolygonTest.java trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java trunk/src/net/sourceforge/geom4j/SegmentSet.java trunk/src/net/sourceforge/geom4j/SegmentTest.java trunk/src/net/sourceforge/geom4j/TriangulationTest.java trunk/src/net/sourceforge/geom4j/Vector.java trunk/src/net/sourceforge/geom4j/VectorTest.java Modified: trunk/src/net/sourceforge/geom4j/ClosestPairOfPointsAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/ClosestPairOfPointsAlgorithmTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/ClosestPairOfPointsAlgorithmTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -43,7 +43,8 @@ ps.add(new Point(2, 2)); ps.add(new Point(5, 3)); ps.add(new Point(11, 4)); - ps.add(new Point(9, 2)); } + ps.add(new Point(9, 2)); + } @Test public void testNaive() { Modified: trunk/src/net/sourceforge/geom4j/Config.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Config.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/Config.java 2009-12-18 22:27:28 UTC (rev 23) @@ -68,4 +68,8 @@ public static double roundValue(double x) { return Math.round(x / precision) * precision; } + + public static boolean isZero(double x) { + return x < precision && x > -precision; + } } Modified: trunk/src/net/sourceforge/geom4j/DistanceTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/DistanceTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/DistanceTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -88,15 +88,15 @@ public void testPolygonDistance() { // TODO: refactor this and other tests using varargs Figure p = new Point(10, 20); - Figure square = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(0, 25)) - .addVertex(new Point(25, 25)) - .addVertex(new Point(25, 0)); - Figure triangle = new Polygon() - .addVertex(new Point(5, 10)) - .addVertex(new Point(10, 15)) - .addVertex(new Point(15, 15)); + Figure square = new Polygon( + new Point(0, 0), + new Point(0, 25), + new Point(25, 25), + new Point(25, 0)); + Figure triangle = new Polygon( + new Point(5, 10), + new Point(10, 15), + new Point(15, 15)); assertTrue(Config.equal(5, p.distanceTo(square))); assertTrue(Config.equal(5, p.distanceTo(triangle))); assertTrue(Config.equal(5, triangle.distanceTo(square))); Modified: trunk/src/net/sourceforge/geom4j/IntersectionTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/IntersectionTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/IntersectionTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -92,30 +92,30 @@ @Test public void testLineAndPolygonIntersection() { Figure line = new Line(new Point(0, 0), new Point(1, 1)); - Figure poly = new Polygon().addVertex(new Point(0, 0)) - .addVertex(new Point(10, 20)) - .addVertex(new Point(100, 20)) - .addVertex(new Point(90, 0)); - assertEquals(new PointSet().add(new Point(0, 0)) - .add(new Point(20, 20)), + Figure poly = new Polygon(new Point(0, 0), + new Point(10, 20), + new Point(100, 20), + new Point(90, 0)); + assertEquals(new PointSet(new Point(0, 0), + new Point(20, 20)), line.intersects(poly)); } @Test public void testPolygonIntersection() { - Figure square = new Polygon().addVertex(new Point( 0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point( 0, 100)); - Figure trapezoid = new Polygon().addVertex(new Point(-20, 40)) - .addVertex(new Point(120, 40)) - .addVertex(new Point( 90, 130)) - .addVertex(new Point( 40, 130)); - assertEquals(new PointSet().add(new Point( 0, 40)) - .add(new Point( 0, 70)) - .add(new Point( 20, 100)) - .add(new Point(100, 100)) - .add(new Point(100, 40)), + Figure square = new Polygon(new Point( 0, 0), + new Point(100, 0), + new Point(100, 100), + new Point( 0, 100)); + Figure trapezoid = new Polygon(new Point(-20, 40), + new Point(120, 40), + new Point( 90, 130), + new Point( 40, 130)); + assertEquals(new PointSet(new Point( 0, 40), + new Point( 0, 70), + new Point( 20, 100), + new Point(100, 100), + new Point(100, 40)), square.intersects(trapezoid)); } } Modified: trunk/src/net/sourceforge/geom4j/Line.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Line.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/Line.java 2009-12-18 22:27:28 UTC (rev 23) @@ -41,13 +41,41 @@ @Override public int hashCode() { final int prime = 31; - int result = 1; - // TODO: !!! this is completely wrong - result = prime * result + p1.hashCode(); - result = prime * result + p2.hashCode(); - return result; + long result = 1; + result = prime * result + Double.doubleToLongBits(getSlope()); + result = prime * result + Double.doubleToLongBits(getIntercept()); + return (int) result; } + /** + * A slope quotient of the straight line equation in the "slope-intercept" form. + * It is K in the equation y = Kx + B. + * @return slope quotient value (Double.POSITIVE_INFINITY if the line is vertical) + */ + public double getSlope() { + double dx = p2.getX() - p1.getX(); + double dy = p2.getY() - p1.getY(); + if (Config.isZero(dx)) { + return Double.POSITIVE_INFINITY; + } else { + return dy / dx; + } + } + + /** + * A y-intercept of the straight line equation in the "slope-intercept" form. + * It is B in the equation y = Kx + B. + * @return y-intercept value (Double.NaN if the line is vertical) + */ + public double getIntercept() { + double dx = p2.getX() - p1.getX(); + if (Config.isZero(dx)) { + return Double.NaN; + } else { + return (p2.getX()*p1.getY() - p1.getX()*p2.getY()) / dx; + } + } + @Override public boolean equals(Object obj) { if (obj instanceof Line) { Modified: trunk/src/net/sourceforge/geom4j/LineTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/LineTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/LineTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -16,6 +16,7 @@ package net.sourceforge.geom4j; import static org.junit.Assert.*; + import org.junit.Test; public class LineTest { @@ -105,4 +106,37 @@ assertFalse(l1.parallelTo(l3)); } + + @Test + public void testCorrectHashCode() { + Point p1 = new Point(10, 10); + Point p2 = new Point(20, 20); + Point p3 = new Point(30, 30); + Point p4 = new Point(40, 40); + Line line1 = new Line(p1, p2); + Line line2 = new Line(p2, p1); + Line line3 = new Line(p3, p4); + assertEquals(line1.hashCode(), line2.hashCode()); + assertEquals(line1.hashCode(), line3.hashCode()); + } + + @Test + public void testSlopeAndIntercept() { + assertSlopeIntercept(new Point(8, 2), new Point(12, 4), 0.5, -2); + assertSlopeIntercept(new Point(3, 2), new Point(-3, 10), -4.0/3, 6); + assertSlopeIntercept(new Point(2, -5), new Point(5, -5), 0, -5); + assertVertical(new Point(3, 1), new Point(3, 2)); + } + + private void assertSlopeIntercept(Point p1, Point p2, double k, double b) { + Line line = new Line(p1, p2); + assertEquals(0, Double.compare(k, line.getSlope())); + assertEquals(0, Double.compare(b, line.getIntercept())); + } + + private void assertVertical(Point p1, Point p2) { + Line line = new Line(p1, p2); + assertTrue(Double.isInfinite(line.getSlope())); + assertTrue(Double.isNaN(line.getIntercept())); + } } Modified: trunk/src/net/sourceforge/geom4j/PointPairTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointPairTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/PointPairTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -29,5 +29,14 @@ assertEquals(new PointPair(p1, p2), new PointPair(p2, p1)); assertFalse(new PointPair(p1, p2).equals(new PointPair(p2, new Point(40, 20)))); } + + @Test + public void testCorrectHashCode() { + Point p1 = new Point(10, 10); + Point p2 = new Point(30, 5); + PointPair pp1 = new PointPair(p1, p2); + PointPair pp2 = new PointPair(p2, p1); + assertEquals(pp1.hashCode(), pp2.hashCode()); + } } Modified: trunk/src/net/sourceforge/geom4j/PolygonTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/PolygonTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -47,11 +47,11 @@ @Test public void testContains() { - Polygon p = new Polygon().addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon p = new Polygon(new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); assertTrue(p.contains(p)); assertEquals(p, p); assertTrue(p.contains(new Point(50, 100))); @@ -63,92 +63,92 @@ @Test public void testConvex() { - Polygon convex1 = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon convex1 = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); assertTrue(convex1.isConvex()); - Polygon convex2 = new Polygon() - .addVertex(new Point(0, 50)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(0, 0)); + Polygon convex2 = new Polygon( + new Point(0, 50), + new Point(50, 100), + new Point(100, 100), + new Point(100, 0), + new Point(0, 0)); assertTrue(convex2.isConvex()); - Polygon convex3 = new Polygon() - .addVertex(new Point(0, 50)) - .addVertex(new Point(0, 100)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 50)) - .addVertex(new Point(0, 0)); + Polygon convex3 = new Polygon( + new Point(0, 50), + new Point(0, 100), + new Point(100, 100), + new Point(50, 50), + new Point(0, 0)); assertTrue(convex3.isConvex()); - Polygon nonConvex1 = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 50)) - .addVertex(new Point(50, 50)) - .addVertex(new Point(50, 100)); + Polygon nonConvex1 = new Polygon( + new Point(0, 0), + new Point(100, 50), + new Point(50, 50), + new Point(50, 100)); assertFalse(nonConvex1.isConvex()); - Polygon nonConvex2 = new Polygon() - .addVertex(new Point(100, 50)) - .addVertex(new Point(0, 0)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(50, 50)); + Polygon nonConvex2 = new Polygon( + new Point(100, 50), + new Point(0, 0), + new Point(50, 100), + new Point(50, 50)); assertFalse(nonConvex2.isConvex()); - Polygon nonConvex3 = new Polygon() - .addVertex(new Point(0, 50)) - .addVertex(new Point(0, 100)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 51)) - .addVertex(new Point(0, 0)); + Polygon nonConvex3 = new Polygon( + new Point(0, 50), + new Point(0, 100), + new Point(100, 100), + new Point(50, 51), + new Point(0, 0)); assertFalse(nonConvex3.isConvex()); } @Test public void testArea() { - Polygon square = new Polygon() - .addVertex(new Point(10, 10)) - .addVertex(new Point(10, 20)) - .addVertex(new Point(20, 20)) - .addVertex(new Point(20, 10)); + Polygon square = new Polygon( + new Point(10, 10), + new Point(10, 20), + new Point(20, 20), + new Point(20, 10)); assertTrue(Config.equal(100, square.getArea())); - Polygon romb = new Polygon() - .addVertex(new Point(10, 10)) - .addVertex(new Point(50, 40)) - .addVertex(new Point(20, 80)) - .addVertex(new Point(-20, 50)); + Polygon romb = new Polygon( + new Point(10, 10), + new Point(50, 40), + new Point(20, 80), + new Point(-20, 50)); assertTrue(Config.equal(2500, romb.getArea())); assertTrue(Config.equal(4+8+6+6+6, p.getArea())); - Polygon mess = new Polygon() - .addVertex(new Point(1, 2)) - .addVertex(new Point(5, 2)) - .addVertex(new Point(2, 3)) - .addVertex(new Point(5, 3)) - .addVertex(new Point(2, 4)) - .addVertex(new Point(5, 6)) - .addVertex(new Point(3, 5)) - .addVertex(new Point(4, 7)) - .addVertex(new Point(4, 6)) - .addVertex(new Point(5, 7)) - .addVertex(new Point(1, 8)); + Polygon mess = new Polygon( + new Point(1, 2), + new Point(5, 2), + new Point(2, 3), + new Point(5, 3), + new Point(2, 4), + new Point(5, 6), + new Point(3, 5), + new Point(4, 7), + new Point(4, 6), + new Point(5, 7), + new Point(1, 8)); assertTrue(Config.equal(24-2-1.5-4.5-1.5, mess.getArea())); } @Test public void testSimplicity() { assertTrue(p.isSimple()); - assertTrue(new Polygon() - .addVertex(new Point(10, 10)) - .addVertex(new Point(10, 20)) - .addVertex(new Point(20, 20)) - .addVertex(new Point(20, 10)) + assertTrue(new Polygon( + new Point(10, 10), + new Point(10, 20), + new Point(20, 20), + new Point(20, 10)) .isSimple()); - assertFalse(new Polygon() - .addVertex(new Point(10, 10)) - .addVertex(new Point(10, 20)) - .addVertex(new Point(20, 10)) - .addVertex(new Point(20, 20)) + assertFalse(new Polygon( + new Point(10, 10), + new Point(10, 20), + new Point(20, 10), + new Point(20, 20)) .isSimple()); } } Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -19,18 +19,18 @@ @Before public void setUp() throws Exception { - ss1 = new SegmentSet() - .add(new Segment(new Point(1, 1), new Point(10, 10))) - .add(new Segment(new Point(10, 1), new Point(1, 10))) - .add(new Segment(new Point(1, 4), new Point(5, 2))); - ps1 = new PointSet().add(new Point(5.5, 5.5)).add(new Point(3, 3)); + ss1 = new SegmentSet( + new Segment(new Point(1, 1), new Point(10, 10)), + new Segment(new Point(10, 1), new Point(1, 10)), + new Segment(new Point(1, 4), new Point(5, 2))); + ps1 = new PointSet(new Point(5.5, 5.5), new Point(3, 3)); - Polygon p = new Polygon() - .addVertex(new Point(1, 1)) - .addVertex(new Point(1, 2)) - .addVertex(new Point(2, 3)) - .addVertex(new Point(3, 2)) - .addVertex(new Point(3, 1)); + Polygon p = new Polygon( + new Point(1, 1), + new Point(1, 2), + new Point(2, 3), + new Point(3, 2), + new Point(3, 1)); ss2 = p.getSegmentSet(); ps2 = new PointSet(p.getVertices()); @@ -115,10 +115,10 @@ @Test public void testBentleyOttmanSpecialCase1() { - SegmentSet ss = new SegmentSet() - .add(new Segment(new Point(7.288243310311859, 165.47985174930534), new Point(687.1335778157106, 878.6923172710824))) - .add(new Segment(new Point(189.62978423631606, 843.6858467771974), new Point(997.5303220302917, 900.134939913248))) - .add(new Segment(new Point(89.01407073223055, 331.76634850487164), new Point(967.7067870144189, 441.51160041074144))); + SegmentSet ss = new SegmentSet( + new Segment(new Point(7.288243310311859, 165.47985174930534), new Point(687.1335778157106, 878.6923172710824)), + new Segment(new Point(189.62978423631606, 843.6858467771974), new Point(997.5303220302917, 900.134939913248)), + new Segment(new Point(89.01407073223055, 331.76634850487164), new Point(967.7067870144189, 441.51160041074144))); PointSet naiveSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.NAIVE); PointSet boSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.BENTLEY_OTTMANN); assertEquals(naiveSet, boSet); Modified: trunk/src/net/sourceforge/geom4j/SegmentSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/SegmentSet.java 2009-12-18 22:27:28 UTC (rev 23) @@ -15,6 +15,8 @@ package net.sourceforge.geom4j; +import java.util.Arrays; + /** * A set of segments * @@ -22,6 +24,13 @@ */ public class SegmentSet extends CompoundFigure<Segment> { + public SegmentSet() { + } + + public SegmentSet(Segment... segments) { + super(Arrays.asList(segments)); + } + @Override public SegmentSet add(Segment f) { return (SegmentSet) super.add(f); Modified: trunk/src/net/sourceforge/geom4j/SegmentTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/SegmentTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -16,6 +16,7 @@ package net.sourceforge.geom4j; import static org.junit.Assert.*; + import org.junit.Test; public class SegmentTest { @@ -101,4 +102,13 @@ assertEquals(topLeft, topHorizontal.getLeftPoint()); assertEquals(topRight, topHorizontal.getRightPoint()); } + + @Test + public void testCorrectHashCode() { + Point p1 = new Point(10, 10); + Point p2 = new Point(30, 5); + Segment s1 = new Segment(p1, p2); + Segment s2 = new Segment(p2, p1); + assertEquals(s1.hashCode(), s2.hashCode()); + } } Modified: trunk/src/net/sourceforge/geom4j/TriangulationTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -38,12 +38,11 @@ @Test public void testFitting() { - Polygon polygon = new Polygon(new Point[] { + Polygon polygon = new Polygon( new Point(10, 0), new Point(0, 40), new Point(60, 70), - new Point(30, 10) - }); + new Point(30, 10)); Triangulation triangulation = new Triangulation(); triangulation.add(new Triangle(new Point(0, 40), new Point(30, 10), new Point(10, 0))) .add(new Triangle(new Point(30, 10), new Point(0, 40), new Point(60, 70))); Modified: trunk/src/net/sourceforge/geom4j/Vector.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Vector.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/Vector.java 2009-12-18 22:27:28 UTC (rev 23) @@ -63,6 +63,21 @@ @Override + public boolean equals(Object obj) { + if (obj instanceof Vector) { + Vector v = (Vector) obj; + return x == v.x && y == v.y; + } + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + return (int) (Double.doubleToLongBits(x) + prime * Double.doubleToLongBits(y)); + } + + @Override public String toString() { return "(" + x + ", " + y + ")"; } Modified: trunk/src/net/sourceforge/geom4j/VectorTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/VectorTest.java 2009-12-18 18:16:58 UTC (rev 22) +++ trunk/src/net/sourceforge/geom4j/VectorTest.java 2009-12-18 22:27:28 UTC (rev 23) @@ -51,4 +51,14 @@ assertTrue(Config.equal(Math.sqrt(2), new Vector(1.0, -1.0).length())); assertTrue(Config.equal(10, new Vector(0, -10).length())); } + + @Test + public void testCorrectHashCode() { + Vector v1 = new Vector(2, 1); + Vector v2 = new Vector(1, 2); + Vector v3 = new Vector(new Point(5, 6), new Point(7, 7)); + assertEquals(v1, v3); + assertFalse(v1.equals(v2)); + assertEquals(v1.hashCode(), v3.hashCode()); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2010-01-12 22:49:16
|
Revision: 26 http://geom4j.svn.sourceforge.net/geom4j/?rev=26&view=rev Author: skhenkin Date: 2010-01-12 22:49:05 +0000 (Tue, 12 Jan 2010) Log Message: ----------- Method for finding the middle point of a segment. Fix for Bentley-Ottman algorithm for segment intersections. Triangulation.fit() improved. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java trunk/src/net/sourceforge/geom4j/Segment.java trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java trunk/src/net/sourceforge/geom4j/SegmentSet.java trunk/src/net/sourceforge/geom4j/SegmentTest.java trunk/src/net/sourceforge/geom4j/Triangulation.java trunk/src/net/sourceforge/geom4j/TriangulationTest.java Modified: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java 2010-01-12 22:49:05 UTC (rev 26) @@ -47,7 +47,7 @@ new Point(3, 3), new Point(3, 4)); Triangulation triangulation = polygon.triangulate(PolygonTriangulationAlgorithm.CONVEX); - assertTrue(triangulation.fits(polygon)); + assertFalse(triangulation.fits(polygon)); } } Modified: trunk/src/net/sourceforge/geom4j/Segment.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Segment.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/Segment.java 2010-01-12 22:49:05 UTC (rev 26) @@ -296,4 +296,12 @@ } } } + + /** + * Get middle point of the segment + */ + public Point getMiddlePoint() { + return new Point((startPoint.getX() + endPoint.getX()) / 2, + (startPoint.getY() + endPoint.getY()) / 2); + } } Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithm.java 2010-01-12 22:49:05 UTC (rev 26) @@ -146,10 +146,11 @@ */ protected void addIntersectionEvent( SmartQueue<Event> eventQueue, Segment seg1, - Segment seg2, Point curPoint) { + Segment seg2, Point curPoint, boolean isLeftPoint) { if (seg1 != null && seg2 != null) { for (Point p : seg1.intersects(seg2).getPoints()) { - if (p.getX() >= curPoint.getX()) { + if (p.getX() >= curPoint.getX() && isLeftPoint + || p.getX() > curPoint.getX() && !isLeftPoint) { IntersectionEvent templateEvent = new IntersectionEvent(p, seg1, seg2, sweepLineComparator); Event existingEvent = eventQueue.find(templateEvent); if (existingEvent != null && existingEvent instanceof IntersectionEvent) { @@ -191,9 +192,9 @@ Segment segAfter = sweepLine.next(segment); sweepLineComparator.setBefore(false); addIntersectionEvent(eventQueue, segAfter, segment, - segment.getLeftPoint()); + segment.getLeftPoint(), true); addIntersectionEvent(eventQueue, segment, segBefore, - segment.getLeftPoint()); + segment.getLeftPoint(), true); } @Override @@ -244,7 +245,7 @@ Segment segBefore = sweepLine.previous(segment); Segment segAfter = sweepLine.next(segment); addIntersectionEvent(eventQueue, segAfter, segBefore, - segment.getRightPoint()); + segment.getRightPoint(), false); sweepLine.remove(segment); } @@ -312,8 +313,8 @@ Segment sBelow = getFirstSegment(); sweepLine.revert(sBelow, sAbove); sweepLineComparator.setBefore(false); - addIntersectionEvent(eventQueue, sAbove, sweepLine.previous(sAbove), p); - addIntersectionEvent(eventQueue, sweepLine.next(sBelow), sBelow, p); + addIntersectionEvent(eventQueue, sAbove, sweepLine.previous(sAbove), p, true); + addIntersectionEvent(eventQueue, sweepLine.next(sBelow), sBelow, p, true); } private Segment getLastSegment() { Modified: trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/SegmentIntersectionAlgorithmTest.java 2010-01-12 22:49:05 UTC (rev 26) @@ -208,4 +208,23 @@ PointSet boSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.BENTLEY_OTTMANN); assertEquals(naiveSet, boSet); } + + @Test + public void testBentleyOttmanSpecialCase() { + double[][] coords = new double[][] { + //{ 0, 0, 20, 40}, + {20, 40, 40, 10}, + //{ 0, 0, 20, 20}, + {20, 20, 40, 10}, + { 0, 0, 40, 10} + }; + SegmentSet ss = new SegmentSet(); + for (int i = 0; i < coords.length; i++) { + ss.add(new Segment(new Point(coords[i][0], coords[i][1]), + new Point(coords[i][2], coords[i][3]))); + } + PointSet naiveSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.NAIVE); + PointSet boSet = ss.getAllIntersectionPoints(SegmentIntersectionAlgorithm.BENTLEY_OTTMANN); + assertEquals(naiveSet, boSet); + } } Modified: trunk/src/net/sourceforge/geom4j/SegmentSet.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentSet.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/SegmentSet.java 2010-01-12 22:49:05 UTC (rev 26) @@ -16,6 +16,7 @@ package net.sourceforge.geom4j; import java.util.Arrays; +import java.util.Collection; /** * A set of segments @@ -29,14 +30,22 @@ } public SegmentSet(Segment... segments) { - super(Arrays.asList(segments)); + this(Arrays.asList(segments)); } + public SegmentSet(Collection<Segment> segments) { + super(segments); + } + @Override public SegmentSet add(Segment f) { return (SegmentSet) super.add(f); } + public SegmentSet add(SegmentSet segmentSet) { + return (SegmentSet) super.add(segmentSet); + } + /** * Get all intersections between the segments of this set. * @param alg intersection algorithm to use Modified: trunk/src/net/sourceforge/geom4j/SegmentTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/SegmentTest.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/SegmentTest.java 2010-01-12 22:49:05 UTC (rev 26) @@ -116,5 +116,11 @@ Segment s1 = new Segment(p1, p2); Segment s2 = new Segment(p2, p1); assertEquals(s1.hashCode(), s2.hashCode()); - } + } + + @Test + public void testMiddlePoint() { + assertEquals(new Point(5, 5), new Segment(new Point(3, 8), new Point(7, 2)).getMiddlePoint()); + assertEquals(new Point(0, 0), new Segment(new Point(-30, -80), new Point(30, 80)).getMiddlePoint()); + } } Modified: trunk/src/net/sourceforge/geom4j/Triangulation.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Triangulation.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/Triangulation.java 2010-01-12 22:49:05 UTC (rev 26) @@ -33,7 +33,9 @@ * 2. The vertices of triangulation and polygon match. * 3. Every segment in triangulation comes at most twice. * 4. Segments that come once form the polygon. - * 5. Other segments don't intersect in points other than polygon vertices. + * 5. All segments don't intersect in points other than polygon vertices. + * 6. Segments that come twice lie on the outside of the polygon. + * Actually we test if their middle points lie on the outside. */ // 1 if (size() != polygon.getVertexCount() - 2) { @@ -64,9 +66,16 @@ return false; } // 5 - if (!new PointSet(polygon.getVertices()).contains(doubles.getAllIntersectionPoints())) { + if (!new PointSet(polygon.getVertices()).contains( + new SegmentSet().add(singles).add(doubles).getAllIntersectionPoints())) { return false; } + // 6 + for (Segment s: doubles) { + if (!s.getMiddlePoint().isInside(polygon)) { + return false; + } + } // everything is fine return true; } Modified: trunk/src/net/sourceforge/geom4j/TriangulationTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2010-01-09 21:20:22 UTC (rev 25) +++ trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2010-01-12 22:49:05 UTC (rev 26) @@ -54,4 +54,47 @@ .add(new Triangle(new Point(30, 10), new Point(0, 40), new Point(60, 70))); assertTrue(triangulation.fits(polygon)); } + + @Test + public void testIncorrectFitting1() { + Polygon polygon = new Polygon( + new Point(0, 0), + new Point(20, 40), + new Point(40, 10), + new Point(20, 20)); + Triangulation triangulation = new Triangulation(); + triangulation.add(new Triangle(new Point(0, 0), new Point(20, 40), new Point(40, 10))) + .add(new Triangle(new Point(0, 0), new Point(20, 20), new Point(40, 10))); + assertFalse(triangulation.fits(polygon)); + } + + @Test + public void testIncorrectFitting2() { + Polygon polygon = new Polygon( + new Point(0, 0), + new Point(10, 40), + new Point(50, 30), + new Point(20, 0), + new Point(10, 20)); + Triangulation triangulation = new Triangulation(); + triangulation.add(new Triangle(new Point(0, 0), new Point(10, 40), new Point(50, 30))) + .add(new Triangle(new Point(0, 0), new Point(50, 30), new Point(20, 0))) + .add(new Triangle(new Point(0, 0), new Point(10, 20), new Point(20, 0))); + assertFalse(triangulation.fits(polygon)); + } + + @Test + public void testIncorrectFitting3() { + Polygon polygon = new Polygon( + new Point(0, 0), + new Point(10, 40), + new Point(50, 30), + new Point(20, 0), + new Point(10, 20)); + Triangulation triangulation = new Triangulation(); + triangulation.add(new Triangle(new Point(0, 0), new Point(10, 40), new Point(50, 30))) + .add(new Triangle(new Point(0, 0), new Point(50, 30), new Point(10, 20))) + .add(new Triangle(new Point(20, 0), new Point(10, 20), new Point(50, 30))); + assertFalse(triangulation.fits(polygon)); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2010-01-16 11:32:25
|
Revision: 31 http://geom4j.svn.sourceforge.net/geom4j/?rev=31&view=rev Author: skhenkin Date: 2010-01-16 11:32:17 +0000 (Sat, 16 Jan 2010) Log Message: ----------- Refactoring of the code to remove Polygon.addVertex() method completely. So from now on Polygon is an immutable class. This is also a preparation step for making polygons always counter-clockwise traversed. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithmTest.java trunk/src/net/sourceforge/geom4j/PointTest.java trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/PolygonTest.java trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java trunk/src/net/sourceforge/geom4j/TriangulationTest.java trunk/src/net/sourceforge/geom4j/demo/Demo.java Modified: trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java 2010-01-16 11:32:17 UTC (rev 31) @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.LinkedList; import java.util.List; /** @@ -61,13 +62,14 @@ static ConvexHullAlgorithm JARVIS_MARCH = new ConvexHullAlgorithm() { @Override public Polygon run(PointSet ps) { - Polygon hull = new Polygon(); + List<Point> hull = new LinkedList<Point>(); Point pointOnHull = ps.getLeftmost(); + Point firstVertex = pointOnHull; do { - hull.addVertex(pointOnHull); + hull.add(pointOnHull); pointOnHull = getNextPoint(ps, pointOnHull); - } while (!pointOnHull.equals(hull.getFirstVertex())); - return hull; + } while (!pointOnHull.equals(firstVertex)); + return new Polygon(hull); } /* Find the point with the biggest polar angle @@ -143,12 +145,12 @@ } stack[++m] = points.get(i); } - - Polygon hull = new Polygon(); + + List<Point> hull = new LinkedList<Point>(); for (int i = 0; i <= m; i++) { - hull.addVertex(stack[i]); + hull.add(stack[i]); } - return hull; + return new Polygon(hull); } /** Test if p1-p2-p3 form a non-left turn, i.e. straight line or right turn */ Modified: trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithmTest.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithmTest.java 2010-01-16 11:32:17 UTC (rev 31) @@ -52,15 +52,15 @@ ps.add(new Point(11, 4)); ps.add(new Point(9, 2)); - ch = new Polygon(); - ch.addVertex(new Point(5, 1)); - ch.addVertex(new Point(9, 2)); - ch.addVertex(new Point(11, 4)); - ch.addVertex(new Point(10, 5)); - ch.addVertex(new Point(5, 7)); - ch.addVertex(new Point(3, 6)); - ch.addVertex(new Point(2, 4)); - ch.addVertex(new Point(2, 2)); + ch = new Polygon( + new Point(5, 1), + new Point(9, 2), + new Point(11, 4), + new Point(10, 5), + new Point(5, 7), + new Point(3, 6), + new Point(2, 4), + new Point(2, 2)); } Modified: trunk/src/net/sourceforge/geom4j/PointTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointTest.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/PointTest.java 2010-01-16 11:32:17 UTC (rev 31) @@ -53,18 +53,18 @@ @Test public void testPointInPolygon() { - Polygon p = new Polygon() - .addVertex(new Point(2, 0)) - .addVertex(new Point(1, 2)) - .addVertex(new Point(2, 5)) - .addVertex(new Point(3, 3)) - .addVertex(new Point(4, 5)) - .addVertex(new Point(5, 3)) - .addVertex(new Point(6, 5)) - .addVertex(new Point(7, 2)) - .addVertex(new Point(6, 1)) - .addVertex(new Point(5, 1)) - .addVertex(new Point(4, 0)); + Polygon p = new Polygon( + new Point(2, 0), + new Point(1, 2), + new Point(2, 5), + new Point(3, 3), + new Point(4, 5), + new Point(5, 3), + new Point(6, 5), + new Point(7, 2), + new Point(6, 1), + new Point(5, 1), + new Point(4, 0)); assertTrue(new Point(6, 4).isInside(p)); assertFalse(new Point(5, 4).isInside(p)); Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-16 11:32:17 UTC (rev 31) @@ -16,17 +16,18 @@ package net.sourceforge.geom4j; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; /** * A polygon is a closed shape in a plane. + * This is immutable class. * * @author Sergey Khenkin * @since 1.0.0 */ public class Polygon extends BasicFigure { - // TODO: think of making this class also immutable protected List<Point> vertices; @@ -41,11 +42,10 @@ } } - public Polygon addVertex(Point p) { - vertices.add(p); - return this; + public Polygon(Collection<Point> vertices) { + this(vertices.toArray(new Point[0])); } - + /** Get number of polygon's vertices (corners) */ public int getVertexCount() { return vertices.size(); @@ -267,4 +267,22 @@ public Triangulation triangulate(PolygonTriangulationAlgorithm alg) { return alg.run(this); } + + /** + * Check if the traversal order of the vertices in the polygon is clockwise. + * @return true for clockwise order and false for counter-clockwise + */ + public boolean isClockwise() { + return true; // TODO: implement + } + + /** Check if the vertex number i is convex */ + public boolean isConvexVertex(int i) { + return true; // TODO: implement + } + + /** Check if the vertex number i is concave */ + public boolean isConcaveVertex(int i) { + return !isConvexVertex(i); + } } Modified: trunk/src/net/sourceforge/geom4j/PolygonTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-16 11:32:17 UTC (rev 31) @@ -27,18 +27,19 @@ * @since 1.0.0 */ public class PolygonTest { - Polygon p = new Polygon(); + Polygon p; @Before public void setUp() throws Exception { - p.addVertex(new Point(1, 4)) - .addVertex(new Point(4, 8)) - .addVertex(new Point(8, 8)) - .addVertex(new Point(8, 10)) - .addVertex(new Point(11, 6)) - .addVertex(new Point(7, 3)) - .addVertex(new Point(3, 6)) - .addVertex(new Point(3, 4)); + p = new Polygon( + new Point(1, 4), + new Point(4, 8), + new Point(8, 8), + new Point(8, 10), + new Point(11, 6), + new Point(7, 3), + new Point(3, 6), + new Point(3, 4)); } @Test Modified: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-16 11:32:17 UTC (rev 31) @@ -32,7 +32,8 @@ /** - * Straightforward algorithm for convex polygon triangulation + * Straightforward algorithm for convex polygon triangulation. + * Complexity is O(n) where n is the number of vertices of the polygon. */ static PolygonTriangulationAlgorithm CONVEX = new PolygonTriangulationAlgorithm() { @Override @@ -48,4 +49,23 @@ } }; + /** + * Ear clipping triangulation algorithm for simple polygons without holes. + * Has computational complexity of O(n*n) where n is the number + * of vertices of the polygon. + * The idea of the method is to use the assertion that any simple polygon + * without holes has at least two so called 'ears'. An ear is a triangle + * with two sides on the edge of the polygon and the other one completely + * inside it. The algorithm then consists of finding such an ear, removing + * it from the polygon (which results in a new polygon that still meets + * the conditions) and repeating until there is only one triangle left. + */ + static PolygonTriangulationAlgorithm EAR_CLIPPING = new PolygonTriangulationAlgorithm() { + @Override + public Triangulation run(Polygon polygon) { + Triangulation triangulation = new Triangulation(); + return triangulation; + } + }; + } Modified: trunk/src/net/sourceforge/geom4j/TriangulationTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/TriangulationTest.java 2010-01-16 11:32:17 UTC (rev 31) @@ -34,10 +34,7 @@ new Point(60, 70), new Point(30, 10) }; - Polygon polygon = new Polygon(); - for (Point point: points) { - polygon.addVertex(point); - } + Polygon polygon = new Polygon(points); assertEquals(new PointSet(points), polygon.triangulate().getVertices()); } Modified: trunk/src/net/sourceforge/geom4j/demo/Demo.java =================================================================== --- trunk/src/net/sourceforge/geom4j/demo/Demo.java 2010-01-16 08:24:36 UTC (rev 30) +++ trunk/src/net/sourceforge/geom4j/demo/Demo.java 2010-01-16 11:32:17 UTC (rev 31) @@ -57,16 +57,16 @@ */ private static void intersectionDemo() { // Create some polygon - Figure polygon = new Polygon() - .addVertex(new Point( 0, 0)) - .addVertex(new Point(10, 10)) - .addVertex(new Point(20, 0)) - .addVertex(new Point(30, 10)) - .addVertex(new Point(40, 0)) - .addVertex(new Point(50, 10)) - .addVertex(new Point(60, 0)) - .addVertex(new Point(70, 10)) - .addVertex(new Point(80, 0)); + Figure polygon = new Polygon( + new Point( 0, 0), + new Point(10, 10), + new Point(20, 0), + new Point(30, 10), + new Point(40, 0), + new Point(50, 10), + new Point(60, 0), + new Point(70, 10), + new Point(80, 0)); // Create some line Figure line = new Line(new Point(0, 5), new Point(80, 5)); // Iterate through the points of intersection @@ -81,17 +81,17 @@ */ private static void distanceDemo() { // Create some polygon - Figure square = new Polygon() - .addVertex(new Point( 0, 0)) - .addVertex(new Point( 0, 20)) - .addVertex(new Point(20, 20)) - .addVertex(new Point(20, 0)); + Figure square = new Polygon( + new Point( 0, 0), + new Point( 0, 20), + new Point(20, 20), + new Point(20, 0)); // Create another one - Figure diamond = new Polygon() - .addVertex(new Point(30, 10)) - .addVertex(new Point(35, 15)) - .addVertex(new Point(30, 20)) - .addVertex(new Point(25, 15)); + Figure diamond = new Polygon( + new Point(30, 10), + new Point(35, 15), + new Point(30, 20), + new Point(25, 15)); // Get the distance between them System.out.println("Distance: " + square.distanceTo(diamond)); } @@ -100,12 +100,12 @@ * Demonstrate convex polygon check */ private static void convexPolygonDemo() { - Polygon convex = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon convex = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); System.out.println(String.format("Polygon %s is %sconvex", convex, convex.isConvex() ? "" : "not ")); } @@ -113,11 +113,11 @@ * Demonstrate polygon area calculation */ private static void polygonAreaDemo() { - Polygon romb = new Polygon() - .addVertex(new Point(10, 10)) - .addVertex(new Point(50, 40)) - .addVertex(new Point(20, 80)) - .addVertex(new Point(-20, 50)); + Polygon romb = new Polygon( + new Point(10, 10), + new Point(50, 40), + new Point(20, 80), + new Point(-20, 50)); System.out.println("Area: " + romb.getArea()); } @@ -154,10 +154,10 @@ * Demonstrate point-in-polygon test */ private static void pointInsidePolygonDemo() { - Polygon triangle = new Polygon() - .addVertex(new Point(1, 1)) - .addVertex(new Point(6, 3)) - .addVertex(new Point(5, 4)); + Polygon triangle = new Polygon( + new Point(1, 1), + new Point(6, 3), + new Point(5, 4)); if (new Point(5, 3).isInside(triangle)) { System.out.println("Point (5, 3) is on the inside of the triangle."); @@ -184,20 +184,20 @@ * Demonstrate polygon simplicity test */ private static void simplePolygonDemo() { - Polygon p1 = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon p1 = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); System.out.println(String.format("Polygon %s is %ssimple", p1, p1.isSimple() ? "" : "not ")); - Polygon p2 = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon p2 = new Polygon( + new Point(0, 0), + new Point(100, 100), + new Point(100, 0), + new Point(50, 100), + new Point(0, 50)); System.out.println(String.format("Polygon %s is %ssimple", p2, p2.isSimple() ? "" : "not ")); } @@ -206,11 +206,11 @@ * Demonstrate how to determine if one figure contains another */ private static void contentDemo() { - Figure square = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(0, 100)); + Figure square = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(0, 100)); Figure segment = new Segment(new Point(100, 20), new Point(100, 40)); if (square.contains(segment)) { System.out.println("Square " + square + " contains segment " + segment); @@ -221,12 +221,12 @@ * Demonstrate polygon triangulation */ private static void triangulationDemo() { - Polygon polygon = new Polygon() - .addVertex(new Point(0, 0)) - .addVertex(new Point(100, 0)) - .addVertex(new Point(100, 100)) - .addVertex(new Point(50, 100)) - .addVertex(new Point(0, 50)); + Polygon polygon = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); Triangulation triangulation = polygon.triangulate(); System.out.println(String.format("Triangulation of the polygon %s is %s", polygon, triangulation)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2010-01-16 23:19:57
|
Revision: 32 http://geom4j.svn.sourceforge.net/geom4j/?rev=32&view=rev Author: skhenkin Date: 2010-01-16 23:19:49 +0000 (Sat, 16 Jan 2010) Log Message: ----------- Polygon class refactored to store vertices in counter-clockwise traversal order Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/PolygonTest.java Added Paths: ----------- trunk/src/net/sourceforge/geom4j/util/Utils.java trunk/src/net/sourceforge/geom4j/util/UtilsTest.java Modified: trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java 2010-01-16 11:32:17 UTC (rev 31) +++ trunk/src/net/sourceforge/geom4j/ConvexHullAlgorithm.java 2010-01-16 23:19:49 UTC (rev 32) @@ -21,6 +21,8 @@ import java.util.LinkedList; import java.util.List; +import net.sourceforge.geom4j.util.Utils; + /** * An algorithm to construct convex hull (convex envelope) for a point set. * The convex hull is represented as a polygon. @@ -138,7 +140,7 @@ int m = 2; // a pointer to the top stack element for (int i = 2; i < points.size(); i++) { // test each point of the set - while (nonLeftTurn(stack[m-1], stack[m], points.get(i))) { + while (Utils.isNonLeftTurn(stack[m-1], stack[m], points.get(i))) { /* rollback if the turn is non-left, i.e. it breaks the rule for a convex polygon */ m--; @@ -151,14 +153,6 @@ hull.add(stack[i]); } return new Polygon(hull); - } - - /** Test if p1-p2-p3 form a non-left turn, i.e. straight line or right turn */ - private boolean nonLeftTurn(Point p1, Point p2, Point p3) { - Vector v1 = new Vector(p1, p2); - Vector v2 = new Vector(p2, p3); - return v1.crossProduct(v2) <= 0; - } - + } }; } Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-16 11:32:17 UTC (rev 31) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-16 23:19:49 UTC (rev 32) @@ -17,9 +17,12 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; +import net.sourceforge.geom4j.util.Utils; + /** * A polygon is a closed shape in a plane. * This is immutable class. @@ -40,6 +43,7 @@ for (Point vertex: vertices) { this.vertices.add(vertex); } + orderCounterClockwise(); } public Polygon(Collection<Point> vertices) { @@ -196,21 +200,13 @@ /** Check if the polygon is convex or not */ public boolean isConvex() { - if (vertices.size() < 3) return true; // it's not really a polygon - // convex polygon <=> all cross products of its sides are of the same sign - Iterator<Point> i = vertices.iterator(); - Point p1 = vertices.get(getVertexCount()-1); - Point p2 = i.next(); - Vector v1 = new Vector(vertices.get(getVertexCount()-2), p1); - Vector v2 = new Vector(p1, p2); - double sign0 = Math.signum(v1.crossProduct(v2)); - while (i.hasNext()) { - p1 = p2; - p2 = i.next(); - v1 = v2; - v2 = new Vector(p1, p2); - double sign = Math.signum(v1.crossProduct(v2)); - if (sign0*sign < 0) { + /* check if there's no right turns in counter-clockwise traversal + * of the polygon vertices + */ + int n = vertices.size(); + for (int i = 0; i < n; i++) { + if (!Utils.isNonRightTurn(getPreviousVertex(i), + getVertex(i), getNextVertex(i))) { return false; } } @@ -233,6 +229,14 @@ return vertices.get(i); } + public Point getNextVertex(int i) { + return getVertex(i == vertices.size()-1 ? 0 : i+1); + } + + public Point getPreviousVertex(int i) { + return getVertex(i == 0 ? vertices.size()-1 : i-1); + } + public Point getFirstVertex() { return getVertex(0); } @@ -267,18 +271,39 @@ public Triangulation triangulate(PolygonTriangulationAlgorithm alg) { return alg.run(this); } - + + /** Order vertices so that the traversal order is counter clockwise */ + private void orderCounterClockwise() { + if (isClockwise()) { + Collections.reverse(vertices); + } + } + /** * Check if the traversal order of the vertices in the polygon is clockwise. * @return true for clockwise order and false for counter-clockwise */ - public boolean isClockwise() { - return true; // TODO: implement + boolean isClockwise() { + int n = vertices.size(); + // find rightmost vertex + int maxIdx = 0; + double maxX = Double.MIN_VALUE; + for (int i = 0; i < n; i++) { + if (vertices.get(i).getX() > maxX) { + maxIdx = i; + maxX = vertices.get(i).getX(); + } + } + // check if the the turn at this point is wrong + return isConcaveVertex(maxIdx) + || getPreviousVertex(maxIdx).getX() == getVertex(maxIdx).getX() + && getVertex(maxIdx).getX() == getNextVertex(maxIdx).getX() + && getPreviousVertex(maxIdx).getY() < getNextVertex(maxIdx).getY(); } /** Check if the vertex number i is convex */ public boolean isConvexVertex(int i) { - return true; // TODO: implement + return Utils.isNonRightTurn(getPreviousVertex(i), getVertex(i), getNextVertex(i)); } /** Check if the vertex number i is concave */ Modified: trunk/src/net/sourceforge/geom4j/PolygonTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-16 11:32:17 UTC (rev 31) +++ trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-16 23:19:49 UTC (rev 32) @@ -33,13 +33,13 @@ public void setUp() throws Exception { p = new Polygon( new Point(1, 4), - new Point(4, 8), + new Point(3, 4), + new Point(3, 6), + new Point(7, 3), + new Point(11, 6), + new Point(8, 10), new Point(8, 8), - new Point(8, 10), - new Point(11, 6), - new Point(7, 3), - new Point(3, 6), - new Point(3, 4)); + new Point(4, 8)); } @Test @@ -158,4 +158,72 @@ new Point(20, 20)) .isSimple()); } + + @Test + public void testIsClockwise() { + assertFalse(new Polygon( + new Point(4, 4), + new Point(5, 3), + new Point(6, 4), + new Point(5, 5)).isClockwise()); + assertFalse(new Polygon( + new Point(4, 4), + new Point(5, 5), + new Point(6, 4), + new Point(5, 3)).isClockwise()); + assertFalse(new Polygon( + new Point(4, 4), + new Point(5, 3), + new Point(6, 3), + new Point(6, 4), + new Point(6, 5), + new Point(5, 5)).isClockwise()); + } + + @Test + public void testConvexAndConcaveVertex1() { + Polygon p = new Polygon( + new Point(4, 4), //0 + new Point(5, 3), //1 + new Point(6, 3), //2 + new Point(6, 4), //3 + new Point(6, 5), //4 + new Point(5, 6), //5 + new Point(6, 7), //6 + new Point(5, 7), //7 + new Point(4, 6)); //8 + assertTrue(p.isConvexVertex(0)); + assertTrue(p.isConvexVertex(1)); + assertTrue(p.isConvexVertex(2)); + assertTrue(p.isConvexVertex(3)); + assertTrue(p.isConvexVertex(4)); + assertTrue(p.isConcaveVertex(5)); + assertTrue(p.isConvexVertex(6)); + assertTrue(p.isConvexVertex(7)); + assertTrue(p.isConvexVertex(8)); + + } + + @Test + public void testConvexAndConcaveVertex2() { + assertTrue(p.isConvexVertex(0)); + assertTrue(p.isConvexVertex(1)); + assertTrue(p.isConcaveVertex(2)); + assertTrue(p.isConvexVertex(3)); + assertTrue(p.isConvexVertex(4)); + assertTrue(p.isConvexVertex(5)); + assertTrue(p.isConcaveVertex(6)); + assertTrue(p.isConvexVertex(7)); + } + + @Test + public void testPreviousAndNextVertex() { + assertEquals(new Point(4, 8), p.getPreviousVertex(0)); + assertEquals(new Point(3, 4), p.getNextVertex(0)); + assertEquals(new Point(7, 3), p.getPreviousVertex(4)); + assertEquals(new Point(8, 10), p.getNextVertex(4)); + assertEquals(new Point(8, 8), p.getPreviousVertex(7)); + assertEquals(new Point(1, 4), p.getNextVertex(7)); + } } + Added: trunk/src/net/sourceforge/geom4j/util/Utils.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/Utils.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/Utils.java 2010-01-16 23:19:49 UTC (rev 32) @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import net.sourceforge.geom4j.Point; +import net.sourceforge.geom4j.Vector; + +/** + * Miscellaneous utility functions. + * + * @author Sergey Khenkin + * @since 1.0.0 + */ +public class Utils { + /** + * Test if p1-p2-p3 form a non-left turn, i.e. straight line or right turn + */ + public static boolean isNonLeftTurn(Point p1, Point p2, Point p3) { + return pointProduct(p1, p2, p3) <= 0; + } + + /** + * Test if p1-p2-p3 form a non-right turn, i.e. straight line or left turn + */ + public static boolean isNonRightTurn(Point p1, Point p2, Point p3) { + return pointProduct(p1, p2, p3) >= 0; + } + + private static double pointProduct(Point p1, Point p2, Point p3) { + Vector v1 = new Vector(p1, p2); + Vector v2 = new Vector(p2, p3); + return v1.crossProduct(v2); + } +} Added: trunk/src/net/sourceforge/geom4j/util/UtilsTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/util/UtilsTest.java (rev 0) +++ trunk/src/net/sourceforge/geom4j/util/UtilsTest.java 2010-01-16 23:19:49 UTC (rev 32) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009 Sergey Khenkin. All rights reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.geom4j.util; + +import net.sourceforge.geom4j.Point; + +import org.junit.Test; +import static net.sourceforge.geom4j.util.Utils.*; +import static org.junit.Assert.*; + +/** + * Tests for miscellaneous utility functions + * + * @author Sergey Khenkin + * @since 1.0.0 + */ +public class UtilsTest { + @Test + public void testNonLeftTurn() { + assertTrue(isNonLeftTurn(new Point(5, 5), new Point(9, 8), new Point(11, 3))); + assertTrue(isNonLeftTurn(new Point(5, 5), new Point(9, 9), new Point(11, 11))); + assertFalse(isNonLeftTurn(new Point(5, 5), new Point(9, 9), new Point(8, 11))); + } + + @Test + public void testNonRightTurn() { + assertFalse(isNonRightTurn(new Point(5, 5), new Point(9, 8), new Point(11, 3))); + assertTrue(isNonRightTurn(new Point(5, 5), new Point(9, 9), new Point(11, 11))); + assertTrue(isNonRightTurn(new Point(5, 5), new Point(9, 9), new Point(8, 11))); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2010-01-17 08:59:09
|
Revision: 33 http://geom4j.svn.sourceforge.net/geom4j/?rev=33&view=rev Author: skhenkin Date: 2010-01-17 08:59:01 +0000 (Sun, 17 Jan 2010) Log Message: ----------- Bug in Point.isInside() fixed. An initial version of ear clipping polygon triangulation algorithm implemented. Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/Point.java trunk/src/net/sourceforge/geom4j/PointTest.java trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/PolygonTest.java trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java Modified: trunk/src/net/sourceforge/geom4j/Point.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Point.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/Point.java 2010-01-17 08:59:01 UTC (rev 33) @@ -183,6 +183,10 @@ * and http://erich.realtimerendering.com/ptinpoly/ */ public boolean isInside(Polygon p) { + if (p.contains(this)) { + // the point lies on border of the polygon + return true; + } // Construct a "ray" to test crossings with. This is actually a long enough segment double maxX = getX() + 1; for (Point pt: p.getVertices()) { Modified: trunk/src/net/sourceforge/geom4j/PointTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PointTest.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/PointTest.java 2010-01-17 08:59:01 UTC (rev 33) @@ -88,4 +88,15 @@ assertTrue(new Point(3, 1).isInside(p)); assertFalse(new Point(1, 0).isInside(p)); } + + + @Test + public void testPointInPolygonSpecialCase1() { + Polygon poly = new Polygon( + new Point(2, 0), + new Point(8, 3), + new Point(7, 5)); + Point pt = new Point(4, 2); + assertTrue(pt.isInside(poly)); + } } Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-17 08:59:01 UTC (rev 33) @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import net.sourceforge.geom4j.util.Utils; @@ -225,23 +226,31 @@ return Math.abs(s); } - public Point getVertex(int i) { + public final Point getVertex(int i) { return vertices.get(i); } - public Point getNextVertex(int i) { - return getVertex(i == vertices.size()-1 ? 0 : i+1); + public final int getNextVertexIndex(int i) { + return i == vertices.size()-1 ? 0 : i+1; } - public Point getPreviousVertex(int i) { - return getVertex(i == 0 ? vertices.size()-1 : i-1); + public final Point getNextVertex(int i) { + return getVertex(getNextVertexIndex(i)); } - public Point getFirstVertex() { + public final int getPreviousVertexIndex(int i) { + return i == 0 ? vertices.size()-1 : i-1; + } + + public final Point getPreviousVertex(int i) { + return getVertex(getPreviousVertexIndex(i)); + } + + public final Point getFirstVertex() { return getVertex(0); } - public Point getLastVertex() { + public final Point getLastVertex() { return getVertex(getVertexCount()-1); } @@ -303,11 +312,27 @@ /** Check if the vertex number i is convex */ public boolean isConvexVertex(int i) { - return Utils.isNonRightTurn(getPreviousVertex(i), getVertex(i), getNextVertex(i)); + return !Utils.isNonLeftTurn(getPreviousVertex(i), getVertex(i), getNextVertex(i)); } /** Check if the vertex number i is concave */ public boolean isConcaveVertex(int i) { - return !isConvexVertex(i); + return !Utils.isNonRightTurn(getPreviousVertex(i), getVertex(i), getNextVertex(i)); } + + /** + * Create a polygon which is the same as given but without a vertex + * with the specified index. + */ + public Polygon withoutVertex(int vertexIndex) { + int n = this.vertices.size(); + Point[] vertices = new Point[n-1]; + for (int i = 0; i < vertexIndex; i++) { + vertices[i] = this.vertices.get(i); + } + for (int i = vertexIndex + 1; i < n; i++) { + vertices[i-1] = this.vertices.get(i); + } + return new Polygon(vertices); + } } Modified: trunk/src/net/sourceforge/geom4j/PolygonTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/PolygonTest.java 2010-01-17 08:59:01 UTC (rev 33) @@ -195,7 +195,8 @@ assertTrue(p.isConvexVertex(0)); assertTrue(p.isConvexVertex(1)); assertTrue(p.isConvexVertex(2)); - assertTrue(p.isConvexVertex(3)); + assertFalse(p.isConvexVertex(3)); + assertFalse(p.isConcaveVertex(3)); assertTrue(p.isConvexVertex(4)); assertTrue(p.isConcaveVertex(5)); assertTrue(p.isConvexVertex(6)); @@ -225,5 +226,21 @@ assertEquals(new Point(8, 8), p.getPreviousVertex(7)); assertEquals(new Point(1, 4), p.getNextVertex(7)); } + + @Test + public void testWithoutVertex() { + Polygon p1 = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(100, 100), + new Point(50, 100), + new Point(0, 50)); + Polygon p2 = new Polygon( + new Point(0, 0), + new Point(100, 0), + new Point(50, 100), + new Point(0, 50)); + assertEquals(p2, p1.withoutVertex(2)); + } } Modified: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-17 08:59:01 UTC (rev 33) @@ -64,8 +64,47 @@ @Override public Triangulation run(Polygon polygon) { Triangulation triangulation = new Triangulation(); + while (polygon.getVertexCount() >= 3) { + int earIndex = findEarVertexIndex(polygon); + triangulation.add(new Triangle( + polygon.getPreviousVertex(earIndex), + polygon.getVertex(earIndex), + polygon.getNextVertex(earIndex))); + polygon = polygon.withoutVertex(earIndex); + } return triangulation; } + + /** + * Find the index of an ear vertex in polygon + */ + private int findEarVertexIndex(Polygon polygon) { + int n = polygon.getVertexCount(); + for (int i = 0; i < n; i++) { + if (polygon.isConvexVertex(i)) { + // check if a concave vertex is inside the triangle + boolean concaveVertexInside = false; + for (int j = 0; j < n ; j++) { + if (j == i || j == polygon.getPreviousVertexIndex(i) + || j == polygon.getNextVertexIndex(i)) { + continue; // skip vertices that form current triangle + } + if (!polygon.isConvexVertex(j)){ + if (polygon.getVertex(j).isInside(new Triangle(polygon.getPreviousVertex(i), + polygon.getVertex(i), + polygon.getNextVertex(i)))) { + concaveVertexInside = true; + break; + } + } + } + if (!concaveVertexInside) { + return i; + } + } + } + throw new IllegalArgumentException("No ear can be found in polygon " + polygon); + } }; } Modified: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java 2010-01-16 23:19:49 UTC (rev 32) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithmTest.java 2010-01-17 08:59:01 UTC (rev 33) @@ -36,7 +36,7 @@ } @Test - public void testX() { + public void testConvexFailure() { Polygon polygon = new Polygon( new Point(1, 1), new Point(3, 2), @@ -50,4 +50,29 @@ assertFalse(triangulation.fits(polygon)); } + @Test + public void testEarClipping() { + Polygon polygon = new Polygon( + new Point(1, 1), + new Point(3, 2), + new Point(2, 0), + new Point(8, 3), + new Point(7, 5), + new Point(4, 2), + new Point(3, 3), + new Point(3, 4)); + Triangulation triangulation = polygon.triangulate(PolygonTriangulationAlgorithm.EAR_CLIPPING); + assertTrue(triangulation.fits(polygon)); + } + + @Test + public void testEarClippingPrimitive() { + Polygon polygon = new Polygon( + new Point(1, 1), + new Point(2, 0), + new Point(3, 2)); + Triangulation triangulation = polygon.triangulate(PolygonTriangulationAlgorithm.EAR_CLIPPING); + assertTrue(triangulation.fits(polygon)); + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <skh...@us...> - 2010-01-17 09:24:11
|
Revision: 34 http://geom4j.svn.sourceforge.net/geom4j/?rev=34&view=rev Author: skhenkin Date: 2010-01-17 09:24:03 +0000 (Sun, 17 Jan 2010) Log Message: ----------- ToDo item added for improving ear clipping method performance Modified Paths: -------------- trunk/src/net/sourceforge/geom4j/Polygon.java trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java Modified: trunk/src/net/sourceforge/geom4j/Polygon.java =================================================================== --- trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-17 08:59:01 UTC (rev 33) +++ trunk/src/net/sourceforge/geom4j/Polygon.java 2010-01-17 09:24:03 UTC (rev 34) @@ -19,7 +19,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import net.sourceforge.geom4j.util.Utils; Modified: trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java =================================================================== --- trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-17 08:59:01 UTC (rev 33) +++ trunk/src/net/sourceforge/geom4j/PolygonTriangulationAlgorithm.java 2010-01-17 09:24:03 UTC (rev 34) @@ -61,7 +61,10 @@ * the conditions) and repeating until there is only one triangle left. */ static PolygonTriangulationAlgorithm EAR_CLIPPING = new PolygonTriangulationAlgorithm() { - @Override + /* TODO: improve it to be really O(n*n) + * keeping the lists of convex and non-convex vertices separate + */ + @Override public Triangulation run(Polygon polygon) { Triangulation triangulation = new Triangulation(); while (polygon.getVertexCount() >= 3) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |