Simplified SubLine API by introducing a public Segment class.

JIRA: MATH-592

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1137749 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2011-06-20 19:11:43 +00:00
parent 588f3b4c59
commit f7ee41ad19
5 changed files with 188 additions and 198 deletions

View File

@ -22,11 +22,17 @@ import java.util.List;
import org.apache.commons.math.exception.MathInternalError; import org.apache.commons.math.exception.MathInternalError;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D; import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Interval;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D; import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math.geometry.partitioning.BSPTree; import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor;
import org.apache.commons.math.geometry.partitioning.BoundaryAttribute;
import org.apache.commons.math.geometry.partitioning.SubHyperplane; import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.AbstractRegion; import org.apache.commons.math.geometry.partitioning.AbstractRegion;
import org.apache.commons.math.geometry.partitioning.utilities.AVLTree; import org.apache.commons.math.geometry.partitioning.utilities.AVLTree;
import org.apache.commons.math.geometry.partitioning.utilities.OrderedTuple;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
/** This class represents a 2D region: a set of polygons. /** This class represents a 2D region: a set of polygons.
@ -202,14 +208,14 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
// sort the segments according to their start point // sort the segments according to their start point
final SegmentsBuilder visitor = new SegmentsBuilder(); final SegmentsBuilder visitor = new SegmentsBuilder();
getTree(true).visit(visitor); getTree(true).visit(visitor);
final AVLTree<Segment> sorted = visitor.getSorted(); final AVLTree<ComparableSegment> sorted = visitor.getSorted();
// identify the loops, starting from the open ones // identify the loops, starting from the open ones
// (their start segments are naturally at the sorted set beginning) // (their start segments are naturally at the sorted set beginning)
final ArrayList<List<Segment>> loops = new ArrayList<List<Segment>>(); final ArrayList<List<ComparableSegment>> loops = new ArrayList<List<ComparableSegment>>();
while (!sorted.isEmpty()) { while (!sorted.isEmpty()) {
final AVLTree<Segment>.Node node = sorted.getSmallest(); final AVLTree<ComparableSegment>.Node node = sorted.getSmallest();
final List<Segment> loop = followLoop(node, sorted); final List<ComparableSegment> loop = followLoop(node, sorted);
if (loop != null) { if (loop != null) {
loops.add(loop); loops.add(loop);
} }
@ -219,7 +225,7 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
vertices = new Vector2D[loops.size()][]; vertices = new Vector2D[loops.size()][];
int i = 0; int i = 0;
for (final List<Segment> loop : loops) { for (final List<ComparableSegment> loop : loops) {
if (loop.size() < 2) { if (loop.size() < 2) {
// single infinite line // single infinite line
final Line line = loop.get(0).getLine(); final Line line = loop.get(0).getLine();
@ -280,11 +286,11 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
* @return a list of connected sub-hyperplanes starting at * @return a list of connected sub-hyperplanes starting at
* {@code node} * {@code node}
*/ */
private List<Segment> followLoop(final AVLTree<Segment>.Node node, private List<ComparableSegment> followLoop(final AVLTree<ComparableSegment>.Node node,
final AVLTree<Segment> sorted) { final AVLTree<ComparableSegment> sorted) {
final ArrayList<Segment> loop = new ArrayList<Segment>(); final ArrayList<ComparableSegment> loop = new ArrayList<ComparableSegment>();
Segment segment = node.getElement(); ComparableSegment segment = node.getElement();
loop.add(segment); loop.add(segment);
final Vector2D globalStart = segment.getStart(); final Vector2D globalStart = segment.getStart();
Vector2D end = segment.getEnd(); Vector2D end = segment.getEnd();
@ -296,15 +302,15 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
while ((end != null) && (open || (globalStart.distance(end) > 1.0e-10))) { while ((end != null) && (open || (globalStart.distance(end) > 1.0e-10))) {
// search the sub-hyperplane starting where the previous one ended // search the sub-hyperplane starting where the previous one ended
AVLTree<Segment>.Node selectedNode = null; AVLTree<ComparableSegment>.Node selectedNode = null;
Segment selectedSegment = null; ComparableSegment selectedSegment = null;
double selectedDistance = Double.POSITIVE_INFINITY; double selectedDistance = Double.POSITIVE_INFINITY;
final Segment lowerLeft = new Segment(end, -1.0e-10, -1.0e-10); final ComparableSegment lowerLeft = new ComparableSegment(end, -1.0e-10, -1.0e-10);
final Segment upperRight = new Segment(end, +1.0e-10, +1.0e-10); final ComparableSegment upperRight = new ComparableSegment(end, +1.0e-10, +1.0e-10);
for (AVLTree<Segment>.Node n = sorted.getNotSmaller(lowerLeft); for (AVLTree<ComparableSegment>.Node n = sorted.getNotSmaller(lowerLeft);
(n != null) && (n.getElement().compareTo(upperRight) <= 0); (n != null) && (n.getElement().compareTo(upperRight) <= 0);
n = n.getNext()) { n = n.getNext()) {
segment = (Segment) n.getElement(); segment = n.getElement();
final double distance = end.distance(segment.getStart()); final double distance = end.distance(segment.getStart());
if (distance < selectedDistance) { if (distance < selectedDistance) {
selectedNode = n; selectedNode = n;
@ -339,4 +345,125 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
} }
private static class ComparableSegment extends Segment implements Comparable<ComparableSegment> {
/** Sorting key. */
private OrderedTuple sortingKey;
/** Build a segment.
* @param start start point of the segment
* @param end end point of the segment
* @param line line containing the segment
*/
public ComparableSegment(final Vector2D start, final Vector2D end, final Line line) {
super(start, end, line);
sortingKey = (start == null) ?
new OrderedTuple(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY) :
new OrderedTuple(start.getX(), start.getY());
}
/** Build a dummy segment.
* <p>
* The object built is not a real segment, only the sorting key is used to
* allow searching in the neighborhood of a point. This is an horrible hack ...
* </p>
* @param start start point of the segment
* @param dx abscissa offset from the start point
* @param dy ordinate offset from the start point
*/
public ComparableSegment(final Vector2D start, final double dx, final double dy) {
super(null, null, null);
sortingKey = new OrderedTuple(start.getX() + dx, start.getY() + dy);
}
/** {@inheritDoc} */
public int compareTo(final ComparableSegment o) {
return sortingKey.compareTo(o.sortingKey);
}
/** {@inheritDoc} */
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
} else if (other instanceof ComparableSegment) {
return compareTo((ComparableSegment) other) == 0;
} else {
return false;
}
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return getStart().hashCode() ^ getEnd().hashCode() ^
getLine().hashCode() ^ sortingKey.hashCode();
}
}
/** Visitor building segments. */
private static class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
/** Sorted segments. */
private AVLTree<ComparableSegment> sorted;
/** Simple constructor. */
public SegmentsBuilder() {
sorted = new AVLTree<ComparableSegment>();
}
/** {@inheritDoc} */
public Order visitOrder(final BSPTree<Euclidean2D> node) {
return Order.MINUS_SUB_PLUS;
}
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree<Euclidean2D> node) {
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean2D> attribute = (BoundaryAttribute<Euclidean2D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false);
}
if (attribute.getPlusInside() != null) {
addContribution(attribute.getPlusInside(), true);
}
}
/** {@inheritDoc} */
public void visitLeafNode(final BSPTree<Euclidean2D> node) {
}
/** Add he contribution of a boundary facet.
* @param sub boundary facet
* @param reversed if true, the facet has the inside on its plus side
*/
private void addContribution(final SubHyperplane<Euclidean2D> sub, final boolean reversed) {
@SuppressWarnings("unchecked")
final AbstractSubHyperplane<Euclidean2D, Euclidean1D> absSub =
(AbstractSubHyperplane<Euclidean2D, Euclidean1D>) sub;
final Line line = (Line) sub.getHyperplane();
final List<Interval> intervals = ((IntervalsSet) absSub.getRemainingRegion()).asList();
for (final Interval i : intervals) {
final Vector2D start = Double.isInfinite(i.getLower()) ?
null : (Vector2D) line.toSpace(new Vector1D(i.getLower()));
final Vector2D end = Double.isInfinite(i.getUpper()) ?
null : (Vector2D) line.toSpace(new Vector1D(i.getUpper()));
if (reversed) {
sorted.insert(new ComparableSegment(end, start, line.getReverse()));
} else {
sorted.insert(new ComparableSegment(start, end, line));
}
}
}
/** Get the sorted segments.
* @return sorted segments
*/
public AVLTree<ComparableSegment> getSorted() {
return sorted;
}
}
} }

View File

@ -16,25 +16,21 @@
*/ */
package org.apache.commons.math.geometry.euclidean.twod; package org.apache.commons.math.geometry.euclidean.twod;
import org.apache.commons.math.geometry.partitioning.utilities.OrderedTuple;
/** This class holds segments information before they are connected. /** Simple container for a two-points segment.
* @version $Id$ * @version $Id$
* @since 3.0 * @since 3.0
*/ */
class Segment implements Comparable<Segment> { public class Segment {
/** Start point of the segment. */ /** Start point of the segment. */
private final Vector2D start; private final Vector2D start;
/** End point of the segments. */ /** End point of the segments. */
private final Vector2D end; private final Vector2D end;
/** Line containing the segment. */ /** Line containing the segment. */
private final Line line; private final Line line;
/** Sorting key. */
private OrderedTuple sortingKey;
/** Build a segment. /** Build a segment.
* @param start start point of the segment * @param start start point of the segment
@ -45,25 +41,6 @@ class Segment implements Comparable<Segment> {
this.start = start; this.start = start;
this.end = end; this.end = end;
this.line = line; this.line = line;
sortingKey = (start == null) ?
new OrderedTuple(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY) :
new OrderedTuple(start.getX(), start.getY());
}
/** Build a dummy segment.
* <p>
* The object built is not a real segment, only the sorting key is used to
* allow searching in the neighborhood of a point. This is an horrible hack ...
* </p>
* @param start start point of the segment
* @param dx abscissa offset from the start point
* @param dy ordinate offset from the start point
*/
public Segment(final Vector2D start, final double dx, final double dy) {
this.start = null;
this.end = null;
this.line = null;
sortingKey = new OrderedTuple(start.getX() + dx, start.getY() + dy);
} }
/** Get the start point of the segment. /** Get the start point of the segment.
@ -87,27 +64,4 @@ class Segment implements Comparable<Segment> {
return line; return line;
} }
/** {@inheritDoc} */
public int compareTo(final Segment o) {
return sortingKey.compareTo(o.sortingKey);
}
/** {@inheritDoc} */
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
} else if (other instanceof Segment) {
return compareTo((Segment) other) == 0;
} else {
return false;
}
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return start.hashCode() ^ end.hashCode() ^ line.hashCode() ^ sortingKey.hashCode();
}
} }

View File

@ -1,97 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.math.geometry.euclidean.twod;
import java.util.List;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Interval;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor;
import org.apache.commons.math.geometry.partitioning.BoundaryAttribute;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.utilities.AVLTree;
/** Visitor building segments.
* @version $Id$
* @since 3.0
*/
class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
/** Sorted segments. */
private AVLTree<Segment> sorted;
/** Simple constructor. */
public SegmentsBuilder() {
sorted = new AVLTree<Segment>();
}
/** {@inheritDoc} */
public Order visitOrder(final BSPTree<Euclidean2D> node) {
return Order.MINUS_SUB_PLUS;
}
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree<Euclidean2D> node) {
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean2D> attribute = (BoundaryAttribute<Euclidean2D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false);
}
if (attribute.getPlusInside() != null) {
addContribution(attribute.getPlusInside(), true);
}
}
/** {@inheritDoc} */
public void visitLeafNode(final BSPTree<Euclidean2D> node) {
}
/** Add he contribution of a boundary facet.
* @param sub boundary facet
* @param reversed if true, the facet has the inside on its plus side
*/
private void addContribution(final SubHyperplane<Euclidean2D> sub, final boolean reversed) {
@SuppressWarnings("unchecked")
final AbstractSubHyperplane<Euclidean2D, Euclidean1D> absSub =
(AbstractSubHyperplane<Euclidean2D, Euclidean1D>) sub;
final Line line = (Line) sub.getHyperplane();
final List<Interval> intervals = ((IntervalsSet) absSub.getRemainingRegion()).asList();
for (final Interval i : intervals) {
final Vector2D start = Double.isInfinite(i.getLower()) ?
null : (Vector2D) line.toSpace(new Vector1D(i.getLower()));
final Vector2D end = Double.isInfinite(i.getUpper()) ?
null : (Vector2D) line.toSpace(new Vector1D(i.getUpper()));
if (reversed) {
sorted.insert(new Segment(end, start, line.getReverse()));
} else {
sorted.insert(new Segment(start, end, line));
}
}
}
/** Get the sorted segments.
* @return sorted segments
*/
public AVLTree<Segment> getSorted() {
return sorted;
}
}

View File

@ -56,6 +56,13 @@ public class SubLine extends AbstractSubHyperplane<Euclidean2D, Euclidean1D> {
super(new Line(start, end), buildIntervalSet(start, end)); super(new Line(start, end), buildIntervalSet(start, end));
} }
/** Create a sub-line from a segment.
* @param segment single segment forming the sub-line
*/
public SubLine(final Segment segment) {
super(segment.getLine(), buildIntervalSet(segment.getStart(), segment.getEnd()));
}
/** Get the endpoints of the sub-line. /** Get the endpoints of the sub-line.
* <p> * <p>
* A subline may be any arbitrary number of disjoints segments, so the endpoints * A subline may be any arbitrary number of disjoints segments, so the endpoints
@ -70,16 +77,16 @@ public class SubLine extends AbstractSubHyperplane<Euclidean2D, Euclidean1D> {
* </p> * </p>
* @return list of segments endpoints * @return list of segments endpoints
*/ */
public List<Vector2D[]> getSegments() { public List<Segment> getSegments() {
final Line line = (Line) getHyperplane(); final Line line = (Line) getHyperplane();
final List<Interval> list = ((IntervalsSet) getRemainingRegion()).asList(); final List<Interval> list = ((IntervalsSet) getRemainingRegion()).asList();
final List<Vector2D[]> segments = new ArrayList<Vector2D[]>(); final List<Segment> segments = new ArrayList<Segment>();
for (final Interval interval : list) { for (final Interval interval : list) {
final Vector2D start = line.toSpace(new Vector1D(interval.getLower())); final Vector2D start = line.toSpace(new Vector1D(interval.getLower()));
final Vector2D end = line.toSpace(new Vector1D(interval.getUpper())); final Vector2D end = line.toSpace(new Vector1D(interval.getUpper()));
segments.add(new Vector2D[] { start, end }); segments.add(new Segment(start, end, line));
} }
return segments; return segments;

View File

@ -28,35 +28,36 @@ public class SubLineTest {
@Test @Test
public void testEndPoints() { public void testEndPoints() {
SubLine sub = new SubLine(new Vector2D(-1, -7), new Vector2D(7, -1)); Vector2D p1 = new Vector2D(-1, -7);
List<Vector2D[]> segments = sub.getSegments(); Vector2D p2 = new Vector2D(7, -1);
Segment segment = new Segment(p1, p2, new Line(p1, p2));
SubLine sub = new SubLine(segment);
List<Segment> segments = sub.getSegments();
Assert.assertEquals(1, segments.size()); Assert.assertEquals(1, segments.size());
Assert.assertEquals(-1, segments.get(0)[0].getX(), 1.0e-10); Assert.assertEquals(0.0, new Vector2D(-1, -7).distance(segments.get(0).getStart()), 1.0e-10);
Assert.assertEquals(-7, segments.get(0)[0].getY(), 1.0e-10); Assert.assertEquals(0.0, new Vector2D( 7, -1).distance(segments.get(0).getEnd()), 1.0e-10);
Assert.assertEquals( 7, segments.get(0)[1].getX(), 1.0e-10);
Assert.assertEquals(-1, segments.get(0)[1].getY(), 1.0e-10);
} }
@Test @Test
public void testNoEndPoints() { public void testNoEndPoints() {
SubLine wholeLine = new Line(new Vector2D(-1, 7), new Vector2D(7, 1)).wholeHyperplane(); SubLine wholeLine = new Line(new Vector2D(-1, 7), new Vector2D(7, 1)).wholeHyperplane();
List<Vector2D[]> segments = wholeLine.getSegments(); List<Segment> segments = wholeLine.getSegments();
Assert.assertEquals(1, segments.size()); Assert.assertEquals(1, segments.size());
Assert.assertTrue(Double.isInfinite(segments.get(0)[0].getX()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getX()) &&
segments.get(0)[0].getX() < 0); segments.get(0).getStart().getX() < 0);
Assert.assertTrue(Double.isInfinite(segments.get(0)[0].getY()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getY()) &&
segments.get(0)[0].getY() > 0); segments.get(0).getStart().getY() > 0);
Assert.assertTrue(Double.isInfinite(segments.get(0)[1].getX()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getX()) &&
segments.get(0)[1].getX() > 0); segments.get(0).getEnd().getX() > 0);
Assert.assertTrue(Double.isInfinite(segments.get(0)[1].getY()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getY()) &&
segments.get(0)[1].getY() < 0); segments.get(0).getEnd().getY() < 0);
} }
@Test @Test
public void testNoSegments() { public void testNoSegments() {
SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)), SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)),
new RegionFactory<Euclidean1D>().getComplement(new IntervalsSet())); new RegionFactory<Euclidean1D>().getComplement(new IntervalsSet()));
List<Vector2D[]> segments = empty.getSegments(); List<Segment> segments = empty.getSegments();
Assert.assertEquals(0, segments.size()); Assert.assertEquals(0, segments.size());
} }
@ -65,7 +66,7 @@ public class SubLineTest {
SubLine twoSubs = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)), SubLine twoSubs = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)),
new RegionFactory<Euclidean1D>().union(new IntervalsSet(1, 2), new RegionFactory<Euclidean1D>().union(new IntervalsSet(1, 2),
new IntervalsSet(3, 4))); new IntervalsSet(3, 4)));
List<Vector2D[]> segments = twoSubs.getSegments(); List<Segment> segments = twoSubs.getSegments();
Assert.assertEquals(2, segments.size()); Assert.assertEquals(2, segments.size());
} }
@ -73,28 +74,26 @@ public class SubLineTest {
public void testHalfInfiniteNeg() { public void testHalfInfiniteNeg() {
SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)), SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)),
new IntervalsSet(Double.NEGATIVE_INFINITY, 0.0)); new IntervalsSet(Double.NEGATIVE_INFINITY, 0.0));
List<Vector2D[]> segments = empty.getSegments(); List<Segment> segments = empty.getSegments();
Assert.assertEquals(1, segments.size()); Assert.assertEquals(1, segments.size());
Assert.assertTrue(Double.isInfinite(segments.get(0)[0].getX()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getX()) &&
segments.get(0)[0].getX() < 0); segments.get(0).getStart().getX() < 0);
Assert.assertTrue(Double.isInfinite(segments.get(0)[0].getY()) && Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getY()) &&
segments.get(0)[0].getY() < 0); segments.get(0).getStart().getY() < 0);
Assert.assertEquals( 3, segments.get(0)[1].getX(), 1.0e-10); Assert.assertEquals(0.0, new Vector2D(3, -4).distance(segments.get(0).getEnd()), 1.0e-10);
Assert.assertEquals(-4, segments.get(0)[1].getY(), 1.0e-10);
} }
@Test @Test
public void testHalfInfinitePos() { public void testHalfInfinitePos() {
SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)), SubLine empty = new SubLine(new Line(new Vector2D(-1, -7), new Vector2D(7, -1)),
new IntervalsSet(0.0, Double.POSITIVE_INFINITY)); new IntervalsSet(0.0, Double.POSITIVE_INFINITY));
List<Vector2D[]> segments = empty.getSegments(); List<Segment> segments = empty.getSegments();
Assert.assertEquals(1, segments.size()); Assert.assertEquals(1, segments.size());
Assert.assertEquals( 3, segments.get(0)[0].getX(), 1.0e-10); Assert.assertEquals(0.0, new Vector2D(3, -4).distance(segments.get(0).getStart()), 1.0e-10);
Assert.assertEquals(-4, segments.get(0)[0].getY(), 1.0e-10); Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getX()) &&
Assert.assertTrue(Double.isInfinite(segments.get(0)[1].getX()) && segments.get(0).getEnd().getX() > 0);
segments.get(0)[1].getX() > 0); Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getY()) &&
Assert.assertTrue(Double.isInfinite(segments.get(0)[1].getY()) && segments.get(0).getEnd().getY() > 0);
segments.get(0)[1].getY() > 0);
} }
@Test @Test