Skip to content
Permalink
Browse files
GEOMETRY-96: optimizing HyperplaneSubset.Builder implementations to o…
…nly create internal BSP trees when needed
  • Loading branch information
darkma773r committed May 6, 2020
1 parent 0ac8384 commit 7ccde75624c83b9d0e41e7a2c81ef0d96639dcae
Show file tree
Hide file tree
Showing 18 changed files with 1,258 additions and 614 deletions.
@@ -21,8 +21,6 @@

import org.apache.commons.geometry.core.Transform;
import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
import org.apache.commons.geometry.core.partitioning.HyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.euclidean.twod.ConvexArea;
import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
@@ -118,7 +116,7 @@ public EmbeddedTreePlaneSubset transform(final Transform<Vector3D> transform) {
* a plane equivalent to this instance
*/
public void add(final PlaneConvexSubset subset) {
validatePlane(subset.getPlane());
Planes.validatePlanesEquivalent(getPlane(), subset.getPlane());

region.add(subset.getSubspaceRegion());
}
@@ -129,70 +127,8 @@ public void add(final PlaneConvexSubset subset) {
* a plane equivalent to this instance
*/
public void add(final EmbeddedTreePlaneSubset subset) {
validatePlane(subset.getPlane());
Planes.validatePlanesEquivalent(getPlane(), subset.getPlane());

region.union(subset.getSubspaceRegion());
}

/** Validate that the given plane is equivalent to the plane
* defining this instance.
* @param inputPlane plane to validate
* @throws IllegalArgumentException if the given plane is not equivalent
* to the plane for this instance
*/
private void validatePlane(final Plane inputPlane) {
final Plane plane = getPlane();

if (!plane.eq(inputPlane, plane.getPrecision())) {
throw new IllegalArgumentException("Argument is not on the same " +
"plane. Expected " + plane + " but was " +
inputPlane);
}
}

/** {@link HyperplaneSubset.Builder} implementation for plane subsets.
*/
public static class Builder implements HyperplaneSubset.Builder<Vector3D> {

/** Plane subset instance created by this builder. */
private final EmbeddedTreePlaneSubset subset;

/** Construct a new instance for building a subset region for the given plane.
* @param plane the underlying plane for the subset
*/
public Builder(final Plane plane) {
this.subset = new EmbeddedTreePlaneSubset(plane);
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneSubset<Vector3D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneConvexSubset<Vector3D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public EmbeddedTreePlaneSubset build() {
return subset;
}

/** Internal method for adding hyperplane subsets to this builder.
* @param sub the hyperplane subset to add; either convex or non-convex
*/
private void addInternal(final HyperplaneSubset<Vector3D> sub) {
if (sub instanceof PlaneConvexSubset) {
subset.add((PlaneConvexSubset) sub);
} else if (sub instanceof EmbeddedTreePlaneSubset) {
subset.add((EmbeddedTreePlaneSubset) sub);
} else {
throw new IllegalArgumentException("Unsupported plane subset type: " + sub.getClass().getName());
}
}
}
}
@@ -16,11 +16,15 @@
*/
package org.apache.commons.geometry.euclidean.threed;

import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;

import org.apache.commons.geometry.core.partitioning.AbstractRegionEmbeddingHyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.HyperplaneBoundedRegion;
import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
import org.apache.commons.geometry.core.partitioning.HyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.core.partitioning.SplitLocation;
import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
@@ -61,8 +65,12 @@ public Plane getHyperplane() {

/** {@inheritDoc} */
@Override
public EmbeddedTreePlaneSubset.Builder builder() {
return new EmbeddedTreePlaneSubset.Builder(plane);
public abstract List<PlaneConvexSubset> toConvex();

/** {@inheritDoc} */
@Override
public HyperplaneSubset.Builder<Vector3D> builder() {
return new Builder(plane);
}

/** Return the object used to perform floating point comparisons, which is the
@@ -142,4 +150,103 @@ protected <T extends PlaneSubset> Split<T> splitInternal(final Hyperplane<Vector
return new Split<>(minus, plus);
}
}

/** Internal implementation of the {@link HyperplaneSubset.Builder} interface. In cases where only a single
* convex subset is given to the builder, this class returns the convex subset instance directly. In all other
* cases, an {@link EmbeddedTreePlaneSubset} is used to construct the final subset.
*/
private static final class Builder implements HyperplaneSubset.Builder<Vector3D> {
/** Plane that a subset is being constructed for. */
private final Plane plane;

/** Embedded tree subset. */
private EmbeddedTreePlaneSubset treeSubset;

/** Convex subset added as the first subset to the builder. This is returned directly if
* no other subsets are added.
*/
private PlaneConvexSubset convexSubset;

/** Create a new subset builder for the given plane.
* @param plane plane to build a subset for
*/
Builder(final Plane plane) {
this.plane = plane;
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneSubset<Vector3D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneConvexSubset<Vector3D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public PlaneSubset build() {
// return the convex subset directly if that was all we were given
if (convexSubset != null) {
return convexSubset;
}
return getTreeSubset();
}

/** Internal method for adding hyperplane subsets to this builder.
* @param sub the hyperplane subset to add; may be either convex or non-convex
*/
private void addInternal(final HyperplaneSubset<Vector3D> sub) {
Objects.requireNonNull(sub, "Hyperplane subset must not be null");

if (sub instanceof PlaneConvexSubset) {
addConvexSubset((PlaneConvexSubset) sub);
} else if (sub instanceof EmbeddedTreePlaneSubset) {
addTreeSubset((EmbeddedTreePlaneSubset) sub);
} else {
throw new IllegalArgumentException("Unsupported hyperplane subset type: " + sub.getClass().getName());
}
}

/** Add a convex subset to the builder.
* @param convex convex subset to add
*/
private void addConvexSubset(final PlaneConvexSubset convex) {
Planes.validatePlanesEquivalent(plane, convex.getPlane());

if (treeSubset == null && convexSubset == null) {
convexSubset = convex;
} else {
getTreeSubset().add(convex);
}
}

/** Add an embedded tree subset to the builder.
* @param tree embedded tree subset to add
*/
private void addTreeSubset(final EmbeddedTreePlaneSubset tree) {
// no need to validate the line here since the add() method does that for us
getTreeSubset().add(tree);
}

/** Get the tree subset for the builder, creating it if needed.
* @return the tree subset for the builder
*/
private EmbeddedTreePlaneSubset getTreeSubset() {
if (treeSubset == null) {
treeSubset = new EmbeddedTreePlaneSubset(plane);

if (convexSubset != null) {
treeSubset.add(convexSubset);

convexSubset = null;
}
}

return treeSubset;
}
}
}
@@ -261,4 +261,16 @@ public static PlaneConvexSubset subsetFromVertices(final Collection<Vector3D> pt

return new PlaneConvexSubset(plane, area);
}

/** Validate that the actual plane is equivalent to the expected plane, throwing an exception if not.
* @param expected the expected plane
* @param actual the actual plane
* @throws IllegalArgumentException if the actual plane is not equivalent to the expected plane
*/
static void validatePlanesEquivalent(final Plane expected, final Plane actual) {
if (!expected.eq(actual, expected.getPrecision())) {
throw new IllegalArgumentException("Arguments do not represent the same plane. Expected " +
expected + " but was " + actual + ".");
}
}
}
@@ -22,6 +22,7 @@
import java.util.stream.StreamSupport;

import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.HyperplaneBoundedRegion;
import org.apache.commons.geometry.core.partitioning.HyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.core.partitioning.bsp.AbstractBSPTree;
@@ -31,7 +32,6 @@
import org.apache.commons.geometry.euclidean.threed.line.Line3D;
import org.apache.commons.geometry.euclidean.threed.line.LineConvexSubset3D;
import org.apache.commons.geometry.euclidean.threed.line.LinecastPoint3D;
import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
import org.apache.commons.geometry.euclidean.twod.Vector2D;

/** Binary space partitioning (BSP) tree representing a region in three dimensional
@@ -353,8 +353,8 @@ public RegionSizeProperties<Vector3D> getRegionSizeProperties() {
* @param reverse if true, the boundary contribution is reversed before being added to the total.
*/
private void addBoundaryContribution(final HyperplaneSubset<Vector3D> boundary, boolean reverse) {
final EmbeddedTreePlaneSubset boundarySubset = (EmbeddedTreePlaneSubset) boundary;
final RegionBSPTree2D base = boundarySubset.getSubspaceRegion();
final PlaneSubset boundarySubset = (PlaneSubset) boundary;
final HyperplaneBoundedRegion<Vector2D> base = boundarySubset.getSubspaceRegion();

final double area = base.getSize();
final Vector2D baseBarycenter = base.getBarycenter();
@@ -22,8 +22,6 @@
import org.apache.commons.geometry.core.RegionLocation;
import org.apache.commons.geometry.core.Transform;
import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
import org.apache.commons.geometry.core.partitioning.HyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.Split;
import org.apache.commons.geometry.core.partitioning.SplitLocation;
import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
@@ -156,7 +154,7 @@ public Split<EmbeddedTreeLineSubset> split(final Hyperplane<Vector2D> splitter)
* a line equivalent to this instance
*/
public void add(final LineConvexSubset subset) {
validateLine(subset.getLine());
Lines.validateLinesEquivalent(getLine(), subset.getLine());

region.add(subset.getInterval());
}
@@ -168,7 +166,7 @@ public void add(final LineConvexSubset subset) {
* a line equivalent to this instance
*/
public void add(final EmbeddedTreeLineSubset subset) {
validateLine(subset.getLine());
Lines.validateLinesEquivalent(getLine(), subset.getLine());

region.union(subset.getSubspaceRegion());
}
@@ -197,66 +195,4 @@ public String toString() {
RegionLocation classifyAbscissa(final double abscissa) {
return region.classify(abscissa);
}

/** Validate that the given line is equivalent to the line
* defining this instance.
* @param inputLine the line to validate
* @throws IllegalArgumentException if the given line is not equivalent
* to the line for this instance
*/
private void validateLine(final Line inputLine) {
final Line line = getLine();

if (!line.eq(inputLine, line.getPrecision())) {
throw new IllegalArgumentException("Argument is not on the same " +
"line. Expected " + line + " but was " +
inputLine);
}
}

/** {@link HyperplaneSubset.Builder} implementation for line subsets.
*/
public static final class Builder implements HyperplaneSubset.Builder<Vector2D> {

/** Line subset instance created by this builder. */
private final EmbeddedTreeLineSubset lineSubset;

/** Construct a new instance for building a line subset for the given line.
* @param line the underlying line for the subset
*/
public Builder(final Line line) {
this.lineSubset = new EmbeddedTreeLineSubset(line);
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneSubset<Vector2D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public void add(final HyperplaneConvexSubset<Vector2D> sub) {
addInternal(sub);
}

/** {@inheritDoc} */
@Override
public EmbeddedTreeLineSubset build() {
return lineSubset;
}

/** Internal method for adding hyperplane subsets to this builder.
* @param sub the hyperplane subset to add; either convex or non-convex
*/
private void addInternal(final HyperplaneSubset<Vector2D> sub) {
if (sub instanceof LineConvexSubset) {
lineSubset.add((LineConvexSubset) sub);
} else if (sub instanceof EmbeddedTreeLineSubset) {
lineSubset.add((EmbeddedTreeLineSubset) sub);
} else {
throw new IllegalArgumentException("Unsupported hyperplane subset type: " + sub.getClass().getName());
}
}
}
}

0 comments on commit 7ccde75

Please sign in to comment.