EmbeddedTreeLineSubset.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.geometry.euclidean.twod;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.apache.commons.geometry.core.RegionLocation;
  21. import org.apache.commons.geometry.core.Transform;
  22. import org.apache.commons.geometry.core.internal.HyperplaneSubsets;
  23. import org.apache.commons.geometry.core.partitioning.Hyperplane;
  24. import org.apache.commons.geometry.core.partitioning.Split;
  25. import org.apache.commons.geometry.core.partitioning.SplitLocation;
  26. import org.apache.commons.geometry.euclidean.oned.Interval;
  27. import org.apache.commons.geometry.euclidean.oned.OrientedPoint;
  28. import org.apache.commons.geometry.euclidean.oned.OrientedPoints;
  29. import org.apache.commons.geometry.euclidean.oned.RegionBSPTree1D;
  30. import org.apache.commons.geometry.euclidean.oned.Vector1D;
  31. import org.apache.commons.geometry.euclidean.twod.Line.SubspaceTransform;
  32. import org.apache.commons.numbers.core.Precision;

  33. /** Class representing an arbitrary subset of a line using a {@link RegionBSPTree1D}.
  34.  * This class can represent convex, non-convex, finite, infinite, and empty regions.
  35.  *
  36.  * <p>This class is mutable and <em>not</em> thread safe.</p>
  37.  */
  38. public final class EmbeddedTreeLineSubset extends LineSubset {
  39.     /** The 1D region representing the area on the line. */
  40.     private final RegionBSPTree1D region;

  41.     /** Construct a new, empty subset for the given line.
  42.      * @param line line defining the subset
  43.      */
  44.     public EmbeddedTreeLineSubset(final Line line) {
  45.         this(line, false);
  46.     }

  47.     /** Construct a new subset for the given line. If {@code full}
  48.      * is true, then the subset will cover the entire line; otherwise,
  49.      * it will be empty.
  50.      * @param line line defining the subset
  51.      * @param full if true, the subset will cover the entire space;
  52.      *      otherwise it will be empty
  53.      */
  54.     public EmbeddedTreeLineSubset(final Line line, final boolean full) {
  55.         this(line, new RegionBSPTree1D(full));
  56.     }

  57.     /** Construct a new instance from its defining line and subspace region. The give
  58.      * BSP tree is used directly by this instance; it is not copied.
  59.      * @param line line defining the subset
  60.      * @param region subspace region for the instance
  61.      */
  62.     public EmbeddedTreeLineSubset(final Line line, final RegionBSPTree1D region) {
  63.         super(line);

  64.         this.region = region;
  65.     }

  66.     /** {@inheritDoc} */
  67.     @Override
  68.     public boolean isFull() {
  69.         return region.isFull();
  70.     }

  71.     /** {@inheritDoc} */
  72.     @Override
  73.     public boolean isEmpty() {
  74.         return region.isEmpty();
  75.     }

  76.     /** {@inheritDoc} */
  77.     @Override
  78.     public double getSize() {
  79.         return region.getSize();
  80.     }

  81.     /** {@inheritDoc} */
  82.     @Override
  83.     public Vector2D getCentroid() {
  84.         final Vector1D subspaceCentroid = region.getCentroid();
  85.         if (subspaceCentroid != null) {
  86.             return getLine().toSpace(subspaceCentroid);
  87.         }
  88.         return null;
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public Bounds2D getBounds() {
  93.         final double min = region.getMin();
  94.         final double max = region.getMax();

  95.         if (Double.isFinite(min) && Double.isFinite(max)) {
  96.             final Line line = getLine();

  97.             return Bounds2D.builder()
  98.                     .add(line.toSpace(min))
  99.                     .add(line.toSpace(max))
  100.                     .build();
  101.         }

  102.         return null;
  103.     }

  104.     /** {@inheritDoc} */
  105.     @Override
  106.     public Vector2D closest(final Vector2D pt) {
  107.         return HyperplaneSubsets.closestToEmbeddedRegion(pt, getLine(), region);
  108.     }

  109.     /** {@inheritDoc} */
  110.     @Override
  111.     public EmbeddedTreeLineSubset transform(final Transform<Vector2D> transform) {
  112.         final SubspaceTransform st = getLine().subspaceTransform(transform);

  113.         final RegionBSPTree1D tRegion = RegionBSPTree1D.empty();
  114.         tRegion.copy(region);
  115.         tRegion.transform(st.getTransform());

  116.         return new EmbeddedTreeLineSubset(st.getLine(), tRegion);
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public List<LineConvexSubset> toConvex() {
  121.         final List<Interval> intervals = region.toIntervals();

  122.         final Line line = getLine();
  123.         final List<LineConvexSubset> convexSubsets = new ArrayList<>(intervals.size());

  124.         for (final Interval interval : intervals) {
  125.             convexSubsets.add(Lines.subsetFromInterval(line, interval));
  126.         }

  127.         return convexSubsets;
  128.     }

  129.     /** {@inheritDoc} */
  130.     @Override
  131.     public RegionBSPTree1D getSubspaceRegion() {
  132.         return region;
  133.     }

  134.     /** {@inheritDoc}
  135.      *
  136.      * <p>In all cases, the current instance is not modified. However, In order to avoid
  137.      * unnecessary copying, this method will use the current instance as the split value when
  138.      * the instance lies entirely on the plus or minus side of the splitter. For example, if
  139.      * this instance lies entirely on the minus side of the splitter, the subplane
  140.      * returned by {@link Split#getMinus()} will be this instance. Similarly, {@link Split#getPlus()}
  141.      * will return the current instance if it lies entirely on the plus side. Callers need to make
  142.      * special note of this, since this class is mutable.</p>
  143.      */
  144.     @Override
  145.     public Split<EmbeddedTreeLineSubset> split(final Hyperplane<Vector2D> splitter) {
  146.         final Line thisLine = getLine();
  147.         final Line splitterLine = (Line) splitter;
  148.         final Precision.DoubleEquivalence precision = getPrecision();

  149.         final Vector2D intersection = splitterLine.intersection(thisLine);
  150.         if (intersection == null) {
  151.             return getNonIntersectingSplitResult(splitterLine, this);
  152.         }

  153.         final double abscissa = thisLine.abscissa(intersection);
  154.         final OrientedPoint subspaceSplitter = OrientedPoints.fromLocationAndDirection(
  155.                 abscissa,
  156.                 splitterPlusIsPositiveFacing(splitterLine),
  157.                 precision);

  158.         final Split<RegionBSPTree1D> subspaceSplit = region.split(subspaceSplitter);
  159.         final SplitLocation subspaceSplitLoc = subspaceSplit.getLocation();

  160.         if (SplitLocation.MINUS == subspaceSplitLoc) {
  161.             return new Split<>(this, null);
  162.         } else if (SplitLocation.PLUS == subspaceSplitLoc) {
  163.             return new Split<>(null, this);
  164.         }

  165.         final EmbeddedTreeLineSubset minus = (subspaceSplit.getMinus() != null) ?
  166.                 new EmbeddedTreeLineSubset(thisLine, subspaceSplit.getMinus()) :
  167.                 null;
  168.         final EmbeddedTreeLineSubset plus = (subspaceSplit.getPlus() != null) ?
  169.                 new EmbeddedTreeLineSubset(thisLine, subspaceSplit.getPlus()) :
  170.                 null;

  171.         return new Split<>(minus, plus);
  172.     }

  173.     /** Add a line subset to this instance.
  174.      * @param subset the line subset to add
  175.      * @throws IllegalArgumentException if the given line subset is not from
  176.      *      a line equivalent to this instance
  177.      */
  178.     public void add(final LineConvexSubset subset) {
  179.         Lines.validateLinesEquivalent(getLine(), subset.getLine());

  180.         region.add(subset.getInterval());
  181.     }

  182.     /** Add the region represented by the given line subset to this instance.
  183.      * The argument is not modified.
  184.      * @param subset line subset to add
  185.      * @throws IllegalArgumentException if the given line subset is not from
  186.      *      a line equivalent to this instance
  187.      */
  188.     public void add(final EmbeddedTreeLineSubset subset) {
  189.         Lines.validateLinesEquivalent(getLine(), subset.getLine());

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

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public String toString() {
  195.         final Line line = getLine();

  196.         final StringBuilder sb = new StringBuilder();
  197.         sb.append(this.getClass().getSimpleName())
  198.             .append('[')
  199.             .append("lineOrigin= ")
  200.             .append(line.getOrigin())
  201.             .append(", lineDirection= ")
  202.             .append(line.getDirection())
  203.             .append(", region= ")
  204.             .append(region)
  205.             .append(']');

  206.         return sb.toString();
  207.     }

  208.     /** {@inheritDoc} */
  209.     @Override
  210.     RegionLocation classifyAbscissa(final double abscissa) {
  211.         return region.classify(abscissa);
  212.     }
  213. }