Skip to content
Browse files
GEOMETRY-95: improving implementation of Sphere.toTree(); removing Hy…
…perplaneSubset.Builder interface to improve performance and numerical robustness
  • Loading branch information
darkma773r committed May 31, 2020
1 parent 69a1d4f commit 843d6a91abe72b7e8372c286a1e202622f7bc52f
Show file tree
Hide file tree
Showing 31 changed files with 751 additions and 2,008 deletions.
@@ -242,14 +242,35 @@ protected <R extends AbstractConvexHyperplaneBoundedRegion<P, S>> Split<R> split
new Split<>(null, thisInstance);

final List<S> minusBoundaries = new ArrayList<>();
final List<S> plusBoundaries = new ArrayList<>();
// the splitter passes through the region; split the other region boundaries
// by the splitter
final ArrayList<S> minusBoundaries = new ArrayList<>();
final ArrayList<S> plusBoundaries = new ArrayList<>();

splitBoundaries(splitter, boundaryType, minusBoundaries, plusBoundaries);

// if the splitter was trimmed by the region boundaries, double-check that the split boundaries
// actually lie on both sides of the splitter; this is another case where floating point errors
// can cause a discrepancy between the results of splitting the splitter by the boundaries and
// splitting the boundaries by the splitter
if (!trimmedSplitter.isFull()) {
if (minusBoundaries.isEmpty()) {
if (plusBoundaries.isEmpty()) {
return new Split<>(null, null);
return new Split<>(null, thisInstance);
} else if (plusBoundaries.isEmpty()) {
return new Split<>(thisInstance, null);

// we have a consistent region split; create the new plus and minus regions


return new Split<>(factory.apply(minusBoundaries), factory.apply(plusBoundaries));
@@ -114,12 +114,6 @@ default boolean contains(P pt) {
P closest(P pt);

/** Return a {@link Builder} instance for joining multiple
* hyperplane subsets together.
* @return a new builder instance
Builder<P> builder();

/** Return a new hyperplane subset resulting from the application of the given transform.
* The current instance is not modified.
* @param transform the transform instance to apply
@@ -134,27 +128,4 @@ default boolean contains(P pt) {
* region as this instance
List<? extends HyperplaneConvexSubset<P>> toConvex();

/** Interface for joining multiple {@link HyperplaneSubset}s into a single
* instance.
* @param <P> Point implementation type
interface Builder<P extends Point<P>> {

/** Add a {@link HyperplaneSubset} instance to the builder.
* @param sub subset to add to this instance
void add(HyperplaneSubset<P> sub);

/** Add a {@link HyperplaneConvexSubset} instance to the builder.
* @param sub convex subset to add to this instance
void add(HyperplaneConvexSubset<P> sub);

/** Get a {@link HyperplaneSubset} representing the union
* of all input subsets.
* @return subset representing the union of all input subsets
HyperplaneSubset<P> build();
@@ -26,13 +26,13 @@
import org.apache.commons.geometry.core.RegionLocation;
import org.apache.commons.geometry.core.internal.IteratorTransform;
import org.apache.commons.geometry.core.partitioning.BoundarySource;
import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
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.HyperplaneLocation;
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.partitioning.HyperplaneSubset;
import org.apache.commons.geometry.core.partitioning.bsp.BSPTreeVisitor.ClosestFirstVisitor;

/** Abstract {@link BSPTree} specialized for representing regions of space. For example,
@@ -135,8 +135,7 @@ public double getBoundarySize() {
for (final AbstractRegionNode<P, N> node : nodes()) {
boundary = node.getCutBoundary();
if (boundary != null) {
sum += boundary.getInsideFacing().getSize();
sum += boundary.getOutsideFacing().getSize();
sum += boundary.getSize();

@@ -699,25 +698,21 @@ public RegionCutBoundary<P> getCutBoundary() {
private RegionCutBoundary<P> computeBoundary() {
HyperplaneConvexSubset<P> sub = getCut();

// find the portions of the node cut sub-hyperplane that touch inside and
// find the portions of the node cut hyperplane subset that touch inside and
// outside cells in the minus sub-tree
HyperplaneSubset.Builder<P> minusInBuilder = sub.builder();
HyperplaneSubset.Builder<P> minusOutBuilder = sub.builder();

characterizeHyperplaneSubset(sub, getMinus(), minusInBuilder, minusOutBuilder);
final List<HyperplaneConvexSubset<P>> minusIn = new ArrayList<>();
final List<HyperplaneConvexSubset<P>> minusOut = new ArrayList<>();

List<? extends HyperplaneConvexSubset<P>> minusIn =;
List<? extends HyperplaneConvexSubset<P>> minusOut =;
characterizeHyperplaneSubset(sub, getMinus(), minusIn, minusOut);

// create the result boundary builders
HyperplaneSubset.Builder<P> insideFacing = sub.builder();
HyperplaneSubset.Builder<P> outsideFacing = sub.builder();
final ArrayList<HyperplaneConvexSubset<P>> insideFacing = new ArrayList<>();
final ArrayList<HyperplaneConvexSubset<P>> outsideFacing = new ArrayList<>();

if (!minusIn.isEmpty()) {
// Add to the boundary anything that touches an inside cell in the minus sub-tree
// and an outside cell in the plus sub-tree. These portions are oriented with their
// plus side pointing to the outside of the region.
for (HyperplaneConvexSubset<P> minusInFragment : minusIn) {
for (final HyperplaneConvexSubset<P> minusInFragment : minusIn) {
characterizeHyperplaneSubset(minusInFragment, getPlus(), null, outsideFacing);
@@ -726,26 +721,31 @@ private RegionCutBoundary<P> computeBoundary() {
// Add to the boundary anything that touches an outside cell in the minus sub-tree
// and an inside cell in the plus sub-tree. These portions are oriented with their
// plus side pointing to the inside of the region.
for (HyperplaneConvexSubset<P> minusOutFragment : minusOut) {
for (final HyperplaneConvexSubset<P> minusOutFragment : minusOut) {
characterizeHyperplaneSubset(minusOutFragment, getPlus(), insideFacing, null);

return new RegionCutBoundary<>(,;

return new RegionCutBoundary<>(
insideFacing.isEmpty() ? null : insideFacing,
outsideFacing.isEmpty() ? null : outsideFacing);

/** Recursive method to characterize a hyperplane convex subset with respect to the region's
* boundaries.
* @param sub the hyperplane convex subset to characterize
* @param node the node to characterize the hyperplane convex subset against
* @param in the builder that will receive the portions of the subset that lie in the inside
* @param in list that will receive the portions of the subset that lie in the inside
* of the region; may be null
* @param out the builder that will receive the portions of the subset that lie on the outside
* @param out list that will receive the portions of the subset that lie on the outside
* of the region; may be null
private void characterizeHyperplaneSubset(final HyperplaneConvexSubset<P> sub,
final AbstractRegionNode<P, N> node, final HyperplaneSubset.Builder<P> in,
final HyperplaneSubset.Builder<P> out) {
final AbstractRegionNode<P, N> node, final List<HyperplaneConvexSubset<P>> in,
final List<HyperplaneConvexSubset<P>> out) {

if (sub != null) {
if (node.isLeaf()) {
@@ -1123,21 +1123,14 @@ protected void acceptInput(final N input) {
if (input.isInternal()) {
final RegionCutBoundary<P> cutBoundary = input.getCutBoundary();

final HyperplaneSubset<P> outsideFacing = cutBoundary.getOutsideFacing();
final HyperplaneSubset<P> insideFacing = cutBoundary.getInsideFacing();

if (outsideFacing != null && !outsideFacing.isEmpty()) {
for (HyperplaneConvexSubset<P> boundary : outsideFacing.toConvex()) {

for (final HyperplaneConvexSubset<P> boundary : cutBoundary.getOutsideFacing()) {
if (insideFacing != null && !insideFacing.isEmpty()) {
for (HyperplaneConvexSubset<P> boundary : insideFacing.toConvex()) {
HyperplaneConvexSubset<P> reversed = boundary.reverse();

for (final HyperplaneConvexSubset<P> boundary : cutBoundary.getInsideFacing()) {
HyperplaneConvexSubset<P> reversed = boundary.reverse();


0 comments on commit 843d6a9

Please sign in to comment.