GCC Code Coverage Report


Directory: ./
File: include/2geom/elliptical-arc.h
Date: 2024-03-18 17:01:34
Exec Total Coverage
Lines: 44 65 67.7%
Functions: 26 31 83.9%
Branches: 15 36 41.7%

Line Branch Exec Source
1 /**
2 * \file
3 * \brief Elliptical arc curve
4 *
5 *//*
6 * Authors:
7 * MenTaLguY <mental@rydia.net>
8 * Marco Cecchetti <mrcekets at gmail.com>
9 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
10 *
11 * Copyright 2007-2009 Authors
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it either under the terms of the GNU Lesser General Public
15 * License version 2.1 as published by the Free Software Foundation
16 * (the "LGPL") or, at your option, under the terms of the Mozilla
17 * Public License Version 1.1 (the "MPL"). If you do not alter this
18 * notice, a recipient may use your version of this file under either
19 * the MPL or the LGPL.
20 *
21 * You should have received a copy of the LGPL along with this library
22 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * You should have received a copy of the MPL along with this library
25 * in the file COPYING-MPL-1.1
26 *
27 * The contents of this file are subject to the Mozilla Public License
28 * Version 1.1 (the "License"); you may not use this file except in
29 * compliance with the License. You may obtain a copy of the License at
30 * http://www.mozilla.org/MPL/
31 *
32 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
33 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
34 * the specific language governing rights and limitations.
35 */
36
37 #ifndef LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
38 #define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
39
40 #include <algorithm>
41 #include <2geom/affine.h>
42 #include <2geom/angle.h>
43 #include <2geom/bezier-curve.h>
44 #include <2geom/curve.h>
45 #include <2geom/ellipse.h>
46 #include <2geom/sbasis-curve.h> // for non-native methods
47 #include <2geom/utils.h>
48
49 namespace Geom
50 {
51
52 class EllipticalArc : public Curve
53 {
54 public:
55 /** @brief Creates an arc with all variables set to zero. */
56 EllipticalArc()
57 : _initial_point(0,0)
58 , _final_point(0,0)
59 , _large_arc(false)
60 {}
61 /** @brief Create a new elliptical arc.
62 * @param ip Initial point of the arc
63 * @param r Rays of the ellipse as a point
64 * @param rot Angle of rotation of the X axis of the ellipse in radians
65 * @param large If true, the large arc is chosen (always >= 180 degrees), otherwise
66 * the smaller arc is chosen
67 * @param sweep If true, the clockwise arc is chosen, otherwise the counter-clockwise
68 * arc is chosen
69 * @param fp Final point of the arc */
70 EllipticalArc( Point const &ip, Point const &r,
71 Coord rot_angle, bool large_arc, bool sweep,
72 Point const &fp
73 )
74 : _initial_point(ip)
75 , _final_point(fp)
76 , _ellipse(0, 0, r[X], r[Y], rot_angle)
77 , _angles(0, 0, sweep)
78 , _large_arc(large_arc)
79 {
80 _updateCenterAndAngles();
81 }
82
83 /// Create a new elliptical arc, giving the ellipse's rays as separate coordinates.
84 7 EllipticalArc( Point const &ip, Coord rx, Coord ry,
85 Coord rot_angle, bool large_arc, bool sweep,
86 Point const &fp
87 )
88 14 : _initial_point(ip)
89 7 , _final_point(fp)
90 7 , _ellipse(0, 0, rx, ry, rot_angle)
91
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 , _angles(0, 0, sweep)
92
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 , _large_arc(large_arc)
93 {
94
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 _updateCenterAndAngles();
95 7 }
96
97 /// @name Retrieve basic information
98 /// @{
99
100 /** @brief Get a coordinate of the elliptical arc's center.
101 * @param d The dimension to retrieve
102 * @return The selected coordinate of the center */
103 108 Coord center(Dim2 d) const { return _ellipse.center(d); }
104
105 /** @brief Get the arc's center
106 * @return The arc's center, situated on the intersection of the ellipse's rays */
107 87 Point center() const { return _ellipse.center(); }
108
109 /** @brief Get one of the ellipse's rays
110 * @param d Dimension to retrieve
111 * @return The selected ray of the ellipse */
112 456155 Coord ray(Dim2 d) const { return _ellipse.ray(d); }
113
114 /** @brief Get both rays as a point
115 * @return Point with X equal to the X ray and Y to Y ray */
116 10413 Point rays() const { return _ellipse.rays(); }
117
118 /** @brief Get the defining ellipse's rotation
119 * @return Angle between the +X ray of the ellipse and the +X axis */
120 20521 Angle rotationAngle() const {
121 20521 return _ellipse.rotationAngle();
122 }
123
124 /** @brief Whether the arc is larger than half an ellipse.
125 * @return True if the arc is larger than \f$\pi\f$, false otherwise */
126 565 bool largeArc() const { return _large_arc; }
127
128 /** @brief Whether the arc turns clockwise
129 * @return True if the arc makes a clockwise turn when going from initial to final
130 * point, false otherwise */
131 30122 bool sweep() const { return _angles.sweep(); }
132
133 9917 Angle initialAngle() const { return _angles.initialAngle(); }
134 9871 Angle finalAngle() const { return _angles.finalAngle(); }
135 /// @}
136
137 /// @name Modify parameters
138 /// @{
139
140 /// Change all of the arc's parameters.
141 void set( Point const &ip, double rx, double ry,
142 double rot_angle, bool large_arc, bool sweep,
143 Point const &fp
144 )
145 {
146 _initial_point = ip;
147 _final_point = fp;
148 _ellipse.setRays(rx, ry);
149 _ellipse.setRotationAngle(rot_angle);
150 _angles.setSweep(sweep);
151 _large_arc = large_arc;
152 _updateCenterAndAngles();
153 }
154
155 /// Change all of the arc's parameters.
156 void set( Point const &ip, Point const &r,
157 Angle rot_angle, bool large_arc, bool sweep,
158 Point const &fp
159 )
160 {
161 _initial_point = ip;
162 _final_point = fp;
163 _ellipse.setRays(r);
164 _ellipse.setRotationAngle(rot_angle);
165 _angles.setSweep(sweep);
166 _large_arc = large_arc;
167 _updateCenterAndAngles();
168 }
169
170 /** @brief Change the initial and final point in one operation.
171 * This method exists because modifying any of the endpoints causes rather costly
172 * recalculations of the center and extreme angles.
173 * @param ip New initial point
174 * @param fp New final point */
175 void setEndpoints(Point const &ip, Point const &fp) {
176 _initial_point = ip;
177 _final_point = fp;
178 _updateCenterAndAngles();
179 }
180 /// @}
181
182 /// @name Evaluate the arc as a function
183 /// @{
184 /** Check whether the arc contains the given angle
185 * @param t The angle to check
186 * @return True if the arc contains the angle, false otherwise */
187 bool containsAngle(Angle angle) const { return _angles.contains(angle); }
188
189 /** @brief Evaluate the arc at the specified angular coordinate
190 * @param t Angle
191 * @return Point corresponding to the given angle */
192 Point pointAtAngle(Coord t) const;
193
194 /** @brief Evaluate one of the arc's coordinates at the specified angle
195 * @param t Angle
196 * @param d The dimension to retrieve
197 * @return Selected coordinate of the arc at the specified angle */
198 Coord valueAtAngle(Coord t, Dim2 d) const;
199
200 /// Compute the curve time value corresponding to the given angular value.
201 20178 Coord timeAtAngle(Angle a) const { return _angles.timeAtAngle(a); }
202
203 /// Compute the angular domain value corresponding to the given time value.
204 20615 Angle angleAt(Coord t) const { return _angles.angleAt(t); }
205
206 /** @brief Compute the amount by which the angle parameter changes going from start to end.
207 * This has range \f$(-2\pi, 2\pi)\f$ and thus cannot be represented as instance
208 * of the class Angle. Add this to the initial angle to obtain the final angle. */
209 23 Coord sweepAngle() const { return _angles.sweepAngle(); }
210
211 /** @brief Get the elliptical angle spanned by the arc.
212 * This is basically the absolute value of sweepAngle(). */
213 23 Coord angularExtent() const { return _angles.extent(); }
214
215 /// Get the angular interval of the arc.
216 9 AngleInterval angularInterval() const { return _angles; }
217
218 /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\f$.
219 Point pointAt(Coord t) const override;
220
221 /// Evaluate a single coordinate on the arc in the curve domain.
222 Coord valueAt(Coord t, Dim2 d) const override;
223
224 /** @brief Compute a transform that maps the unit circle to the arc's ellipse.
225 * Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
226 * This function returns the transform that maps the unit circle to the arc's ellipse.
227 * @return Transform from unit circle to the arc's ellipse */
228 42468 Affine unitCircleTransform() const {
229 42468 Affine result = _ellipse.unitCircleTransform();
230 42468 return result;
231 }
232
233 /** @brief Compute a transform that maps the arc's ellipse to the unit circle. */
234 Affine inverseUnitCircleTransform() const {
235 Affine result = _ellipse.inverseUnitCircleTransform();
236 return result;
237 }
238 /// @}
239
240 /// @name Deal with degenerate ellipses.
241 /// @{
242 /** @brief Check whether both rays are nonzero.
243 * If they are not, the arc is represented as a line segment instead. */
244 248380 bool isChord() const {
245
4/4
✓ Branch 1 taken 166378 times.
✓ Branch 2 taken 82002 times.
✓ Branch 4 taken 81602 times.
✓ Branch 5 taken 84776 times.
248380 return ray(X) == 0 || ray(Y) == 0;
246 }
247
248 /** @brief Get the line segment connecting the arc's endpoints.
249 * @return A linear segment with initial and final point corresponding to those of the arc. */
250 80000 LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
251 /// @}
252
253 // implementation of overloads goes here
254 6386 Point initialPoint() const override { return _initial_point; }
255 6327 Point finalPoint() const override { return _final_point; }
256 303 Curve* duplicate() const override { return new EllipticalArc(*this); }
257 void setInitial(Point const &p) override {
258 _initial_point = p;
259 _updateCenterAndAngles();
260 }
261 30 void setFinal(Point const &p) override {
262 30 _final_point = p;
263 30 _updateCenterAndAngles();
264 30 }
265 106 bool isDegenerate() const override {
266 106 return _initial_point == _final_point;
267 }
268 90138 bool isLineSegment() const override { return isChord(); }
269 40038 Rect boundsFast() const override {
270 40038 return boundsExact();
271 }
272 Rect boundsExact() const override;
273 void expandToTransformed(Rect &bbox, Affine const &transform) const override;
274 // TODO: native implementation of the following methods
275 OptRect boundsLocal(OptInterval const &i, unsigned int deg) const override {
276 return SBasisCurve(toSBasis()).boundsLocal(i, deg);
277 }
278 std::vector<double> roots(double v, Dim2 d) const override;
279 #ifdef HAVE_GSL
280 std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const override;
281 37 double nearestTime( Point const& p, double from = 0, double to = 1 ) const override {
282
7/12
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 12 times.
✓ Branch 8 taken 25 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 25 times.
✓ Branch 12 taken 25 times.
✓ Branch 13 taken 12 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 37 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
37 if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
283 return from;
284 }
285
1/2
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
37 return allNearestTimes(p, from, to).front();
286 }
287 #endif
288 std::vector<CurveIntersection> intersect(Curve const &other, Coord eps=EPSILON) const override;
289 int degreesOfFreedom() const override { return 7; }
290 Curve *derivative() const override;
291
292 using Curve::operator*=;
293 void operator*=(Translate const &tr) override;
294 void operator*=(Scale const &s) override;
295 void operator*=(Rotate const &r) override;
296 void operator*=(Zoom const &z) override;
297 void operator*=(Affine const &m) override;
298
299 std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const override;
300 D2<SBasis> toSBasis() const override;
301 Curve *portion(double f, double t) const override;
302 Curve *reverse() const override;
303 bool isNear(Curve const &other, Coord precision) const override;
304 void feed(PathSink &sink, bool moveto_initial) const override;
305 int winding(Point const &p) const override;
306
307 protected:
308 bool _equalTo(Curve const &c) const override;
309
310 private:
311 void _updateCenterAndAngles();
312 std::vector<ShapeIntersection> _filterIntersections(std::vector<ShapeIntersection> &&xs, bool is_first) const;
313 bool _validateIntersection(ShapeIntersection &xing, bool is_first) const;
314 std::vector<ShapeIntersection> _intersectSameEllipse(EllipticalArc const *other) const;
315
316 Point _initial_point, _final_point;
317 Ellipse _ellipse;
318 AngleInterval _angles;
319 bool _large_arc;
320 }; // end class EllipticalArc
321
322
323 // implemented in elliptical-arc-from-sbasis.cpp
324 /** @brief Fit an elliptical arc to an SBasis fragment.
325 * @relates EllipticalArc */
326 bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in,
327 double tolerance = EPSILON, unsigned num_samples = 20);
328
329 /** @brief Debug output for elliptical arcs.
330 * @relates EllipticalArc */
331 std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea);
332
333 } // end namespace Geom
334
335 #endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
336
337 /*
338 Local Variables:
339 mode:c++
340 c-file-style:"stroustrup"
341 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
342 indent-tabs-mode:nil
343 fill-column:99
344 End:
345 */
346 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
347