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}