LinecastPoint2D.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.Comparator;
  20. import java.util.List;
  21. import java.util.ListIterator;

  22. import org.apache.commons.geometry.euclidean.AbstractLinecastPoint;
  23. import org.apache.commons.numbers.core.Precision;

  24. /** Class representing intersections resulting from linecast operations in Euclidean
  25.  * 2D space. This class contains the intersection point along with the boundary normal
  26.  * of the target at the point of intersection.
  27.  * @see Linecastable2D
  28.  */
  29. public class LinecastPoint2D extends AbstractLinecastPoint<Vector2D, Vector2D.Unit, Line> {

  30.     /** Comparator that sorts intersection instances by increasing abscissa order. If two abscissa
  31.      * values are equal, the comparison uses {@link Vector2D#COORDINATE_ASCENDING_ORDER} with the
  32.      * intersection normals.
  33.      */
  34.     public static final Comparator<LinecastPoint2D> ABSCISSA_ORDER = (a, b) -> {
  35.         int cmp = Double.compare(a.getAbscissa(), b.getAbscissa());
  36.         if (cmp == 0) {
  37.             cmp = Vector2D.COORDINATE_ASCENDING_ORDER.compare(a.getNormal(), b.getNormal());
  38.         }
  39.         return cmp;
  40.     };

  41.     /** Construct a new instance from its components.
  42.      * @param point the linecast intersection point
  43.      * @param normal the surface of the linecast target at the intersection point
  44.      * @param line intersecting line
  45.      */
  46.     public LinecastPoint2D(final Vector2D point, final Vector2D normal, final Line line) {
  47.         super(point, normal.normalize(), line);
  48.     }

  49.     /** Return true if this instance should be considered equivalent to the argument, using the
  50.      * given precision context for comparison. Instances are considered equivalent if they have equivalent
  51.      * points, normals, and lines.
  52.      * @param other point to compare with
  53.      * @param precision precision context to use for the comparison
  54.      * @return true if this instance should be considered equivalent to the argument
  55.      */
  56.     public boolean eq(final LinecastPoint2D other, final Precision.DoubleEquivalence precision) {
  57.         return getPoint().eq(other.getPoint(), precision) &&
  58.                 getNormal().eq(other.getNormal(), precision);
  59.     }

  60.     /** Sort the given list of linecast points by increasing abscissa value and filter to remove
  61.      * duplicate entries (as determined by the {@link #eq(LinecastPoint2D, Precision.DoubleEquivalence)} method).
  62.      * The argument is modified.
  63.      * @param pts list of points to sort and filter
  64.      */
  65.     public static void sortAndFilter(final List<LinecastPoint2D> pts) {
  66.         pts.sort(ABSCISSA_ORDER);

  67.         double currentAbscissa = Double.POSITIVE_INFINITY;
  68.         final List<LinecastPoint2D> abscissaList = new ArrayList<>();

  69.         final ListIterator<LinecastPoint2D> it = pts.listIterator();
  70.         LinecastPoint2D pt;
  71.         while (it.hasNext()) {
  72.             pt = it.next();
  73.             if (!pt.getLine().getPrecision().eq(currentAbscissa, pt.getAbscissa())) {
  74.                 // new abscissa value
  75.                 currentAbscissa = pt.getAbscissa();
  76.                 abscissaList.clear();

  77.                 abscissaList.add(pt);
  78.             } else if (containsEq(pt, abscissaList)) {
  79.                 // duplicate found for this abscissa value
  80.                 it.remove();
  81.             } else {
  82.                 // not a duplicate
  83.                 abscissaList.add(pt);
  84.             }
  85.         }
  86.     }

  87.     /** Return true if the given linecast point is equivalent to any of those in the given list.
  88.      * @param pt point to test
  89.      * @param list list to test against
  90.      * @return true if the given linecast point is equivalent to any of those in the given list
  91.      */
  92.     private static boolean containsEq(final LinecastPoint2D pt, final List<? extends LinecastPoint2D> list) {
  93.         final Precision.DoubleEquivalence precision = pt.getLine().getPrecision();

  94.         for (final LinecastPoint2D listPt : list) {
  95.             if (listPt.eq(pt, precision)) {
  96.                 return true;
  97.             }
  98.         }

  99.         return false;
  100.     }
  101. }