001/*
002 *   GeomTriangle  -- Represents an abstract planar triangle in n-D space.
003 *
004 *   Copyright (C) 2015-2016, 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 static geomss.geom.AbstractGeomElement.RESOURCES;
025import jahuwaldt.js.param.Parameter;
026import java.text.MessageFormat;
027import static java.util.Objects.nonNull;
028import javax.measure.quantity.Area;
029import javax.measure.quantity.Dimensionless;
030import javax.measure.quantity.Length;
031import javax.measure.unit.Unit;
032import javolution.text.Text;
033import javolution.text.TextBuilder;
034
035/**
036 * The interface and implementation in common to all triangles in n-dimensional space. A
037 * triangle is represented by exactly three vertex points arranged in a counter-clockwise
038 * direction (or winding) when viewed from the "outside" (the direction the normal vector
039 * points).
040 *
041 * <p> Modified by: Joseph A. Huwaldt   </p>
042 *
043 * @author Joseph A. Huwaldt, Date: August 26, 2015
044 * @version February 16, 2016
045 */
046@SuppressWarnings({"serial", "CloneableImplementsClone"})
047public abstract class GeomTriangle extends AbstractGeomElement<GeomTriangle>
048        implements PointGeometry<GeomTriangle>, Transformable<GeomTriangle> {
049
050    /**
051     * A flag indicating if this triangle is degenerate or not. A value of -1 means
052     * degeneracy has not been assessed.  A value of 0 means the triangle is NOT
053     * degenerate.  A value of 1 means that the triangle IS degenerate.
054     */
055    private int _degFlg = -1;
056    
057    
058    /**
059     * Return the first vertex in this triangle.
060     *
061     * @return The first vertex in this triangle.
062     */
063    public abstract GeomPoint getP1();
064
065    /**
066     * Return the second vertex in this triangle.
067     *
068     * @return The second vertex in this triangle.
069     */
070    public abstract GeomPoint getP2();
071
072    /**
073     * Return the third and last vertex in this triangle.
074     *
075     * @return The third and last vertex in this triangle.
076     */
077    public abstract GeomPoint getP3();
078
079    /**
080     * Return the surface unit normal vector for this triangle. If the triangle is
081     * degenerate (zero area), then the normal vector will have zero length.
082     *
083     * @return The surface normal vector for this triangle.
084     */
085    public abstract GeomVector<Dimensionless> getNormal();
086
087    /**
088     * Return the surface area of one side of this triangle. The returned area is always
089     * positive, but can be zero.
090     *
091     * @return The surface area of one side of this triangle.
092     */
093    public abstract Parameter<Area> getArea();
094
095    /**
096     * Return <code>true</code> if this triangle is degenerate (i.e.: has zero surface
097     * area). A degenerate triangle is defined as a triangle that has any edge with length
098     * less than the input tolerance.
099     *
100     * @param tol The tolerance for determining if this triangle is degenerate. A value of
101     *            <code>null</code> would indicate that exactly zero area is required to
102     *            be degenerate.
103     * @return true if this triangle is degenerate.
104     */
105    public boolean isDegenerate(Parameter<Length> tol) {
106        if (_degFlg < 0) {
107            GeomPoint p1 = getP1();
108            GeomPoint p2 = getP2();
109            if (p1.isApproxEqual(p2, tol)) {
110                _degFlg = 1;
111                return true;
112            }
113            GeomPoint p3 = getP3();
114            boolean output = p1.isApproxEqual(p3,tol) || p2.isApproxEqual(p3,tol);
115            _degFlg = (output ? 1 : 0);
116            return output;
117        }
118        return _degFlg > 0;
119    }
120
121    /**
122     * Return a new triangle that is identical to this one, but with the order of the
123     * points (and the surface normal direction) reversed.
124     *
125     * @return A new Triangle that is identical to this one, but with the order of the
126     *         points reversed.
127     */
128    public abstract GeomTriangle reverse();
129
130    /**
131     * Return all three vertices of this triangle as an ordered list of points.
132     *
133     * @return A list containing the three vertices of this triangle in counter-clockwise
134     *         order.
135     */
136    public PointString<? extends GeomPoint> getPoints() {
137        GeomPoint p1 = getP1();
138        GeomPoint p2 = getP2();
139        GeomPoint p3 = getP3();
140        PointString<? extends GeomPoint> str = PointString.valueOf(p1, p2, p3);
141        return str;
142    }
143
144    /**
145     * Return all three vertices of this triangle contained in the supplied array of
146     * points. The input array must have at least 3 elements.
147     *
148     * @param points An existing array with at leat 3 elements that will be filled in with
149     *               points from this triangle. May not be null.
150     * @return The input array with the three vertices of this triangle, in counter-
151     *         clockwise order, placed in the 1st 3 elements.
152     */
153    public GeomPoint[] getPoints(GeomPoint[] points) {
154        points[0] = getP1();
155        points[1] = getP2();
156        points[2] = getP3();
157        return points;
158    }
159
160    /**
161     * Returns an new {@link GeomList} containing the ordered points in this triangle.
162     *
163     * @return A new GeomList containing the ordered points from this triangle.
164     */
165    public GeomList<GeomPoint> getAll() {
166        GeomList<GeomPoint> list = GeomList.newInstance();
167        list.add(getP1());
168        list.add(getP2());
169        list.add(getP3());
170        return list;
171    }
172
173    /**
174     * Returns the number of child-elements that make up this geometry element. This
175 implementation always returns 3 as a GeomTriangle has three vertices.
176     *
177     * @return The number of points in this triangle (always returns 3).
178     */
179    @Override
180    public int size() {
181        return 3;
182    }
183
184    /**
185     * Returns the number of physical dimensions of the geometry element.
186     *
187     * @return The number of physical dimensions of this geometry element.
188     */
189    @Override
190    public int getPhyDimension() {
191        return getP1().getPhyDimension();
192    }
193
194    /**
195     * Returns the number of parametric dimensions of the geometry element. This
196     * implementation always returns 0 as a Triangle is not parametric.
197     */
198    @Override
199    public int getParDimension() {
200        return 0;
201    }
202
203    /**
204     * Return the total number of points in this geometry element. This implementation
205     * always returns 3 as a GeomTriangle has three vertices.
206     *
207     * @return Always returns 3.
208     */
209    @Override
210    public int getNumberOfPoints() {
211        return 3;
212    }
213
214    /**
215     * Returns the most extreme point, either minimum or maximum, in the specified
216     * coordinate direction on this triangle.
217     *
218     * @param dim An index indicating the dimension to find the min/max point for (0=X,
219     *            1=Y, 2=Z, etc).
220     * @param max Set to <code>true</code> to return the maximum value, <code>false</code>
221     *            to return the minimum.
222     * @param tol Fractional tolerance (in parameter space) to refine the min/max point
223     *            position to.
224     * @return The point found on this triangle that is the min or max in the specified
225     *         coordinate direction.
226     */
227    @Override
228    public GeomPoint getLimitPoint(int dim, boolean max, double tol) {
229        //  Check the input dimension for consistancy.
230        int thisDim = getPhyDimension();
231        if (dim < 0 || dim >= thisDim)
232            throw new DimensionException(MessageFormat.format(
233                    RESOURCES.getString("inputDimOutOfRange"), "triangle"));
234
235        //  Get the three points.
236        PointString str = getPoints();
237
238        //  Return the limit of the three points.
239        return str.getLimitPoint(dim, max, tol);
240    }
241
242    /**
243     * Return <code>true</code> if this GeomTriangle contains valid and finite numerical
244     * components. A value of <code>false</code> will be returned if any of the coordinate
245     * values are NaN or Inf.
246     *
247     * @return true if this GeomTriangle contains valid and finite data.
248     */
249    @Override
250    public boolean isValid() {
251        return getP1().isValid() && getP2().isValid() && getP3().isValid();
252    }
253
254    /**
255     * Returns the unit in which this GeomTriangle is stated.
256     *
257     * @return The unit in which this GeomTriangle is stated.
258     */
259    @Override
260    public Unit<Length> getUnit() {
261        return getP1().getUnit();
262    }
263
264    /**
265     * Returns the text representation of this geometry element that consists of the name
266     * followed by the three defining points. For example:
267     * <pre>
268     *   {aTri = {{0.0 m, 0.0 m},{1.0 m, 0.0 m},{0.5 m, 1.0 m}}
269     * </pre>
270     * If there is no name, then the output looks like this:
271     * <pre>
272     *   {{0.0 m, 0.0 m},{1.0 m, 0.0 m},{0.5 m, 1.0 m}}
273     * </pre>
274     *
275     * @return the text representation of this geometry element.
276     */
277    @Override
278    public Text toText() {
279        TextBuilder tmp = TextBuilder.newInstance();
280        tmp.append('{');
281        String nameStr = getName();
282        boolean hasName = nonNull(nameStr);
283        if (hasName) {
284            tmp.append(nameStr);
285            tmp.append(" = {");
286        }
287
288        tmp.append(getP1().toText());
289        tmp.append(",");
290        tmp.append(getP2().toText());
291        tmp.append(",");
292        tmp.append(getP3().toText());
293
294        if (hasName)
295            tmp.append('}');
296        tmp.append('}');
297        Text txt = tmp.toText();
298        TextBuilder.recycle(tmp);
299        return txt;
300    }
301
302    /**
303     * Resets the internal state of this object to its default values. Subclasses that
304     * override this method must call <code>super.reset();</code> to ensure that the state
305     * is reset properly.
306     */
307    @Override
308    public void reset() {
309        super.reset();
310        this._degFlg = -1;
311    }
312    
313}