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 19 import java.util.List; 20 21 import org.apache.commons.geometry.core.RegionEmbedding; 22 import org.apache.commons.geometry.core.RegionLocation; 23 import org.apache.commons.geometry.core.partitioning.HyperplaneBoundedRegion; 24 import org.apache.commons.geometry.core.partitioning.HyperplaneSubset; 25 import org.apache.commons.geometry.core.partitioning.Split; 26 import org.apache.commons.geometry.euclidean.oned.Vector1D; 27 import org.apache.commons.numbers.core.Precision; 28 29 /** Class representing a subset of points on a line in 2D Euclidean space. For example, line segments 30 * and rays are line subsets. Line subsets may be finite or infinite. 31 */ 32 public abstract class LineSubset implements HyperplaneSubset<Vector2D>, RegionEmbedding<Vector2D, Vector1D> { 33 /** The line containing this instance. */ 34 private final Line line; 35 36 /** Construct a new instance based on the given line. 37 * @param line line forming the base of the instance 38 */ 39 LineSubset(final Line line) { 40 this.line = line; 41 } 42 43 /** Get the line containing this subset. This method is an alias 44 * for {@link #getHyperplane()}. 45 * @return the line containing this subset 46 * @see #getHyperplane() 47 */ 48 public Line getLine() { 49 return getHyperplane(); 50 } 51 52 /** {@inheritDoc} */ 53 @Override 54 public Line getHyperplane() { 55 return line; 56 } 57 58 /** {@inheritDoc} */ 59 @Override 60 public Vector1D toSubspace(final Vector2D pt) { 61 return line.toSubspace(pt); 62 } 63 64 /** Get a {@link Bounds2D} object defining an axis-aligned bounding box containing all 65 * vertices for this subset. Null is returned if the subset is infinite or does not 66 * contain any vertices. 67 * @return the bounding box for this instance or null if no valid bounds could be determined 68 */ 69 public abstract Bounds2D getBounds(); 70 71 /** {@inheritDoc} */ 72 @Override 73 public abstract HyperplaneBoundedRegion<Vector1D> getSubspaceRegion(); 74 75 /** {@inheritDoc} */ 76 @Override 77 public Vector2D toSpace(final Vector1D pt) { 78 return line.toSpace(pt); 79 } 80 81 /** {@inheritDoc} */ 82 @Override 83 public abstract List<LineConvexSubset> toConvex(); 84 85 /** {@inheritDoc} */ 86 @Override 87 public RegionLocation classify(final Vector2D pt) { 88 if (line.contains(pt)) { 89 return classifyAbscissa(line.abscissa(pt)); 90 } 91 92 return RegionLocation.OUTSIDE; 93 } 94 95 /** Get the unique intersection of this subset with the given line. Null is 96 * returned if no unique intersection point exists (ie, the lines are 97 * parallel or coincident) or the line does not intersect this instance. 98 * @param inputLine line to intersect with this line subset 99 * @return the unique intersection point between the line and this line subset 100 * or null if no such point exists. 101 * @see Line#intersection(Line) 102 */ 103 public Vector2D intersection(final Line inputLine) { 104 final Vector2D pt = line.intersection(inputLine); 105 return (pt != null && contains(pt)) ? pt : null; 106 } 107 108 /** Get the unique intersection of this instance with the given line subset. Null 109 * is returned if the lines containing the line subsets do not have a unique intersection 110 * point (ie, they are parallel or coincident) or the intersection point is unique 111 * but is not contained in both line subsets. 112 * @param subset line subset to intersect with 113 * @return the unique intersection point between this line subset and the argument or 114 * null if no such point exists. 115 * @see Line#intersection(Line) 116 */ 117 public Vector2D intersection(final LineSubset subset) { 118 final Vector2D pt = intersection(subset.getLine()); 119 return (pt != null && subset.contains(pt)) ? pt : null; 120 } 121 122 /** Return the object used to perform floating point comparisons, which is the 123 * same object used by the underlying {@link Line}). 124 * @return precision object used to perform floating point comparisons. 125 */ 126 public Precision.DoubleEquivalence getPrecision() { 127 return line.getPrecision(); 128 } 129 130 /** Classify the given line abscissa value with respect to the subspace region. 131 * @param abscissa the abscissa value to classify 132 * @return the region location of the line abscissa value 133 */ 134 abstract RegionLocation classifyAbscissa(double abscissa); 135 136 /** Get a split result for cases where no intersection exists between the splitting line and the 137 * line underlying the given line subset. This occurs when the two lines are parallel or coincident. 138 * @param <T> Line subset type 139 * @param splitter splitting line 140 * @param subset line subset instance being split 141 * @return return result of the non-intersecting split operation 142 */ 143 <T extends LineSubset> Split<T> getNonIntersectingSplitResult(final Line splitter, final T subset) { 144 // check which side of the splitter we lie on 145 final double offset = splitter.offset(subset.getLine()); 146 final int comp = getPrecision().compare(offset, 0.0); 147 148 if (comp < 0) { 149 return new Split<>(subset, null); 150 } else if (comp > 0) { 151 return new Split<>(null, subset); 152 } else { 153 return new Split<>(null, null); 154 } 155 } 156 157 /** Return true if the plus side of the given splitter line is facing in the positive direction 158 * of this line. 159 * @param splitterLine line splitting this instance 160 * @return true if the plus side of the given line is facing in the positive direction of this 161 * line 162 */ 163 boolean splitterPlusIsPositiveFacing(final Line splitterLine) { 164 return line.getOffsetDirection().dot(splitterLine.getDirection()) <= 0; 165 } 166 167 /** Create a split result for the given splitter line, given the low and high split portion of this 168 * instance. The arguments are assigned to the split result's minus and plus properties based on the 169 * relative orientation of the splitter line. 170 * @param <T> Line subset type 171 * @param splitter splitter line 172 * @param low portion of the split result closest to negative infinity on this line 173 * @param high portion of th split result closest to positive infinity on this line 174 * @return a split result for the given splitter line. 175 */ 176 <T extends LineSubset> Split<T> createSplitResult(final Line splitter, final T low, final T high) { 177 return splitterPlusIsPositiveFacing(splitter) ? 178 new Split<>(low, high) : 179 new Split<>(high, low); 180 } 181 }