/* * Copyright 2006, United States Government as represented by the Administrator * for the National Aeronautics and Space Administration. No copyright is * claimed in the United States under Title 17, U.S. Code. All Other Rights * Reserved. */ package gov.nasa.ial.mde.solver; import gov.nasa.ial.mde.math.IntervalXY; import gov.nasa.ial.mde.math.NumberModel; import gov.nasa.ial.mde.math.PointXY; import gov.nasa.ial.mde.solver.classifier.QuadraticClassifier; import gov.nasa.ial.mde.solver.classifier.QuadraticClassifier.QuadraticType; import gov.nasa.ial.mde.solver.symbolic.AnalyzedEquation; /** * Subclass of SolvedGraph responsible for recording features unique to ellipses. * * @author Dr. Robert Shelton * @version 1.0 * @since 1.0 */ public class SolvedEllipse extends SolvedConic { /** Identify new features so we can access them with SolvedGraph.putFeature */ protected String[] newFeatures = { "center", "focus", "focalLength", "eccentricity", "semiMajorAxis", "semiMinorAxis", "majorAxis", "minorAxis", "majorAxisInclination", "minorAxisInclination", "radius" // for the special case of a circle }; // enums for major axis direction */ private final static int NO_D = 0, HORIZONTAL = 1, VERTICAL = 2; private int majorAxisD = NO_D; /** * Constructs a solved ellipse for the specified analyzed equation. * * @param equation the analyzed equation. */ public SolvedEllipse(AnalyzedEquation equation) { super(equation); // specificFeatureNames = new String[newFeatures.length]; // System.arraycopy(newFeatures, 0, specificFeatureNames, 0, newFeatures.length); /* QC is the QuadraticClassifier field in SolvedConic */ double alpha = QC.getRotation(); // rotation angle in degrees /* * coeffs={a, b, c, d, e} where a(u-h)^2 + b(v-k)^2 + cu + dv + e = 0 */ double[] coeffs = QC.getNormalizedCoefficients(); PointXY center = new PointXY(QC.UV2XY(QC.getTranslation())); String[] vars = analyzedEq.getActualVariables(); // saves a lot of typing double majorAxisInclination, minorAxisInclination, A, /* semi-major axis */ B, /* semi-minor axis */ C; /* focal length */ putNewFeatures(newFeatures); // enable use of new features if (QC.getIdentity() == QuadraticType.SinglePoint) { putFeature("graphName", "single point"); // self-explanatory putFeature("center", center); return; } putFeature("center", center); putFeature("equationType", "conic section"); // ditto putFeature("graphClosure", "true"); // might be hard to determine in general /* normalize so that coeffs[4] = -1 in order to solve for A and B */ for (int i = 0; i < 5; i++) coeffs[i] /= (-coeffs[4]); /* Satisfy the java compiler's obsessive need for initializations */ A = B = -1.0; /* * coeffs[0] and coeffs[1] better both be positive. Determine horizontal/vertical direction * of major axis by which of 1/Math.sqrt(coeffs[0]) or 1/Math.sqrt(coeffs[1]) is larger. * horizontal case : (u-h)^2)/A^2 + (v-k)^2/B^2 = 1 vertical case: (v-k)^2/A^2 + * (u-h)^2/B^2 = 1 First make sure coeffs are compatible with design assumptions */ if (coeffs[0] <= 0 || coeffs[1] <= 0 || coeffs[2] != 0.0 || coeffs[3] != 0.0) throw new IllegalArgumentException("SolvedEllipse instantiated with bad args."); if (coeffs[0] < coeffs[1]) { majorAxisD = HORIZONTAL; A = 1.0 / Math.sqrt(coeffs[0]); B = 1.0 / Math.sqrt(coeffs[1]); } // end if if (coeffs[1] < coeffs[0]) { majorAxisD = VERTICAL; A = 1.0 / Math.sqrt(coeffs[1]); B = 1.0 / Math.sqrt(coeffs[0]); } // end if /* * if coeffs[0] == coeffs[1] then we have a circle. In this case, all we need is a center * and radius. We already have the center, and we overwrite the graphName attribute, define * the radius domain and range, and quit */ if (coeffs[0] == coeffs[1]) { putFeature("graphName", "circle"); putFeature("radius", new NumberModel(A = 1.0 / Math.sqrt(coeffs[0]))); putFeature("domain", new IntervalXY(vars[0], center.x - A, center.x + A)); putFeature("range", new IntervalXY(vars[1], center.y - A, center.y + A)); return; } // end if putFeature("graphName", "ellipse"); // self-explanatory /* might as well take care of the obvious */ putFeature("semiMajorAxis", new NumberModel(A)); putFeature("semiMinorAxis", new NumberModel(B)); /* * Determine axis inclinations which depend on horizontal/vertical orientation */ switch (majorAxisD) { case HORIZONTAL : majorAxisInclination = alpha; break; case VERTICAL : majorAxisInclination = alpha + 90.0; break; default : throw new IllegalStateException("Internal error in init"); } // end switch minorAxisInclination = majorAxisInclination + 90.0; /* normalize angles between -179.9999 and 180.0 */ majorAxisInclination = QuadraticClassifier.normalizeAngleInDegrees(majorAxisInclination); minorAxisInclination = QuadraticClassifier.normalizeAngleInDegrees(minorAxisInclination); putFeature ("majorAxisInclination", new NumberModel(majorAxisInclination)); putFeature ("minorAxisInclination", new NumberModel(minorAxisInclination)); /* on with the rest of the calculations -- focalLength */ C = Math.sqrt(A * A - B * B); double[] focalDisplacement = { C * Math.cos(Math.PI * majorAxisInclination / 180.0), C * Math.sin(Math.PI * majorAxisInclination / 180.0)}; PointXY F1 = center.sum(new PointXY(focalDisplacement)); PointXY F2 = center.difference(new PointXY(focalDisplacement)); putFeature("focalLength", new NumberModel(C)); putFeature("eccentricity", new NumberModel(C / A)); putFeature("focus", F1); addToFeature("focus", F2); putFeature("majorAxis", QuadraticClassifier.getEquationOfALine(center, majorAxisInclination, vars)); putFeature("minorAxis", QuadraticClassifier.getEquationOfALine(center, minorAxisInclination, vars)); /* * do domain and range for alpha = 0; otherwise it's a pretty complicated calculation */ if (alpha == 0) switch (majorAxisD) { case HORIZONTAL : putFeature("domain", new IntervalXY(vars[0], center.x - A, center.x + A)); putFeature("range", new IntervalXY(vars[1], center.y - B, center.y + B)); break; case VERTICAL : putFeature("domain", new IntervalXY(vars[0], center.x - B, center.x + B)); putFeature("range", new IntervalXY(vars[1], center.y - A, center.y + A)); break; default : // no need to throw exception -- condition has already been tested break; } // end switch } // end SolvedEllipse } // end class SolvedEllipse