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. /** This class represents a 1D interval.
* @see IntervalsSet * @see IntervalsSet
* @version $Revision$ $Date$ * @version $Id:$
* @since 3.0
*/ */
public class Interval { public class Interval {

View File

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

View File

@ -16,28 +16,22 @@
*/ */
package org.apache.commons.math.geometry.euclidean.oned; package org.apache.commons.math.geometry.euclidean.oned;
import org.apache.commons.math.exception.MathUnsupportedOperationException; import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.geometry.partitioning.BSPTree;
import org.apache.commons.math.geometry.partitioning.Hyperplane; 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.Region;
import org.apache.commons.math.geometry.partitioning.SubHyperplane; import org.apache.commons.math.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.SubSpace;
/** This class represents a 1D oriented hyperplane. /** This class represents a 1D oriented hyperplane.
* <p>An hyperplane in 1D is a simple point, its orientation being a * <p>An hyperplane in 1D is a simple point, its orientation being a
* boolean.</p> * boolean.</p>
* <p>Instances of this class are guaranteed to be immutable.</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. */ /** Vector location. */
private static final Region DUMMY_REGION = new DummyRegion(); private Vector1D location;
/** Point location. */
private Point1D location;
/** Orientation. */ /** Orientation. */
private boolean direct; private boolean direct;
@ -45,9 +39,9 @@ public class OrientedPoint implements Hyperplane {
/** Simple constructor. /** Simple constructor.
* @param location location of the hyperplane * @param location location of the hyperplane
* @param direct if true, the plus side of the hyperplane is towards * @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.location = location;
this.direct = direct; this.direct = direct;
} }
@ -57,62 +51,16 @@ public class OrientedPoint implements Hyperplane {
* the instance.</p> * the instance.</p>
* @return the instance itself * @return the instance itself
*/ */
public Hyperplane copySelf() { public OrientedPoint copySelf() {
return this; return this;
} }
/** Get the offset (oriented distance) of a point to the hyperplane. /** {@inheritDoc} */
* @param point point to check public double getOffset(final Vector<Euclidean1D> point) {
* @return offset of the point final double delta = ((Vector1D) point).getX() - location.getX();
*/
public double getOffset(final Point point) {
final double delta = ((Point1D) point).getAbscissa() - location.getAbscissa();
return direct ? delta : -delta; 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. /** Build a region covering the whole hyperplane.
* <p>Since this class represent zero dimension spaces which does * <p>Since this class represent zero dimension spaces which does
* not have lower dimension sub-spaces, this method returns a dummy * 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> * properly, it should <em>not</em> be used otherwise.</p>
* @return a dummy region * @return a dummy region
*/ */
public Region wholeHyperplane() { public SubOrientedPoint wholeHyperplane() {
return DUMMY_REGION; return new SubOrientedPoint(this, null);
} }
/** Build a region covering the whole space. /** Build a region covering the whole space.
* @return a region containing the instance (really an {@link * @return a region containing the instance (really an {@link
* IntervalsSet IntervalsSet} instance) * IntervalsSet IntervalsSet} instance)
*/ */
public Region wholeSpace() { public IntervalsSet wholeSpace() {
return new IntervalsSet(); return new IntervalsSet();
} }
@ -147,40 +95,14 @@ public class OrientedPoint implements Hyperplane {
* @return true if the instance and the other hyperplane have * @return true if the instance and the other hyperplane have
* the same orientation * the same orientation
*/ */
public boolean sameOrientationAs(final Hyperplane other) { public boolean sameOrientationAs(final Hyperplane<Euclidean1D> other) {
return !(direct ^ ((OrientedPoint) other).direct); 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. /** Get the hyperplane location on the real line.
* @return the hyperplane location * @return the hyperplane location
*/ */
public Point1D getLocation() { public Vector1D getLocation() {
return location; return location;
} }
@ -198,25 +120,4 @@ public class OrientedPoint implements Hyperplane {
direct = !direct; 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; package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.oned.Point1D; import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.partitioning.Point; import org.apache.commons.math.geometry.euclidean.oned.Euclidean1D;
import org.apache.commons.math.geometry.partitioning.SubSpace; import org.apache.commons.math.geometry.euclidean.oned.Vector1D;
import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
/** The class represent lines in a three dimensional space. /** 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 * which is closest to the origin. Abscissa increases in the line
* direction.</p> * direction.</p>
* @version $Revision$ $Date$ * @version $Id:$
* @since 3.0
*/ */
public class Line implements SubSpace { public class Line implements Embedding<Euclidean3D, Euclidean1D> {
/** Line direction. */ /** Line direction. */
private Vector3D direction; private Vector3D direction;
/** Line point closest to the origin. */ /** Line point closest to the origin. */
private Point3D zero; private Vector3D zero;
/** Build a line from a point and a direction. /** Build a line from a point and a direction.
* @param p point belonging to the line (this can be any point) * @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"); throw new IllegalArgumentException("null norm");
} }
this.direction = new Vector3D(1.0 / norm, dir); 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() { public Line revert() {
direction = direction.negate(); return new Line(zero, direction.negate());
} }
/** Get the normalized direction vector. /** 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} * @param point point to check (must be a {@link Vector3D Vector3D}
* instance) * instance)
* @return abscissa of the point (really a * @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) { public Vector1D toSubSpace(final Vector<Euclidean3D> point) {
final double x = Vector3D.dotProduct(((Vector3D) point).subtract(zero), direction); Vector3D p3 = (Vector3D) point;
return new Point1D(x); return new Vector1D(Vector3D.dotProduct(p3.subtract(zero), direction));
} }
/** Get one point from the line. /** Get one point from the line.
* @param point desired abscissa for the point (must be a * @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 * @return one point belonging to the line, at specified abscissa
* (really a {@link Vector3D Vector3D} instance) * (really a {@link Vector3D Vector3D} instance)
*/ */
public Point toSpace(final Point point) { public Vector3D toSpace(final Vector<Euclidean1D> point) {
return new Point3D(1.0, zero, ((Point1D) point).getAbscissa(), direction); Vector1D p1 = (Vector1D) point;
return new Vector3D(1.0, zero, p1.getX(), direction);
} }
/** Check if the instance is similar to another line. /** Check if the instance is similar to another line.

View File

@ -16,20 +16,24 @@
*/ */
package org.apache.commons.math.geometry.euclidean.threed; 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.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.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor; 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.geometry.partitioning.SubHyperplane;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
import java.util.ArrayList;
/** Extractor for {@link PolygonsSet polyhedrons sets} outlines. /** Extractor for {@link PolygonsSet polyhedrons sets} outlines.
* <p>This class extracts the 2D outlines from {{@link PolygonsSet * <p>This class extracts the 2D outlines from {{@link PolygonsSet
* polyhedrons sets} in a specified projection plane.</p> * polyhedrons sets} in a specified projection plane.</p>
* @version $Revision$ $Date$ * @version $Id:$
* @since 3.0
*/ */
public class OutlineExtractor { public class OutlineExtractor {
@ -56,7 +60,7 @@ public class OutlineExtractor {
* @param polyhedronsSet polyhedrons set whose outline must be extracted * @param polyhedronsSet polyhedrons set whose outline must be extracted
* @return an outline, as an array of loops. * @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 // project all boundary facets into one polygons set
final BoundaryProjector projector = new BoundaryProjector(); final BoundaryProjector projector = new BoundaryProjector();
@ -64,9 +68,9 @@ public class OutlineExtractor {
final PolygonsSet projected = projector.getProjected(); final PolygonsSet projected = projector.getProjected();
// Remove the spurious intermediate vertices from the outline // 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) { for (int i = 0; i < outline.length; ++i) {
final Point2D[] rawLoop = outline[i]; final Vector2D[] rawLoop = outline[i];
int end = rawLoop.length; int end = rawLoop.length;
int j = 0; int j = 0;
while (j < end) { while (j < end) {
@ -83,7 +87,7 @@ public class OutlineExtractor {
} }
if (end != rawLoop.length) { if (end != rawLoop.length) {
// resize the array // resize the array
outline[i] = new Point2D[end]; outline[i] = new Vector2D[end];
System.arraycopy(rawLoop, 0, outline[i], 0, 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) * @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 * @return true if the point is exactly between its neighbours
*/ */
private boolean pointIsBetween(final Point2D[] loop, final int n, final int i) { private boolean pointIsBetween(final Vector2D[] loop, final int n, final int i) {
final Point2D previous = loop[(i + n - 1) % n]; final Vector2D previous = loop[(i + n - 1) % n];
final Point2D current = loop[i]; final Vector2D current = loop[i];
final Point2D next = loop[(i + 1) % n]; final Vector2D next = loop[(i + 1) % n];
final double dx1 = current.x - previous.x; final double dx1 = current.getX() - previous.getX();
final double dy1 = current.y - previous.y; final double dy1 = current.getY() - previous.getY();
final double dx2 = next.x - current.x; final double dx2 = next.getX() - current.getX();
final double dy2 = next.y - current.y; final double dy2 = next.getY() - current.getY();
final double cross = dx1 * dy2 - dx2 * dy1; final double cross = dx1 * dy2 - dx2 * dy1;
final double dot = dx1 * dx2 + dy1 * dy2; final double dot = dx1 * dx2 + dy1 * dy2;
final double d1d2 = FastMath.sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * 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. */ /** 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. */ /** Projection of the polyhedrons set on the plane. */
private PolygonsSet projected; private PolygonsSet projected;
@ -123,18 +127,19 @@ public class OutlineExtractor {
/** Simple constructor. /** Simple constructor.
*/ */
public BoundaryProjector() { public BoundaryProjector() {
projected = new PolygonsSet(new BSPTree(Boolean.FALSE)); projected = new PolygonsSet(new BSPTree<Euclidean2D>(Boolean.FALSE));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Order visitOrder(final BSPTree node) { public Order visitOrder(final BSPTree<Euclidean3D> node) {
return Order.MINUS_SUB_PLUS; return Order.MINUS_SUB_PLUS;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) { public void visitInternalNode(final BSPTree<Euclidean3D> node) {
final Region.BoundaryAttribute attribute = @SuppressWarnings("unchecked")
(Region.BoundaryAttribute) node.getAttribute(); final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) { if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false); addContribution(attribute.getPlusOutside(), false);
} }
@ -144,19 +149,22 @@ public class OutlineExtractor {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) { public void visitLeafNode(final BSPTree<Euclidean3D> node) {
} }
/** Add he contribution of a boundary facet. /** Add he contribution of a boundary facet.
* @param facet boundary facet * @param facet boundary facet
* @param reversed if true, the facet has the inside on its plus side * @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 // extract the vertices of the facet
@SuppressWarnings("unchecked")
final AbstractSubHyperplane<Euclidean3D, Euclidean2D> absFacet =
(AbstractSubHyperplane<Euclidean3D, Euclidean2D>) facet;
final Plane plane = (Plane) facet.getHyperplane(); final Plane plane = (Plane) facet.getHyperplane();
Point2D[][] vertices = Vector2D[][] vertices =
((PolygonsSet) facet.getRemainingRegion()).getVertices(); ((PolygonsSet) absFacet.getRemainingRegion()).getVertices();
final double scal = Vector3D.dotProduct(plane.getNormal(), w); final double scal = Vector3D.dotProduct(plane.getNormal(), w);
if (FastMath.abs(scal) > 1.0e-3) { if (FastMath.abs(scal) > 1.0e-3) {
@ -164,10 +172,10 @@ public class OutlineExtractor {
if ((scal < 0) ^ reversed) { if ((scal < 0) ^ reversed) {
// the facet is seen from the inside, // the facet is seen from the inside,
// we need to invert its boundary orientation // 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) { for (int i = 0; i < vertices.length; ++i) {
final Point2D[] loop = vertices[i]; final Vector2D[] loop = vertices[i];
final Point2D[] newLoop = new Point2D[loop.length]; final Vector2D[] newLoop = new Vector2D[loop.length];
if (loop[0] == null) { if (loop[0] == null) {
newLoop[0] = null; newLoop[0] = null;
for (int j = 1; j < loop.length; ++j) { 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 // compute the projection of the facet in the outline plane
final ArrayList<SubHyperplane> edges = new ArrayList<SubHyperplane>(); final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
for (Point2D[] loop : vertices) { for (Vector2D[] loop : vertices) {
final boolean closed = loop[0] != null; final boolean closed = loop[0] != null;
int previous = closed ? (loop.length - 1) : 1; int previous = closed ? (loop.length - 1) : 1;
Vector3D previous3D = (Vector3D) plane.toSpace(loop[previous]); Vector3D previous3D = (Vector3D) plane.toSpace(loop[previous]);
int current = (previous + 1) % loop.length; 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)); Vector3D.dotProduct(previous3D, v));
while (current < loop.length) { while (current < loop.length) {
final Vector3D current3D = (Vector3D) plane.toSpace(loop[current]); 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)); Vector3D.dotProduct(current3D, v));
final org.apache.commons.math.geometry.euclidean.twod.Line line = final org.apache.commons.math.geometry.euclidean.twod.Line line =
new org.apache.commons.math.geometry.euclidean.twod.Line(pPoint, cPoint); 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)) { if (closed || (previous != 1)) {
// the previous point is a real vertex // the previous point is a real vertex
@ -210,7 +218,7 @@ public class OutlineExtractor {
final double angle = line.getAngle() + 0.5 * FastMath.PI; final double angle = line.getAngle() + 0.5 * FastMath.PI;
final org.apache.commons.math.geometry.euclidean.twod.Line l = final org.apache.commons.math.geometry.euclidean.twod.Line l =
new org.apache.commons.math.geometry.euclidean.twod.Line(pPoint, angle); 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))) { if (closed || (current != (loop.length - 1))) {
@ -219,7 +227,7 @@ public class OutlineExtractor {
final double angle = line.getAngle() + 0.5 * FastMath.PI; final double angle = line.getAngle() + 0.5 * FastMath.PI;
final org.apache.commons.math.geometry.euclidean.twod.Line l = final org.apache.commons.math.geometry.euclidean.twod.Line l =
new org.apache.commons.math.geometry.euclidean.twod.Line(cPoint, angle); new org.apache.commons.math.geometry.euclidean.twod.Line(cPoint, angle);
edge = l.split(edge).getMinus(); edge = edge.split(l).getMinus();
} }
edges.add(edge); edges.add(edge);
@ -233,7 +241,7 @@ public class OutlineExtractor {
final PolygonsSet projectedFacet = new PolygonsSet(edges); final PolygonsSet projectedFacet = new PolygonsSet(edges);
// add the contribution of the facet to the global outline // 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; package org.apache.commons.math.geometry.euclidean.threed;
import org.apache.commons.math.geometry.euclidean.oned.Point1D; import org.apache.commons.math.geometry.Vector;
import org.apache.commons.math.geometry.euclidean.twod.Point2D; 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.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.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; import org.apache.commons.math.util.FastMath;
/** The class represent planes in a three dimensional space. /** 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. */ /** Offset of the origin with respect to the plane. */
private double originOffset; private double originOffset;
/** Origin of the plane frame. */ /** Origin of the plane frame. */
private Point3D origin; private Vector3D origin;
/** First vector of the plane frame (in plane). */ /** First vector of the plane frame (in plane). */
private Vector3D u; private Vector3D u;
@ -100,7 +99,7 @@ public class Plane implements Hyperplane {
* shared (except for immutable objects).</p> * shared (except for immutable objects).</p>
* @return a new hyperplane, copy of the instance * @return a new hyperplane, copy of the instance
*/ */
public Hyperplane copySelf() { public Plane copySelf() {
return new Plane(this); return new Plane(this);
} }
@ -143,7 +142,7 @@ public class Plane implements Hyperplane {
/** Reset the plane frame. /** Reset the plane frame.
*/ */
private void setFrame() { private void setFrame() {
origin = new Point3D(-originOffset, w); origin = new Vector3D(-originOffset, w);
u = w.orthogonal(); u = w.orthogonal();
v = Vector3D.crossProduct(w, u); 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 * @return the origin point of the plane frame (point closest to the
* 3D-space origin) * 3D-space origin)
*/ */
public Point3D getOrigin() { public Vector3D getOrigin() {
return origin; return origin;
} }
@ -217,24 +216,24 @@ public class Plane implements Hyperplane {
* @param point point of the space (must be a {@link Vector3D * @param point point of the space (must be a {@link Vector3D
* Vector3D} instance) * Vector3D} instance)
* @return in-plane point (really a {@link * @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 * @see #toSpace
*/ */
public Point toSubSpace(final Point point) { public Vector2D toSubSpace(final Vector<Euclidean3D> point) {
final Vector3D p3D = (Vector3D) point; final Vector3D p3D = (Vector3D) point;
return new Point2D(Vector3D.dotProduct(p3D, u), return new Vector2D(Vector3D.dotProduct(p3D, u),
Vector3D.dotProduct(p3D, v)); Vector3D.dotProduct(p3D, v));
} }
/** Transform an in-plane point into a 3D space point. /** Transform an in-plane point into a 3D space point.
* @param point in-plane point (must be a {@link * @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) * @return 3D space point (really a {@link Vector3D Vector3D} instance)
* @see #toSubSpace * @see #toSubSpace
*/ */
public Point toSpace(final Point point) { public Vector3D toSpace(final Vector<Euclidean2D> point) {
final Point2D p2D = (Point2D) point; final Vector2D p2D = (Vector2D) point;
return new Point3D(p2D.x, u, p2D.y, v, -originOffset, w); return new Vector3D(p2D.getX(), u, p2D.getY(), v, -originOffset, w);
} }
/** Get one point from the 3D-space. /** 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 * @return one point in the 3D-space, with given coordinates and offset
* relative to the plane * relative to the plane
*/ */
public Vector3D getPointAt(final Point2D inPlane, final double offset) { public Vector3D getPointAt(final Vector2D inPlane, final double offset) {
return new Vector3D(inPlane.x, u, inPlane.y, v, offset - originOffset, w); return new Vector3D(inPlane.getX(), u, inPlane.getY(), v, offset - originOffset, w);
} }
/** Check if the instance is similar to another plane. /** 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 * @return intersection point between between the line and the
* instance (null if the line is parallel to the instance) * 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 Vector3D direction = line.getDirection();
final double dot = Vector3D.dotProduct(w, direction); final double dot = Vector3D.dotProduct(w, direction);
if (FastMath.abs(dot) < 1.0e-10) { if (FastMath.abs(dot) < 1.0e-10) {
return null; 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; 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. /** 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 * @return line at the intersection of the instance and the
* other plane (really a {@link Line Line} instance) * other plane (really a {@link Line Line} instance)
*/ */
public SubSpace intersection(final Hyperplane other) { public Line intersection(final Plane other) {
final Plane otherP = (Plane) other; final Vector3D direction = Vector3D.crossProduct(w, other.w);
final Vector3D direction = Vector3D.crossProduct(w, otherP.w);
if (direction.getNorm() < 1.0e-10) { if (direction.getNorm() < 1.0e-10) {
return null; return null;
} }
return new Line(intersection(this, otherP, new Plane(direction)), return new Line(intersection(this, other, new Plane(direction)),
direction); direction);
} }
@ -374,15 +372,15 @@ public class Plane implements Hyperplane {
/** Build a region covering the whole hyperplane. /** Build a region covering the whole hyperplane.
* @return a region covering the whole hyperplane * @return a region covering the whole hyperplane
*/ */
public Region wholeHyperplane() { public SubPlane wholeHyperplane() {
return new PolygonsSet(); return new SubPlane(this, new PolygonsSet());
} }
/** Build a region covering the whole space. /** Build a region covering the whole space.
* @return a region containing the instance (really a {@link * @return a region containing the instance (really a {@link
* PolyhedronsSet PolyhedronsSet} instance) * PolyhedronsSet PolyhedronsSet} instance)
*/ */
public Region wholeSpace() { public PolyhedronsSet wholeSpace() {
return new PolyhedronsSet(); return new PolyhedronsSet();
} }
@ -390,7 +388,7 @@ public class Plane implements Hyperplane {
* @param p point to check * @param p point to check
* @return true if p belongs to the plane * @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; return FastMath.abs(getOffset(p)) < 1.0e-10;
} }
@ -416,7 +414,7 @@ public class Plane implements Hyperplane {
* @param point point to check * @param point point to check
* @return offset of the point * @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; 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 * @return true if the instance and the other hyperplane have
* the same orientation * 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; 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; package org.apache.commons.math.geometry.euclidean.threed;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.util.Arrays;
import java.util.Collection; 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.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor; 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.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Point;
import org.apache.commons.math.geometry.partitioning.Region; 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.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.Transform; import org.apache.commons.math.geometry.partitioning.Transform;
import org.apache.commons.math.geometry.partitioning.AbstractRegion;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
/** This class represents a 3D region: a set of polyhedrons. /** 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. /** 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> * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
* @param tree inside/outside BSP tree representing the region * @param tree inside/outside BSP tree representing the region
*/ */
public PolyhedronsSet(final BSPTree tree) { public PolyhedronsSet(final BSPTree<Euclidean3D> tree) {
super(tree); super(tree);
} }
@ -61,19 +67,19 @@ public class PolyhedronsSet extends Region {
* its plus side.</p> * its plus side.</p>
* <p>The boundary elements can be in any order, and can form * <p>The boundary elements can be in any order, and can form
* several non-connected sets (like for example polyhedrons with holes * 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 * fact, the elements do not even need to be connected together
* (their topological connections are not used here). However, if the * (their topological connections are not used here). However, if the
* boundary does not really separate an inside open from an outside * boundary does not really separate an inside open from an outside
* open (open having here its topological meaning), then subsequent * 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> * not be meaningful anymore.</p>
* <p>If the boundary is empty, the region will represent the whole * <p>If the boundary is empty, the region will represent the whole
* space.</p> * space.</p>
* @param boundary collection of boundary elements, as a * @param boundary collection of boundary elements, as a
* collection of {@link SubHyperplane SubHyperplane} objects * collection of {@link SubHyperplane SubHyperplane} objects
*/ */
public PolyhedronsSet(final Collection<SubHyperplane> boundary) { public PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary) {
super(boundary); super(boundary);
} }
@ -85,21 +91,21 @@ public class PolyhedronsSet extends Region {
* @param zMin low bound along the z direction * @param zMin low bound along the z direction
* @param zMax high bound along the z direction * @param zMax high bound along the z direction
*/ */
@SuppressWarnings("unchecked")
public PolyhedronsSet(final double xMin, final double xMax, public PolyhedronsSet(final double xMin, final double xMax,
final double yMin, final double yMax, final double yMin, final double yMax,
final double zMin, final double zMax) { 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(xMin, 0, 0), Vector3D.MINUS_I),
new Plane(new Vector3D(xMax, 0, 0), Vector3D.PLUS_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, yMin, 0), Vector3D.MINUS_J),
new Plane(new Vector3D(0, yMax, 0), Vector3D.PLUS_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, zMin), Vector3D.MINUS_K),
new Plane(new Vector3D(0, 0, zMax), Vector3D.PLUS_K) new Plane(new Vector3D(0, 0, zMax), Vector3D.PLUS_K)).getTree(false));
})).getTree(false));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Region buildNew(final BSPTree tree) { public PolyhedronsSet buildNew(final BSPTree<Euclidean3D> tree) {
return new PolyhedronsSet(tree); return new PolyhedronsSet(tree);
} }
@ -113,32 +119,34 @@ public class PolyhedronsSet extends Region {
// the polyhedrons set as a finite outside // the polyhedrons set as a finite outside
// surrounded by an infinite inside // surrounded by an infinite inside
setSize(Double.POSITIVE_INFINITY); setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point3D.UNDEFINED); setBarycenter(Vector3D.NaN);
} else { } else {
// the polyhedrons set is finite, apply the remaining scaling factors // the polyhedrons set is finite, apply the remaining scaling factors
setSize(getSize() / 3.0); 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. */ /** Visitor computing geometrical properties. */
private class FacetsContributionVisitor implements BSPTreeVisitor { private class FacetsContributionVisitor implements BSPTreeVisitor<Euclidean3D> {
/** Simple constructor. */ /** Simple constructor. */
public FacetsContributionVisitor() { public FacetsContributionVisitor() {
setSize(0); setSize(0);
setBarycenter(new Point3D(0, 0, 0)); setBarycenter(new Vector3D(0, 0, 0));
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Order visitOrder(final BSPTree node) { public Order visitOrder(final BSPTree<Euclidean3D> node) {
return Order.MINUS_SUB_PLUS; return Order.MINUS_SUB_PLUS;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) { public void visitInternalNode(final BSPTree<Euclidean3D> node) {
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute(); @SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) { if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false); addContribution(attribute.getPlusOutside(), false);
} }
@ -148,32 +156,32 @@ public class PolyhedronsSet extends Region {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) { public void visitLeafNode(final BSPTree<Euclidean3D> node) {
} }
/** Add he contribution of a boundary facet. /** Add he contribution of a boundary facet.
* @param facet boundary facet * @param facet boundary facet
* @param reversed if true, the facet has the inside on its plus side * @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(); final double area = polygon.getSize();
if (Double.isInfinite(area)) { if (Double.isInfinite(area)) {
setSize(Double.POSITIVE_INFINITY); setSize(Double.POSITIVE_INFINITY);
setBarycenter(Point3D.UNDEFINED); setBarycenter(Vector3D.NaN);
} else { } else {
final Plane plane = (Plane) facet.getHyperplane(); 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()); double scaled = area * Vector3D.dotProduct(facetB, plane.getNormal());
if (reversed) { if (reversed) {
scaled = -scaled; scaled = -scaled;
} }
setSize(getSize() + 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 * given point, or null if the line does not intersect any
* sub-hyperplaned * 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); 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 * given point, or null if the line does not intersect any
* sub-hyperplaned * sub-hyperplaned
*/ */
private SubHyperplane recurseFirstIntersection(final BSPTree node, private SubHyperplane<Euclidean3D> recurseFirstIntersection(final BSPTree<Euclidean3D> node,
final Vector3D point, final Vector3D point,
final Line line) { final Line line) {
final SubHyperplane cut = node.getCut(); final SubHyperplane<Euclidean3D> cut = node.getCut();
if (cut == null) { if (cut == null) {
return null; return null;
} }
final BSPTree minus = node.getMinus(); final BSPTree<Euclidean3D> minus = node.getMinus();
final BSPTree plus = node.getPlus(); final BSPTree<Euclidean3D> plus = node.getPlus();
final Plane plane = (Plane) cut.getHyperplane(); final Plane plane = (Plane) cut.getHyperplane();
// establish search order // 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 boolean in = FastMath.abs(offset) < 1.0e-10;
final BSPTree near; final BSPTree<Euclidean3D> near;
final BSPTree far; final BSPTree<Euclidean3D> far;
if (offset < 0) { if (offset < 0) {
near = minus; near = minus;
far = plus; far = plus;
@ -227,14 +235,14 @@ public class PolyhedronsSet extends Region {
if (in) { if (in) {
// search in the cut hyperplane // search in the cut hyperplane
final SubHyperplane facet = boundaryFacet(point, node); final SubHyperplane<Euclidean3D> facet = boundaryFacet(point, node);
if (facet != null) { if (facet != null) {
return facet; return facet;
} }
} }
// search in the near branch // search in the near branch
final SubHyperplane crossed = recurseFirstIntersection(near, point, line); final SubHyperplane<Euclidean3D> crossed = recurseFirstIntersection(near, point, line);
if (crossed != null) { if (crossed != null) {
return crossed; return crossed;
} }
@ -243,7 +251,7 @@ public class PolyhedronsSet extends Region {
// search in the cut hyperplane // search in the cut hyperplane
final Vector3D hit3D = plane.intersection(line); final Vector3D hit3D = plane.intersection(line);
if (hit3D != null) { if (hit3D != null) {
final SubHyperplane facet = boundaryFacet(hit3D, node); final SubHyperplane<Euclidean3D> facet = boundaryFacet(hit3D, node);
if (facet != null) { if (facet != null) {
return facet; return facet;
} }
@ -261,15 +269,18 @@ public class PolyhedronsSet extends Region {
* @return the boundary facet this points belongs to (or null if it * @return the boundary facet this points belongs to (or null if it
* does not belong to any boundary facet) * does not belong to any boundary facet)
*/ */
private SubHyperplane boundaryFacet(final Vector3D point, final BSPTree node) { private SubHyperplane<Euclidean3D> boundaryFacet(final Vector3D point,
final Point point2D = node.getCut().getHyperplane().toSubSpace((Point) point); final BSPTree<Euclidean3D> node) {
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute(); final Vector2D point2D = ((Plane) node.getCut().getHyperplane()).toSubSpace(point);
@SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if ((attribute.getPlusOutside() != null) && if ((attribute.getPlusOutside() != null) &&
(attribute.getPlusOutside().getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) { (((SubPlane) attribute.getPlusOutside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
return attribute.getPlusOutside(); return attribute.getPlusOutside();
} }
if ((attribute.getPlusInside() != null) && if ((attribute.getPlusInside() != null) &&
(attribute.getPlusInside().getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) { (((SubPlane) attribute.getPlusInside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
return attribute.getPlusInside(); return attribute.getPlusInside();
} }
return null; return null;
@ -286,7 +297,7 @@ public class PolyhedronsSet extends Region {
} }
/** 3D rotation as a Transform. */ /** 3D rotation as a Transform. */
private static class RotationTransform implements Transform { private static class RotationTransform implements Transform<Euclidean3D, Euclidean2D> {
/** Center point of the rotation. */ /** Center point of the rotation. */
private Vector3D center; private Vector3D center;
@ -295,10 +306,10 @@ public class PolyhedronsSet extends Region {
private Rotation rotation; private Rotation rotation;
/** Cached original hyperplane. */ /** Cached original hyperplane. */
private Hyperplane cachedOriginal; private Plane cachedOriginal;
/** Cached 2D transform valid inside the cached original hyperplane. */ /** Cached 2D transform valid inside the cached original hyperplane. */
private Transform cachedTransform; private Transform<Euclidean2D, Euclidean1D> cachedTransform;
/** Build a rotation transform. /** Build a rotation transform.
* @param center center point of the rotation * @param center center point of the rotation
@ -310,40 +321,41 @@ public class PolyhedronsSet extends Region {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Point apply(final Point point) { public Vector3D apply(final Vector<Euclidean3D> point) {
final Vector3D delta = ((Vector3D) point).subtract(center); 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} */ /** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) { public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
return ((Plane) hyperplane).rotate(center, rotation); return ((Plane) hyperplane).rotate(center, rotation);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub, public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
final Hyperplane original, final Hyperplane transformed) { final Hyperplane<Euclidean3D> original,
final Hyperplane<Euclidean3D> transformed) {
if (original != cachedOriginal) { if (original != cachedOriginal) {
// we have changed hyperplane, reset the in-hyperplane transform // we have changed hyperplane, reset the in-hyperplane transform
final Plane oPlane = (Plane) original; final Plane oPlane = (Plane) original;
final Plane tPlane = (Plane) transformed; final Plane tPlane = (Plane) transformed;
final Vector3D p00 = oPlane.getOrigin(); final Vector3D p00 = oPlane.getOrigin();
final Vector3D p10 = (Vector3D) oPlane.toSpace(new Point2D(1.0, 0.0)); final Vector3D p10 = (Vector3D) oPlane.toSpace(new Vector2D(1.0, 0.0));
final Vector3D p01 = (Vector3D) oPlane.toSpace(new Point2D(0.0, 1.0)); final Vector3D p01 = (Vector3D) oPlane.toSpace(new Vector2D(0.0, 1.0));
final Point2D tP00 = (Point2D) tPlane.toSubSpace(apply((Point) p00)); final Vector2D tP00 = (Vector2D) tPlane.toSubSpace(apply(p00));
final Point2D tP10 = (Point2D) tPlane.toSubSpace(apply((Point) p10)); final Vector2D tP10 = (Vector2D) tPlane.toSubSpace(apply(p10));
final Point2D tP01 = (Point2D) tPlane.toSubSpace(apply((Point) p01)); final Vector2D tP01 = (Vector2D) tPlane.toSubSpace(apply(p01));
final AffineTransform at = final AffineTransform at =
new AffineTransform(tP10.getX() - tP00.getX(), tP10.getY() - tP00.getY(), new AffineTransform(tP10.getX() - tP00.getX(), tP10.getY() - tP00.getY(),
tP01.getX() - tP00.getX(), tP01.getY() - tP00.getY(), tP01.getX() - tP00.getX(), tP01.getY() - tP00.getY(),
tP00.getX(), tP00.getY()); tP00.getX(), tP00.getY());
cachedOriginal = original; cachedOriginal = (Plane) original;
cachedTransform = org.apache.commons.math.geometry.euclidean.twod.Line.getTransform(at); 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. */ /** 3D translation as a transform. */
private static class TranslationTransform implements Transform { private static class TranslationTransform implements Transform<Euclidean3D, Euclidean2D> {
/** Translation vector. */ /** Translation vector. */
private Vector3D translation; private Vector3D translation;
/** Cached original hyperplane. */ /** Cached original hyperplane. */
private Hyperplane cachedOriginal; private Plane cachedOriginal;
/** Cached 2D transform valid inside the cached original hyperplane. */ /** Cached 2D transform valid inside the cached original hyperplane. */
private Transform cachedTransform; private Transform<Euclidean2D, Euclidean1D> cachedTransform;
/** Build a translation transform. /** Build a translation transform.
* @param translation translation vector * @param translation translation vector
@ -377,34 +389,35 @@ public class PolyhedronsSet extends Region {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Point apply(final Point point) { public Vector3D apply(final Vector<Euclidean3D> point) {
return new Point3D(1.0, (Vector3D) point, 1.0, translation); return new Vector3D(1.0, (Vector3D) point, 1.0, translation);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) { public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
return ((Plane) hyperplane).translate(translation); return ((Plane) hyperplane).translate(translation);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub, public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
final Hyperplane original, final Hyperplane transformed) { final Hyperplane<Euclidean3D> original,
final Hyperplane<Euclidean3D> transformed) {
if (original != cachedOriginal) { if (original != cachedOriginal) {
// we have changed hyperplane, reset the in-hyperplane transform // we have changed hyperplane, reset the in-hyperplane transform
final Plane oPlane = (Plane) original; final Plane oPlane = (Plane) original;
final Plane tPlane = (Plane) transformed; 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 = final AffineTransform at =
AffineTransform.getTranslateInstance(shift.getX(), shift.getY()); AffineTransform.getTranslateInstance(shift.getX(), shift.getY());
cachedOriginal = original; cachedOriginal = (Plane) original;
cachedTransform = cachedTransform =
org.apache.commons.math.geometry.euclidean.twod.Line.getTransform(at); 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.MathIllegalArgumentException;
import org.apache.commons.math.exception.util.LocalizedFormats; 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.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.OrientedPoint; import org.apache.commons.math.geometry.euclidean.oned.OrientedPoint;
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.BSPTree; import org.apache.commons.math.geometry.partitioning.Embedding;
import org.apache.commons.math.geometry.partitioning.Hyperplane; 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.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.SubSpace;
import org.apache.commons.math.geometry.partitioning.Transform; import org.apache.commons.math.geometry.partitioning.Transform;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
import org.apache.commons.math.util.MathUtils; 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 * 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> * 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. */ /** Angle with respect to the abscissa axis. */
private double angle; private double angle;
@ -78,7 +78,7 @@ public class Line implements Hyperplane {
* @param p1 first point * @param p1 first point
* @param p2 second point * @param p2 second point
*/ */
public Line(final Point2D p1, final Point2D p2) { public Line(final Vector2D p1, final Vector2D p2) {
reset(p1, p2); reset(p1, p2);
} }
@ -86,7 +86,7 @@ public class Line implements Hyperplane {
* @param p point belonging to the line * @param p point belonging to the line
* @param angle angle of the line with respect to abscissa axis * @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); reset(p, angle);
} }
@ -116,7 +116,7 @@ public class Line implements Hyperplane {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Hyperplane copySelf() { public Line copySelf() {
return new Line(this); return new Line(this);
} }
@ -125,20 +125,20 @@ public class Line implements Hyperplane {
* @param p1 first point * @param p1 first point
* @param p2 second point * @param p2 second point
*/ */
public void reset(final Point2D p1, final Point2D p2) { public void reset(final Vector2D p1, final Vector2D p2) {
final double dx = p2.x - p1.x; final double dx = p2.getX() - p1.getX();
final double dy = p2.y - p1.y; final double dy = p2.getY() - p1.getY();
final double d = FastMath.hypot(dx, dy); final double d = FastMath.hypot(dx, dy);
if (d == 0.0) { if (d == 0.0) {
angle = 0.0; angle = 0.0;
cos = 1.0; cos = 1.0;
sin = 0.0; sin = 0.0;
originOffset = p1.y; originOffset = p1.getY();
} else { } else {
angle = FastMath.PI + FastMath.atan2(-dy, -dx); angle = FastMath.PI + FastMath.atan2(-dy, -dx);
cos = FastMath.cos(angle); cos = FastMath.cos(angle);
sin = FastMath.sin(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 p point belonging to the line
* @param alpha angle of the line with respect to abscissa axis * @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); this.angle = MathUtils.normalizeAngle(alpha, FastMath.PI);
cos = FastMath.cos(this.angle); cos = FastMath.cos(this.angle);
sin = FastMath.sin(this.angle); sin = FastMath.sin(this.angle);
originOffset = cos * p.y - sin * p.x; originOffset = cos * p.getY() - sin * p.getX();
} }
/** Revert the instance. /** Revert the instance.
@ -176,57 +176,44 @@ public class Line implements Hyperplane {
-cos, -sin, -originOffset); -cos, -sin, -originOffset);
} }
/** Transform a 2D space point into a line point. /** {@inheritDoc} */
* @param point 2D point (must be a {@link Point2D Point2D} public Vector1D toSubSpace(final Vector<Euclidean2D> point) {
* instance) Vector2D p2 = (Vector2D) point;
* @return line point corresponding to the 2D point (really a {@link return new Vector1D(cos * p2.getX() + sin * p2.getY());
* 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);
} }
/** Get one point from the line. /** {@inheritDoc} */
* @param point desired abscissa for the point (must be a {@link public Vector2D toSpace(final Vector<Euclidean1D> point) {
* org.apache.commons.math.geometry.euclidean.oned.Point1D Point1D} instance) final double abscissa = ((Vector1D) point).getX();
* @return line point at specified abscissa (really a {@link Point2D return new Vector2D(abscissa * cos - originOffset * sin,
* Point2D} instance)
*/
public Point toSpace(final Point point) {
final double abscissa = ((Point1D) point).getAbscissa();
return new Point2D(abscissa * cos - originOffset * sin,
abscissa * sin + originOffset * cos); abscissa * sin + originOffset * cos);
} }
/** Get the intersection point of the instance and another line. /** Get the intersection point of the instance and another line.
* @param other other line * @param other other line
* @return intersection point of the instance and the 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 Line otherL = (Line) other;
final double d = sin * otherL.cos - otherL.sin * cos; final double d = sin * otherL.cos - otherL.sin * cos;
if (FastMath.abs(d) < 1.0e-10) { if (FastMath.abs(d) < 1.0e-10) {
return null; 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); (sin * otherL.originOffset - otherL.sin * originOffset) / d);
} }
/** Build a region covering the whole hyperplane. /** {@inheritDoc} */
* @return a region covering the whole hyperplane public SubLine wholeHyperplane() {
*/ return new SubLine(this, new IntervalsSet());
public Region wholeHyperplane() {
return new IntervalsSet();
} }
/** Build a region covering the whole space. /** Build a region covering the whole space.
* @return a region containing the instance (really a {@link * @return a region containing the instance (really a {@link
* PolygonsSet PolygonsSet} instance) * PolygonsSet PolygonsSet} instance)
*/ */
public Region wholeSpace() { public PolygonsSet wholeSpace() {
return new PolygonsSet(); return new PolygonsSet();
} }
@ -240,7 +227,8 @@ public class Line implements Hyperplane {
* @param line line to check * @param line line to check
* @return offset of the line * @return offset of the line
*/ */
public double getOffset(final Line line) { public double getOffset(final Hyperplane<Euclidean2D> hyperplane) {
Line line = (Line) hyperplane;
return originOffset + return originOffset +
((cos * line.cos + sin * line.sin > 0) ? -line.originOffset : line.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 * 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 * negative if it is on the left side, according to its natural
* orientation.</p> * 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 * @return offset of the point
*/ */
public double getOffset(final Point point) { public double getOffset(final Vector<Euclidean2D> point) {
final Point2D p2D = (Point2D) point; Vector2D p2 = (Vector2D) point;
return sin * p2D.x - cos * p2D.y + originOffset; return sin * p2.getX() - cos * p2.getY() + originOffset;
} }
/** Check if the instance has the same orientation as another hyperplane. /** 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 * @return true if the instance and the other hyperplane have
* the same orientation * the same orientation
*/ */
public boolean sameOrientationAs(final Hyperplane other) { public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
final Line otherL = (Line) other; final Line otherL = (Line) other;
return (sin * otherL.sin + cos * otherL.cos) >= 0.0; 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 * @return one point in the plane, with given abscissa and offset
* relative to the line * relative to the line
*/ */
public Point2D getPointAt(final Point1D abscissa, final double offset) { public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
final double x = abscissa.getAbscissa(); final double x = abscissa.getX();
final double dOffset = offset - originOffset; 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. /** Check if the line contains a point.
* @param p point to check * @param p point to check
* @return true if p belongs to the line * @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; 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. /** Translate the line to force it passing by a point.
* @param p point by which the line should pass * @param p point by which the line should pass
*/ */
public void translateToPoint(final Point2D p) { public void translateToPoint(final Vector2D p) {
originOffset = cos * p.y - sin * p.x; originOffset = cos * p.getY() - sin * p.getX();
} }
/** Get the angle of the line. /** Get the angle of the line.
@ -342,75 +330,6 @@ public class Line implements Hyperplane {
originOffset = offset; 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 /** Get a {@link org.apache.commons.math.geometry.partitioning.Transform
* Transform} embedding an affine transform. * Transform} embedding an affine transform.
* @param transform affine transform to embed (must be inversible * @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 * apply(Hyperplane)} method would work only for some lines, and
* fail for other ones) * fail for other ones)
* @return a new transform that can be applied to either {@link * @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 * org.apache.commons.math.geometry.partitioning.SubHyperplane
* SubHyperplane} instances * SubHyperplane} instances
* @exception MathIllegalArgumentException if the transform is non invertible * @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); 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 * applied to a large number of lines (for example to a large
* polygon)./<p> * polygon)./<p>
*/ */
private static class LineTransform implements Transform { private static class LineTransform implements Transform<Euclidean2D, Euclidean1D> {
// CHECKSTYLE: stop JavadocVariable check // CHECKSTYLE: stop JavadocVariable check
private double cXX; private double cXX;
@ -478,16 +398,16 @@ public class Line implements Hyperplane {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Point apply(final Point point) { public Vector2D apply(final Vector<Euclidean2D> point) {
final Point2D p2D = (Point2D) point; final Vector2D p2D = (Vector2D) point;
final double x = p2D.getX(); final double x = p2D.getX();
final double y = p2D.getY(); 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); cYX * x + cYY * y + cY1);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Hyperplane apply(final Hyperplane hyperplane) { public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
final Line line = (Line) hyperplane; final Line line = (Line) hyperplane;
final double rOffset = c1X * line.cos + c1Y * line.sin + c11 * line.originOffset; final double rOffset = c1X * line.cos + c1Y * line.sin + c11 * line.originOffset;
final double rCos = cXX * line.cos + cXY * line.sin; final double rCos = cXX * line.cos + cXY * line.sin;
@ -499,12 +419,15 @@ public class Line implements Hyperplane {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public SubHyperplane apply(final SubHyperplane sub, public SubHyperplane<Euclidean1D> apply(final SubHyperplane<Euclidean1D> sub,
final Hyperplane original, final Hyperplane transformed) { final Hyperplane<Euclidean2D> original,
final OrientedPoint op = (OrientedPoint) sub.getHyperplane(); final Hyperplane<Euclidean2D> transformed) {
final Point1D newLoc = final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
(Point1D) transformed.toSubSpace(apply(original.toSpace(op.getLocation()))); final Line originalLine = (Line) original;
return new SubHyperplane(new OrientedPoint(newLoc, op.isDirect())); 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; package org.apache.commons.math.geometry.euclidean.twod;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import org.apache.commons.math.exception.MathIllegalArgumentException; import org.apache.commons.math.exception.MathIllegalArgumentException;
import org.apache.commons.math.exception.util.LocalizedFormats; 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.IntervalsSet;
import org.apache.commons.math.geometry.euclidean.oned.Point1D;
import org.apache.commons.math.geometry.partitioning.Hyperplane;
import org.apache.commons.math.geometry.partitioning.Region; 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.SubHyperplane;
/** This class represent a tree of nested 2D boundary loops. /** This class represent a tree of nested 2D boundary loops.
* <p>This class is used during Piece instances construction. * <p>This class is used for piecewise polygons construction.
* Beams are built using the outline edges as * Polygons are built using the outline edges as
* representative of facets, the orientation of these facets are * representative of boundaries, the orientation of these lines are
* meaningful. However, we want to allow the user to specify its * meaningful. However, we want to allow the user to specify its
* outline loops without having to take care of this orientation. This * outline loops without having to take care of this orientation. This
* class is devoted to correct mis-oriented loops.<p> * class is devoted to correct mis-oriented loops.<p>
* <p>Orientation is computed assuming the piece is finite, i.e. the * <p>Orientation is computed assuming the piecewise polygon is finite,
* outermost loops have their exterior side facing points at infinity, * i.e. the outermost loops have their exterior side facing points at
* and hence are oriented counter-clockwise. The orientation of * infinity, and hence are oriented counter-clockwise. The orientation of
* internal loops is computed as the reverse of the orientation of * internal loops is computed as the reverse of the orientation of
* their immediate surrounding loop.</p> * their immediate surrounding loop.</p>
* @version $Revision$ $Date$ * @version $Id:$
* @since 3.0
*/ */
class NestedLoops { class NestedLoops {
/** Boundary loop. */ /** Boundary loop. */
private Point2D[] loop; private Vector2D[] loop;
/** Surrounded loops. */ /** Surrounded loops. */
private ArrayList<NestedLoops> surrounded; private ArrayList<NestedLoops> surrounded;
/** Polygon enclosing a finite region. */ /** Polygon enclosing a finite region. */
private Region polygon; private Region<Euclidean2D> polygon;
/** Indicator for original loop orientation. */ /** Indicator for original loop orientation. */
private boolean originalIsClockwise; private boolean originalIsClockwise;
@ -74,7 +73,7 @@ class NestedLoops {
* @param loop boundary loop (will be reversed in place if needed) * @param loop boundary loop (will be reversed in place if needed)
* @exception MathIllegalArgumentException if an outline has an open boundary loop * @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) { if (loop[0] == null) {
throw new MathIllegalArgumentException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN); throw new MathIllegalArgumentException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
@ -84,23 +83,21 @@ class NestedLoops {
surrounded = new ArrayList<NestedLoops>(); surrounded = new ArrayList<NestedLoops>();
// build the polygon defined by the loop // build the polygon defined by the loop
final ArrayList<SubHyperplane> edges = new ArrayList<SubHyperplane>(); final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
Point2D current = loop[loop.length - 1]; Vector2D current = loop[loop.length - 1];
for (int i = 0; i < loop.length; ++i) { for (int i = 0; i < loop.length; ++i) {
final Point2D previous = current; final Vector2D previous = current;
current = loop[i]; current = loop[i];
final Line line = new Line(previous, current); final Line line = new Line(previous, current);
final Region region = Region.buildConvex(Arrays.asList(new Hyperplane[] { final IntervalsSet region =
new OrientedPoint((Point1D) line.toSubSpace(previous), false), new IntervalsSet(line.toSubSpace(previous).getX(), line.toSubSpace(current).getX());
new OrientedPoint((Point1D) line.toSubSpace(current), true) edges.add(new SubLine(line, region));
}));
edges.add(new SubHyperplane(line, region));
} }
polygon = new PolygonsSet(edges); polygon = new PolygonsSet(edges);
// ensure the polygon encloses a finite region of the plane // ensure the polygon encloses a finite region of the plane
if (Double.isInfinite(polygon.getSize())) { if (Double.isInfinite(polygon.getSize())) {
polygon = polygon.getComplement(); polygon = new RegionFactory<Euclidean2D>().getComplement(polygon);
originalIsClockwise = false; originalIsClockwise = false;
} else { } else {
originalIsClockwise = true; originalIsClockwise = true;
@ -113,7 +110,7 @@ class NestedLoops {
* @exception MathIllegalArgumentException if an outline has crossing * @exception MathIllegalArgumentException if an outline has crossing
* boundary loops or open boundary loops * 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)); add(new NestedLoops(bLoop));
} }
@ -142,8 +139,9 @@ class NestedLoops {
} }
// we should be separate from the remaining children // we should be separate from the remaining children
RegionFactory<Euclidean2D> factory = new RegionFactory<Euclidean2D>();
for (final NestedLoops child : surrounded) { 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); throw new MathIllegalArgumentException(LocalizedFormats.CROSSING_BOUNDARY_LOOPS);
} }
} }
@ -154,7 +152,7 @@ class NestedLoops {
/** Correct the orientation of the loops contained in the tree. /** Correct the orientation of the loops contained in the tree.
* <p>This is this method that really inverts the loops that where * <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> * they are mis-oriented</p>
*/ */
public void correctOrientation() { public void correctOrientation() {
@ -174,7 +172,7 @@ class NestedLoops {
int min = -1; int min = -1;
int max = loop.length; int max = loop.length;
while (++min < --max) { while (++min < --max) {
final Point2D tmp = loop[min]; final Vector2D tmp = loop[min];
loop[min] = loop[max]; loop[min] = loop[max];
loop[max] = tmp; loop[max] = tmp;
} }

View File

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

View File

@ -18,19 +18,22 @@ package org.apache.commons.math.geometry.euclidean.twod;
import java.util.List; 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.Interval;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet; 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.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor; 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.SubHyperplane;
import org.apache.commons.math.geometry.partitioning.utilities.AVLTree; import org.apache.commons.math.geometry.partitioning.utilities.AVLTree;
/** Visitor building segments. /** Visitor building segments.
* @version $Revision$ $Date$ * @version $Id:$
* @since 3.0
*/ */
class SegmentsBuilder implements BSPTreeVisitor { class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
/** Sorted segments. */ /** Sorted segments. */
private AVLTree<Segment> sorted; private AVLTree<Segment> sorted;
@ -41,13 +44,14 @@ class SegmentsBuilder implements BSPTreeVisitor {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public Order visitOrder(final BSPTree node) { public Order visitOrder(final BSPTree<Euclidean2D> node) {
return Order.MINUS_SUB_PLUS; return Order.MINUS_SUB_PLUS;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitInternalNode(final BSPTree node) { public void visitInternalNode(final BSPTree<Euclidean2D> node) {
final BoundaryAttribute attribute = (BoundaryAttribute) node.getAttribute(); @SuppressWarnings("unchecked")
final BoundaryAttribute<Euclidean2D> attribute = (BoundaryAttribute<Euclidean2D>) node.getAttribute();
if (attribute.getPlusOutside() != null) { if (attribute.getPlusOutside() != null) {
addContribution(attribute.getPlusOutside(), false); addContribution(attribute.getPlusOutside(), false);
} }
@ -57,21 +61,24 @@ class SegmentsBuilder implements BSPTreeVisitor {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitLeafNode(final BSPTree node) { public void visitLeafNode(final BSPTree<Euclidean2D> node) {
} }
/** Add he contribution of a boundary facet. /** Add he contribution of a boundary facet.
* @param sub boundary facet * @param sub boundary facet
* @param reversed if true, the facet has the inside on its plus side * @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 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) { for (final Interval i : intervals) {
final Point2D start = Double.isInfinite(i.getLower()) ? final Vector2D start = Double.isInfinite(i.getLower()) ?
null : (Point2D) line.toSpace(new Point1D(i.getLower())); null : (Vector2D) line.toSpace(new Vector1D(i.getLower()));
final Point2D end = Double.isInfinite(i.getUpper()) ? final Vector2D end = Double.isInfinite(i.getUpper()) ?
null : (Point2D) line.toSpace(new Point1D(i.getUpper())); null : (Vector2D) line.toSpace(new Vector1D(i.getUpper()));
if (reversed) { if (reversed) {
sorted.insert(new Segment(end, start, line.getReverse())); sorted.insert(new Segment(end, start, line.getReverse()));
} else { } 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.Interval;
import org.apache.commons.math.geometry.euclidean.oned.IntervalsSet; 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.Region;
import org.apache.commons.math.geometry.partitioning.RegionFactory;
import org.apache.commons.math.util.FastMath; import org.apache.commons.math.util.FastMath;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -32,12 +33,12 @@ public class IntervalsSetTest {
public void testInterval() { public void testInterval() {
IntervalsSet set = new IntervalsSet(2.3, 5.7); IntervalsSet set = new IntervalsSet(2.3, 5.7);
Assert.assertEquals(3.4, set.getSize(), 1.0e-10); Assert.assertEquals(3.4, set.getSize(), 1.0e-10);
Assert.assertEquals(4.0, ((Point1D) set.getBarycenter()).getAbscissa(), 1.0e-10); Assert.assertEquals(4.0, ((Vector1D) set.getBarycenter()).getX(), 1.0e-10);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(2.3))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(2.3)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(5.7))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(5.7)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(1.2))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(1.2)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.7))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.7)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(3.0))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(3.0)));
Assert.assertEquals(2.3, set.getInf(), 1.0e-10); Assert.assertEquals(2.3, set.getInf(), 1.0e-10);
Assert.assertEquals(5.7, set.getSup(), 1.0e-10); Assert.assertEquals(5.7, set.getSup(), 1.0e-10);
} }
@ -45,17 +46,17 @@ public class IntervalsSetTest {
@Test @Test
public void testInfinite() { public void testInfinite() {
IntervalsSet set = new IntervalsSet(9.0, Double.POSITIVE_INFINITY); IntervalsSet set = new IntervalsSet(9.0, Double.POSITIVE_INFINITY);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(9.0))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(9.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.4))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.4)));
for (double e = 1.0; e <= 6.0; e += 1.0) { for (double e = 1.0; e <= 6.0; e += 1.0) {
Assert.assertEquals(Region.Location.INSIDE, 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.assertTrue(Double.isInfinite(set.getSize()));
Assert.assertEquals(9.0, set.getInf(), 1.0e-10); Assert.assertEquals(9.0, set.getInf(), 1.0e-10);
Assert.assertTrue(Double.isInfinite(set.getSup())); 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.assertEquals(9.0, set.getSup(), 1.0e-10);
Assert.assertTrue(Double.isInfinite(set.getInf())); Assert.assertTrue(Double.isInfinite(set.getInf()));
@ -63,22 +64,23 @@ public class IntervalsSetTest {
@Test @Test
public void testMultiple() { public void testMultiple() {
RegionFactory<Euclidean1D> factory = new RegionFactory<Euclidean1D>();
IntervalsSet set = (IntervalsSet) IntervalsSet set = (IntervalsSet)
Region.intersection(Region.union(Region.difference(new IntervalsSet(1.0, 6.0), factory.intersection(factory.union(factory.difference(new IntervalsSet(1.0, 6.0),
new IntervalsSet(3.0, 5.0)), new IntervalsSet(3.0, 5.0)),
new IntervalsSet(9.0, Double.POSITIVE_INFINITY)), new IntervalsSet(9.0, Double.POSITIVE_INFINITY)),
new IntervalsSet(Double.NEGATIVE_INFINITY, 11.0)); new IntervalsSet(Double.NEGATIVE_INFINITY, 11.0));
Assert.assertEquals(5.0, set.getSize(), 1.0e-10); Assert.assertEquals(5.0, set.getSize(), 1.0e-10);
Assert.assertEquals(5.9, ((Point1D) set.getBarycenter()).getAbscissa(), 1.0e-10); Assert.assertEquals(5.9, ((Vector1D) set.getBarycenter()).getX(), 1.0e-10);
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(0.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(0.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(4.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(4.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(8.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(8.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Point1D(12.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new Vector1D(12.0)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(1.2))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(1.2)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(5.9))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(5.9)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Point1D(9.01))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new Vector1D(9.01)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(5.0))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(5.0)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(11.0))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Vector1D(11.0)));
Assert.assertEquals( 1.0, set.getInf(), 1.0e-10); Assert.assertEquals( 1.0, set.getInf(), 1.0e-10);
Assert.assertEquals(11.0, set.getSup(), 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.Line;
import org.apache.commons.math.geometry.euclidean.threed.Plane; 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.Rotation;
import org.apache.commons.math.geometry.euclidean.threed.Vector3D; import org.apache.commons.math.geometry.euclidean.threed.Vector3D;
import org.junit.Assert; import org.junit.Assert;
@ -29,22 +28,22 @@ public class PlaneTest {
@Test @Test
public void testContains() { public void testContains() {
Plane p = new Plane(new Vector3D(0, 0, 1), new Vector3D(0, 0, 1)); 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 Vector3D(0, 0, 1)));
Assert.assertTrue(p.contains(new Point3D(17, -32, 1))); Assert.assertTrue(p.contains(new Vector3D(17, -32, 1)));
Assert.assertTrue(! p.contains(new Point3D(17, -32, 1.001))); Assert.assertTrue(! p.contains(new Vector3D(17, -32, 1.001)));
} }
@Test @Test
public void testOffset() { public void testOffset() {
Vector3D p1 = new Vector3D(1, 1, 1); Vector3D p1 = new Vector3D(1, 1, 1);
Plane p = new Plane(p1, new Vector3D(0.2, 0, 0)); 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 Vector3D(-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(6, 10, -12)), 1.0e-10);
Assert.assertEquals(0.3, 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); 1.0e-10);
Assert.assertEquals(-0.3, 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); 1.0e-10);
} }
@ -56,9 +55,9 @@ public class PlaneTest {
@Test @Test
public void testThreePoints() { public void testThreePoints() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8); Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2); Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7); Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3); Plane p = new Plane(p1, p2, p3);
Assert.assertTrue(p.contains(p1)); Assert.assertTrue(p.contains(p1));
Assert.assertTrue(p.contains(p2)); Assert.assertTrue(p.contains(p2));
@ -67,9 +66,9 @@ public class PlaneTest {
@Test @Test
public void testRotate() { public void testRotate() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8); Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2); Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7); Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3); Plane p = new Plane(p1, p2, p3);
Vector3D oldNormal = p.getNormal(); Vector3D oldNormal = p.getNormal();
@ -92,9 +91,9 @@ public class PlaneTest {
@Test @Test
public void testTranslate() { public void testTranslate() {
Point3D p1 = new Point3D(1.2, 3.4, -5.8); Vector3D p1 = new Vector3D(1.2, 3.4, -5.8);
Point3D p2 = new Point3D(3.4, -5.8, 1.2); Vector3D p2 = new Vector3D(3.4, -5.8, 1.2);
Point3D p3 = new Point3D(-2.0, 4.3, 0.7); Vector3D p3 = new Vector3D(-2.0, 4.3, 0.7);
Plane p = new Plane(p1, p2, p3); Plane p = new Plane(p1, p2, p3);
p = p.translate(new Vector3D(2.0, p.getU(), -1.5, p.getV())); p = p.translate(new Vector3D(2.0, p.getU(), -1.5, p.getV()));
@ -118,7 +117,7 @@ public class PlaneTest {
public void testIntersection() { public void testIntersection() {
Plane p = new Plane(new Vector3D(1, 2, 3), new Vector3D(-4, 1, -5)); 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)); 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(p.contains(point));
Assert.assertTrue(l.contains(point)); Assert.assertTrue(l.contains(point));
Assert.assertNull(p.intersection(new Line(new Vector3D(10, 10, 10), 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; 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.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.BSPTree;
import org.apache.commons.math.geometry.partitioning.BSPTreeVisitor; 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.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.apache.commons.math.util.FastMath;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -53,41 +46,41 @@ public class PolyhedronsSetTest {
boolean zOK = (z >= 0.0) && (z <= 1.0); boolean zOK = (z >= 0.0) && (z <= 1.0);
Region.Location expected = Region.Location expected =
(xOK && yOK && zOK) ? Region.Location.INSIDE : Region.Location.OUTSIDE; (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[] { checkPoints(Region.Location.BOUNDARY, tree, new Vector3D[] {
new Point3D(0.0, 0.5, 0.5), new Vector3D(0.0, 0.5, 0.5),
new Point3D(1.0, 0.5, 0.5), new Vector3D(1.0, 0.5, 0.5),
new Point3D(0.5, 0.0, 0.5), new Vector3D(0.5, 0.0, 0.5),
new Point3D(0.5, 1.0, 0.5), new Vector3D(0.5, 1.0, 0.5),
new Point3D(0.5, 0.5, 0.0), new Vector3D(0.5, 0.5, 0.0),
new Point3D(0.5, 0.5, 1.0) new Vector3D(0.5, 0.5, 1.0)
}); });
checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] { checkPoints(Region.Location.OUTSIDE, tree, new Vector3D[] {
new Point3D(0.0, 1.2, 1.2), new Vector3D(0.0, 1.2, 1.2),
new Point3D(1.0, 1.2, 1.2), new Vector3D(1.0, 1.2, 1.2),
new Point3D(1.2, 0.0, 1.2), new Vector3D(1.2, 0.0, 1.2),
new Point3D(1.2, 1.0, 1.2), new Vector3D(1.2, 1.0, 1.2),
new Point3D(1.2, 1.2, 0.0), new Vector3D(1.2, 1.2, 0.0),
new Point3D(1.2, 1.2, 1.0) new Vector3D(1.2, 1.2, 1.0)
}); });
} }
@Test @Test
public void testTetrahedron() { public void testTetrahedron() {
Point3D vertex1 = new Point3D(1, 2, 3); Vector3D vertex1 = new Vector3D(1, 2, 3);
Point3D vertex2 = new Point3D(2, 2, 4); Vector3D vertex2 = new Vector3D(2, 2, 4);
Point3D vertex3 = new Point3D(2, 3, 3); Vector3D vertex3 = new Vector3D(2, 3, 3);
Point3D vertex4 = new Point3D(1, 3, 4); Vector3D vertex4 = new Vector3D(1, 3, 4);
@SuppressWarnings("unchecked")
PolyhedronsSet tree = PolyhedronsSet tree =
(PolyhedronsSet) Region.buildConvex(Arrays.asList(new Hyperplane[] { (PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
new Plane(vertex3, vertex2, vertex1), new Plane(vertex3, vertex2, vertex1),
new Plane(vertex2, vertex3, vertex4), new Plane(vertex2, vertex3, vertex4),
new Plane(vertex4, vertex3, vertex1), 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(1.0 / 3.0, tree.getSize(), 1.0e-10);
Assert.assertEquals(2.0 * FastMath.sqrt(3.0), tree.getBoundarySize(), 1.0e-10); Assert.assertEquals(2.0 * FastMath.sqrt(3.0), tree.getBoundarySize(), 1.0e-10);
Vector3D barycenter = (Vector3D) tree.getBarycenter(); Vector3D barycenter = (Vector3D) tree.getBarycenter();
@ -95,18 +88,18 @@ public class PolyhedronsSetTest {
Assert.assertEquals(2.5, barycenter.getY(), 1.0e-10); Assert.assertEquals(2.5, barycenter.getY(), 1.0e-10);
Assert.assertEquals(3.5, barycenter.getZ(), 1.0e-10); Assert.assertEquals(3.5, barycenter.getZ(), 1.0e-10);
double third = 1.0 / 3.0; double third = 1.0 / 3.0;
checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] { checkPoints(Region.Location.BOUNDARY, tree, new Vector3D[] {
vertex1, vertex2, vertex3, vertex4, vertex1, vertex2, vertex3, vertex4,
new Point3D(third, vertex1, third, vertex2, third, vertex3), new Vector3D(third, vertex1, third, vertex2, third, vertex3),
new Point3D(third, vertex2, third, vertex3, third, vertex4), new Vector3D(third, vertex2, third, vertex3, third, vertex4),
new Point3D(third, vertex3, third, vertex4, third, vertex1), new Vector3D(third, vertex3, third, vertex4, third, vertex1),
new Point3D(third, vertex4, third, vertex1, third, vertex2) new Vector3D(third, vertex4, third, vertex1, third, vertex2)
}); });
checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] { checkPoints(Region.Location.OUTSIDE, tree, new Vector3D[] {
new Point3D(1, 2, 4), new Vector3D(1, 2, 4),
new Point3D(2, 2, 3), new Vector3D(2, 2, 3),
new Point3D(2, 3, 4), new Vector3D(2, 3, 4),
new Point3D(1, 3, 3) new Vector3D(1, 3, 3)
}); });
} }
@ -116,13 +109,13 @@ public class PolyhedronsSetTest {
Vector3D vertex2 = new Vector3D(2.0, 2.4, 4.2); Vector3D vertex2 = new Vector3D(2.0, 2.4, 4.2);
Vector3D vertex3 = new Vector3D(2.8, 3.3, 3.7); Vector3D vertex3 = new Vector3D(2.8, 3.3, 3.7);
Vector3D vertex4 = new Vector3D(1.0, 3.6, 4.5); Vector3D vertex4 = new Vector3D(1.0, 3.6, 4.5);
@SuppressWarnings("unchecked")
PolyhedronsSet tree = PolyhedronsSet tree =
(PolyhedronsSet) Region.buildConvex(Arrays.asList(new Hyperplane[] { (PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
new Plane(vertex3, vertex2, vertex1), new Plane(vertex3, vertex2, vertex1),
new Plane(vertex2, vertex3, vertex4), new Plane(vertex2, vertex3, vertex4),
new Plane(vertex4, vertex3, vertex1), new Plane(vertex4, vertex3, vertex1),
new Plane(vertex1, vertex2, vertex4) new Plane(vertex1, vertex2, vertex4));
}));
Vector3D barycenter = (Vector3D) tree.getBarycenter(); Vector3D barycenter = (Vector3D) tree.getBarycenter();
Vector3D s = new Vector3D(10.2, 4.3, -6.7); Vector3D s = new Vector3D(10.2, 4.3, -6.7);
Vector3D c = new Vector3D(-0.2, 2.1, -3.2); Vector3D c = new Vector3D(-0.2, 2.1, -3.2);
@ -152,29 +145,30 @@ public class PolyhedronsSetTest {
1.0, c, 1.0, c,
1.0, r.applyTo(vertex4.subtract(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; return Order.MINUS_SUB_PLUS;
} }
public void visitInternalNode(BSPTree node) { public void visitInternalNode(BSPTree<Euclidean3D> node) {
Region.BoundaryAttribute attribute = @SuppressWarnings("unchecked")
(Region.BoundaryAttribute) node.getAttribute(); BoundaryAttribute<Euclidean3D> attribute =
(BoundaryAttribute<Euclidean3D>) node.getAttribute();
if (attribute.getPlusOutside() != null) { if (attribute.getPlusOutside() != null) {
checkFacet(attribute.getPlusOutside()); checkFacet((SubPlane) attribute.getPlusOutside());
} }
if (attribute.getPlusInside() != null) { 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(); Plane plane = (Plane) facet.getHyperplane();
Point2D[][] vertices = Vector2D[][] vertices =
((PolygonsSet) facet.getRemainingRegion()).getVertices(); ((PolygonsSet) facet.getRemainingRegion()).getVertices();
Assert.assertEquals(1, vertices.length); Assert.assertEquals(1, vertices.length);
for (int i = 0; i < vertices[0].length; ++i) { 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); new PolyhedronsSet(x - w, x + w, y - l, y + l, z - w, z + w);
PolyhedronsSet zBeam = PolyhedronsSet zBeam =
new PolyhedronsSet(x - w, x + w, y - w, y + w, z - l, z + l); new PolyhedronsSet(x - w, x + w, y - w, y + w, z - l, z + l);
PolyhedronsSet tree = RegionFactory<Euclidean3D> factory = new RegionFactory<Euclidean3D>();
(PolyhedronsSet) Region.union(xBeam, Region.union(yBeam, zBeam)); PolyhedronsSet tree = (PolyhedronsSet) factory.union(xBeam, factory.union(yBeam, zBeam));
Vector3D barycenter = (Vector3D) tree.getBarycenter(); Vector3D barycenter = (Vector3D) tree.getBarycenter();
Assert.assertEquals(x, barycenter.getX(), 1.0e-10); 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) { for (int i = 0; i < points.length; ++i) {
Assert.assertEquals(expected, tree.checkPoint(points[i])); Assert.assertEquals(expected, tree.checkPoint(points[i]));
} }

View File

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