001/*
002 *   Entity104_ConicArc  -- Entity representing a general conic section other than a circle.
003 *
004 *   Copyright (C) 2010-2016, Joseph A. Huwaldt. All rights reserved.
005 *   
006 *   This library is free software; you can redistribute it and/or
007 *   modify it under the terms of the GNU Lesser General Public
008 *   License as published by the Free Software Foundation; either
009 *   version 2.1 of the License, or (at your option) any later version.
010 *   
011 *   This library is distributed in the hope that it will be useful,
012 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *   Lesser General Public License for more details.
015 *
016 *   You should have received a copy of the GNU Lesser General Public License
017 *   along with this program; if not, write to the Free Software
018 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
019 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
020 *
021 *   Based on, but heavily modified from, IGESView ( http://ts.nist.gov/Standards/IGES/igesTools.cfm )
022 */
023package geomss.geom.reader.iges;
024
025import geomss.geom.*;
026import geomss.geom.nurbs.*;
027import jahuwaldt.js.param.Parameter;
028import java.io.IOException;
029import java.io.RandomAccessFile;
030import java.text.MessageFormat;
031import javax.measure.quantity.Angle;
032import javax.measure.quantity.Dimensionless;
033import javax.measure.quantity.Length;
034import javax.measure.unit.SI;
035import static javolution.lang.MathLib.*;
036import javolution.util.FastTable;
037
038/**
039 * <b><i>CONIC ARC ENTITY</i></b> - This entity defines an ellipse, a parabola, a
040 * hyperbola, or a portion of one of these conic curves which may be isolated or used as a
041 * component of a Composite Curve or a sub-figure. The definition space coordinate system
042 * is always chosen so that the conic arc lies in a plane either coincident with or
043 * parallel to XT, YT plane. Within such a plane a conic is defined by the six
044 * coefficients in the following equation: A*XT^2 + B*XT*YT + C*YT^2 + D*XT + E*YT + F = 0
045 *
046 * <p>
047 * These entities (forms 0-3), when read from an IGES file, are converted to NURBS curves
048 * of degree 2 (IGES type 126, Form 2, Degree 2). This entity type can not be written out
049 * to an IGES file. All conic arc parameters are stored in the user data with the prefix
050 * "IGES_106_" followed by the parameter name.
051 * </p>
052 *
053 * <p> Modified by: Joseph A. Huwaldt </p>
054 *
055 * @author JDN, Version 1.0
056 * @version September 13, 2016
057 */
058public abstract class Entity104_ConicArc extends GeomSSEntity {
059
060    private double Acoef;       // General Conic Coefficients
061    private double Bcoef;
062    private double Ccoef;
063    private double Dcoef;
064    private double Ecoef;
065    private double Fcoef;
066    private double zt;          // ZT Coordinate of plane of definition
067    private double x1;          // Start point abcissa
068    private double y1;          // Start point ordinate
069    private double x2;          // Terminate point abcissa
070    private double y2;          // Terminate point ordinate
071
072    private double Q1, Q2;      // Used to determine conic type.
073
074    private NurbsCurve curve;   //  THe GeomSS curve representing this conic section.
075
076    /**
077     * Default constructor.
078     *
079     * @param p  part to which this entity is contained
080     * @param de Directory Entry for this entity
081     */
082    public Entity104_ConicArc(Part p, DirEntry de) {
083        super(p, de);
084    }
085
086    /**
087     * Checks to see if the entity should be drawn. No restrictions are imposed.
088     */
089    @Override
090    public void check() {
091    }
092
093    /**
094     * Read the Parameter Data from the String read in by the superclass.
095     *
096     * @param in input file
097     * @throws java.io.IOException
098     */
099    @Override
100    public void read(RandomAccessFile in) throws IOException {
101        super.read(in);
102        String s = getPDString();
103
104        if (Constants.DEBUG) {
105            System.out.println("PD String = \"" + s + "\"");
106        }
107
108        Acoef = getReal(s);
109        Bcoef = getReal(s);
110        Ccoef = getReal(s);
111        Dcoef = getReal(s);
112        Ecoef = getReal(s);
113        Fcoef = getReal(s);
114        zt = getReal(s);
115        if (Math.abs(zt) < Constants.Grain)
116            zt = 0;
117        x1 = getReal(s);
118        if (Math.abs(x1) < Constants.Grain)
119            x1 = 0;
120        y1 = getReal(s);
121        if (Math.abs(y1) < Constants.Grain)
122            y1 = 0;
123        x2 = getReal(s);
124        if (Math.abs(x2) < Constants.Grain)
125            x2 = 0;
126        y2 = getReal(s);
127        if (Math.abs(y2) < Constants.Grain)
128            y2 = 0;
129
130        if (abs(Bcoef) > Constants.Grain || Acoef > Ccoef)
131            throw new IOException(
132                    MessageFormat.format(RESOURCES.getString("rotatedConicsNotSupported"),
133                            getHeader()));
134
135        super.read_additional();
136
137        //  Calculate some needed parameters.
138        double A = Acoef, B = Bcoef, C = Ccoef, D = Dcoef, E = Ecoef, F = Fcoef;
139        Q1 = A * C * F + 0.25 * (B * D * E - A * E * E - B * B * F - D * D * C);
140        Q2 = A * C - 0.25 * B * B;
141
142    }
143
144    /**
145     * The GeomSS geometry element is created from the IGES parameters when this method is
146     * called.
147     */
148    @Override
149    void createGeometry() throws IOException {
150
151        //  Determine the curve type (do not trust the form ID).
152        if (abs(Q2) < Constants.Grain && abs(Q1) >= Constants.Grain) {
153            //  Parabola
154            curve = makeParabolicCurve();
155
156        } else if (Q2 < 0 && abs(Q1) >= Constants.Grain) {
157            //  Hyperbola
158            curve = makeHyperbolicCurve();
159
160        } else if (Q2 > 0 && Q1 * (Acoef + Ccoef) < 0) {
161            //  Ellipse
162            curve = makeEllipseCurve();
163
164        } else {
165            System.out.println(toString());
166            System.out.println("Q1 = " + Q1 + ", Q2 = " + Q2);
167
168            throw new IOException(MessageFormat.format(RESOURCES.getString("unknownConicType"), getHeader()));
169        }
170
171    }
172
173    /**
174     * Method used to apply IGES meta-data to GeomSS elements. This implementation stores
175     * in the user data, in addition to the header info, all the parameter information for
176     * this entity type prefixed by "IGES_104_".
177     */
178    @Override
179    protected void applyMetaData(GeomElement element) {
180        super.applyMetaData(element);
181        element.putUserData("IGES_104_A", Acoef);
182        element.putUserData("IGES_104_B", Bcoef);
183        element.putUserData("IGES_104_C", Ccoef);
184        element.putUserData("IGES_104_D", Dcoef);
185        element.putUserData("IGES_104_E", Ecoef);
186        element.putUserData("IGES_104_F", Fcoef);
187        element.putUserData("IGES_104_ZT", zt);
188        element.putUserData("IGES_104_X1", x1);
189        element.putUserData("IGES_104_Y1", y1);
190        element.putUserData("IGES_104_X2", x2);
191        element.putUserData("IGES_104_Y2", y2);
192    }
193
194    /**
195     * Return a new elliptical arc segment based on the general conic equation
196     * coefficients and end points.
197     */
198    private BasicNurbsCurve makeEllipseCurve() {
199        //  Determine semi-major and semi-minor axis lengths.
200        Parameter<Length> sma = Parameter.valueOf(sqrt(-Fcoef / Acoef), Constants.unit);
201        Parameter<Length> smb = Parameter.valueOf(sqrt(-Fcoef / Ccoef), Constants.unit);
202
203        //  Determine the origin of the ellipse.
204        double xo = -2 * Ccoef * Dcoef / 4 / Q1;
205        double yo = -2 * Acoef * Ecoef / 4 / Q1;
206        Point origin = Point.valueOf(xo, yo, zt, Constants.unit);
207
208        //  Determine the start and ending angles.
209        double p1mOx = x1 - xo;
210        double p1mOy = y1 - yo;
211        double p2mOx = x2 - xo;
212        double p2mOy = y2 - yo;
213        double ths = atan2(p1mOy, p1mOx);
214        double the = atan2(p2mOy, p2mOx);
215        Parameter<Angle> thetaStart = Parameter.valueOf(ths, SI.RADIAN);
216        Parameter<Angle> thetaEnd = Parameter.valueOf(the, SI.RADIAN);
217
218        //  Define X & Y directions.
219        Vector<Dimensionless> xhat = Vector.valueOf(1, 0, 0).toUnitVector();
220        Vector<Dimensionless> yhat = Vector.valueOf(0, 1, 0).toUnitVector();
221
222        //  Create the elliptical arc.
223        return CurveFactory.createEllipticalArc(origin, sma, smb, xhat, yhat, thetaStart, thetaEnd);
224    }
225
226    /**
227     * Return a new hyperbolic arc segment based on the general conic equation
228     * coefficients and end points.
229     */
230    private BasicNurbsCurve makeHyperbolicCurve() {
231        Parameter<Length> tol = Parameter.valueOf(Constants.Grain, Constants.unit);
232
233        //  Create end points.
234        Point p0 = Point.valueOf(x1, y1, zt, Constants.unit);
235        Point p2 = Point.valueOf(x2, y2, zt, Constants.unit);
236
237        //  Create tangent vectors at each end.
238        Vector t0 = derivative(x1, y1);
239        Vector t2 = derivative(x2, y2);
240
241        //  Find the intersection of the two tangent vector lines.
242        MutablePoint p1 = MutablePoint.newInstance(3, Constants.unit);
243        GeomUtil.lineLineIntersect(p0, t0, p2, t2, tol, null, p1, null);
244
245        //  Find the bisector of the line between the end points.
246        GeomPoint pM = p0.plus(p2).divide(2);
247
248        //  Find the shoulder point on the curve.
249        Point S;
250        double xm = pM.getValue(0);
251        double xp1 = p1.getValue(0);
252        double A = Acoef, C = Ccoef, F = Fcoef;
253        if (abs(xm - xp1) < Constants.Grain) {
254            //  Line from m to p1 is vertical.
255            double ys = sqrt(-C * (A * xm * xm + F)) / C;
256            S = Point.valueOf(xm, ys, zt, Constants.unit);
257
258        } else {
259            double yp1 = p1.getValue(1);
260            double ym = pM.getValue(1);
261            double m = (ym - yp1) / (xm - xp1);
262            double b = yp1 - m * xp1;
263            double den = 2 * (A + C * m * m);
264
265            double xs = -2 * C * m * b + 2 * sqrt(-A * F - A * C * b * b - C * F * m * m);
266            xs /= den;
267            double ys = m * xs + b;
268
269            S = Point.valueOf(xs, ys, zt, Constants.unit);
270        }
271
272        //  Determine the weight of the middle control point.
273        double w1 = pM.distance(S).divide(S.distance(p1)).getValue();
274
275        //  Create the control points.
276        FastTable<ControlPoint> cps = FastTable.newInstance();
277        cps.add(ControlPoint.valueOf(p0, 1));
278        cps.add(ControlPoint.valueOf(p1.immutable(), w1));
279        cps.add(ControlPoint.valueOf(p2, 1));
280        MutablePoint.recycle(p1);
281
282        // Create the knot vector.
283        KnotVector kv = KnotVector.newInstance(2, 0., 0., 0., 1., 1., 1.);
284
285        //  Create the curve.
286        BasicNurbsCurve crv = BasicNurbsCurve.newInstance(cps, kv);
287
288        //  Clean up before leaving.
289        FastTable.recycle(cps);
290
291        return crv;
292    }
293
294    /**
295     * Return a new parabolic arc segment based on the general conic equation coefficients
296     * and end points.
297     */
298    private BasicNurbsCurve makeParabolicCurve() {
299        //  Create end points.
300        Point p1 = Point.valueOf(x1, y1, zt, Constants.unit);
301        Point p2 = Point.valueOf(x2, y2, zt, Constants.unit);
302
303        //  Create tangent vectors at each end.
304        Vector t1 = derivative(x1, y1);
305        Vector t2 = derivative(x2, y2);
306
307        //  Create the parabolic arc.
308        return CurveFactory.createParabolicArc(p1, t1, p2, t2);
309    }
310
311    /**
312     * Return the tangent vector to the general conic curve at the specified x,y location.
313     *
314     * @param x The abscissa for the point on the curve to return the tangent vector for.
315     * @param y The ordinate for the point on the curve to return the tangent vector for.
316     * @return The tangent vector for the conic curve at x,y.
317     */
318    private Vector<Dimensionless> derivative(double x, double y) {
319        double num = 2 * Acoef * x + Bcoef * y + Dcoef;
320        double den = -2 * Ccoef * y - Bcoef * x - Ecoef;
321
322        Vector<Dimensionless> T;
323        if (abs(den) < Constants.Grain) {
324            //  Infinite slope (vertical line).
325            T = Vector.valueOf(0, num, 0).toUnitVector();
326
327        } else {
328            double dydx = num / den;
329            T = Vector.valueOf(1, dydx, 0).toUnitVector();
330        }
331
332        return T;
333    }
334
335    /**
336     * Return a reference to the Transformable GeomElement contained in this IGES Entity.
337     *
338     * @return A reference to the Transformable GeomElement contained in this IGES Entity.
339     */
340    @Override
341    protected Transformable getGeomElement() {
342        return curve;
343    }
344
345    /**
346     * Dump to String.
347     *
348     * @return String containing the resulting text.
349     */
350    @Override
351    public String toString() {
352        StringBuilder outStr = new StringBuilder(super.toString());
353        outStr.append("\n");
354
355        outStr.append("A  = "); outStr.append(Acoef);   outStr.append("\n");
356        outStr.append("B  = "); outStr.append(Bcoef);   outStr.append("\n");
357        outStr.append("C  = "); outStr.append(Ccoef);   outStr.append("\n");
358        outStr.append("D  = "); outStr.append(Dcoef);   outStr.append("\n");
359        outStr.append("E  = "); outStr.append(Ecoef);   outStr.append("\n");
360        outStr.append("F  = "); outStr.append(Fcoef);   outStr.append("\n");
361        outStr.append("zt = "); outStr.append(zt);  outStr.append("\n");
362        outStr.append("x1 = "); outStr.append(x1);  outStr.append("\n");
363        outStr.append("y1 = "); outStr.append(y1);  outStr.append("\n");
364        outStr.append("x2 = "); outStr.append(x2);  outStr.append("\n");
365        outStr.append("y2 = "); outStr.append(y2);
366
367        return outStr.toString();
368    }
369
370}