BoundaryProjector.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.partitioning;

  22. import java.util.ArrayList;
  23. import java.util.List;

  24. import org.hipparchus.geometry.Point;
  25. import org.hipparchus.geometry.Space;
  26. import org.hipparchus.geometry.partitioning.Region.Location;
  27. import org.hipparchus.util.FastMath;

  28. /** Local tree visitor to compute projection on boundary.
  29.  * @param <S> Type of the space.
  30.  * @param <P> Type of the points in space.
  31.  * @param <H> Type of the hyperplane.
  32.  * @param <I> Type of the sub-hyperplane.
  33.  * @param <T> Type of the sub-space.
  34.  * @param <Q> Type of the points in sub-space.
  35.  * @param <F> Type of the hyperplane in the destination sub-space.
  36.  * @param <J> Type of the sub-hyperplane in the destination sub-space.
  37.  */
  38. class BoundaryProjector<S extends Space,
  39.                         P extends Point<S, P>,
  40.                         H extends Hyperplane<S, P, H, I>,
  41.                         I extends SubHyperplane<S, P, H, I>,
  42.                         T extends Space,
  43.                         Q extends Point<T, Q>,
  44.                         F extends Hyperplane<T, Q, F, J>,
  45.                         J extends SubHyperplane<T, Q, F, J>>
  46.     implements BSPTreeVisitor<S, P, H, I> {

  47.     /** Original point. */
  48.     private final P original;

  49.     /** Current best projected point. */
  50.     private P projected;

  51.     /** Leaf node closest to the test point. */
  52.     private BSPTree<S, P, H, I> leaf;

  53.     /** Current offset. */
  54.     private double offset;

  55.     /** Simple constructor.
  56.      * @param original original point
  57.      */
  58.     BoundaryProjector(final P original) {
  59.         this.original  = original;
  60.         this.projected = null;
  61.         this.leaf      = null;
  62.         this.offset    = Double.POSITIVE_INFINITY;
  63.     }

  64.     /** {@inheritDoc} */
  65.     @Override
  66.     public Order visitOrder(final BSPTree<S, P, H, I> node) {
  67.         // we want to visit the tree so that the first encountered
  68.         // leaf is the one closest to the test point
  69.         if (node.getCut().getHyperplane().getOffset(original) <= 0) {
  70.             return Order.MINUS_SUB_PLUS;
  71.         } else {
  72.             return Order.PLUS_SUB_MINUS;
  73.         }
  74.     }

  75.     /** {@inheritDoc} */
  76.     @Override
  77.     public void visitInternalNode(final BSPTree<S, P, H, I> node) {

  78.         // project the point on the cut sub-hyperplane
  79.         final H hyperplane = node.getCut().getHyperplane();
  80.         final double signedOffset = hyperplane.getOffset(original);
  81.         if (FastMath.abs(signedOffset) < offset) {

  82.             // project point
  83.             final P regular = hyperplane.project(original);

  84.             // get boundary parts
  85.             final List<Region<T, Q, F, J>> boundaryParts = boundaryRegions(node);

  86.             // check if regular projection really belongs to the boundary
  87.             boolean regularFound = false;
  88.             for (final Region<T, Q, F, J> part : boundaryParts) {
  89.                 if (!regularFound && belongsToPart(regular, hyperplane, part)) {
  90.                     // the projected point lies in the boundary
  91.                     projected    = regular;
  92.                     offset       = FastMath.abs(signedOffset);
  93.                     regularFound = true;
  94.                 }
  95.             }

  96.             if (!regularFound) {
  97.                 // the regular projected point is not on boundary,
  98.                 // so we have to check further if a singular point
  99.                 // (i.e. a vertex in 2D case) is a possible projection
  100.                 for (final Region<T, Q, F, J> part : boundaryParts) {
  101.                     final P spI = singularProjection(regular, hyperplane, part);
  102.                     if (spI != null) {
  103.                         final double distance = original.distance(spI);
  104.                         if (distance < offset) {
  105.                             projected = spI;
  106.                             offset    = distance;
  107.                         }
  108.                     }
  109.                 }

  110.             }

  111.         }

  112.     }

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public void visitLeafNode(final BSPTree<S, P, H, I> node) {
  116.         if (leaf == null) {
  117.             // this is the first leaf we visit,
  118.             // it is the closest one to the original point
  119.             leaf = node;
  120.         }
  121.     }

  122.     /** Get the projection.
  123.      * @return projection
  124.      */
  125.     public BoundaryProjection<S, P> getProjection() {

  126.         // fix offset sign
  127.         offset = FastMath.copySign(offset, (Boolean) leaf.getAttribute() ? -1 : +1);

  128.         return new BoundaryProjection<>(original, projected, offset);

  129.     }

  130.     /** Extract the regions of the boundary on an internal node.
  131.      * @param node internal node
  132.      * @return regions in the node sub-hyperplane
  133.      */
  134.     private List<Region<T, Q, F, J>> boundaryRegions(final BSPTree<S, P, H, I> node) {

  135.         final List<Region<T, Q, F, J>> regions = new ArrayList<>(2);

  136.         @SuppressWarnings("unchecked")
  137.         final BoundaryAttribute<S, P, H, I> ba = (BoundaryAttribute<S, P, H, I>) node.getAttribute();
  138.         addRegion(ba.getPlusInside(),  regions);
  139.         addRegion(ba.getPlusOutside(), regions);

  140.         return regions;

  141.     }

  142.     /** Add a boundary region to a list.
  143.      * @param sub sub-hyperplane defining the region
  144.      * @param list to fill up
  145.      */
  146.     private void addRegion(final SubHyperplane<S, P, H, I> sub, final List<Region<T, Q, F, J>> list) {
  147.         if (sub != null) {
  148.             final Region<T, Q, F, J> region = ((AbstractSubHyperplane<S, P, H, I, T, Q, F, J>) sub).getRemainingRegion();
  149.             if (region != null) {
  150.                 list.add(region);
  151.             }
  152.         }
  153.     }

  154.     /** Check if a projected point lies on a boundary part.
  155.      * @param point projected point to check
  156.      * @param hyperplane hyperplane into which the point was projected
  157.      * @param part boundary part
  158.      * @return true if point lies on the boundary part
  159.      */
  160.     private boolean belongsToPart(final P point, final Hyperplane<S, P, H, I> hyperplane,
  161.                                   final Region<T, Q, F, J> part) {

  162.         // there is a non-null sub-space, we can dive into smaller dimensions
  163.         @SuppressWarnings("unchecked")
  164.         final Embedding<S, P, T, Q> embedding = (Embedding<S, P, T, Q>) hyperplane;
  165.         return part.checkPoint(embedding.toSubSpace(point)) != Location.OUTSIDE;

  166.     }

  167.     /** Get the projection to the closest boundary singular point.
  168.      * @param point projected point to check
  169.      * @param hyperplane hyperplane into which the point was projected
  170.      * @param part boundary part
  171.      * @return projection to a singular point of boundary part (may be null)
  172.      */
  173.     private P singularProjection(final P point, final Hyperplane<S, P, H, I> hyperplane,
  174.                                         final Region<T, Q, F, J> part) {

  175.         // there is a non-null sub-space, we can dive into smaller dimensions
  176.         @SuppressWarnings("unchecked")
  177.         final Embedding<S, P, T, Q> embedding = (Embedding<S, P, T, Q>) hyperplane;
  178.         final BoundaryProjection<T, Q> bp = part.projectToBoundary(embedding.toSubSpace(point));

  179.         // back to initial dimension
  180.         return (bp.getProjected() == null) ? null : embedding.toSpace(bp.getProjected());

  181.     }

  182. }