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 org.apache.commons.geometry.core.RegionLocation; 020import org.apache.commons.geometry.core.Transform; 021import org.apache.commons.geometry.core.partitioning.Split; 022import org.apache.commons.numbers.core.Precision; 023 024/** Class representing a line segment in 2D Euclidean space. A line segment is a portion of 025 * a line with finite start and end points. 026 * 027 * <p>Instances of this class are guaranteed to be immutable.</p> 028 * @see Lines 029 * @see <a href="https://en.wikipedia.org/wiki/Line_segment">Line Segment</a> 030 */ 031public final class Segment extends LineConvexSubset { 032 033 /** Start point for the segment. */ 034 private final Vector2D startPoint; 035 036 /** End point for the segment. */ 037 private final Vector2D endPoint; 038 039 /** Construct a new instance from a line and two points on the line. Callers are responsible for 040 * ensuring that the given points lie on the line and are in order of increasing abscissa. 041 * No validation is performed. 042 * @param line line for the segment 043 * @param startPoint segment start point 044 * @param endPoint segment end point 045 */ 046 Segment(final Line line, final Vector2D startPoint, final Vector2D endPoint) { 047 super(line); 048 049 this.startPoint = startPoint; 050 this.endPoint = endPoint; 051 } 052 053 /** {@inheritDoc} 054 * 055 * <p>This method always returns {@code false}.</p> 056 */ 057 @Override 058 public boolean isFull() { 059 return false; 060 } 061 062 /** {@inheritDoc} 063 * 064 * <p>This method always returns {@code false}.</p> 065 */ 066 @Override 067 public boolean isInfinite() { 068 return false; 069 } 070 071 /** {@inheritDoc} 072 * 073 * <p>This method always returns {@code true}.</p> 074 */ 075 @Override 076 public boolean isFinite() { 077 return true; 078 } 079 080 /** {@inheritDoc} */ 081 @Override 082 public double getSize() { 083 return startPoint.distance(endPoint); 084 } 085 086 /** {@inheritDoc} */ 087 @Override 088 public Vector2D getCentroid() { 089 return startPoint.lerp(endPoint, 0.5); 090 } 091 092 /** {@inheritDoc} */ 093 @Override 094 public Vector2D getStartPoint() { 095 return startPoint; 096 } 097 098 /** {@inheritDoc} */ 099 @Override 100 public double getSubspaceStart() { 101 return getLine().abscissa(startPoint); 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public Vector2D getEndPoint() { 107 return endPoint; 108 } 109 110 /** {@inheritDoc} */ 111 @Override 112 public double getSubspaceEnd() { 113 return getLine().abscissa(endPoint); 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 public Bounds2D getBounds() { 119 return Bounds2D.builder() 120 .add(startPoint) 121 .add(endPoint) 122 .build(); 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public Segment transform(final Transform<Vector2D> transform) { 128 final Vector2D t1 = transform.apply(getStartPoint()); 129 final Vector2D t2 = transform.apply(getEndPoint()); 130 131 final Line tLine = getLine().transform(transform); 132 133 return new Segment(tLine, t1, t2); 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public Segment reverse() { 139 return new Segment(getLine().reverse(), endPoint, startPoint); 140 } 141 142 /** {@inheritDoc} */ 143 @Override 144 public String toString() { 145 final StringBuilder sb = new StringBuilder(); 146 sb.append(getClass().getSimpleName()) 147 .append("[startPoint= ") 148 .append(getStartPoint()) 149 .append(", endPoint= ") 150 .append(getEndPoint()) 151 .append(']'); 152 153 return sb.toString(); 154 } 155 156 /** {@inheritDoc} */ 157 @Override 158 RegionLocation classifyAbscissa(final double abscissa) { 159 final Precision.DoubleEquivalence precision = getPrecision(); 160 final int startCmp = precision.compare(abscissa, getSubspaceStart()); 161 if (startCmp > 0) { 162 final int endCmp = precision.compare(abscissa, getSubspaceEnd()); 163 if (endCmp < 0) { 164 return RegionLocation.INSIDE; 165 } else if (endCmp == 0) { 166 return RegionLocation.BOUNDARY; 167 } 168 } else if (startCmp == 0) { 169 return RegionLocation.BOUNDARY; 170 } 171 172 return RegionLocation.OUTSIDE; 173 } 174 175 /** {@inheritDoc} */ 176 @Override 177 double closestAbscissa(final double abscissa) { 178 return Math.max(getSubspaceStart(), Math.min(getSubspaceEnd(), abscissa)); 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 Split<LineConvexSubset> splitOnIntersection(final Line splitter, final Vector2D intersection) { 184 final Line line = getLine(); 185 186 final Precision.DoubleEquivalence splitterPrecision = splitter.getPrecision(); 187 188 final int startCmp = splitterPrecision.compare(splitter.offset(startPoint), 0.0); 189 final int endCmp = splitterPrecision.compare(splitter.offset(endPoint), 0.0); 190 191 if (startCmp == 0 && endCmp == 0) { 192 // the entire segment is directly on the splitter line 193 return new Split<>(null, null); 194 } else if (startCmp < 1 && endCmp < 1) { 195 // the entire segment is on the minus side 196 return new Split<>(this, null); 197 } else if (startCmp > -1 && endCmp > -1) { 198 // the entire segment is on the plus side 199 return new Split<>(null, this); 200 } 201 202 // we need to split the line 203 final Segment startSegment = new Segment(line, startPoint, intersection); 204 final Segment endSegment = new Segment(line, intersection, endPoint); 205 206 final Segment minus = (startCmp > 0) ? endSegment : startSegment; 207 final Segment plus = (startCmp > 0) ? startSegment : endSegment; 208 209 return new Split<>(minus, plus); 210 } 211}