/** @file * @brief Conic Section *//* * Authors: * Nathan Hurst * * Copyright 2009 authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. */ #ifndef LIB2GEOM_SEEN_CONICSEC_H #define LIB2GEOM_SEEN_CONICSEC_H #include <2geom/exception.h> #include <2geom/angle.h> #include <2geom/rect.h> #include <2geom/affine.h> #include <2geom/point.h> #include <2geom/line.h> #include <2geom/bezier-curve.h> #include <2geom/numeric/linear_system.h> #include <2geom/numeric/symmetric-matrix-fs.h> #include <2geom/numeric/symmetric-matrix-fs-operation.h> #include #include #include #include namespace Geom { class RatQuad{ /** * A curve of the form B02*A + B12*B*w + B22*C/(B02 + B12*w + B22) * These curves can exactly represent a piece conic section less than a certain angle (find out) * **/ public: Point P[3]; double w; RatQuad() {} RatQuad(Point a, Point b, Point c, double w) : w(w) { P[0] = a; P[1] = b; P[2] = c; } double lambda() const; static RatQuad fromPointsTangents(Point P0, Point dP0, Point P, Point P2, Point dP2); static RatQuad circularArc(Point P0, Point P1, Point P2); CubicBezier toCubic() const; CubicBezier toCubic(double lam) const; Point pointAt(double t) const; Point at0() const {return P[0];} Point at1() const {return P[2];} void split(RatQuad &a, RatQuad &b) const; D2 hermite() const; std::vector homogeneous() const; }; class xAx{ public: double c[6]; enum kind_t { PARABOLA, CIRCLE, REAL_ELLIPSE, IMAGINARY_ELLIPSE, RECTANGULAR_HYPERBOLA, HYPERBOLA, DOUBLE_LINE, TWO_REAL_PARALLEL_LINES, TWO_IMAGINARY_PARALLEL_LINES, TWO_REAL_CROSSING_LINES, TWO_IMAGINARY_CROSSING_LINES, // crossing at a real point SINGLE_POINT = TWO_IMAGINARY_CROSSING_LINES, UNKNOWN }; xAx() {} /* * Define the conic section by its algebraic equation coefficients * * c0, .., c5: equation coefficients */ xAx (double c0, double c1, double c2, double c3, double c4, double c5) { set (c0, c1, c2, c3, c4, c5); } /* * Define a conic section by its related symmetric matrix */ xAx (const NL::ConstSymmetricMatrixView<3> & C) { set(C); } /* * Define a conic section by computing the one that fits better with * N points. * * points: points to fit * * precondition: there must be at least 5 non-overlapping points */ xAx (std::vector const& points) { set (points); } /* * Define a section conic by providing the coordinates of one of its * vertex,the major axis inclination angle and the coordinates of its foci * with respect to the unidimensional system defined by the major axis with * origin set at the provided vertex. * * _vertex : section conic vertex V * _angle : section conic major axis angle * _dist1: +/-distance btw V and nearest focus * _dist2: +/-distance btw V and farest focus * * prerequisite: _dist1 <= _dist2 */ xAx (const Point& _vertex, double _angle, double _dist1, double _dist2) { set (_vertex, _angle, _dist1, _dist2); } /* * Define a conic section by providing one of its vertex and its foci. * * _vertex: section conic vertex * _focus1: section conic focus * _focus2: section conic focus */ xAx (const Point& _vertex, const Point& _focus1, const Point& _focus2) { set(_vertex, _focus1, _focus2); } /* * Define a conic section by passing a focus, the related directrix, * and the eccentricity (e) * (e < 1 -> ellipse; e = 1 -> parabola; e > 1 -> hyperbola) * * _focus: a focus of the conic section * _directrix: the directrix related to the given focus * _eccentricity: the eccentricity parameter of the conic section */ xAx (const Point & _focus, const Line & _directrix, double _eccentricity) { set (_focus, _directrix, _eccentricity); } /* * Made up a degenerate conic section as a pair of lines * * l1, l2: lines that made up the conic section */ xAx (const Line& l1, const Line& l2) { set (l1, l2); } /* * Define the conic section by its algebraic equation coefficients * c0, ..., c5: equation coefficients */ void set (double c0, double c1, double c2, double c3, double c4, double c5) { c[0] = c0; c[1] = c1; c[2] = c2; // xx, xy, yy c[3] = c3; c[4] = c4; // x, y c[5] = c5; // 1 } /* * Define a conic section by its related symmetric matrix */ void set (const NL::ConstSymmetricMatrixView<3> & C) { set(C(0,0), 2*C(1,0), C(1,1), 2*C(2,0), 2*C(2,1), C(2,2)); } void set (std::vector const& points); void set (const Point& _vertex, double _angle, double _dist1, double _dist2); void set (const Point& _vertex, const Point& _focus1, const Point& _focus2); void set (const Point & _focus, const Line & _directrix, double _eccentricity); void set (const Line& l1, const Line& l2); static xAx fromPoint(Point p); static xAx fromDistPoint(Point p, double d); static xAx fromLine(Point n, double d); static xAx fromLine(Line l); static xAx fromPoints(std::vector const &pts); template T evaluate_at(T x, T y) const { return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x + c[4]*y + c[5]; } double valueAt(Point P) const; std::vector implicit_form_coefficients() const { return std::vector(c, c+6); } template T evaluate_at(T x, T y, T w) const { return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x*w + c[4]*y*w + c[5]*w*w; } xAx scale(double sx, double sy) const; Point gradient(Point p) const; xAx operator-(xAx const &b) const; xAx operator+(xAx const &b) const; xAx operator+(double const &b) const; xAx operator*(double const &b) const; std::vector crossings(Rect r) const; std::optional toCurve(Rect const & bnd) const; std::vector roots(Point d, Point o) const; std::vector roots(Line const &l) const; static Interval quad_ex(double a, double b, double c, Interval ivl); Geom::Affine hessian() const; std::optional bottom() const; Interval extrema(Rect r) const; /* * Return the symmetric matrix related to the conic section. * Modifying the matrix does not modify the conic section */ NL::SymmetricMatrix<3> get_matrix() const { NL::SymmetricMatrix<3> C(c); C(1,0) *= 0.5; C(2,0) *= 0.5; C(2,1) *= 0.5; return C; } /* * Return the i-th coefficient of the conic section algebraic equation * Modifying the returned value does not modify the conic section coefficient */ double coeff (size_t i) const { return c[i]; } /* * Return the i-th coefficient of the conic section algebraic equation * Modifying the returned value modifies the conic section coefficient */ double& coeff (size_t i) { return c[i]; } kind_t kind () const; std::string categorise() const; /* * Return true if the equation: * c0*x^2 + c1*xy + c2*y^2 + c3*x + c4*y +c5 == 0 * really defines a conic, false otherwise */ bool is_quadratic() const { return (coeff(0) != 0 || coeff(1) != 0 || coeff(2) != 0); } /* * Return true if the conic is degenerate, i.e. if the related matrix * determinant is null, false otherwise */ bool isDegenerate() const { return (det_sgn (get_matrix()) == 0); } /* * Compute the centre of symmetry of the conic section when it exists, * else it return an uninitialized std::optional instance. */ std::optional centre() const { typedef std::optional opt_point_t; double d = coeff(1) * coeff(1) - 4 * coeff(0) * coeff(2); if (are_near (d, 0)) return opt_point_t(); NL::Matrix Q(2, 2); Q(0,0) = coeff(0); Q(1,1) = coeff(2); Q(0,1) = Q(1,0) = coeff(1) * 0.5; NL::Vector T(2); T[0] = - coeff(3) * 0.5; T[1] = - coeff(4) * 0.5; NL::LinearSystem ls (Q, T); NL::Vector sol = ls.SV_solve(); Point C; C[0] = sol[0]; C[1] = sol[1]; return opt_point_t(C); } double axis_angle() const; void roots (std::vector& sol, Coord v, Dim2 d) const; xAx translate (const Point & _offset) const; xAx rotate (double angle) const; /* * Rotate the conic section by the given angle wrt the provided point. * * _rot_centre: the rotation centre * _angle: the rotation angle */ xAx rotate (const Point & _rot_centre, double _angle) const { xAx result = translate (-_rot_centre).rotate (_angle).translate (_rot_centre); return result; } /* * Compute the tangent line of the conic section at the provided point * * _point: the conic section point the tangent line pass through */ Line tangent (const Point & _point) const { NL::Vector pp(3); pp[0] = _point[0]; pp[1] = _point[1]; pp[2] = 1; NL::SymmetricMatrix<3> C = get_matrix(); NL::Vector line = C * pp; return Line(line[0], line[1], line[2]); } /* * For a non degenerate conic compute the dual conic. * TODO: investigate degenerate case */ xAx dual () const { //assert (! isDegenerate()); NL::SymmetricMatrix<3> C = get_matrix(); NL::SymmetricMatrix<3> D = adj(C); xAx dc(D); return dc; } bool decompose (Line& l1, Line& l2) const; /** * @brief Division-free decomposition of a degenerate conic section, without * degeneration test. * * When the conic is degenerate, it consists of 0, 1 or 2 lines in the xy-plane. * This function returns these lines. But it does not check whether the conic * is really degenerate, so calling it on a non-degenerate conic produces a * meaningless result. * * If the number of lines is less than two, the trailing lines in the returned * array will be degenerate. Use Line::isDegenerate() to test for that. * * This version of the decomposition is division-free, which improves numerical * stability compared to decompose(). * * @param epsilon The numerical threshold for floating point comparison of discriminants. */ std::array decompose_df(Coord epsilon = EPSILON) const; /* * Generate a RatQuad object from a conic arc. * * p0: the initial point of the arc * p1: the inner point of the arc * p2: the final point of the arc */ RatQuad toRatQuad (const Point & p0, const Point & p1, const Point & p2) const { Point dp0 = gradient (p0); Point dp2 = gradient (p2); return RatQuad::fromPointsTangents (p0, rot90 (dp0), p1, p2, rot90 (dp2)); } /* * Return the angle related to the normal gradient computed at the passed * point. * * _point: the point at which computes the angle * * prerequisite: the passed point must lie on the conic */ double angle_at (const Point & _point) const { double angle = atan2 (gradient (_point)); if (angle < 0) angle += (2*M_PI); return angle; } /* * Return true if the given point is contained in the conic arc determined * by the passed points. * * _point: the point to be tested * _initial: the initial point of the arc * _inner: an inner point of the arc * _final: the final point of the arc * * prerequisite: the passed points must lie on the conic, the inner point * has to be strictly contained in the arc, except when the * initial and final points are equal: in such a case if the * inner point is also equal to them, then they define an arc * made up by a single point. * */ bool arc_contains (const Point & _point, const Point & _initial, const Point & _inner, const Point & _final) const { AngleInterval ai(angle_at(_initial), angle_at(_inner), angle_at(_final)); return ai.contains(angle_at(_point)); } Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) const; std::vector allNearestTimes (const Point &P) const; /* * Return the point on the conic section nearest to the passed point "P". * * P: the point to compute the nearest one */ Point nearestTime (const Point &P) const { std::vector points = allNearestTimes (P); if ( !points.empty() ) { return points.front(); } // else THROW_LOGICALERROR ("nearestTime: no nearest point found"); return Point(); } }; std::vector intersect(const xAx & C1, const xAx & C2); bool clip (std::vector & rq, const xAx & cs, const Rect & R); inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) { for(double i : x.c) { out_file << i << ", "; } return out_file; } }; #endif // LIB2GEOM_SEEN_CONICSEC_H /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :