commit
c48d7c40cf
|
@ -59,13 +59,18 @@ public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclide
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public SplitSubHyperplane<Euclidean1D> split(final Hyperplane<Euclidean1D> hyperplane) {
|
public SplitSubHyperplane<Euclidean1D> split(final Hyperplane<Euclidean1D> hyperplane) {
|
||||||
final double global = hyperplane.getOffset(((OrientedPoint) getHyperplane()).getLocation());
|
final OrientedPoint thisHyperplane = (OrientedPoint) getHyperplane();
|
||||||
if (global < -1.0e-10) {
|
final double global = hyperplane.getOffset(thisHyperplane.getLocation());
|
||||||
return new SplitSubHyperplane<>(null, this);
|
|
||||||
} else if (global > 1.0e-10) {
|
// use the tolerance value from our parent hyperplane to determine equality
|
||||||
return new SplitSubHyperplane<>(this, null);
|
final double tolerance = thisHyperplane.getTolerance();
|
||||||
|
|
||||||
|
if (global < -tolerance) {
|
||||||
|
return new SplitSubHyperplane<Euclidean1D>(null, this);
|
||||||
|
} else if (global > tolerance) {
|
||||||
|
return new SplitSubHyperplane<Euclidean1D>(this, null);
|
||||||
} else {
|
} else {
|
||||||
return new SplitSubHyperplane<>(null, null);
|
return new SplitSubHyperplane<Euclidean1D>(null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -644,7 +644,11 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
|
||||||
for (ConnectableSegment s = getUnprocessed(segments); s != null; s = getUnprocessed(segments)) {
|
for (ConnectableSegment s = getUnprocessed(segments); s != null; s = getUnprocessed(segments)) {
|
||||||
final List<Segment> loop = followLoop(s);
|
final List<Segment> loop = followLoop(s);
|
||||||
if (loop != null) {
|
if (loop != null) {
|
||||||
if (loop.get(0).getStart() == null) {
|
// an open loop is one that has fewer than two segments or has a null
|
||||||
|
// start point; the case where we have two segments in a closed loop
|
||||||
|
// (ie, an infinitely thin, degenerate loop) will result in null being
|
||||||
|
// returned from the followLoops method
|
||||||
|
if (loop.size() < 2 || loop.get(0).getStart() == null) {
|
||||||
// this is an open loop, we put it on the front
|
// this is an open loop, we put it on the front
|
||||||
loops.add(0, loop);
|
loops.add(0, loop);
|
||||||
} else {
|
} else {
|
||||||
|
@ -863,16 +867,20 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
|
||||||
* @param loop segments loop to filter (will be modified in-place)
|
* @param loop segments loop to filter (will be modified in-place)
|
||||||
*/
|
*/
|
||||||
private void filterSpuriousVertices(final List<Segment> loop) {
|
private void filterSpuriousVertices(final List<Segment> loop) {
|
||||||
for (int i = 0; i < loop.size(); ++i) {
|
// we need at least 2 segments in order for one of the contained vertices
|
||||||
final Segment previous = loop.get(i);
|
// to be unnecessary
|
||||||
int j = (i + 1) % loop.size();
|
if (loop.size() > 1) {
|
||||||
final Segment next = loop.get(j);
|
for (int i = 0; i < loop.size(); ++i) {
|
||||||
if (next != null &&
|
final Segment previous = loop.get(i);
|
||||||
Precision.equals(previous.getLine().getAngle(), next.getLine().getAngle(), Precision.EPSILON)) {
|
int j = (i + 1) % loop.size();
|
||||||
// the vertex between the two edges is a spurious one
|
final Segment next = loop.get(j);
|
||||||
// replace the two segments by a single one
|
if (next != null &&
|
||||||
loop.set(j, new Segment(previous.getStart(), next.getEnd(), previous.getLine()));
|
Precision.equals(previous.getLine().getAngle(), next.getLine().getAngle(), Precision.EPSILON)) {
|
||||||
loop.remove(i--);
|
// the vertex between the two edges is a spurious one
|
||||||
|
// replace the two segments by a single one
|
||||||
|
loop.set(j, new Segment(previous.getStart(), next.getEnd(), previous.getLine()));
|
||||||
|
loop.remove(i--);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1067,23 +1075,26 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
|
||||||
/** Select the node whose cut sub-hyperplane is closest to specified point.
|
/** Select the node whose cut sub-hyperplane is closest to specified point.
|
||||||
* @param point reference point
|
* @param point reference point
|
||||||
* @param candidates candidate nodes
|
* @param candidates candidate nodes
|
||||||
* @return node closest to point, or null if no node is closer than tolerance
|
* @return node closest to point, or null if point is null or no node is closer than tolerance
|
||||||
*/
|
*/
|
||||||
private BSPTree<Euclidean2D> selectClosest(final Cartesian2D point, final Iterable<BSPTree<Euclidean2D>> candidates) {
|
private BSPTree<Euclidean2D> selectClosest(final Cartesian2D point, final Iterable<BSPTree<Euclidean2D>> candidates) {
|
||||||
|
if (point != null) {
|
||||||
|
BSPTree<Euclidean2D> selected = null;
|
||||||
|
double min = Double.POSITIVE_INFINITY;
|
||||||
|
|
||||||
BSPTree<Euclidean2D> selected = null;
|
for (final BSPTree<Euclidean2D> node : candidates) {
|
||||||
double min = Double.POSITIVE_INFINITY;
|
final double distance = FastMath.abs(node.getCut().getHyperplane().getOffset(point));
|
||||||
|
if (distance < min) {
|
||||||
|
selected = node;
|
||||||
|
min = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final BSPTree<Euclidean2D> node : candidates) {
|
if (min <= tolerance) {
|
||||||
final double distance = FastMath.abs(node.getCut().getHyperplane().getOffset(point));
|
return selected;
|
||||||
if (distance < min) {
|
|
||||||
selected = node;
|
|
||||||
min = distance;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return min <= tolerance ? selected : null;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the segments.
|
/** Get the segments.
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* 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.math4.geometry.euclidean.oned;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.math4.geometry.partitioning.Side;
|
||||||
|
import org.apache.commons.math4.geometry.partitioning.SubHyperplane;
|
||||||
|
import org.apache.commons.math4.geometry.partitioning.SubHyperplane.SplitSubHyperplane;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SubOrientedPointTest {
|
||||||
|
private static final double TEST_TOLERANCE = 1e-10;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSize() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = (SubOrientedPoint) hyperplane.wholeHyperplane();
|
||||||
|
|
||||||
|
// act/assert
|
||||||
|
Assert.assertEquals(0.0, pt.getSize(), TEST_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsEmpty() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = (SubOrientedPoint) hyperplane.wholeHyperplane();
|
||||||
|
|
||||||
|
// act/assert
|
||||||
|
Assert.assertFalse(pt.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildNew() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint originalHyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = (SubOrientedPoint) originalHyperplane.wholeHyperplane();
|
||||||
|
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(2), true, TEST_TOLERANCE);
|
||||||
|
IntervalsSet intervals = new IntervalsSet(2, 3, TEST_TOLERANCE);
|
||||||
|
|
||||||
|
// act
|
||||||
|
SubHyperplane<Euclidean1D> result = pt.buildNew(hyperplane, intervals);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertTrue(result instanceof SubOrientedPoint);
|
||||||
|
Assert.assertSame(hyperplane, result.getHyperplane());
|
||||||
|
Assert.assertSame(intervals, ((SubOrientedPoint) result).getRemainingRegion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplit_resultOnMinusSide() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
IntervalsSet interval = new IntervalsSet(4, 5, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
|
||||||
|
|
||||||
|
OrientedPoint splitter = new OrientedPoint(new Cartesian1D(2), true, TEST_TOLERANCE);
|
||||||
|
|
||||||
|
// act
|
||||||
|
SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertEquals(Side.MINUS, split.getSide());
|
||||||
|
|
||||||
|
SubOrientedPoint minusSub = ((SubOrientedPoint) split.getMinus());
|
||||||
|
Assert.assertNotNull(minusSub);
|
||||||
|
|
||||||
|
OrientedPoint minusHyper = (OrientedPoint) minusSub.getHyperplane();
|
||||||
|
Assert.assertEquals(1, minusHyper.getLocation().getX(), TEST_TOLERANCE);
|
||||||
|
|
||||||
|
List<Interval> minusIntervals = ((IntervalsSet) minusSub.getRemainingRegion()).asList();
|
||||||
|
Assert.assertEquals(1, minusIntervals.size());
|
||||||
|
Assert.assertEquals(4, minusIntervals.get(0).getInf(), TEST_TOLERANCE);
|
||||||
|
Assert.assertEquals(5, minusIntervals.get(0).getSup(), TEST_TOLERANCE);
|
||||||
|
|
||||||
|
Assert.assertNull(split.getPlus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplit_resultOnPlusSide() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
IntervalsSet interval = new IntervalsSet(4, 5, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
|
||||||
|
|
||||||
|
OrientedPoint splitter = new OrientedPoint(new Cartesian1D(0), true, TEST_TOLERANCE);
|
||||||
|
|
||||||
|
// act
|
||||||
|
SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertEquals(Side.PLUS, split.getSide());
|
||||||
|
|
||||||
|
Assert.assertNull(split.getMinus());
|
||||||
|
|
||||||
|
SubOrientedPoint plusSub = ((SubOrientedPoint) split.getPlus());
|
||||||
|
Assert.assertNotNull(plusSub);
|
||||||
|
|
||||||
|
OrientedPoint plusHyper = (OrientedPoint) plusSub.getHyperplane();
|
||||||
|
Assert.assertEquals(1, plusHyper.getLocation().getX(), TEST_TOLERANCE);
|
||||||
|
|
||||||
|
List<Interval> plusIntervals = ((IntervalsSet) plusSub.getRemainingRegion()).asList();
|
||||||
|
Assert.assertEquals(1, plusIntervals.size());
|
||||||
|
Assert.assertEquals(4, plusIntervals.get(0).getInf(), TEST_TOLERANCE);
|
||||||
|
Assert.assertEquals(5, plusIntervals.get(0).getSup(), TEST_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplit_equivalentHyperplanes() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
IntervalsSet interval = new IntervalsSet(4, 5, TEST_TOLERANCE);
|
||||||
|
SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
|
||||||
|
|
||||||
|
OrientedPoint splitter = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
|
||||||
|
|
||||||
|
// act
|
||||||
|
SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertEquals(Side.HYPER, split.getSide());
|
||||||
|
|
||||||
|
Assert.assertNull(split.getMinus());
|
||||||
|
Assert.assertNull(split.getPlus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplit_usesToleranceFromParentHyperplane() {
|
||||||
|
// arrange
|
||||||
|
OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, 0.1);
|
||||||
|
SubOrientedPoint pt = (SubOrientedPoint) hyperplane.wholeHyperplane();
|
||||||
|
|
||||||
|
// act/assert
|
||||||
|
SplitSubHyperplane<Euclidean1D> plusSplit = pt.split(new OrientedPoint(new Cartesian1D(0.899), true, 1e-10));
|
||||||
|
Assert.assertNull(plusSplit.getMinus());
|
||||||
|
Assert.assertNotNull(plusSplit.getPlus());
|
||||||
|
|
||||||
|
SplitSubHyperplane<Euclidean1D> lowWithinTolerance = pt.split(new OrientedPoint(new Cartesian1D(0.901), true, 1e-10));
|
||||||
|
Assert.assertNull(lowWithinTolerance.getMinus());
|
||||||
|
Assert.assertNull(lowWithinTolerance.getPlus());
|
||||||
|
|
||||||
|
SplitSubHyperplane<Euclidean1D> highWithinTolerance = pt.split(new OrientedPoint(new Cartesian1D(1.09), true, 1e-10));
|
||||||
|
Assert.assertNull(highWithinTolerance.getMinus());
|
||||||
|
Assert.assertNull(highWithinTolerance.getPlus());
|
||||||
|
|
||||||
|
SplitSubHyperplane<Euclidean1D> minusSplit = pt.split(new OrientedPoint(new Cartesian1D(1.101), true, 1e-10));
|
||||||
|
Assert.assertNotNull(minusSplit.getMinus());
|
||||||
|
Assert.assertNull(minusSplit.getPlus());
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,6 +96,61 @@ public class PolygonsSetTest {
|
||||||
Assert.assertTrue(Double.isInfinite(box.getSize()));
|
Assert.assertTrue(Double.isInfinite(box.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleInfiniteLine() {
|
||||||
|
// arrange
|
||||||
|
double tolerance = 1e-10;
|
||||||
|
Line line = new Line(new Cartesian2D(0, 0), new Cartesian2D(1, 1), tolerance);
|
||||||
|
|
||||||
|
List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
|
||||||
|
boundaries.add(line.wholeHyperplane());
|
||||||
|
|
||||||
|
// act
|
||||||
|
PolygonsSet polygon = new PolygonsSet(boundaries, tolerance);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertTrue(Double.isInfinite(polygon.getSize()));
|
||||||
|
|
||||||
|
Cartesian2D[][] vertices = polygon.getVertices();
|
||||||
|
Assert.assertEquals(1, vertices.length);
|
||||||
|
|
||||||
|
Cartesian2D[] loop = vertices[0];
|
||||||
|
Assert.assertEquals(3, loop.length);
|
||||||
|
Assert.assertEquals(null, loop[0]);
|
||||||
|
checkPointsEqual(line.toSpace(new Cartesian1D(-Float.MAX_VALUE)), loop[1], tolerance);
|
||||||
|
checkPointsEqual(line.toSpace(new Cartesian1D(Float.MAX_VALUE)), loop[2], tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMixOfFiniteAndInfiniteBoundaries() {
|
||||||
|
// arrange
|
||||||
|
double tolerance = 1e-10;
|
||||||
|
|
||||||
|
Line line = new Line(new Cartesian2D(1, 0), new Cartesian2D(1, 1), tolerance);
|
||||||
|
|
||||||
|
List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
|
||||||
|
boundaries.add(buildSegment(new Cartesian2D(0, 1), new Cartesian2D(0, 0)));
|
||||||
|
boundaries.add(buildSegment(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
|
||||||
|
boundaries.add(new SubLine(line, new IntervalsSet(0, Double.POSITIVE_INFINITY, tolerance)));
|
||||||
|
|
||||||
|
// act
|
||||||
|
PolygonsSet polygon = new PolygonsSet(boundaries, tolerance);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.assertTrue(Double.isInfinite(polygon.getSize()));
|
||||||
|
|
||||||
|
Cartesian2D[][] vertices = polygon.getVertices();
|
||||||
|
Assert.assertEquals(1, vertices.length);
|
||||||
|
|
||||||
|
Cartesian2D[] loop = vertices[0];
|
||||||
|
Assert.assertEquals(5, loop.length);
|
||||||
|
Assert.assertEquals(null, loop[0]);
|
||||||
|
checkPointsEqual(new Cartesian2D(0, 1), loop[1], tolerance);
|
||||||
|
checkPointsEqual(new Cartesian2D(0, 0), loop[2], tolerance);
|
||||||
|
checkPointsEqual(new Cartesian2D(1, 0), loop[3], tolerance);
|
||||||
|
checkPointsEqual(new Cartesian2D(1, 0), loop[4], tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStair() {
|
public void testStair() {
|
||||||
Cartesian2D[][] vertices = new Cartesian2D[][] {
|
Cartesian2D[][] vertices = new Cartesian2D[][] {
|
||||||
|
@ -1273,6 +1328,11 @@ public class PolygonsSetTest {
|
||||||
return new SubLine(line, new IntervalsSet(lower, upper, 1.0e-10));
|
return new SubLine(line, new IntervalsSet(lower, upper, 1.0e-10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkPointsEqual(Cartesian2D expected, Cartesian2D actual, double tolerance) {
|
||||||
|
Assert.assertEquals(expected.getX(), actual.getX(), tolerance);
|
||||||
|
Assert.assertEquals(expected.getY(), actual.getY(), tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkPoints(Region.Location expected, PolygonsSet set,
|
private void checkPoints(Region.Location expected, PolygonsSet set,
|
||||||
Cartesian2D[] points) {
|
Cartesian2D[] points) {
|
||||||
for (int i = 0; i < points.length; ++i) {
|
for (int i = 0; i < points.length; ++i) {
|
||||||
|
|
Loading…
Reference in New Issue