use the reorganized Binary Space Partitioning framework for Euclidean spaces 1D, 2D and 3D.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1131127 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2011-06-03 18:08:12 +00:00
parent 6a14866cfc
commit 540bf1525f
20 changed files with 1411 additions and 1320 deletions

View File

@ -19,7 +19,8 @@ package org.apache.commons.math.geometry.euclidean.oned;
/** This class represents a 1D interval.
* @see IntervalsSet
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class Interval {

View File

@ -20,14 +20,16 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.math.geometry.partitioning.AbstractRegion;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
/** This class represents a 1D region: a set of intervals.
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class IntervalsSet extends Region {
public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> {
/** Build an intervals set representing the whole real line.
*/
@ -54,7 +56,7 @@ public class IntervalsSet extends Region {
* {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
* @param tree inside/outside BSP tree representing the intervals set
*/
public IntervalsSet(final BSPTree tree) {
public IntervalsSet(final BSPTree<Euclidean1D> tree) {
super(tree);
}
@ -77,7 +79,7 @@ public class IntervalsSet extends Region {
* space.</p>
* @param boundary collection of boundary elements
*/
public IntervalsSet(final Collection<SubHyperplane> boundary) {
public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary) {
super(boundary);
}
@ -88,52 +90,52 @@ public class IntervalsSet extends Region {
* to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
* @return the built tree
*/
private static BSPTree buildTree(final double lower, final double upper) {
private static BSPTree<Euclidean1D> buildTree(final double lower, final double upper) {
if (Double.isInfinite(lower) && (lower < 0)) {
if (Double.isInfinite(upper) && (upper > 0)) {
// the tree must cover the whole real line
return new BSPTree(Boolean.TRUE);
return new BSPTree<Euclidean1D>(Boolean.TRUE);
}
// the tree must be open on the negative infinity side
final SubHyperplane upperCut =
new SubHyperplane(new OrientedPoint(new Point1D(upper), true));
return new BSPTree(upperCut,
new BSPTree(Boolean.FALSE),
new BSPTree(Boolean.TRUE),
final SubHyperplane<Euclidean1D> upperCut =
new OrientedPoint(new Vector1D(upper), true).wholeHyperplane();
return new BSPTree<Euclidean1D>(upperCut,
new BSPTree<Euclidean1D>(Boolean.FALSE),
new BSPTree<Euclidean1D>(Boolean.TRUE),
null);
}
final SubHyperplane lowerCut =
new SubHyperplane(new OrientedPoint(new Point1D(lower), false));
final SubHyperplane<Euclidean1D> lowerCut =
new OrientedPoint(new Vector1D(lower), false).wholeHyperplane();
if (Double.isInfinite(upper) && (upper > 0)) {
// the tree must be open on the positive infinity side
return new BSPTree(lowerCut,
new BSPTree(Boolean.FALSE),
new BSPTree(Boolean.TRUE),
return new BSPTree<Euclidean1D>(lowerCut,
new BSPTree<Euclidean1D>(Boolean.FALSE),
new BSPTree<Euclidean1D>(Boolean.TRUE),
null);
}
// the tree must be bounded on the two sides
final SubHyperplane upperCut =
new SubHyperplane(new OrientedPoint(new Point1D(upper), true));
return new BSPTree(lowerCut,
new BSPTree(Boolean.FALSE),
new BSPTree(upperCut,
new BSPTree(Boolean.FALSE),
new BSPTree(Boolean.TRUE),
final SubHyperplane<Euclidean1D> upperCut =
new OrientedPoint(new Vector1D(upper), true).wholeHyperplane();
return new BSPTree<Euclidean1D>(lowerCut,
new BSPTree<Euclidean1D>(Boolean.FALSE),
new BSPTree<Euclidean1D>(upperCut,
new BSPTree<Euclidean1D>(Boolean.FALSE),
new BSPTree<Euclidean1D>(Boolean.TRUE),
null),
null);
}
/** {@inheritDoc} */
public Region buildNew(final BSPTree tree) {
public IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) {
return new IntervalsSet(tree);
}
/** {@inheritDoc} */
protected void computeGeometricalProperties() {
if (getTree(false).getCut() == null) {
setBarycenter(Point1D.UNDEFINED);
setBarycenter(Vector1D.NaN);
setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0);
} else {
double size = 0.0;
@ -143,7 +145,7 @@ public class IntervalsSet extends Region {
sum += interval.getLength() * interval.getMidPoint();
}
setSize(size);
setBarycenter(Double.isInfinite(size) ? Point1D.UNDEFINED : new Point1D(sum / size));
setBarycenter(Double.isInfinite(size) ? Vector1D.NaN : new Vector1D(sum / size));
}
}
@ -154,11 +156,11 @@ public class IntervalsSet extends Region {
* instance is empty)
*/
public double getInf() {
BSPTree node = getTree(false);
BSPTree<Euclidean1D> node = getTree(false);
double inf = Double.POSITIVE_INFINITY;
while (node.getCut() != null) {
final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
inf = op.getLocation().getAbscissa();
inf = op.getLocation().getX();
node = op.isDirect() ? node.getMinus() : node.getPlus();
}
return ((Boolean) node.getAttribute()) ? Double.NEGATIVE_INFINITY : inf;
@ -171,11 +173,11 @@ public class IntervalsSet extends Region {
* instance is empty)
*/
public double getSup() {
BSPTree node = getTree(false);
BSPTree<Euclidean1D> node = getTree(false);
double sup = Double.NEGATIVE_INFINITY;
while (node.getCut() != null) {
final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
sup = op.getLocation().getAbscissa();
sup = op.getLocation().getX();
node = op.isDirect() ? node.getPlus() : node.getMinus();
}
return ((Boolean) node.getAttribute()) ? Double.POSITIVE_INFINITY : sup;
@ -207,7 +209,8 @@ public class IntervalsSet extends Region {
* @param lower lower bound of the current convex cell
* @param upper upper bound of the current convex cell
*/
private void recurseList(final BSPTree node, final List<Interval> list,
private void recurseList(final BSPTree<Euclidean1D> node,
final List<Interval> list,
final double lower, final double upper) {
if (node.getCut() == null) {
@ -217,12 +220,14 @@ public class IntervalsSet extends Region {
}
} else {
final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
final Point1D loc = op.getLocation();
double x = loc.getAbscissa();
final Vector1D loc = op.getLocation();
double x = loc.getX();
// make sure we explore the tree in increasing order
final BSPTree low = op.isDirect() ? node.getMinus() : node.getPlus();
final BSPTree high = op.isDirect() ? node.getPlus() : node.getMinus();
final BSPTree<Euclidean1D> low =
op.isDirect() ? node.getMinus() : node.getPlus();
final BSPTree<Euclidean1D> high =
op.isDirect() ? node.getPlus() : node.getMinus();
recurseList(low, list, lower, x);
if ((checkPoint(low, loc) == Location.INSIDE) &&

View File

@ -16,28 +16,22 @@
*/
package org.apache.commons.math.geometry.euclidean.oned;
import org.apache.commons.math.exception.MathUnsupportedOperationException;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.SubSpace;
/** This class represents a 1D oriented hyperplane.
* <p>An hyperplane in 1D is a simple point, its orientation being a
* boolean.</p>
* <p>Instances of this class are guaranteed to be immutable.</p>
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class OrientedPoint implements Hyperplane {
public class OrientedPoint implements Hyperplane<Euclidean1D> {
/** Dummy region returned by the {@link #wholeHyperplane} method. */
private static final Region DUMMY_REGION = new DummyRegion();
/** Point location. */
private Point1D location;
/** Vector location. */
private Vector1D location;
/** Orientation. */
private boolean direct;
@ -45,9 +39,9 @@ public class OrientedPoint implements Hyperplane {
/** Simple constructor.
* @param location location of the hyperplane
* @param direct if true, the plus side of the hyperplane is towards
* abscissae greater than {@code location}
* abscissas greater than {@code location}
*/
public OrientedPoint(final Point1D location, final boolean direct) {
public OrientedPoint(final Vector1D location, final boolean direct) {
this.location = location;
this.direct = direct;
}
@ -57,62 +51,16 @@ public class OrientedPoint implements Hyperplane {
* the instance.</p>
* @return the instance itself
*/
public Hyperplane copySelf() {
public OrientedPoint copySelf() {
return this;
}
/** Get the offset (oriented distance) of a point to the hyperplane.
* @param point point to check
* @return offset of the point
*/
public double getOffset(final Point point) {
final double delta = ((Point1D) point).getAbscissa() - location.getAbscissa();
/** {@inheritDoc} */
public double getOffset(final Vector<Euclidean1D> point) {
final double delta = ((Vector1D) point).getX() - location.getX();
return direct ? delta : -delta;
}
/** Transform a space point into a sub-space point.
* <p>Since this class represent zero dimension spaces which does
* not have lower dimension sub-spaces, this method cannot be
* supported here. It always throws a {@code RuntimeException}
* when called.</p>
* @param point n-dimension point of the space
* @return (n-1)-dimension point of the sub-space corresponding to
* the specified space point
* @see #toSpace
*/
public Point toSubSpace(final Point point) {
throw new MathUnsupportedOperationException(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1);
}
/** Transform a sub-space point into a space point.
* <p>Since this class represent zero dimension spaces which does
* not have lower dimension sub-spaces, this method cannot be
* supported here. It always throws a {@code RuntimeException}
* when called.</p>
* @param point (n-1)-dimension point of the sub-space
* @return n-dimension point of the space corresponding to the
* specified sub-space point
* @see #toSubSpace
*/
public Point toSpace(final Point point) {
throw new MathUnsupportedOperationException(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1);
}
/** Build the sub-space shared by the instance and another hyperplane.
* <p>Since this class represent zero dimension spaces which does
* not have lower dimension sub-spaces, this method cannot be
* supported here. It always throws a {@code RuntimeException}
* when called.</p>
* @param other other sub-space (must have the same dimension as the
* instance)
* @return a sub-space at the intersection of the instance and the
* other sub-space (it has a dimension one unit less than the
* instance)
*/
public SubSpace intersection(final Hyperplane other) {
throw new MathUnsupportedOperationException(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1);
}
/** Build a region covering the whole hyperplane.
* <p>Since this class represent zero dimension spaces which does
* not have lower dimension sub-spaces, this method returns a dummy
@ -122,15 +70,15 @@ public class OrientedPoint implements Hyperplane {
* properly, it should <em>not</em> be used otherwise.</p>
* @return a dummy region
*/
public Region wholeHyperplane() {
return DUMMY_REGION;
public SubOrientedPoint wholeHyperplane() {
return new SubOrientedPoint(this, null);
}
/** Build a region covering the whole space.
* @return a region containing the instance (really an {@link
* IntervalsSet IntervalsSet} instance)
*/
public Region wholeSpace() {
public IntervalsSet wholeSpace() {
return new IntervalsSet();
}
@ -147,40 +95,14 @@ public class OrientedPoint implements Hyperplane {
* @return true if the instance and the other hyperplane have
* the same orientation
*/
public boolean sameOrientationAs(final Hyperplane other) {
public boolean sameOrientationAs(final Hyperplane<Euclidean1D> other) {
return !(direct ^ ((OrientedPoint) other).direct);
}
/** Compute the relative position of a sub-hyperplane with respect
* to the instance.
* @param sub sub-hyperplane to check
* @return one of {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#PLUS PLUS},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#MINUS MINUS}
* or {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#HYPER HYPER}
* (in dimension 1, this method <em>never</em> returns {@link
* org.apache.commons.math.geometry.partitioning.Hyperplane.Side#BOTH BOTH})
*
*/
public Side side(final SubHyperplane sub) {
final double global = getOffset(((OrientedPoint) sub.getHyperplane()).location);
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
/** Split a sub-hyperplane in two parts by the instance.
* @param sub sub-hyperplane to split
* @return an object containing both the part of the sub-hyperplane
* on the plus side of the instance and the part of the
* sub-hyperplane on the minus side of the instance
*/
public SplitSubHyperplane split(final SubHyperplane sub) {
final double global = getOffset(((OrientedPoint) sub.getHyperplane()).location);
return (global < -1.0e-10) ? new SplitSubHyperplane(null, sub) : new SplitSubHyperplane(sub, null);
}
/** Get the hyperplane location on the real line.
* @return the hyperplane location
*/
public Point1D getLocation() {
public Vector1D getLocation() {
return location;
}
@ -198,25 +120,4 @@ public class OrientedPoint implements Hyperplane {
direct = !direct;
}
/** Dummy region representing the whole set of reals. */
private static class DummyRegion extends Region {
/** Simple constructor.
*/
public DummyRegion() {
super();
}
/** {@inheritDoc} */
public Region buildNew(final BSPTree tree) {
return this;
}
/** {@inheritDoc} */
protected void computeGeometricalProperties() {
setSize(0);
setBarycenter(Point1D.ZERO);
}
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.oned;
import org.apache.commons.math.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.Side;
/** This class represents sub-hyperplane for {@link OrOrientedPoint}.
* <p>An hyperplane in 1D is a simple point, its orientation being a
* boolean.</p>
* <p>Instances of this class are guaranteed to be immutable.</p>
* @version $Id:$
* @since 3.0
*/
public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclidean1D> {
/** Simple constructor.
* @param hyperplane underlying hyperplane
* @param remainingRegion remaining region of the hyperplane
*/
public SubOrientedPoint(final Hyperplane<Euclidean1D> hyperplane,
final Region<Euclidean1D> remainingRegion) {
super(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
public double getSize() {
return 0;
}
/** {@inheritDoc} */
protected AbstractSubHyperplane<Euclidean1D, Euclidean1D> buildNew(final Hyperplane<Euclidean1D> hyperplane,
final Region<Euclidean1D> remainingRegion) {
return new SubOrientedPoint(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
public Side side(final Hyperplane<Euclidean1D> hyperplane) {
final double global = hyperplane.getOffset(((OrientedPoint) getHyperplane()).getLocation());
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
/** {@inheritDoc} */
public SplitSubHyperplane<Euclidean1D> split(final Hyperplane<Euclidean1D> hyperplane) {
final double global = hyperplane.getOffset(((OrientedPoint) getHyperplane()).getLocation());
return (global < -1.0e-10) ?
new SplitSubHyperplane<Euclidean1D>(null, this) :
new SplitSubHyperplane<Euclidean1D>(this, null);
}
}

View File

@ -16,9 +16,10 @@
*/
package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.SubSpace;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.util.FastMath;
/** The class represent lines in a three dimensional space.
@ -30,15 +31,16 @@ import org.apache.commons.math.util.FastMath;
* which is closest to the origin. Abscissa increases in the line
* direction.</p>
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class Line implements SubSpace {
public class Line implements Embedding<Euclidean3D, Euclidean1D> {
/** Line direction. */
private Vector3D direction;
/** Line point closest to the origin. */
private Point3D zero;
private Vector3D zero;
/** Build a line from a point and a direction.
* @param p point belonging to the line (this can be any point)
@ -60,13 +62,14 @@ public class Line implements SubSpace {
throw new IllegalArgumentException("null norm");
}
this.direction = new Vector3D(1.0 / norm, dir);
zero = new Point3D(1.0, p, -Vector3D.dotProduct(p, this.direction), this.direction);
zero = new Vector3D(1.0, p, -Vector3D.dotProduct(p, this.direction), this.direction);
}
/** Revert the line direction.
/** Get a line with reversed direction.
* @return a new instance, with reversed direction
*/
public void revertSelf() {
direction = direction.negate();
public Line revert() {
return new Line(zero, direction.negate());
}
/** Get the normalized direction vector.
@ -90,21 +93,22 @@ public class Line implements SubSpace {
* @param point point to check (must be a {@link Vector3D Vector3D}
* instance)
* @return abscissa of the point (really a
* {org.apache.commons.math.geometry.euclidean.oned.Point1D Point1D} instance)
* {org.apache.commons.math.geometry.euclidean.oned.Vector1D Vector1D} instance)
*/
public Point toSubSpace(final Point point) {
final double x = Vector3D.dotProduct(((Vector3D) point).subtract(zero), direction);
return new Point1D(x);
public Vector1D toSubSpace(final Vector<Euclidean3D> point) {
Vector3D p3 = (Vector3D) point;
return new Vector1D(Vector3D.dotProduct(p3.subtract(zero), direction));
}
/** Get one point from the line.
* @param point desired abscissa for the point (must be a
* {org.apache.commons.math.geometry.euclidean.oned.Point1D Point1D} instance)
* {org.apache.commons.math.geometry.euclidean.oned.Vector1D Vector1D} instance)
* @return one point belonging to the line, at specified abscissa
* (really a {@link Vector3D Vector3D} instance)
*/
public Point toSpace(final Point point) {
return new Point3D(1.0, zero, ((Point1D) point).getAbscissa(), direction);
public Vector3D toSpace(final Vector<Euclidean1D> point) {
Vector1D p1 = (Vector1D) point;
return new Vector3D(1.0, zero, p1.getX(), direction);
}
/** Check if the instance is similar to another line.

View File

@ -16,20 +16,24 @@
*/
package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.twod.Point2D;
import java.util.ArrayList;
import org.apache.commons.math.geometry.euclidean.twod.Euclidean2D;
import org.apache.commons.math.geometry.euclidean.twod.PolygonsSet;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
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.Region;
import org.apache.commons.math.geometry.partitioning.BoundaryAttribute;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.util.FastMath;
import java.util.ArrayList;
/** Extractor for {@link PolygonsSet polyhedrons sets} outlines.
* <p>This class extracts the 2D outlines from {{@link PolygonsSet
* polyhedrons sets} in a specified projection plane.</p>
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class OutlineExtractor {
@ -56,7 +60,7 @@ public class OutlineExtractor {
* @param polyhedronsSet polyhedrons set whose outline must be extracted
* @return an outline, as an array of loops.
*/
public Point2D[][] getOutline(final PolyhedronsSet polyhedronsSet) {
public Vector2D[][] getOutline(final PolyhedronsSet polyhedronsSet) {
// project all boundary facets into one polygons set
final BoundaryProjector projector = new BoundaryProjector();
@ -64,9 +68,9 @@ public class OutlineExtractor {
final PolygonsSet projected = projector.getProjected();
// Remove the spurious intermediate vertices from the outline
final Point2D[][] outline = projected.getVertices();
final Vector2D[][] outline = projected.getVertices();
for (int i = 0; i < outline.length; ++i) {
final Point2D[] rawLoop = outline[i];
final Vector2D[] rawLoop = outline[i];
int end = rawLoop.length;
int j = 0;
while (j < end) {
@ -83,7 +87,7 @@ public class OutlineExtractor {
}
if (end != rawLoop.length) {
// resize the array
outline[i] = new Point2D[end];
outline[i] = new Vector2D[end];
System.arraycopy(rawLoop, 0, outline[i], 0, end);
}
}
@ -100,14 +104,14 @@ public class OutlineExtractor {
* @param i index of the point to check (must be between 0 and n-1)
* @return true if the point is exactly between its neighbours
*/
private boolean pointIsBetween(final Point2D[] loop, final int n, final int i) {
final Point2D previous = loop[(i + n - 1) % n];
final Point2D current = loop[i];
final Point2D next = loop[(i + 1) % n];
final double dx1 = current.x - previous.x;
final double dy1 = current.y - previous.y;
final double dx2 = next.x - current.x;
final double dy2 = next.y - current.y;
private boolean pointIsBetween(final Vector2D[] loop, final int n, final int i) {
final Vector2D previous = loop[(i + n - 1) % n];
final Vector2D current = loop[i];
final Vector2D next = loop[(i + 1) % n];
final double dx1 = current.getX() - previous.getX();
final double dy1 = current.getY() - previous.getY();
final double dx2 = next.getX() - current.getX();
final double dy2 = next.getY() - current.getY();
final double cross = dx1 * dy2 - dx2 * dy1;
final double dot = dx1 * dx2 + dy1 * dy2;
final double d1d2 = FastMath.sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2));
@ -115,7 +119,7 @@ public class OutlineExtractor {
}
/** Visitor projecting the boundary facets on a plane. */
private class BoundaryProjector implements BSPTreeVisitor {
private class BoundaryProjector implements BSPTreeVisitor<Euclidean3D> {
/** Projection of the polyhedrons set on the plane. */
private PolygonsSet projected;
@ -123,18 +127,19 @@ public class OutlineExtractor {
/** Simple constructor.
*/
public BoundaryProjector() {
projected = new PolygonsSet(new BSPTree(Boolean.FALSE));
projected = new PolygonsSet(new BSPTree<Euclidean2D>(Boolean.FALSE));
}
/** {@inheritDoc} */
public Order visitOrder(final BSPTree node) {
public Order visitOrder(final BSPTree<Euclidean3D> node) {
return Order.MINUS_SUB_PLUS;
}
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) {
final Region.BoundaryAttribute attribute =
(Region.BoundaryAttribute) node.getAttribute();
public void visitInternalNode(final BSPTree<Euclidean3D> node) {
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false);
}
@ -144,19 +149,22 @@ public class OutlineExtractor {
}
/** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) {
public void visitLeafNode(final BSPTree<Euclidean3D> node) {
}
/** Add he contribution of a boundary facet.
* @param facet boundary facet
* @param reversed if true, the facet has the inside on its plus side
*/
private void addContribution(final SubHyperplane facet, final boolean reversed) {
private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
// extract the vertices of the facet
@SuppressWarnings("unchecked")
final AbstractSubHyperplane<Euclidean3D, Euclidean2D> absFacet =
(AbstractSubHyperplane<Euclidean3D, Euclidean2D>) facet;
final Plane plane = (Plane) facet.getHyperplane();
Point2D[][] vertices =
((PolygonsSet) facet.getRemainingRegion()).getVertices();
Vector2D[][] vertices =
((PolygonsSet) absFacet.getRemainingRegion()).getVertices();
final double scal = Vector3D.dotProduct(plane.getNormal(), w);
if (FastMath.abs(scal) > 1.0e-3) {
@ -164,10 +172,10 @@ public class OutlineExtractor {
if ((scal < 0) ^ reversed) {
// the facet is seen from the inside,
// we need to invert its boundary orientation
final Point2D[][] newVertices = new Point2D[vertices.length][];
final Vector2D[][] newVertices = new Vector2D[vertices.length][];
for (int i = 0; i < vertices.length; ++i) {
final Point2D[] loop = vertices[i];
final Point2D[] newLoop = new Point2D[loop.length];
final Vector2D[] loop = vertices[i];
final Vector2D[] newLoop = new Vector2D[loop.length];
if (loop[0] == null) {
newLoop[0] = null;
for (int j = 1; j < loop.length; ++j) {
@ -187,22 +195,22 @@ public class OutlineExtractor {
}
// compute the projection of the facet in the outline plane
final ArrayList<SubHyperplane> edges = new ArrayList<SubHyperplane>();
for (Point2D[] loop : vertices) {
final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
for (Vector2D[] loop : vertices) {
final boolean closed = loop[0] != null;
int previous = closed ? (loop.length - 1) : 1;
Vector3D previous3D = (Vector3D) plane.toSpace(loop[previous]);
int current = (previous + 1) % loop.length;
Point2D pPoint = new Point2D(Vector3D.dotProduct(previous3D, u),
Vector2D pPoint = new Vector2D(Vector3D.dotProduct(previous3D, u),
Vector3D.dotProduct(previous3D, v));
while (current < loop.length) {
final Vector3D current3D = (Vector3D) plane.toSpace(loop[current]);
final Point2D cPoint = new Point2D(Vector3D.dotProduct(current3D, u),
final Vector2D cPoint = new Vector2D(Vector3D.dotProduct(current3D, u),
Vector3D.dotProduct(current3D, v));
final org.apache.commons.math.geometry.euclidean.twod.Line line =
new org.apache.commons.math.geometry.euclidean.twod.Line(pPoint, cPoint);
SubHyperplane edge = new SubHyperplane(line);
SubHyperplane<Euclidean2D> edge = line.wholeHyperplane();
if (closed || (previous != 1)) {
// the previous point is a real vertex
@ -210,7 +218,7 @@ public class OutlineExtractor {
final double angle = line.getAngle() + 0.5 * FastMath.PI;
final org.apache.commons.math.geometry.euclidean.twod.Line l =
new org.apache.commons.math.geometry.euclidean.twod.Line(pPoint, angle);
edge = l.split(edge).getPlus();
edge = edge.split(l).getPlus();
}
if (closed || (current != (loop.length - 1))) {
@ -219,7 +227,7 @@ public class OutlineExtractor {
final double angle = line.getAngle() + 0.5 * FastMath.PI;
final org.apache.commons.math.geometry.euclidean.twod.Line l =
new org.apache.commons.math.geometry.euclidean.twod.Line(cPoint, angle);
edge = l.split(edge).getMinus();
edge = edge.split(l).getMinus();
}
edges.add(edge);
@ -233,7 +241,7 @@ public class OutlineExtractor {
final PolygonsSet projectedFacet = new PolygonsSet(edges);
// add the contribution of the facet to the global outline
projected = (PolygonsSet) Region.union(projected, projectedFacet);
projected = (PolygonsSet) new RegionFactory<Euclidean2D>().union(projected, projectedFacet);
}
}

View File

@ -16,27 +16,26 @@
*/
package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.euclidean.twod.Point2D;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.euclidean.twod.Euclidean2D;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math.geometry.euclidean.twod.PolygonsSet;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.SubSpace;
import org.apache.commons.math.util.FastMath;
/** The class represent planes in a three dimensional space.
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class Plane implements Hyperplane {
public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Euclidean2D> {
/** Offset of the origin with respect to the plane. */
private double originOffset;
/** Origin of the plane frame. */
private Point3D origin;
private Vector3D origin;
/** First vector of the plane frame (in plane). */
private Vector3D u;
@ -100,7 +99,7 @@ public class Plane implements Hyperplane {
* shared (except for immutable objects).</p>
* @return a new hyperplane, copy of the instance
*/
public Hyperplane copySelf() {
public Plane copySelf() {
return new Plane(this);
}
@ -143,7 +142,7 @@ public class Plane implements Hyperplane {
/** Reset the plane frame.
*/
private void setFrame() {
origin = new Point3D(-originOffset, w);
origin = new Vector3D(-originOffset, w);
u = w.orthogonal();
v = Vector3D.crossProduct(w, u);
}
@ -154,7 +153,7 @@ public class Plane implements Hyperplane {
* @return the origin point of the plane frame (point closest to the
* 3D-space origin)
*/
public Point3D getOrigin() {
public Vector3D getOrigin() {
return origin;
}
@ -217,24 +216,24 @@ public class Plane implements Hyperplane {
* @param point point of the space (must be a {@link Vector3D
* Vector3D} instance)
* @return in-plane point (really a {@link
* org.apache.commons.math.geometry.euclidean.twod.Point2D Point2D} instance)
* org.apache.commons.math.geometry.euclidean.twod.Vector2D Vector2D} instance)
* @see #toSpace
*/
public Point toSubSpace(final Point point) {
public Vector2D toSubSpace(final Vector<Euclidean3D> point) {
final Vector3D p3D = (Vector3D) point;
return new Point2D(Vector3D.dotProduct(p3D, u),
return new Vector2D(Vector3D.dotProduct(p3D, u),
Vector3D.dotProduct(p3D, v));
}
/** Transform an in-plane point into a 3D space point.
* @param point in-plane point (must be a {@link
* org.apache.commons.math.geometry.euclidean.twod.Point2D Point2D} instance)
* org.apache.commons.math.geometry.euclidean.twod.Vector2D Vector2D} instance)
* @return 3D space point (really a {@link Vector3D Vector3D} instance)
* @see #toSubSpace
*/
public Point toSpace(final Point point) {
final Point2D p2D = (Point2D) point;
return new Point3D(p2D.x, u, p2D.y, v, -originOffset, w);
public Vector3D toSpace(final Vector<Euclidean2D> point) {
final Vector2D p2D = (Vector2D) point;
return new Vector3D(p2D.getX(), u, p2D.getY(), v, -originOffset, w);
}
/** Get one point from the 3D-space.
@ -244,8 +243,8 @@ public class Plane implements Hyperplane {
* @return one point in the 3D-space, with given coordinates and offset
* relative to the plane
*/
public Vector3D getPointAt(final Point2D inPlane, final double offset) {
return new Vector3D(inPlane.x, u, inPlane.y, v, offset - originOffset, w);
public Vector3D getPointAt(final Vector2D inPlane, final double offset) {
return new Vector3D(inPlane.getX(), u, inPlane.getY(), v, offset - originOffset, w);
}
/** Check if the instance is similar to another plane.
@ -303,15 +302,15 @@ public class Plane implements Hyperplane {
* @return intersection point between between the line and the
* instance (null if the line is parallel to the instance)
*/
public Point3D intersection(final Line line) {
public Vector3D intersection(final Line line) {
final Vector3D direction = line.getDirection();
final double dot = Vector3D.dotProduct(w, direction);
if (FastMath.abs(dot) < 1.0e-10) {
return null;
}
final Vector3D point = (Vector3D) line.toSpace(Point1D.ZERO);
final Vector3D point = (Vector3D) line.toSpace(Vector1D.ZERO);
final double k = -(originOffset + Vector3D.dotProduct(w, point)) / dot;
return new Point3D(1.0, point, k, direction);
return new Vector3D(1.0, point, k, direction);
}
/** Build the line shared by the instance and another plane.
@ -319,13 +318,12 @@ public class Plane implements Hyperplane {
* @return line at the intersection of the instance and the
* other plane (really a {@link Line Line} instance)
*/
public SubSpace intersection(final Hyperplane other) {
final Plane otherP = (Plane) other;
final Vector3D direction = Vector3D.crossProduct(w, otherP.w);
public Line intersection(final Plane other) {
final Vector3D direction = Vector3D.crossProduct(w, other.w);
if (direction.getNorm() < 1.0e-10) {
return null;
}
return new Line(intersection(this, otherP, new Plane(direction)),
return new Line(intersection(this, other, new Plane(direction)),
direction);
}
@ -374,15 +372,15 @@ public class Plane implements Hyperplane {
/** Build a region covering the whole hyperplane.
* @return a region covering the whole hyperplane
*/
public Region wholeHyperplane() {
return new PolygonsSet();
public SubPlane wholeHyperplane() {
return new SubPlane(this, new PolygonsSet());
}
/** Build a region covering the whole space.
* @return a region containing the instance (really a {@link
* PolyhedronsSet PolyhedronsSet} instance)
*/
public Region wholeSpace() {
public PolyhedronsSet wholeSpace() {
return new PolyhedronsSet();
}
@ -390,7 +388,7 @@ public class Plane implements Hyperplane {
* @param p point to check
* @return true if p belongs to the plane
*/
public boolean contains(final Point3D p) {
public boolean contains(final Vector3D p) {
return FastMath.abs(getOffset(p)) < 1.0e-10;
}
@ -416,7 +414,7 @@ public class Plane implements Hyperplane {
* @param point point to check
* @return offset of the point
*/
public double getOffset(final Point point) {
public double getOffset(final Vector<Euclidean3D> point) {
return Vector3D.dotProduct((Vector3D) point, w) + originOffset;
}
@ -425,102 +423,8 @@ public class Plane implements Hyperplane {
* @return true if the instance and the other hyperplane have
* the same orientation
*/
public boolean sameOrientationAs(final Hyperplane other) {
public boolean sameOrientationAs(final Hyperplane<Euclidean3D> other) {
return Vector3D.dotProduct(((Plane) other).w, w) > 0.0;
}
/** Compute the relative position of a sub-hyperplane with respect
* to the instance.
* @param sub sub-hyperplane to check
* @return one of {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#PLUS PLUS},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#MINUS MINUS},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#BOTH BOTH},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#HYPER HYPER}
*/
public Side side(final SubHyperplane sub) {
final Plane otherPlane = (Plane) sub.getHyperplane();
final Line inter = (Line) intersection(otherPlane);
if (inter == null) {
// the hyperplanes are parallel,
// any point can be used to check their relative position
final double global = getOffset(otherPlane);
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
// create a 2D line in the otherPlane canonical 2D frame such that:
// - the line is the crossing line of the two planes in 3D
// - the line splits the otherPlane in two half planes with an
// orientation consistent with the orientation of the instance
// (i.e. the 3D half space on the plus side (resp. minus side)
// of the instance contains the 2D half plane on the plus side
// (resp. minus side) of the 2D line
Point2D p = (Point2D) otherPlane.toSubSpace(inter.toSpace(Point1D.ZERO));
Point2D q = (Point2D) otherPlane.toSubSpace(inter.toSpace(Point1D.ONE));
if (Vector3D.dotProduct(Vector3D.crossProduct(inter.getDirection(),
otherPlane.getNormal()),
w) < 0) {
final Point2D tmp = p;
p = q;
q = tmp;
}
final Hyperplane line2D = new org.apache.commons.math.geometry.euclidean.twod.Line(p, q);
// check the side on the 2D plane
return sub.getRemainingRegion().side(line2D);
}
/** Split a sub-hyperplane in two parts by the instance.
* @param sub sub-hyperplane to split
* @return an object containing both the part of the sub-hyperplane
* on the plus side of the instance and the part of the
* sub-hyperplane on the minus side of the instance
*/
public SplitSubHyperplane split(final SubHyperplane sub) {
final Plane otherPlane = (Plane) sub.getHyperplane();
final Line inter = (Line) intersection(otherPlane);
if (inter == null) {
// the hyperplanes are parallel
final double global = getOffset(otherPlane);
return (global < -1.0e-10) ? new SplitSubHyperplane(null, sub) : new SplitSubHyperplane(sub, null);
}
// the hyperplanes do intersect
Point2D p = (Point2D) otherPlane.toSubSpace(inter.toSpace(Point1D.ZERO));
Point2D q = (Point2D) otherPlane.toSubSpace(inter.toSpace(Point1D.ONE));
if (Vector3D.dotProduct(Vector3D.crossProduct(inter.getDirection(),
otherPlane.getNormal()),
w) < 0) {
final Point2D tmp = p;
p = q;
q = tmp;
}
final SubHyperplane l2DMinus =
new SubHyperplane(new org.apache.commons.math.geometry.euclidean.twod.Line(p, q));
final SubHyperplane l2DPlus =
new SubHyperplane(new org.apache.commons.math.geometry.euclidean.twod.Line(q, p));
final BSPTree splitTree =
sub.getRemainingRegion().getTree(false).split(l2DMinus);
final BSPTree plusTree = Region.isEmpty(splitTree.getPlus()) ?
new BSPTree(Boolean.FALSE) :
new BSPTree(l2DPlus, new BSPTree(Boolean.FALSE),
splitTree.getPlus(), null);
final BSPTree minusTree = Region.isEmpty(splitTree.getMinus()) ?
new BSPTree(Boolean.FALSE) :
new BSPTree(l2DMinus, new BSPTree(Boolean.FALSE),
splitTree.getMinus(), null);
return new SplitSubHyperplane(new SubHyperplane(otherPlane.copySelf(),
new PolygonsSet(plusTree)),
new SubHyperplane(otherPlane.copySelf(),
new PolygonsSet(minusTree)));
}
}

View File

@ -17,23 +17,29 @@
package org.apache.commons.math.geometry.euclidean.threed;
import java.awt.geom.AffineTransform;
import java.util.Arrays;
import java.util.Collection;
import org.apache.commons.math.geometry.euclidean.twod.Point2D;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.twod.Euclidean2D;
import org.apache.commons.math.geometry.euclidean.twod.SubLine;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
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.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.Transform;
import org.apache.commons.math.geometry.partitioning.AbstractRegion;
import org.apache.commons.math.util.FastMath;
/** This class represents a 3D region: a set of polyhedrons.
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class PolyhedronsSet extends Region {
public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
/** Build a polyhedrons set representing the whole real line.
*/
@ -50,7 +56,7 @@ public class PolyhedronsSet extends Region {
* {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
* @param tree inside/outside BSP tree representing the region
*/
public PolyhedronsSet(final BSPTree tree) {
public PolyhedronsSet(final BSPTree<Euclidean3D> tree) {
super(tree);
}
@ -61,19 +67,19 @@ public class PolyhedronsSet extends Region {
* its plus side.</p>
* <p>The boundary elements can be in any order, and can form
* several non-connected sets (like for example polyhedrons with holes
* or a set of disjoints polyhedrons considered as a whole). In
* or a set of disjoint polyhedrons considered as a whole). In
* fact, the elements do not even need to be connected together
* (their topological connections are not used here). However, if the
* boundary does not really separate an inside open from an outside
* open (open having here its topological meaning), then subsequent
* calls to the {@link Region#checkPoint(Point) checkPoint} method will
* calls to the {@link Region#checkPoint(Vector) checkPoint} method will
* not be meaningful anymore.</p>
* <p>If the boundary is empty, the region will represent the whole
* space.</p>
* @param boundary collection of boundary elements, as a
* collection of {@link SubHyperplane SubHyperplane} objects
*/
public PolyhedronsSet(final Collection<SubHyperplane> boundary) {
public PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary) {
super(boundary);
}
@ -85,21 +91,21 @@ public class PolyhedronsSet extends Region {
* @param zMin low bound along the z direction
* @param zMax high bound along the z direction
*/
@SuppressWarnings("unchecked")
public PolyhedronsSet(final double xMin, final double xMax,
final double yMin, final double yMax,
final double zMin, final double zMax) {
this(buildConvex(Arrays.asList(new Hyperplane[] {
this(new RegionFactory<Euclidean3D>().buildConvex(
new Plane(new Vector3D(xMin, 0, 0), Vector3D.MINUS_I),
new Plane(new Vector3D(xMax, 0, 0), Vector3D.PLUS_I),
new Plane(new Vector3D(0, yMin, 0), Vector3D.MINUS_J),
new Plane(new Vector3D(0, yMax, 0), Vector3D.PLUS_J),
new Plane(new Vector3D(0, 0, zMin), Vector3D.MINUS_K),
new Plane(new Vector3D(0, 0, zMax), Vector3D.PLUS_K)
})).getTree(false));
new Plane(new Vector3D(0, 0, zMax), Vector3D.PLUS_K)).getTree(false));
}
/** {@inheritDoc} */
public Region buildNew(final BSPTree tree) {
public PolyhedronsSet buildNew(final BSPTree<Euclidean3D> tree) {
return new PolyhedronsSet(tree);
}
@ -113,32 +119,34 @@ public class PolyhedronsSet extends Region {
// the polyhedrons set as a finite outside
// surrounded by an infinite inside
setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point3D.UNDEFINED);
setBarycenter(Vector3D.NaN);
} else {
// the polyhedrons set is finite, apply the remaining scaling factors
setSize(getSize() / 3.0);
setBarycenter(new Point3D(1.0 / (4 * getSize()), (Vector3D) getBarycenter()));
setBarycenter(new Vector3D(1.0 / (4 * getSize()), (Vector3D) getBarycenter()));
}
}
/** Visitor computing geometrical properties. */
private class FacetsContributionVisitor implements BSPTreeVisitor {
private class FacetsContributionVisitor implements BSPTreeVisitor<Euclidean3D> {
/** Simple constructor. */
public FacetsContributionVisitor() {
setSize(0);
setBarycenter(new Point3D(0, 0, 0));
setBarycenter(new Vector3D(0, 0, 0));
}
/** {@inheritDoc} */
public Order visitOrder(final BSPTree node) {
public Order visitOrder(final BSPTree<Euclidean3D> node) {
return Order.MINUS_SUB_PLUS;
}
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) {
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute();
public void visitInternalNode(final BSPTree<Euclidean3D> node) {
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false);
}
@ -148,32 +156,32 @@ public class PolyhedronsSet extends Region {
}
/** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) {
public void visitLeafNode(final BSPTree<Euclidean3D> node) {
}
/** Add he contribution of a boundary facet.
* @param facet boundary facet
* @param reversed if true, the facet has the inside on its plus side
*/
private void addContribution(final SubHyperplane facet, final boolean reversed) {
private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
final Region polygon = facet.getRemainingRegion();
final Region<Euclidean2D> polygon = ((SubPlane) facet).getRemainingRegion();
final double area = polygon.getSize();
if (Double.isInfinite(area)) {
setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point3D.UNDEFINED);
setBarycenter(Vector3D.NaN);
} else {
final Plane plane = (Plane) facet.getHyperplane();
final Vector3D facetB = (Point3D) plane.toSpace(polygon.getBarycenter());
final Vector3D facetB = plane.toSpace(polygon.getBarycenter());
double scaled = area * Vector3D.dotProduct(facetB, plane.getNormal());
if (reversed) {
scaled = -scaled;
}
setSize(getSize() + scaled);
setBarycenter(new Point3D(1.0, (Point3D) getBarycenter(), scaled, facetB));
setBarycenter(new Vector3D(1.0, (Vector3D) getBarycenter(), scaled, facetB));
}
@ -188,7 +196,7 @@ public class PolyhedronsSet extends Region {
* given point, or null if the line does not intersect any
* sub-hyperplaned
*/
public SubHyperplane firstIntersection(final Vector3D point, final Line line) {
public SubHyperplane<Euclidean3D> firstIntersection(final Vector3D point, final Line line) {
return recurseFirstIntersection(getTree(true), point, line);
}
@ -200,23 +208,23 @@ public class PolyhedronsSet extends Region {
* given point, or null if the line does not intersect any
* sub-hyperplaned
*/
private SubHyperplane recurseFirstIntersection(final BSPTree node,
final Vector3D point,
final Line line) {
private SubHyperplane<Euclidean3D> recurseFirstIntersection(final BSPTree<Euclidean3D> node,
final Vector3D point,
final Line line) {
final SubHyperplane cut = node.getCut();
final SubHyperplane<Euclidean3D> cut = node.getCut();
if (cut == null) {
return null;
}
final BSPTree minus = node.getMinus();
final BSPTree plus = node.getPlus();
final Plane plane = (Plane) cut.getHyperplane();
final BSPTree<Euclidean3D> minus = node.getMinus();
final BSPTree<Euclidean3D> plus = node.getPlus();
final Plane plane = (Plane) cut.getHyperplane();
// establish search order
final double offset = plane.getOffset((Point) point);
final double offset = plane.getOffset(point);
final boolean in = FastMath.abs(offset) < 1.0e-10;
final BSPTree near;
final BSPTree far;
final BSPTree<Euclidean3D> near;
final BSPTree<Euclidean3D> far;
if (offset < 0) {
near = minus;
far = plus;
@ -227,14 +235,14 @@ public class PolyhedronsSet extends Region {
if (in) {
// search in the cut hyperplane
final SubHyperplane facet = boundaryFacet(point, node);
final SubHyperplane<Euclidean3D> facet = boundaryFacet(point, node);
if (facet != null) {
return facet;
}
}
// search in the near branch
final SubHyperplane crossed = recurseFirstIntersection(near, point, line);
final SubHyperplane<Euclidean3D> crossed = recurseFirstIntersection(near, point, line);
if (crossed != null) {
return crossed;
}
@ -243,7 +251,7 @@ public class PolyhedronsSet extends Region {
// search in the cut hyperplane
final Vector3D hit3D = plane.intersection(line);
if (hit3D != null) {
final SubHyperplane facet = boundaryFacet(hit3D, node);
final SubHyperplane<Euclidean3D> facet = boundaryFacet(hit3D, node);
if (facet != null) {
return facet;
}
@ -261,15 +269,18 @@ public class PolyhedronsSet extends Region {
* @return the boundary facet this points belongs to (or null if it
* does not belong to any boundary facet)
*/
private SubHyperplane boundaryFacet(final Vector3D point, final BSPTree node) {
final Point point2D = node.getCut().getHyperplane().toSubSpace((Point) point);
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute();
private SubHyperplane<Euclidean3D> boundaryFacet(final Vector3D point,
final BSPTree<Euclidean3D> node) {
final Vector2D point2D = ((Plane) node.getCut().getHyperplane()).toSubSpace(point);
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if ((attribute.getPlusOutside() != null) &&
(attribute.getPlusOutside().getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
(((SubPlane) attribute.getPlusOutside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
return attribute.getPlusOutside();
}
if ((attribute.getPlusInside() != null) &&
(attribute.getPlusInside().getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
(((SubPlane) attribute.getPlusInside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
return attribute.getPlusInside();
}
return null;
@ -286,7 +297,7 @@ public class PolyhedronsSet extends Region {
}
/** 3D rotation as a Transform. */
private static class RotationTransform implements Transform {
private static class RotationTransform implements Transform<Euclidean3D, Euclidean2D> {
/** Center point of the rotation. */
private Vector3D center;
@ -295,10 +306,10 @@ public class PolyhedronsSet extends Region {
private Rotation rotation;
/** Cached original hyperplane. */
private Hyperplane cachedOriginal;
private Plane cachedOriginal;
/** Cached 2D transform valid inside the cached original hyperplane. */
private Transform cachedTransform;
private Transform<Euclidean2D, Euclidean1D> cachedTransform;
/** Build a rotation transform.
* @param center center point of the rotation
@ -310,40 +321,41 @@ public class PolyhedronsSet extends Region {
}
/** {@inheritDoc} */
public Point apply(final Point point) {
public Vector3D apply(final Vector<Euclidean3D> point) {
final Vector3D delta = ((Vector3D) point).subtract(center);
return new Point3D(1.0, center, 1.0, rotation.applyTo(delta));
return new Vector3D(1.0, center, 1.0, rotation.applyTo(delta));
}
/** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) {
public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
return ((Plane) hyperplane).rotate(center, rotation);
}
/** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub,
final Hyperplane original, final Hyperplane transformed) {
public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
final Hyperplane<Euclidean3D> original,
final Hyperplane<Euclidean3D> transformed) {
if (original != cachedOriginal) {
// we have changed hyperplane, reset the in-hyperplane transform
final Plane oPlane = (Plane) original;
final Plane tPlane = (Plane) transformed;
final Vector3D p00 = oPlane.getOrigin();
final Vector3D p10 = (Vector3D) oPlane.toSpace(new Point2D(1.0, 0.0));
final Vector3D p01 = (Vector3D) oPlane.toSpace(new Point2D(0.0, 1.0));
final Point2D tP00 = (Point2D) tPlane.toSubSpace(apply((Point) p00));
final Point2D tP10 = (Point2D) tPlane.toSubSpace(apply((Point) p10));
final Point2D tP01 = (Point2D) tPlane.toSubSpace(apply((Point) p01));
final Vector3D p10 = (Vector3D) oPlane.toSpace(new Vector2D(1.0, 0.0));
final Vector3D p01 = (Vector3D) oPlane.toSpace(new Vector2D(0.0, 1.0));
final Vector2D tP00 = (Vector2D) tPlane.toSubSpace(apply(p00));
final Vector2D tP10 = (Vector2D) tPlane.toSubSpace(apply(p10));
final Vector2D tP01 = (Vector2D) tPlane.toSubSpace(apply(p01));
final AffineTransform at =
new AffineTransform(tP10.getX() - tP00.getX(), tP10.getY() - tP00.getY(),
tP01.getX() - tP00.getX(), tP01.getY() - tP00.getY(),
tP00.getX(), tP00.getY());
cachedOriginal = original;
cachedOriginal = (Plane) original;
cachedTransform = org.apache.commons.math.geometry.euclidean.twod.Line.getTransform(at);
}
return sub.applyTransform(cachedTransform);
return ((SubLine) sub).applyTransform(cachedTransform);
}
}
@ -358,16 +370,16 @@ public class PolyhedronsSet extends Region {
}
/** 3D translation as a transform. */
private static class TranslationTransform implements Transform {
private static class TranslationTransform implements Transform<Euclidean3D, Euclidean2D> {
/** Translation vector. */
private Vector3D translation;
/** Cached original hyperplane. */
private Hyperplane cachedOriginal;
private Plane cachedOriginal;
/** Cached 2D transform valid inside the cached original hyperplane. */
private Transform cachedTransform;
private Transform<Euclidean2D, Euclidean1D> cachedTransform;
/** Build a translation transform.
* @param translation translation vector
@ -377,34 +389,35 @@ public class PolyhedronsSet extends Region {
}
/** {@inheritDoc} */
public Point apply(final Point point) {
return new Point3D(1.0, (Vector3D) point, 1.0, translation);
public Vector3D apply(final Vector<Euclidean3D> point) {
return new Vector3D(1.0, (Vector3D) point, 1.0, translation);
}
/** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) {
public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
return ((Plane) hyperplane).translate(translation);
}
/** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub,
final Hyperplane original, final Hyperplane transformed) {
public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
final Hyperplane<Euclidean3D> original,
final Hyperplane<Euclidean3D> transformed) {
if (original != cachedOriginal) {
// we have changed hyperplane, reset the in-hyperplane transform
final Plane oPlane = (Plane) original;
final Plane tPlane = (Plane) transformed;
final Point2D shift = (Point2D) tPlane.toSubSpace(apply((Point) oPlane.getOrigin()));
final Vector2D shift = (Vector2D) tPlane.toSubSpace(apply(oPlane.getOrigin()));
final AffineTransform at =
AffineTransform.getTranslateInstance(shift.getX(), shift.getY());
cachedOriginal = original;
cachedOriginal = (Plane) original;
cachedTransform =
org.apache.commons.math.geometry.euclidean.twod.Line.getTransform(at);
}
return sub.applyTransform(cachedTransform);
return ((SubLine) sub).applyTransform(cachedTransform);
}

View File

@ -0,0 +1,138 @@
/*
* 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.threed;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.euclidean.twod.Euclidean2D;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math.geometry.euclidean.twod.PolygonsSet;
import org.apache.commons.math.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.Side;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
/** This class represents a sub-hyperplane for {@link Plane}.
* @version $Id:$
* @since 3.0
*/
public class SubPlane extends AbstractSubHyperplane<Euclidean3D, Euclidean2D> {
/** Simple constructor.
* @param hyperplane underlying hyperplane
* @param remainingRegion remaining region of the hyperplane
*/
public SubPlane(final Hyperplane<Euclidean3D> hyperplane,
final Region<Euclidean2D> remainingRegion) {
super(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
protected AbstractSubHyperplane<Euclidean3D, Euclidean2D> buildNew(final Hyperplane<Euclidean3D> hyperplane,
final Region<Euclidean2D> remainingRegion) {
return new SubPlane(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
public Side side(Hyperplane<Euclidean3D> hyperplane) {
final Plane otherPlane = (Plane) hyperplane;
final Plane thisPlane = (Plane) getHyperplane();
final Line inter = (Line) otherPlane.intersection(thisPlane);
if (inter == null) {
// the hyperplanes are parallel,
// any point can be used to check their relative position
final double global = otherPlane.getOffset(thisPlane);
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
// create a 2D line in the otherPlane canonical 2D frame such that:
// - the line is the crossing line of the two planes in 3D
// - the line splits the otherPlane in two half planes with an
// orientation consistent with the orientation of the instance
// (i.e. the 3D half space on the plus side (resp. minus side)
// of the instance contains the 2D half plane on the plus side
// (resp. minus side) of the 2D line
Vector2D p = thisPlane.toSubSpace(inter.toSpace(Vector1D.ZERO));
Vector2D q = thisPlane.toSubSpace(inter.toSpace(Vector1D.ONE));
Vector3D crossP = Vector3D.crossProduct(inter.getDirection(), thisPlane.getNormal());
if (Vector3D.dotProduct(crossP, otherPlane.getNormal()) < 0) {
final Vector2D tmp = p;
p = q;
q = tmp;
}
final org.apache.commons.math.geometry.euclidean.twod.Line line2D =
new org.apache.commons.math.geometry.euclidean.twod.Line(p, q);
// check the side on the 2D plane
return getRemainingRegion().side(line2D);
}
/** Split the instance in two parts by an hyperplane.
* @param hyperplane splitting hyperplane
* @return an object containing both the part of the instance
* on the plus side of the instance and the part of the
* instance on the minus side of the instance
*/
public SplitSubHyperplane<Euclidean3D> split(Hyperplane<Euclidean3D> hyperplane) {
final Plane otherPlane = (Plane) hyperplane;
final Plane thisPlane = (Plane) getHyperplane();
final Line inter = (Line) otherPlane.intersection(thisPlane);
if (inter == null) {
// the hyperplanes are parallel
final double global = otherPlane.getOffset(thisPlane);
return (global < -1.0e-10) ?
new SplitSubHyperplane<Euclidean3D>(null, this) :
new SplitSubHyperplane<Euclidean3D>(this, null);
}
// the hyperplanes do intersect
Vector2D p = thisPlane.toSubSpace(inter.toSpace(Vector1D.ZERO));
Vector2D q = thisPlane.toSubSpace(inter.toSpace(Vector1D.ONE));
Vector3D crossP = Vector3D.crossProduct(inter.getDirection(), thisPlane.getNormal());
if (Vector3D.dotProduct(crossP, otherPlane.getNormal()) < 0) {
final Vector2D tmp = p;
p = q;
q = tmp;
}
final SubHyperplane<Euclidean2D> l2DMinus =
new org.apache.commons.math.geometry.euclidean.twod.Line(p, q).wholeHyperplane();
final SubHyperplane<Euclidean2D> l2DPlus =
new org.apache.commons.math.geometry.euclidean.twod.Line(q, p).wholeHyperplane();
final BSPTree<Euclidean2D> splitTree = getRemainingRegion().getTree(false).split(l2DMinus);
final BSPTree<Euclidean2D> plusTree = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
new BSPTree<Euclidean2D>(Boolean.FALSE) :
new BSPTree<Euclidean2D>(l2DPlus, new BSPTree<Euclidean2D>(Boolean.FALSE),
splitTree.getPlus(), null);
final BSPTree<Euclidean2D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
new BSPTree<Euclidean2D>(Boolean.FALSE) :
new BSPTree<Euclidean2D>(l2DMinus, new BSPTree<Euclidean2D>(Boolean.FALSE),
splitTree.getMinus(), null);
return new SplitSubHyperplane<Euclidean3D>(new SubPlane(thisPlane.copySelf(), new PolygonsSet(plusTree)),
new SubPlane(thisPlane.copySelf(), new PolygonsSet(minusTree)));
}
}

View File

@ -20,15 +20,14 @@ import java.awt.geom.AffineTransform;
import org.apache.commons.math.exception.MathIllegalArgumentException;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.OrientedPoint;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.SubSpace;
import org.apache.commons.math.geometry.partitioning.Transform;
import org.apache.commons.math.util.FastMath;
import org.apache.commons.math.util.MathUtils;
@ -57,9 +56,10 @@ import org.apache.commons.math.util.MathUtils;
* left half plane is the set of points with negative offsets and the
* right half plane is the set of points with positive offsets.</p>
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class Line implements Hyperplane {
public class Line implements Hyperplane<Euclidean2D>, Embedding<Euclidean2D, Euclidean1D> {
/** Angle with respect to the abscissa axis. */
private double angle;
@ -78,7 +78,7 @@ public class Line implements Hyperplane {
* @param p1 first point
* @param p2 second point
*/
public Line(final Point2D p1, final Point2D p2) {
public Line(final Vector2D p1, final Vector2D p2) {
reset(p1, p2);
}
@ -86,7 +86,7 @@ public class Line implements Hyperplane {
* @param p point belonging to the line
* @param angle angle of the line with respect to abscissa axis
*/
public Line(final Point2D p, final double angle) {
public Line(final Vector2D p, final double angle) {
reset(p, angle);
}
@ -116,7 +116,7 @@ public class Line implements Hyperplane {
}
/** {@inheritDoc} */
public Hyperplane copySelf() {
public Line copySelf() {
return new Line(this);
}
@ -125,20 +125,20 @@ public class Line implements Hyperplane {
* @param p1 first point
* @param p2 second point
*/
public void reset(final Point2D p1, final Point2D p2) {
final double dx = p2.x - p1.x;
final double dy = p2.y - p1.y;
public void reset(final Vector2D p1, final Vector2D p2) {
final double dx = p2.getX() - p1.getX();
final double dy = p2.getY() - p1.getY();
final double d = FastMath.hypot(dx, dy);
if (d == 0.0) {
angle = 0.0;
cos = 1.0;
sin = 0.0;
originOffset = p1.y;
originOffset = p1.getY();
} else {
angle = FastMath.PI + FastMath.atan2(-dy, -dx);
cos = FastMath.cos(angle);
sin = FastMath.sin(angle);
originOffset = (p2.x * p1.y - p1.x * p2.y) / d;
originOffset = (p2.getX() * p1.getY() - p1.getX() * p2.getY()) / d;
}
}
@ -146,11 +146,11 @@ public class Line implements Hyperplane {
* @param p point belonging to the line
* @param alpha angle of the line with respect to abscissa axis
*/
public void reset(final Point2D p, final double alpha) {
public void reset(final Vector2D p, final double alpha) {
this.angle = MathUtils.normalizeAngle(alpha, FastMath.PI);
cos = FastMath.cos(this.angle);
sin = FastMath.sin(this.angle);
originOffset = cos * p.y - sin * p.x;
originOffset = cos * p.getY() - sin * p.getX();
}
/** Revert the instance.
@ -176,57 +176,44 @@ public class Line implements Hyperplane {
-cos, -sin, -originOffset);
}
/** Transform a 2D space point into a line point.
* @param point 2D point (must be a {@link Point2D Point2D}
* instance)
* @return line point corresponding to the 2D point (really a {@link
* org.apache.commons.math.geometry.euclidean.oned.Point1D Point1D} instance)
* @see #toSpace
*/
public Point toSubSpace(final Point point) {
final Point2D p2D = (Point2D) point;
return new Point1D(cos * p2D.x + sin * p2D.y);
/** {@inheritDoc} */
public Vector1D toSubSpace(final Vector<Euclidean2D> point) {
Vector2D p2 = (Vector2D) point;
return new Vector1D(cos * p2.getX() + sin * p2.getY());
}
/** Get one point from the line.
* @param point desired abscissa for the point (must be a {@link
* org.apache.commons.math.geometry.euclidean.oned.Point1D Point1D} instance)
* @return line point at specified abscissa (really a {@link Point2D
* Point2D} instance)
*/
public Point toSpace(final Point point) {
final double abscissa = ((Point1D) point).getAbscissa();
return new Point2D(abscissa * cos - originOffset * sin,
/** {@inheritDoc} */
public Vector2D toSpace(final Vector<Euclidean1D> point) {
final double abscissa = ((Vector1D) point).getX();
return new Vector2D(abscissa * cos - originOffset * sin,
abscissa * sin + originOffset * cos);
}
/** Get the intersection point of the instance and another line.
* @param other other line
* @return intersection point of the instance and the other line
* (really a {@link Point2D Point2D} instance)
* (really a {@link Vector2D Vector2D} instance)
*/
public SubSpace intersection(final Hyperplane other) {
public Vector2D intersection(final Hyperplane<Euclidean2D> other) {
final Line otherL = (Line) other;
final double d = sin * otherL.cos - otherL.sin * cos;
if (FastMath.abs(d) < 1.0e-10) {
return null;
}
return new Point2D((cos * otherL.originOffset - otherL.cos * originOffset) / d,
return new Vector2D((cos * otherL.originOffset - otherL.cos * originOffset) / d,
(sin * otherL.originOffset - otherL.sin * originOffset) / d);
}
/** Build a region covering the whole hyperplane.
* @return a region covering the whole hyperplane
*/
public Region wholeHyperplane() {
return new IntervalsSet();
/** {@inheritDoc} */
public SubLine wholeHyperplane() {
return new SubLine(this, new IntervalsSet());
}
/** Build a region covering the whole space.
* @return a region containing the instance (really a {@link
* PolygonsSet PolygonsSet} instance)
*/
public Region wholeSpace() {
public PolygonsSet wholeSpace() {
return new PolygonsSet();
}
@ -240,7 +227,8 @@ public class Line implements Hyperplane {
* @param line line to check
* @return offset of the line
*/
public double getOffset(final Line line) {
public double getOffset(final Hyperplane<Euclidean2D> hyperplane) {
Line line = (Line) hyperplane;
return originOffset +
((cos * line.cos + sin * line.sin > 0) ? -line.originOffset : line.originOffset);
}
@ -250,12 +238,12 @@ public class Line implements Hyperplane {
* positive if the point is on the right side of the line and
* negative if it is on the left side, according to its natural
* orientation.</p>
* @param point point to check (must be a {@link Point2D Point2D} instance)
* @param point point to check (must be a {@link Vector2D Vector2D} instance)
* @return offset of the point
*/
public double getOffset(final Point point) {
final Point2D p2D = (Point2D) point;
return sin * p2D.x - cos * p2D.y + originOffset;
public double getOffset(final Vector<Euclidean2D> point) {
Vector2D p2 = (Vector2D) point;
return sin * p2.getX() - cos * p2.getY() + originOffset;
}
/** Check if the instance has the same orientation as another hyperplane.
@ -271,7 +259,7 @@ public class Line implements Hyperplane {
* @return true if the instance and the other hyperplane have
* the same orientation
*/
public boolean sameOrientationAs(final Hyperplane other) {
public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
final Line otherL = (Line) other;
return (sin * otherL.sin + cos * otherL.cos) >= 0.0;
}
@ -282,17 +270,17 @@ public class Line implements Hyperplane {
* @return one point in the plane, with given abscissa and offset
* relative to the line
*/
public Point2D getPointAt(final Point1D abscissa, final double offset) {
final double x = abscissa.getAbscissa();
public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
final double x = abscissa.getX();
final double dOffset = offset - originOffset;
return new Point2D(x * cos + dOffset * sin, x * sin - dOffset * cos);
return new Vector2D(x * cos + dOffset * sin, x * sin - dOffset * cos);
}
/** Check if the line contains a point.
* @param p point to check
* @return true if p belongs to the line
*/
public boolean contains(final Point2D p) {
public boolean contains(final Vector2D p) {
return FastMath.abs(getOffset(p)) < 1.0e-10;
}
@ -308,8 +296,8 @@ public class Line implements Hyperplane {
/** Translate the line to force it passing by a point.
* @param p point by which the line should pass
*/
public void translateToPoint(final Point2D p) {
originOffset = cos * p.y - sin * p.x;
public void translateToPoint(final Vector2D p) {
originOffset = cos * p.getY() - sin * p.getX();
}
/** Get the angle of the line.
@ -342,75 +330,6 @@ public class Line implements Hyperplane {
originOffset = offset;
}
/** Compute the relative position of a sub-hyperplane with respect
* to the instance.
* @param sub sub-hyperplane to check
* @return one of {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#PLUS PLUS},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#MINUS MINUS},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#BOTH BOTH},
* {@link org.apache.commons.math.geometry.partitioning.Hyperplane.Side#HYPER HYPER}
*/
public Side side(final SubHyperplane sub) {
final Hyperplane otherHyp = sub.getHyperplane();
final Point2D crossing = (Point2D) intersection(otherHyp);
if (crossing == null) {
// the lines are parallel,
final double global = getOffset((Line) otherHyp);
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
// the lines do intersect
final boolean direct = FastMath.sin(((Line) otherHyp).angle - angle) < 0;
final Point1D x = (Point1D) otherHyp.toSubSpace(crossing);
return sub.getRemainingRegion().side(new OrientedPoint(x, direct));
}
/** Split a sub-hyperplane in two parts by the instance.
* @param sub sub-hyperplane to split
* @return an object containing both the part of the sub-hyperplane
* on the plus side of the instance and the part of the
* sub-hyperplane on the minus side of the instance
*/
public SplitSubHyperplane split(final SubHyperplane sub) {
final Line otherLine = (Line) sub.getHyperplane();
final Point2D crossing = (Point2D) intersection(otherLine);
if (crossing == null) {
// the lines are parallel
final double global = getOffset(otherLine);
return (global < -1.0e-10) ?
new SplitSubHyperplane(null, sub) :
new SplitSubHyperplane(sub, null);
}
// the lines do intersect
final boolean direct = FastMath.sin(otherLine.angle - angle) < 0;
final Point1D x = (Point1D) otherLine.toSubSpace(crossing);
final SubHyperplane subPlus = new SubHyperplane(new OrientedPoint(x, !direct));
final SubHyperplane subMinus = new SubHyperplane(new OrientedPoint(x, direct));
final BSPTree splitTree =
sub.getRemainingRegion().getTree(false).split(subMinus);
final BSPTree plusTree = Region.isEmpty(splitTree.getPlus()) ?
new BSPTree(Boolean.FALSE) :
new BSPTree(subPlus, new BSPTree(Boolean.FALSE),
splitTree.getPlus(), null);
final BSPTree minusTree = Region.isEmpty(splitTree.getMinus()) ?
new BSPTree(Boolean.FALSE) :
new BSPTree(subMinus, new BSPTree(Boolean.FALSE),
splitTree.getMinus(), null);
return new SplitSubHyperplane(new SubHyperplane(otherLine.copySelf(),
new IntervalsSet(plusTree)),
new SubHyperplane(otherLine.copySelf(),
new IntervalsSet(minusTree)));
}
/** Get a {@link org.apache.commons.math.geometry.partitioning.Transform
* Transform} embedding an affine transform.
* @param transform affine transform to embed (must be inversible
@ -419,12 +338,13 @@ public class Line implements Hyperplane {
* apply(Hyperplane)} method would work only for some lines, and
* fail for other ones)
* @return a new transform that can be applied to either {@link
* Point2D Point2D}, {@link Line Line} or {@link
* Vector2D Vector2D}, {@link Line Line} or {@link
* org.apache.commons.math.geometry.partitioning.SubHyperplane
* SubHyperplane} instances
* @exception MathIllegalArgumentException if the transform is non invertible
*/
public static Transform getTransform(final AffineTransform transform) throws MathIllegalArgumentException {
public static Transform<Euclidean2D, Euclidean1D> getTransform(final AffineTransform transform)
throws MathIllegalArgumentException {
return new LineTransform(transform);
}
@ -435,7 +355,7 @@ public class Line implements Hyperplane {
* applied to a large number of lines (for example to a large
* polygon)./<p>
*/
private static class LineTransform implements Transform {
private static class LineTransform implements Transform<Euclidean2D, Euclidean1D> {
// CHECKSTYLE: stop JavadocVariable check
private double cXX;
@ -478,16 +398,16 @@ public class Line implements Hyperplane {
}
/** {@inheritDoc} */
public Point apply(final Point point) {
final Point2D p2D = (Point2D) point;
public Vector2D apply(final Vector<Euclidean2D> point) {
final Vector2D p2D = (Vector2D) point;
final double x = p2D.getX();
final double y = p2D.getY();
return new Point2D(cXX * x + cXY * y + cX1,
return new Vector2D(cXX * x + cXY * y + cX1,
cYX * x + cYY * y + cY1);
}
/** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) {
public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
final Line line = (Line) hyperplane;
final double rOffset = c1X * line.cos + c1Y * line.sin + c11 * line.originOffset;
final double rCos = cXX * line.cos + cXY * line.sin;
@ -499,12 +419,15 @@ public class Line implements Hyperplane {
}
/** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub,
final Hyperplane original, final Hyperplane transformed) {
final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
final Point1D newLoc =
(Point1D) transformed.toSubSpace(apply(original.toSpace(op.getLocation())));
return new SubHyperplane(new OrientedPoint(newLoc, op.isDirect()));
public SubHyperplane<Euclidean1D> apply(final SubHyperplane<Euclidean1D> sub,
final Hyperplane<Euclidean2D> original,
final Hyperplane<Euclidean2D> transformed) {
final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
final Line originalLine = (Line) original;
final Line transformedLine = (Line) transformed;
final Vector1D newLoc =
transformedLine.toSubSpace(apply(originalLine.toSpace(op.getLocation())));
return new OrientedPoint(newLoc, op.isDirect()).wholeHyperplane();
}
}

View File

@ -17,44 +17,43 @@
package org.apache.commons.math.geometry.euclidean.twod;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.commons.math.exception.MathIllegalArgumentException;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.geometry.euclidean.oned.OrientedPoint;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
/** This class represent a tree of nested 2D boundary loops.
* <p>This class is used during Piece instances construction.
* Beams are built using the outline edges as
* representative of facets, the orientation of these facets are
* <p>This class is used for piecewise polygons construction.
* Polygons are built using the outline edges as
* representative of boundaries, the orientation of these lines are
* meaningful. However, we want to allow the user to specify its
* outline loops without having to take care of this orientation. This
* class is devoted to correct mis-oriented loops.<p>
* <p>Orientation is computed assuming the piece is finite, i.e. the
* outermost loops have their exterior side facing points at infinity,
* and hence are oriented counter-clockwise. The orientation of
* <p>Orientation is computed assuming the piecewise polygon is finite,
* i.e. the outermost loops have their exterior side facing points at
* infinity, and hence are oriented counter-clockwise. The orientation of
* internal loops is computed as the reverse of the orientation of
* their immediate surrounding loop.</p>
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
class NestedLoops {
/** Boundary loop. */
private Point2D[] loop;
private Vector2D[] loop;
/** Surrounded loops. */
private ArrayList<NestedLoops> surrounded;
/** Polygon enclosing a finite region. */
private Region polygon;
private Region<Euclidean2D> polygon;
/** Indicator for original loop orientation. */
private boolean originalIsClockwise;
@ -74,7 +73,7 @@ class NestedLoops {
* @param loop boundary loop (will be reversed in place if needed)
* @exception MathIllegalArgumentException if an outline has an open boundary loop
*/
private NestedLoops(final Point2D[] loop) throws MathIllegalArgumentException {
private NestedLoops(final Vector2D[] loop) throws MathIllegalArgumentException {
if (loop[0] == null) {
throw new MathIllegalArgumentException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
@ -84,23 +83,21 @@ class NestedLoops {
surrounded = new ArrayList<NestedLoops>();
// build the polygon defined by the loop
final ArrayList<SubHyperplane> edges = new ArrayList<SubHyperplane>();
Point2D current = loop[loop.length - 1];
final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
Vector2D current = loop[loop.length - 1];
for (int i = 0; i < loop.length; ++i) {
final Point2D previous = current;
final Vector2D previous = current;
current = loop[i];
final Line line = new Line(previous, current);
final Region region = Region.buildConvex(Arrays.asList(new Hyperplane[] {
new OrientedPoint((Point1D) line.toSubSpace(previous), false),
new OrientedPoint((Point1D) line.toSubSpace(current), true)
}));
edges.add(new SubHyperplane(line, region));
final IntervalsSet region =
new IntervalsSet(line.toSubSpace(previous).getX(), line.toSubSpace(current).getX());
edges.add(new SubLine(line, region));
}
polygon = new PolygonsSet(edges);
// ensure the polygon encloses a finite region of the plane
if (Double.isInfinite(polygon.getSize())) {
polygon = polygon.getComplement();
polygon = new RegionFactory<Euclidean2D>().getComplement(polygon);
originalIsClockwise = false;
} else {
originalIsClockwise = true;
@ -113,7 +110,7 @@ class NestedLoops {
* @exception MathIllegalArgumentException if an outline has crossing
* boundary loops or open boundary loops
*/
public void add(final Point2D[] bLoop) throws MathIllegalArgumentException {
public void add(final Vector2D[] bLoop) throws MathIllegalArgumentException {
add(new NestedLoops(bLoop));
}
@ -142,8 +139,9 @@ class NestedLoops {
}
// we should be separate from the remaining children
RegionFactory<Euclidean2D> factory = new RegionFactory<Euclidean2D>();
for (final NestedLoops child : surrounded) {
if (!Region.intersection(node.polygon, child.polygon).isEmpty()) {
if (!factory.intersection(node.polygon, child.polygon).isEmpty()) {
throw new MathIllegalArgumentException(LocalizedFormats.CROSSING_BOUNDARY_LOOPS);
}
}
@ -154,7 +152,7 @@ class NestedLoops {
/** Correct the orientation of the loops contained in the tree.
* <p>This is this method that really inverts the loops that where
* provided through the {@link #add(Point2D[]) add} method if
* provided through the {@link #add(Vector2D[]) add} method if
* they are mis-oriented</p>
*/
public void correctOrientation() {
@ -174,7 +172,7 @@ class NestedLoops {
int min = -1;
int max = loop.length;
while (++min < --max) {
final Point2D tmp = loop[min];
final Vector2D tmp = loop[min];
loop[min] = loop[max];
loop[max] = tmp;
}

View File

@ -17,25 +17,27 @@
package org.apache.commons.math.geometry.euclidean.twod;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.exception.MathInternalError;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.AbstractRegion;
import org.apache.commons.math.geometry.partitioning.utilities.AVLTree;
import org.apache.commons.math.util.FastMath;
/** This class represents a 2D region: a set of polygons.
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
public class PolygonsSet extends Region {
public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
/** Vertices organized as boundary loops. */
private Point2D[][] vertices;
private Vector2D[][] vertices;
/** Build a polygons set representing the whole real line.
*/
@ -52,7 +54,7 @@ public class PolygonsSet extends Region {
* {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
* @param tree inside/outside BSP tree representing the region
*/
public PolygonsSet(final BSPTree tree) {
public PolygonsSet(final BSPTree<Euclidean2D> tree) {
super(tree);
}
@ -63,7 +65,7 @@ public class PolygonsSet extends Region {
* its plus side.</p>
* <p>The boundary elements can be in any order, and can form
* several non-connected sets (like for example polygons with holes
* or a set of disjoints polyhedrons considered as a whole). In
* or a set of disjoint polyhedrons considered as a whole). In
* fact, the elements do not even need to be connected together
* (their topological connections are not used here). However, if the
* boundary does not really separate an inside open from an outside
@ -76,7 +78,7 @@ public class PolygonsSet extends Region {
* @param boundary collection of boundary elements, as a
* collection of {@link SubHyperplane SubHyperplane} objects
*/
public PolygonsSet(final Collection<SubHyperplane> boundary) {
public PolygonsSet(final Collection<SubHyperplane<Euclidean2D>> boundary) {
super(boundary);
}
@ -88,7 +90,7 @@ public class PolygonsSet extends Region {
*/
public PolygonsSet(final double xMin, final double xMax,
final double yMin, final double yMax) {
this(buildConvex(boxBoundary(xMin, xMax, yMin, yMax)).getTree(false));
super(boxBoundary(xMin, xMax, yMin, yMax));
}
/** Create a list of hyperplanes representing the boundary of a box.
@ -98,42 +100,42 @@ public class PolygonsSet extends Region {
* @param yMax high bound along the y direction
* @return boundary of the box
*/
private static List<Hyperplane> boxBoundary(final double xMin, final double xMax,
final double yMin, final double yMax) {
final Point2D minMin = new Point2D(xMin, yMin);
final Point2D minMax = new Point2D(xMin, yMax);
final Point2D maxMin = new Point2D(xMax, yMin);
final Point2D maxMax = new Point2D(xMax, yMax);
return Arrays.asList(new Hyperplane[] {
private static Line[] boxBoundary(final double xMin, final double xMax,
final double yMin, final double yMax) {
final Vector2D minMin = new Vector2D(xMin, yMin);
final Vector2D minMax = new Vector2D(xMin, yMax);
final Vector2D maxMin = new Vector2D(xMax, yMin);
final Vector2D maxMax = new Vector2D(xMax, yMax);
return new Line[] {
new Line(minMin, maxMin),
new Line(maxMin, maxMax),
new Line(maxMax, minMax),
new Line(minMax, minMin)
});
};
}
/** {@inheritDoc} */
public Region buildNew(final BSPTree tree) {
public PolygonsSet buildNew(final BSPTree<Euclidean2D> tree) {
return new PolygonsSet(tree);
}
/** {@inheritDoc} */
protected void computeGeometricalProperties() {
final Point2D[][] v = getVertices();
final Vector2D[][] v = getVertices();
if (v.length == 0) {
if ((Boolean) getTree(false).getAttribute()) {
setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point2D.UNDEFINED);
setBarycenter(Vector2D.NaN);
} else {
setSize(0);
setBarycenter(new Point2D(0, 0));
setBarycenter(new Vector2D(0, 0));
}
} else if (v[0][0] == null) {
// there is at least one open-loop: the polygon is infinite
setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point2D.UNDEFINED);
setBarycenter(Vector2D.NaN);
} else {
// all loops are closed, we compute some integrals around the shape
@ -141,14 +143,14 @@ public class PolygonsSet extends Region {
double sumX = 0;
double sumY = 0;
for (Point2D[] loop : v) {
double x1 = loop[loop.length - 1].x;
double y1 = loop[loop.length - 1].y;
for (final Point2D point : loop) {
for (Vector2D[] loop : v) {
double x1 = loop[loop.length - 1].getX();
double y1 = loop[loop.length - 1].getY();
for (final Vector2D point : loop) {
final double x0 = x1;
final double y0 = y1;
x1 = point.x;
y1 = point.y;
x1 = point.getX();
y1 = point.getY();
final double factor = x0 * y1 - y0 * x1;
sum += factor;
sumX += factor * (x0 + x1);
@ -159,10 +161,10 @@ public class PolygonsSet extends Region {
if (sum < 0) {
// the polygon as a finite outside surrounded by an infinite inside
setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point2D.UNDEFINED);
setBarycenter(Vector2D.NaN);
} else {
setSize(sum / 2);
setBarycenter(new Point2D(sumX / (3 * sum), sumY / (3 * sum)));
setBarycenter(new Vector2D(sumX / (3 * sum), sumY / (3 * sum)));
}
}
@ -192,19 +194,19 @@ public class PolygonsSet extends Region {
* loops with the open loops first (the returned value is guaranteed
* to be non-null)
*/
public Point2D[][] getVertices() {
public Vector2D[][] getVertices() {
if (vertices == null) {
if (getTree(false).getCut() == null) {
vertices = new Point2D[0][];
vertices = new Vector2D[0][];
} else {
// sort the segmfinal ents according to their start point
// sort the segments according to their start point
final SegmentsBuilder visitor = new SegmentsBuilder();
getTree(true).visit(visitor);
final AVLTree<Segment> sorted = visitor.getSorted();
// identify the loops, starting from the open ones
// (their start segments final 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>>();
while (!sorted.isEmpty()) {
final AVLTree<Segment>.Node node = sorted.getSmallest();
@ -215,31 +217,30 @@ public class PolygonsSet extends Region {
}
// tranform the loops in an array of arrays of points
vertices = new Point2D[loops.size()][];
vertices = new Vector2D[loops.size()][];
int i = 0;
for (final List<Segment> loop : loops) {
if (loop.size() < 2) {
// sifinal ngle infinite line
final Line line = ((Segment) loop.get(0)).getLine();
vertices[i++] = new Point2D[] {
// single infinite line
final Line line = loop.get(0).getLine();
vertices[i++] = new Vector2D[] {
null,
(Point2D) line.toSpace(new Point1D(-Float.MAX_VALUE)),
(Point2D) line.toSpace(new Point1D(+Float.MAX_VALUE))
line.toSpace(new Vector1D(-Float.MAX_VALUE)),
line.toSpace(new Vector1D(+Float.MAX_VALUE))
};
} else if (((Segment) loop.get(0)).getStart() == null) {
// open lofinal op with at least one real point
final Point2D[] array = new Point2D[loop.size() + 2];
} else if (loop.get(0).getStart() == null) {
// open loop with at least one real point
final Vector2D[] array = new Vector2D[loop.size() + 2];
int j = 0;
for (Segment segment : loop) {
if (j == 0) {
// null point and first dummy point
double x =
((Point1D) segment.getLine().toSubSpace(segment.getEnd())).getAbscissa();
double x = segment.getLine().toSubSpace(segment.getEnd()).getX();
x -= FastMath.max(1.0, FastMath.abs(x / 2));
array[j++] = null;
array[j++] = (Point2D) segment.getLine().toSpace(new Point1D(x));
array[j++] = segment.getLine().toSpace(new Vector1D(x));
}
if (j < (array.length - 1)) {
@ -249,16 +250,15 @@ public class PolygonsSet extends Region {
if (j == (array.length - 1)) {
// last dummy point
double x =
((Point1D) segment.getLine().toSubSpace(segment.getStart())).getAbscissa();
double x = segment.getLine().toSubSpace(segment.getStart()).getX();
x += FastMath.max(1.0, FastMath.abs(x / 2));
array[j++] = (Point2D) segment.getLine().toSpace(new Point1D(x));
array[j++] = segment.getLine().toSpace(new Vector1D(x));
}
}
vertices[i++] = array;
} else {
final Point2D[] array = new Point2D[loop.size()];
final Vector2D[] array = new Vector2D[loop.size()];
int j = 0;
for (Segment segment : loop) {
array[j++] = segment.getStart();
@ -285,10 +285,10 @@ public class PolygonsSet extends Region {
final AVLTree<Segment> sorted) {
final ArrayList<Segment> loop = new ArrayList<Segment>();
Segment segment = (Segment) node.getElement();
Segment segment = node.getElement();
loop.add(segment);
final Point2D globalStart = segment.getStart();
Point2D end = segment.getEnd();
final Vector2D globalStart = segment.getStart();
Vector2D end = segment.getEnd();
node.delete();
// is this an open or a closed loop ?
@ -333,7 +333,7 @@ public class PolygonsSet extends Region {
}
if ((end == null) && !open) {
throw new RuntimeException("internal error");
throw new MathInternalError();
}
return loop;

View File

@ -19,15 +19,16 @@ 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.
* @version $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
class Segment implements Comparable<Segment> {
/** Start point of the segment. */
private final Point2D start;
private final Vector2D start;
/** End point of the segments. */
private final Point2D end;
private final Vector2D end;
/** Line containing the segment. */
private final Line line;
@ -40,13 +41,13 @@ class Segment implements Comparable<Segment> {
* @param end end point of the segment
* @param line line containing the segment
*/
public Segment(final Point2D start, final Point2D end, final Line line) {
public Segment(final Vector2D start, final Vector2D end, final Line line) {
this.start = start;
this.end = end;
this.line = line;
sortingKey = (start == null) ?
new OrderedTuple(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY) :
new OrderedTuple(start.x, start.y);
new OrderedTuple(start.getX(), start.getY());
}
/** Build a dummy segment.
@ -58,24 +59,24 @@ class Segment implements Comparable<Segment> {
* @param dx abscissa offset from the start point
* @param dy ordinate offset from the start point
*/
public Segment(final Point2D start, final double dx, final double dy) {
public Segment(final Vector2D start, final double dx, final double dy) {
this.start = null;
this.end = null;
this.line = null;
sortingKey = new OrderedTuple(start.x + dx, start.y + dy);
sortingKey = new OrderedTuple(start.getX() + dx, start.getY() + dy);
}
/** Get the start point of the segment.
* @return start point of the segment
*/
public Point2D getStart() {
public Vector2D getStart() {
return start;
}
/** Get the end point of the segment.
* @return end point of the segment
*/
public Point2D getEnd() {
public Vector2D getEnd() {
return end;
}

View File

@ -18,19 +18,22 @@ 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.Point1D;
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.Region.BoundaryAttribute;
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 $Revision$ $Date$
* @version $Id:$
* @since 3.0
*/
class SegmentsBuilder implements BSPTreeVisitor {
class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
/** Sorted segments. */
private AVLTree<Segment> sorted;
@ -41,13 +44,14 @@ class SegmentsBuilder implements BSPTreeVisitor {
}
/** {@inheritDoc} */
public Order visitOrder(final BSPTree node) {
public Order visitOrder(final BSPTree<Euclidean2D> node) {
return Order.MINUS_SUB_PLUS;
}
/** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) {
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute();
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);
}
@ -57,21 +61,24 @@ class SegmentsBuilder implements BSPTreeVisitor {
}
/** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) {
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 sub, final boolean reversed) {
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) sub.getRemainingRegion()).asList();
final List<Interval> intervals = ((IntervalsSet) absSub.getRemainingRegion()).asList();
for (final Interval i : intervals) {
final Point2D start = Double.isInfinite(i.getLower()) ?
null : (Point2D) line.toSpace(new Point1D(i.getLower()));
final Point2D end = Double.isInfinite(i.getUpper()) ?
null : (Point2D) line.toSpace(new Point1D(i.getUpper()));
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 {

View File

@ -0,0 +1,108 @@
/*
* 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 org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.OrientedPoint;
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.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.Side;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.util.FastMath;
/** This class represents a sub-hyperplane for {@link Line}.
* @version $Id:$
* @since 3.0
*/
public class SubLine extends AbstractSubHyperplane<Euclidean2D, Euclidean1D> {
/** Simple constructor.
* @param hyperplane underlying hyperplane
* @param remainingRegion remaining region of the hyperplane
*/
public SubLine(final Hyperplane<Euclidean2D> hyperplane,
final Region<Euclidean1D> remainingRegion) {
super(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
protected AbstractSubHyperplane<Euclidean2D, Euclidean1D> buildNew(final Hyperplane<Euclidean2D> hyperplane,
final Region<Euclidean1D> remainingRegion) {
return new SubLine(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
public Side side(final Hyperplane<Euclidean2D> hyperplane) {
final Line thisLine = (Line) getHyperplane();
final Line otherLine = (Line) hyperplane;
final Vector2D crossing = thisLine.intersection(otherLine);
if (crossing == null) {
// the lines are parallel,
final double global = otherLine.getOffset(thisLine);
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
// the lines do intersect
final boolean direct = FastMath.sin(thisLine.getAngle() - otherLine.getAngle()) < 0;
final Vector1D x = (Vector1D) thisLine.toSubSpace(crossing);
return getRemainingRegion().side(new OrientedPoint(x, direct));
}
/** {@inheritDoc} */
public SplitSubHyperplane<Euclidean2D> split(final Hyperplane<Euclidean2D> hyperplane) {
final Line thisLine = (Line) getHyperplane();
final Line otherLine = (Line) hyperplane;
final Vector2D crossing = thisLine.intersection(otherLine);
if (crossing == null) {
// the lines are parallel
final double global = otherLine.getOffset(thisLine);
return (global < -1.0e-10) ?
new SplitSubHyperplane<Euclidean2D>(null, this) :
new SplitSubHyperplane<Euclidean2D>(this, null);
}
// the lines do intersect
final boolean direct = FastMath.sin(thisLine.getAngle() - otherLine.getAngle()) < 0;
final Vector1D x = (Vector1D) thisLine.toSubSpace(crossing);
final SubHyperplane<Euclidean1D> subPlus = new OrientedPoint(x, !direct).wholeHyperplane();
final SubHyperplane<Euclidean1D> subMinus = new OrientedPoint(x, direct).wholeHyperplane();
final BSPTree<Euclidean1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);
final BSPTree<Euclidean1D> plusTree = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
new BSPTree<Euclidean1D>(Boolean.FALSE) :
new BSPTree<Euclidean1D>(subPlus, new BSPTree<Euclidean1D>(Boolean.FALSE),
splitTree.getPlus(), null);
final BSPTree<Euclidean1D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
new BSPTree<Euclidean1D>(Boolean.FALSE) :
new BSPTree<Euclidean1D>(subMinus, new BSPTree<Euclidean1D>(Boolean.FALSE),
splitTree.getMinus(), null);
return new SplitSubHyperplane<Euclidean2D>(new SubLine(thisLine.copySelf(), new IntervalsSet(plusTree)),
new SubLine(thisLine.copySelf(), new IntervalsSet(minusTree)));
}
}

View File

@ -20,8 +20,9 @@ import java.util.List;
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.Point1D;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
@ -32,12 +33,12 @@ public class IntervalsSetTest {
public void testInterval() {
IntervalsSet set = new IntervalsSet(2.3, 5.7);
Assert.assertEquals(3.4, set.getSize(), 1.0e-10);
Assert.assertEquals(4.0, ((Point1D) set.getBarycenter()).getAbscissa(), 1.0e-10);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(2.3)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(5.7)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(1.2)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.7)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(3.0)));
Assert.assertEquals(4.0, ((Vector1D) set.getBarycenter()).getX(), 1.0e-10);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(2.3)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(5.7)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(1.2)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.7)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(3.0)));
Assert.assertEquals(2.3, set.getInf(), 1.0e-10);
Assert.assertEquals(5.7, set.getSup(), 1.0e-10);
}
@ -45,17 +46,17 @@ public class IntervalsSetTest {
@Test
public void testInfinite() {
IntervalsSet set = new IntervalsSet(9.0, Double.POSITIVE_INFINITY);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(9.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.4)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(9.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.4)));
for (double e = 1.0; e <= 6.0; e += 1.0) {
Assert.assertEquals(Region.Location.INSIDE,
set.checkPoint(new Point1D(FastMath.pow(10.0, e))));
set.checkPoint(new Vector1D(FastMath.pow(10.0, e))));
}
Assert.assertTrue(Double.isInfinite(set.getSize()));
Assert.assertEquals(9.0, set.getInf(), 1.0e-10);
Assert.assertTrue(Double.isInfinite(set.getSup()));
set = (IntervalsSet) set.getComplement();
set = (IntervalsSet) new RegionFactory<Euclidean1D>().getComplement(set);
Assert.assertEquals(9.0, set.getSup(), 1.0e-10);
Assert.assertTrue(Double.isInfinite(set.getInf()));
@ -63,22 +64,23 @@ public class IntervalsSetTest {
@Test
public void testMultiple() {
RegionFactory<Euclidean1D> factory = new RegionFactory<Euclidean1D>();
IntervalsSet set = (IntervalsSet)
Region.intersection(Region.union(Region.difference(new IntervalsSet(1.0, 6.0),
new IntervalsSet(3.0, 5.0)),
new IntervalsSet(9.0, Double.POSITIVE_INFINITY)),
new IntervalsSet(Double.NEGATIVE_INFINITY, 11.0));
factory.intersection(factory.union(factory.difference(new IntervalsSet(1.0, 6.0),
new IntervalsSet(3.0, 5.0)),
new IntervalsSet(9.0, Double.POSITIVE_INFINITY)),
new IntervalsSet(Double.NEGATIVE_INFINITY, 11.0));
Assert.assertEquals(5.0, set.getSize(), 1.0e-10);
Assert.assertEquals(5.9, ((Point1D) set.getBarycenter()).getAbscissa(), 1.0e-10);
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(0.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(4.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(12.0)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(1.2)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(5.9)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(9.01)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(5.0)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(11.0)));
Assert.assertEquals(5.9, ((Vector1D) set.getBarycenter()).getX(), 1.0e-10);
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(0.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(4.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(12.0)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(1.2)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(5.9)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(9.01)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(5.0)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(11.0)));
Assert.assertEquals( 1.0, set.getInf(), 1.0e-10);
Assert.assertEquals(11.0, set.getSup(), 1.0e-10);

View File

@ -18,7 +18,6 @@ package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.threed.Line;
import org.apache.commons.math.geometry.euclidean.threed.Plane;
import org.apache.commons.math.geometry.euclidean.threed.Point3D;
import org.apache.commons.math.geometry.euclidean.threed.Rotation;
import org.apache.commons.math.geometry.euclidean.threed.Vector3D;
import org.junit.Assert;
@ -29,22 +28,22 @@ public class PlaneTest {
@Test
public void testContains() {
Plane p = new Plane(new Vector3D(0, 0, 1), new Vector3D(0, 0, 1));
Assert.assertTrue(p.contains(new Point3D(0, 0, 1)));
Assert.assertTrue(p.contains(new Point3D(17, -32, 1)));
Assert.assertTrue(! p.contains(new Point3D(17, -32, 1.001)));
Assert.assertTrue(p.contains(new Vector3D(0, 0, 1)));
Assert.assertTrue(p.contains(new Vector3D(17, -32, 1)));
Assert.assertTrue(! p.contains(new Vector3D(17, -32, 1.001)));
}
@Test
public void testOffset() {
Vector3D p1 = new Vector3D(1, 1, 1);
Plane p = new Plane(p1, new Vector3D(0.2, 0, 0));
Assert.assertEquals(-5.0, p.getOffset(new Point3D(-4, 0, 0)), 1.0e-10);
Assert.assertEquals(+5.0, p.getOffset(new Point3D(6, 10, -12)), 1.0e-10);
Assert.assertEquals(-5.0, p.getOffset(new Vector3D(-4, 0, 0)), 1.0e-10);
Assert.assertEquals(+5.0, p.getOffset(new Vector3D(6, 10, -12)), 1.0e-10);
Assert.assertEquals(0.3,
p.getOffset(new Point3D(1.0, p1, 0.3, p.getNormal())),
p.getOffset(new Vector3D(1.0, p1, 0.3, p.getNormal())),
1.0e-10);
Assert.assertEquals(-0.3,
p.getOffset(new Point3D(1.0, p1, -0.3, p.getNormal())),
p.getOffset(new Vector3D(1.0, p1, -0.3, p.getNormal())),
1.0e-10);
}
@ -56,9 +55,9 @@ public class PlaneTest {
@Test
public void testThreePoints() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3);
Assert.assertTrue(p.contains(p1));
Assert.assertTrue(p.contains(p2));
@ -67,9 +66,9 @@ public class PlaneTest {
@Test
public void testRotate() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3);
Vector3D oldNormal = p.getNormal();
@ -92,9 +91,9 @@ public class PlaneTest {
@Test
public void testTranslate() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3);
p = p.translate(new Vector3D(2.0, p.getU(), -1.5, p.getV()));
@ -118,7 +117,7 @@ public class PlaneTest {
public void testIntersection() {
Plane p = new Plane(new Vector3D(1, 2, 3), new Vector3D(-4, 1, -5));
Line l = new Line(new Vector3D(0.2, -3.5, 0.7), new Vector3D(1, 1, -1));
Point3D point = p.intersection(l);
Vector3D point = p.intersection(l);
Assert.assertTrue(p.contains(point));
Assert.assertTrue(l.contains(point));
Assert.assertNull(p.intersection(new Line(new Vector3D(10, 10, 10),

View File

@ -16,20 +16,13 @@
*/
package org.apache.commons.math.geometry.euclidean.threed;
import java.util.Arrays;
import org.apache.commons.math.geometry.euclidean.threed.Plane;
import org.apache.commons.math.geometry.euclidean.threed.Point3D;
import org.apache.commons.math.geometry.euclidean.threed.PolyhedronsSet;
import org.apache.commons.math.geometry.euclidean.threed.Rotation;
import org.apache.commons.math.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math.geometry.euclidean.twod.Point2D;
import org.apache.commons.math.geometry.euclidean.twod.PolygonsSet;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.BoundaryAttribute;
import org.apache.commons.math.geometry.partitioning.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
@ -53,41 +46,41 @@ public class PolyhedronsSetTest {
boolean zOK = (z >= 0.0) && (z <= 1.0);
Region.Location expected =
(xOK && yOK && zOK) ? Region.Location.INSIDE : Region.Location.OUTSIDE;
Assert.assertEquals(expected, tree.checkPoint(new Point3D(x, y, z)));
Assert.assertEquals(expected, tree.checkPoint(new Vector3D(x, y, z)));
}
}
}
checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] {
new Point3D(0.0, 0.5, 0.5),
new Point3D(1.0, 0.5, 0.5),
new Point3D(0.5, 0.0, 0.5),
new Point3D(0.5, 1.0, 0.5),
new Point3D(0.5, 0.5, 0.0),
new Point3D(0.5, 0.5, 1.0)
checkPoints(Region.Location.BOUNDARY, tree, new Vector3D[] {
new Vector3D(0.0, 0.5, 0.5),
new Vector3D(1.0, 0.5, 0.5),
new Vector3D(0.5, 0.0, 0.5),
new Vector3D(0.5, 1.0, 0.5),
new Vector3D(0.5, 0.5, 0.0),
new Vector3D(0.5, 0.5, 1.0)
});
checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] {
new Point3D(0.0, 1.2, 1.2),
new Point3D(1.0, 1.2, 1.2),
new Point3D(1.2, 0.0, 1.2),
new Point3D(1.2, 1.0, 1.2),
new Point3D(1.2, 1.2, 0.0),
new Point3D(1.2, 1.2, 1.0)
checkPoints(Region.Location.OUTSIDE, tree, new Vector3D[] {
new Vector3D(0.0, 1.2, 1.2),
new Vector3D(1.0, 1.2, 1.2),
new Vector3D(1.2, 0.0, 1.2),
new Vector3D(1.2, 1.0, 1.2),
new Vector3D(1.2, 1.2, 0.0),
new Vector3D(1.2, 1.2, 1.0)
});
}
@Test
public void testTetrahedron() {
Point3D vertex1 = new Point3D(1, 2, 3);
Point3D vertex2 = new Point3D(2, 2, 4);
Point3D vertex3 = new Point3D(2, 3, 3);
Point3D vertex4 = new Point3D(1, 3, 4);
Vector3D vertex1 = new Vector3D(1, 2, 3);
Vector3D vertex2 = new Vector3D(2, 2, 4);
Vector3D vertex3 = new Vector3D(2, 3, 3);
Vector3D vertex4 = new Vector3D(1, 3, 4);
@SuppressWarnings("unchecked")
PolyhedronsSet tree =
(PolyhedronsSet) Region.buildConvex(Arrays.asList(new Hyperplane[] {
(PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
new Plane(vertex3, vertex2, vertex1),
new Plane(vertex2, vertex3, vertex4),
new Plane(vertex4, vertex3, vertex1),
new Plane(vertex1, vertex2, vertex4)
}));
new Plane(vertex1, vertex2, vertex4));
Assert.assertEquals(1.0 / 3.0, tree.getSize(), 1.0e-10);
Assert.assertEquals(2.0 * FastMath.sqrt(3.0), tree.getBoundarySize(), 1.0e-10);
Vector3D barycenter = (Vector3D) tree.getBarycenter();
@ -95,18 +88,18 @@ public class PolyhedronsSetTest {
Assert.assertEquals(2.5, barycenter.getY(), 1.0e-10);
Assert.assertEquals(3.5, barycenter.getZ(), 1.0e-10);
double third = 1.0 / 3.0;
checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] {
checkPoints(Region.Location.BOUNDARY, tree, new Vector3D[] {
vertex1, vertex2, vertex3, vertex4,
new Point3D(third, vertex1, third, vertex2, third, vertex3),
new Point3D(third, vertex2, third, vertex3, third, vertex4),
new Point3D(third, vertex3, third, vertex4, third, vertex1),
new Point3D(third, vertex4, third, vertex1, third, vertex2)
new Vector3D(third, vertex1, third, vertex2, third, vertex3),
new Vector3D(third, vertex2, third, vertex3, third, vertex4),
new Vector3D(third, vertex3, third, vertex4, third, vertex1),
new Vector3D(third, vertex4, third, vertex1, third, vertex2)
});
checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] {
new Point3D(1, 2, 4),
new Point3D(2, 2, 3),
new Point3D(2, 3, 4),
new Point3D(1, 3, 3)
checkPoints(Region.Location.OUTSIDE, tree, new Vector3D[] {
new Vector3D(1, 2, 4),
new Vector3D(2, 2, 3),
new Vector3D(2, 3, 4),
new Vector3D(1, 3, 3)
});
}
@ -116,13 +109,13 @@ public class PolyhedronsSetTest {
Vector3D vertex2 = new Vector3D(2.0, 2.4, 4.2);
Vector3D vertex3 = new Vector3D(2.8, 3.3, 3.7);
Vector3D vertex4 = new Vector3D(1.0, 3.6, 4.5);
@SuppressWarnings("unchecked")
PolyhedronsSet tree =
(PolyhedronsSet) Region.buildConvex(Arrays.asList(new Hyperplane[] {
(PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
new Plane(vertex3, vertex2, vertex1),
new Plane(vertex2, vertex3, vertex4),
new Plane(vertex4, vertex3, vertex1),
new Plane(vertex1, vertex2, vertex4)
}));
new Plane(vertex1, vertex2, vertex4));
Vector3D barycenter = (Vector3D) tree.getBarycenter();
Vector3D s = new Vector3D(10.2, 4.3, -6.7);
Vector3D c = new Vector3D(-0.2, 2.1, -3.2);
@ -152,29 +145,30 @@ public class PolyhedronsSetTest {
1.0, c,
1.0, r.applyTo(vertex4.subtract(c)))
};
tree.getTree(true).visit(new BSPTreeVisitor() {
tree.getTree(true).visit(new BSPTreeVisitor<Euclidean3D>() {
public Order visitOrder(BSPTree node) {
public Order visitOrder(BSPTree<Euclidean3D> node) {
return Order.MINUS_SUB_PLUS;
}
public void visitInternalNode(BSPTree node) {
Region.BoundaryAttribute attribute =
(Region.BoundaryAttribute) node.getAttribute();
public void visitInternalNode(BSPTree<Euclidean3D> node) {
@SuppressWarnings("unchecked")
BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) {
checkFacet(attribute.getPlusOutside());
checkFacet((SubPlane) attribute.getPlusOutside());
}
if (attribute.getPlusInside() != null) {
checkFacet(attribute.getPlusInside());
checkFacet((SubPlane) attribute.getPlusInside());
}
}
public void visitLeafNode(BSPTree node) {
public void visitLeafNode(BSPTree<Euclidean3D> node) {
}
private void checkFacet(SubHyperplane facet) {
private void checkFacet(SubPlane facet) {
Plane plane = (Plane) facet.getHyperplane();
Point2D[][] vertices =
Vector2D[][] vertices =
((PolygonsSet) facet.getRemainingRegion()).getVertices();
Assert.assertEquals(1, vertices.length);
for (int i = 0; i < vertices[0].length; ++i) {
@ -222,8 +216,8 @@ public class PolyhedronsSetTest {
new PolyhedronsSet(x - w, x + w, y - l, y + l, z - w, z + w);
PolyhedronsSet zBeam =
new PolyhedronsSet(x - w, x + w, y - w, y + w, z - l, z + l);
PolyhedronsSet tree =
(PolyhedronsSet) Region.union(xBeam, Region.union(yBeam, zBeam));
RegionFactory<Euclidean3D> factory = new RegionFactory<Euclidean3D>();
PolyhedronsSet tree = (PolyhedronsSet) factory.union(xBeam, factory.union(yBeam, zBeam));
Vector3D barycenter = (Vector3D) tree.getBarycenter();
Assert.assertEquals(x, barycenter.getX(), 1.0e-10);
@ -234,7 +228,7 @@ public class PolyhedronsSetTest {
}
private void checkPoints(Region.Location expected, PolyhedronsSet tree, Point3D[] points) {
private void checkPoints(Region.Location expected, PolyhedronsSet tree, Vector3D[] points) {
for (int i = 0; i < points.length; ++i) {
Assert.assertEquals(expected, tree.checkPoint(points[i]));
}

View File

@ -16,9 +16,10 @@
*/
package org.apache.commons.math.geometry.euclidean.twod;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.euclidean.twod.Line;
import org.apache.commons.math.geometry.euclidean.twod.Point2D;
import org.apache.commons.math.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math.geometry.partitioning.Transform;
import org.apache.commons.math.util.FastMath;
import org.junit.Assert;
@ -30,48 +31,48 @@ public class LineTest {
@Test
public void testContains() {
Line l = new Line(new Point2D(0, 1), new Point2D(1, 2));
Assert.assertTrue(l.contains(new Point2D(0, 1)));
Assert.assertTrue(l.contains(new Point2D(1, 2)));
Assert.assertTrue(l.contains(new Point2D(7, 8)));
Assert.assertTrue(! l.contains(new Point2D(8, 7)));
Line l = new Line(new Vector2D(0, 1), new Vector2D(1, 2));
Assert.assertTrue(l.contains(new Vector2D(0, 1)));
Assert.assertTrue(l.contains(new Vector2D(1, 2)));
Assert.assertTrue(l.contains(new Vector2D(7, 8)));
Assert.assertTrue(! l.contains(new Vector2D(8, 7)));
}
@Test
public void testAbscissa() {
Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2));
Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2));
Assert.assertEquals(0.0,
((Point1D) l.toSubSpace(new Point2D(-3, 4))).getAbscissa(),
((Vector1D) l.toSubSpace(new Vector2D(-3, 4))).getX(),
1.0e-10);
Assert.assertEquals(0.0,
((Point1D) l.toSubSpace(new Point2D( 3, -4))).getAbscissa(),
((Vector1D) l.toSubSpace(new Vector2D( 3, -4))).getX(),
1.0e-10);
Assert.assertEquals(-5.0,
((Point1D) l.toSubSpace(new Point2D( 7, -1))).getAbscissa(),
((Vector1D) l.toSubSpace(new Vector2D( 7, -1))).getX(),
1.0e-10);
Assert.assertEquals( 5.0,
((Point1D) l.toSubSpace(new Point2D(-1, -7))).getAbscissa(),
((Vector1D) l.toSubSpace(new Vector2D(-1, -7))).getX(),
1.0e-10);
}
@Test
public void testOffset() {
Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2));
Assert.assertEquals(-5.0, l.getOffset(new Point2D(5, -3)), 1.0e-10);
Assert.assertEquals(+5.0, l.getOffset(new Point2D(-5, 2)), 1.0e-10);
Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2));
Assert.assertEquals(-5.0, l.getOffset(new Vector2D(5, -3)), 1.0e-10);
Assert.assertEquals(+5.0, l.getOffset(new Vector2D(-5, 2)), 1.0e-10);
}
@Test
public void testPointAt() {
Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2));
Line l = new Line(new Vector2D(2, 1), new Vector2D(-2, -2));
for (double a = -2.0; a < 2.0; a += 0.2) {
Point1D pA = new Point1D(a);
Point2D point = (Point2D) l.toSpace(pA);
Assert.assertEquals(a, ((Point1D) l.toSubSpace(point)).getAbscissa(), 1.0e-10);
Vector1D pA = new Vector1D(a);
Vector2D point = (Vector2D) l.toSpace(pA);
Assert.assertEquals(a, ((Vector1D) l.toSubSpace(point)).getX(), 1.0e-10);
Assert.assertEquals(0.0, l.getOffset(point), 1.0e-10);
for (double o = -2.0; o < 2.0; o += 0.2) {
point = l.getPointAt(pA, o);
Assert.assertEquals(a, ((Point1D) l.toSubSpace(point)).getAbscissa(), 1.0e-10);
Assert.assertEquals(a, ((Vector1D) l.toSubSpace(point)).getX(), 1.0e-10);
Assert.assertEquals(o, l.getOffset(point), 1.0e-10);
}
}
@ -79,38 +80,36 @@ public class LineTest {
@Test
public void testOriginOffset() {
Line l1 = new Line(new Point2D(0, 1), new Point2D(1, 2));
Line l1 = new Line(new Vector2D(0, 1), new Vector2D(1, 2));
Assert.assertEquals(FastMath.sqrt(0.5), l1.getOriginOffset(), 1.0e-10);
Line l2 = new Line(new Point2D(1, 2), new Point2D(0, 1));
Line l2 = new Line(new Vector2D(1, 2), new Vector2D(0, 1));
Assert.assertEquals(-FastMath.sqrt(0.5), l2.getOriginOffset(), 1.0e-10);
}
@Test
public void testParallel() {
Line l1 = new Line(new Point2D(0, 1), new Point2D(1, 2));
Line l2 = new Line(new Point2D(2, 2), new Point2D(3, 3));
Line l1 = new Line(new Vector2D(0, 1), new Vector2D(1, 2));
Line l2 = new Line(new Vector2D(2, 2), new Vector2D(3, 3));
Assert.assertTrue(l1.isParallelTo(l2));
Line l3 = new Line(new Point2D(1, 0), new Point2D(0.5, -0.5));
Line l3 = new Line(new Vector2D(1, 0), new Vector2D(0.5, -0.5));
Assert.assertTrue(l1.isParallelTo(l3));
Line l4 = new Line(new Point2D(1, 0), new Point2D(0.5, -0.51));
Line l4 = new Line(new Vector2D(1, 0), new Vector2D(0.5, -0.51));
Assert.assertTrue(! l1.isParallelTo(l4));
}
@Test
public void testTransform() {
Line l1 = new Line(new Point2D(1.0 ,1.0), new Point2D(4.0 ,1.0));
Transform t1 = Line.getTransform(new AffineTransform(0.0, 0.5,
-1.0, 0.0,
1.0, 1.5));
Line l1 = new Line(new Vector2D(1.0 ,1.0), new Vector2D(4.0 ,1.0));
Transform<Euclidean2D, Euclidean1D> t1 =
Line.getTransform(new AffineTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5));
Assert.assertEquals(0.5 * FastMath.PI,
((Line) t1.apply(l1)).getAngle(),
1.0e-10);
Line l2 = new Line(new Point2D(0.0, 0.0), new Point2D(1.0, 1.0));
Transform t2 = Line.getTransform(new AffineTransform(0.0, 0.5,
-1.0, 0.0,
1.0, 1.5));
Line l2 = new Line(new Vector2D(0.0, 0.0), new Vector2D(1.0, 1.0));
Transform<Euclidean2D, Euclidean1D> t2 =
Line.getTransform(new AffineTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5));
Assert.assertEquals(FastMath.atan2(1.0, -2.0),
((Line) t2.apply(l2)).getAngle(),
1.0e-10);
@ -119,11 +118,11 @@ public class LineTest {
@Test
public void testIntersection() {
Line l1 = new Line(new Point2D( 0, 1), new Point2D(1, 2));
Line l2 = new Line(new Point2D(-1, 2), new Point2D(2, 1));
Point2D p = (Point2D) l1.intersection(l2);
Assert.assertEquals(0.5, p.x, 1.0e-10);
Assert.assertEquals(1.5, p.y, 1.0e-10);
Line l1 = new Line(new Vector2D( 0, 1), new Vector2D(1, 2));
Line l2 = new Line(new Vector2D(-1, 2), new Vector2D(2, 1));
Vector2D p = (Vector2D) l1.intersection(l2);
Assert.assertEquals(0.5, p.getX(), 1.0e-10);
Assert.assertEquals(1.5, p.getY(), 1.0e-10);
}
}