001/* 002 * GeomPlane -- Represents a 2D plane in n-D space. 003 * 004 * Copyright (C) 2009-2015, Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom; 023 024import jahuwaldt.js.param.Parameter; 025import static java.util.Objects.nonNull; 026import static java.util.Objects.requireNonNull; 027import javax.measure.quantity.Dimensionless; 028import javax.measure.quantity.Length; 029import javolution.text.Text; 030import javolution.text.TextBuilder; 031 032/** 033 * The interface and implementation in common to all 2D planes in n-dimensional space. 034 * 035 * <p> Modified by: Joseph A. Huwaldt </p> 036 * 037 * @author Joseph A. Huwaldt, Date: June 14, 2009 038 * @version November 25, 2015 039 */ 040@SuppressWarnings({"serial", "CloneableImplementsClone"}) 041public abstract class GeomPlane extends AbstractGeomElement<GeomPlane> implements Transformable<GeomPlane> { 042 043 /** 044 * Return an immutable version of this plane. 045 * 046 * @return An immutable version of this plane. 047 */ 048 public abstract Plane immutable(); 049 050 /** 051 * Returns the number of child-elements that make up this geometry element. This 052 * implementation always returns 0 as a GeomPlane is not made up of any other 053 * elements. 054 */ 055 @Override 056 public int size() { 057 return 0; 058 } 059 060 /** 061 * Returns the number of parametric dimensions of the geometry element. This 062 * implementation always returns 0 as a GeomPlane is not parametric. 063 */ 064 @Override 065 public int getParDimension() { 066 return 0; 067 } 068 069 /** 070 * Return the normal vector for the plane. The normal vector is a unit vector that is 071 * perpendicular to the plane. 072 * 073 * @return The normal vector for the plane. 074 */ 075 public abstract GeomVector<Dimensionless> getNormal(); 076 077 /** 078 * Return the constant term of the plane equation (e.g.: "D" for a 3D plane: 079 * <code>A*x + B*y + C*z = D</code>). 080 * 081 * @return The constant term of the plane equation for this plane. 082 */ 083 public abstract Parameter<Length> getConstant(); 084 085 /** 086 * Return the reference point for this plane. The reference point is an arbitrary 087 * point that is contained in the plane and is used as a reference when drawing the 088 * plane. 089 * 090 * @return The reference point for this plane. 091 */ 092 public abstract GeomPoint getRefPoint(); 093 094 /** 095 * Return a copy of this plane converted to the specified number of physical 096 * dimensions. If the number of dimensions is greater than this element, then zeros 097 * are added to the additional dimensions. If the number of dimensions is less than 098 * this element, then the extra dimensions are simply dropped (truncated). If the new 099 * dimensions are the same as the dimension of this element, then this element is 100 * simply returned. 101 * 102 * @param newDim The dimension of the plane to return. 103 * @return A copy of this plane converted to the new dimensions. 104 */ 105 @Override 106 public abstract GeomPlane toDimension(int newDim); 107 108 /** 109 * Return a new Plane that is identical to this plane, but with a different reference 110 * point (the plane is shifted to pass through the specified point). 111 * 112 * @param p The new reference point that the plane should pass through. May not be null. 113 * @return A new Plane that is identical to this plane, but with a different reference 114 * point. 115 */ 116 public Plane changeRefPoint(GeomPoint p) { 117 Plane pln = Plane.valueOf(getNormal(), p.immutable()); 118 copyState(pln); 119 return pln; 120 } 121 122 /** 123 * Return the coordinate point representing the minimum bounding box corner (e.g.: min 124 * X, min Y, min Z). 125 * 126 * @return The minimum bounding box coordinate for this geometry element. 127 */ 128 @Override 129 public Point getBoundsMin() { 130 return getRefPoint().immutable(); 131 } 132 133 /** 134 * Return the coordinate point representing the maximum bounding box corner (e.g.: max 135 * X, max Y, max Z). 136 * 137 * @return The maximum bounding box coordinate for this geometry element. 138 */ 139 @Override 140 public Point getBoundsMax() { 141 return getRefPoint().immutable(); 142 } 143 144 /** 145 * Returns the most extreme point, either minimum or maximum, in the specified 146 * coordinate direction on this geometry element. This implementation simply returns 147 * this plane's reference point. 148 * 149 * @param dim An index indicating the dimension to find the min/max point for (0=X, 150 * 1=Y, 2=Z, etc). 151 * @param max Set to <code>true</code> to return the maximum value, <code>false</code> 152 * to return the minimum. 153 * @param tol Fractional tolerance to refine the min/max point position to if 154 * necessary. 155 * @return The point found on this element that is the min or max in the specified 156 * coordinate direction. 157 * @see #getBoundsMin 158 * @see #getBoundsMax 159 */ 160 @Override 161 public GeomPoint getLimitPoint(int dim, boolean max, double tol) { 162 return getRefPoint(); 163 } 164 165 /** 166 * Return the closest point on this plane to the input point. 167 * 168 * @param p The point to find the closest point on this plane to. May not be null. 169 * @return The point on the plane closest to the input point. 170 */ 171 public Point getClosest(GeomPoint p) { 172 Point cp = GeomUtil.pointPlaneClosest(requireNonNull(p), getRefPoint(), getNormal()); 173 return cp; 174 } 175 176 /** 177 * Return the intersection between a curve and this plane. 178 * 179 * @param curve The curve to intersect with this plane. May not be null. 180 * @param tol Fractional tolerance (in parameter space) to refine the point 181 * positions to. 182 * @return A PointString containing zero or more subrange points made by the 183 * intersection of this plane with the specified curve. If no intersection is 184 * found, an empty PointString is returned. 185 */ 186 public PointString intersect(Curve curve, double tol) { 187 return curve.intersect(this, tol); 188 } 189 190 /** 191 * Return the intersection between an infinite line/ray and this plane. 192 * 193 * @param L0 A point on the line (origin of the line). May not be null. 194 * @param Lu The direction vector for the line (does not have to be a unit vector). 195 * May not be null. 196 * @param out The pre-defined output point on the line that will be filled in to 197 * represent the intersection of the line and the plane (not modified if 198 * there is no intersection). If <code>null</code> is passed, then the 199 * point is not calculated, but the return codes of this method are still 200 * valid. 201 * @return DISJOINT if the line and plane are disjoint (parallel), INTERSECT if there 202 * is a unique intersection and COINCIDENT if the line is coincident with the 203 * plane. 204 */ 205 public IntersectType intersect(GeomPoint L0, GeomVector Lu, MutablePoint out) throws DimensionException { 206 return GeomUtil.linePlaneIntersect(L0, Lu, this, out); 207 } 208 209 /** 210 * Return the intersection between a line segment and this plane. 211 * 212 * @param line The line segment to intersect with this plane. May not be null. 213 * @return A PointString containing zero or one subrange points on the line made by 214 * the intersection of the line segment with this plane. If no intersection is 215 * found, an empty PointString is returned. 216 */ 217 public PointString<SubrangePoint> intersect(LineSegment line) throws DimensionException { 218 return line.intersect(this, 0); 219 } 220 221 /** 222 * Returns a copy of this GeomPlane instance 223 * {@link javolution.context.AllocatorContext allocated} by the calling thread 224 * (possibly on the stack). 225 * 226 * @return an identical and independent copy of this point. 227 */ 228 @Override 229 public abstract GeomPlane copy(); 230 231 /** 232 * Return <code>true</code> if this GeomPlane contains valid and finite numerical 233 * components. A value of <code>false</code> will be returned if any of the coordinate 234 * values are NaN or Inf. 235 */ 236 @Override 237 public boolean isValid() { 238 GeomVector<Dimensionless> n = getNormal(); 239 if (n.isValid()) { 240 double C = getConstant().getValue(); 241 if (!(Double.isNaN(C) || Double.isInfinite(C))) 242 return true; 243 } 244 return false; 245 } 246 247 /** 248 * Returns transformed version of this element. The returned object implements 249 * {@link GeomTransform} and contains this element as a child. 250 * 251 * @param transform The transformation to apply to this geometry. May not be null. 252 * @return A new plane that is identical to this one with the specified 253 * transformation applied. 254 * @throws DimensionException if this plane is not 3D. 255 */ 256 @Override 257 public GeomPlaneTrans getTransformed(GTransform transform) { 258 return GeomPlaneTrans.newInstance(this, requireNonNull(transform)); 259 } 260 261 /** 262 * Returns the text representation of this geometry element that consists of the name 263 * followed by the equation of a plane. For example: 264 * <pre> 265 * {aPlane = {1, 0, 0, 2 ft}} 266 * </pre> 267 * If there is no name, then the output looks like this: 268 * <pre> 269 * {1, 0, 0, 2 ft} 270 * </pre> 271 * 272 * @return the text representation of this geometry element. 273 */ 274 @Override 275 public Text toText() { 276 final int dimension = this.getPhyDimension(); 277 TextBuilder tmp = TextBuilder.newInstance(); 278 tmp.append('{'); 279 String nameStr = getName(); 280 boolean hasName = nonNull(nameStr); 281 if (hasName) { 282 tmp.append(nameStr); 283 tmp.append(" = {"); 284 } 285 GeomVector<Dimensionless> n = getNormal(); 286 for (int i = 0; i < dimension; i++) { 287 tmp.append(n.get(i)); 288 tmp.append(", "); 289 } 290 tmp.append(getConstant()); 291 if (hasName) 292 tmp.append('}'); 293 tmp.append('}'); 294 Text txt = tmp.toText(); 295 TextBuilder.recycle(tmp); 296 return txt; 297 } 298 299}