001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.euclidean.twod; 018 019import java.util.List; 020 021import org.apache.commons.geometry.core.RegionEmbedding; 022import org.apache.commons.geometry.core.RegionLocation; 023import org.apache.commons.geometry.core.partitioning.HyperplaneBoundedRegion; 024import org.apache.commons.geometry.core.partitioning.HyperplaneSubset; 025import org.apache.commons.geometry.core.partitioning.Split; 026import org.apache.commons.geometry.euclidean.oned.Vector1D; 027import org.apache.commons.numbers.core.Precision; 028 029/** Class representing a subset of points on a line in 2D Euclidean space. For example, line segments 030 * and rays are line subsets. Line subsets may be finite or infinite. 031 */ 032public abstract class LineSubset implements HyperplaneSubset<Vector2D>, RegionEmbedding<Vector2D, Vector1D> { 033 /** The line containing this instance. */ 034 private final Line line; 035 036 /** Construct a new instance based on the given line. 037 * @param line line forming the base of the instance 038 */ 039 LineSubset(final Line line) { 040 this.line = line; 041 } 042 043 /** Get the line containing this subset. This method is an alias 044 * for {@link #getHyperplane()}. 045 * @return the line containing this subset 046 * @see #getHyperplane() 047 */ 048 public Line getLine() { 049 return getHyperplane(); 050 } 051 052 /** {@inheritDoc} */ 053 @Override 054 public Line getHyperplane() { 055 return line; 056 } 057 058 /** {@inheritDoc} */ 059 @Override 060 public Vector1D toSubspace(final Vector2D pt) { 061 return line.toSubspace(pt); 062 } 063 064 /** Get a {@link Bounds2D} object defining an axis-aligned bounding box containing all 065 * vertices for this subset. Null is returned if the subset is infinite or does not 066 * contain any vertices. 067 * @return the bounding box for this instance or null if no valid bounds could be determined 068 */ 069 public abstract Bounds2D getBounds(); 070 071 /** {@inheritDoc} */ 072 @Override 073 public abstract HyperplaneBoundedRegion<Vector1D> getSubspaceRegion(); 074 075 /** {@inheritDoc} */ 076 @Override 077 public Vector2D toSpace(final Vector1D pt) { 078 return line.toSpace(pt); 079 } 080 081 /** {@inheritDoc} */ 082 @Override 083 public abstract List<LineConvexSubset> toConvex(); 084 085 /** {@inheritDoc} */ 086 @Override 087 public RegionLocation classify(final Vector2D pt) { 088 if (line.contains(pt)) { 089 return classifyAbscissa(line.abscissa(pt)); 090 } 091 092 return RegionLocation.OUTSIDE; 093 } 094 095 /** Get the unique intersection of this subset with the given line. Null is 096 * returned if no unique intersection point exists (ie, the lines are 097 * parallel or coincident) or the line does not intersect this instance. 098 * @param inputLine line to intersect with this line subset 099 * @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}