1 /** @file
2  * @brief Demonstration of elliptical arc functions
3  *//*
4  * Authors:
5  *   Marco Cecchetti <mrcekets at gmail.com>
6  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
7  * Copyright 2008-2015 Authors
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it either under the terms of the GNU Lesser General Public
11  * License version 2.1 as published by the Free Software Foundation
12  * (the "LGPL") or, at your option, under the terms of the Mozilla
13  * Public License Version 1.1 (the "MPL"). If you do not alter this
14  * notice, a recipient may use your version of this file under either
15  * the MPL or the LGPL.
16  *
17  * You should have received a copy of the LGPL along with this library
18  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  * You should have received a copy of the MPL along with this library
21  * in the file COPYING-MPL-1.1
22  *
23  * The contents of this file are subject to the Mozilla Public License
24  * Version 1.1 (the "License"); you may not use this file except in
25  * compliance with the License. You may obtain a copy of the License at
26  * http://www.mozilla.org/MPL/
27  *
28  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
29  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
30  * the specific language governing rights and limitations.
31  */
32 
33 #include <2geom/elliptical-arc.h>
34 
35 #include <toys/path-cairo.h>
36 #include <toys/toy-framework-2.h>
37 #include <2geom/cairo-path-sink.h>
38 
39 #include <vector>
40 #include <string>
41 
42 
43 using namespace Geom;
44 
45 
46 
angle_formatter(double angle)47 std::string angle_formatter(double angle)
48 {
49     return default_formatter(decimal_round(deg_from_rad(angle),2));
50 }
51 
52 
53 
54 class EllipticalArcToy: public Toy
55 {
56 
57     enum menu_item_t
58     {
59         SHOW_MENU = 0,
60         TEST_BASIC,
61         TEST_COMPARISON,
62         TEST_PORTION,
63         TEST_REVERSE,
64         TEST_NEAREST_POINTS,
65         TEST_DERIVATIVE,
66         TEST_ROOTS,
67         TEST_BOUNDS,
68         TEST_FITTING,
69         TEST_TRANSFORM,
70         TOTAL_ITEMS // this one must be the last item
71     };
72 
73     enum handle_label_t
74     {
75         START_POINT = 0,
76         END_POINT,
77         POINT
78     };
79 
80     enum toggle_label_t
81     {
82         LARGE_ARC_FLAG = 0,
83         SWEEP_FLAG,
84         X_Y_TOGGLE
85     };
86 
87     enum slider_label_t
88     {
89         RX_SLIDER = 0,
90         RY_SLIDER,
91         ROT_ANGLE_SLIDER,
92         T_SLIDER,
93         FROM_SLIDER = T_SLIDER,
94         TO_SLIDER,
95         TM0_SLIDER = T_SLIDER,
96         TM1_SLIDER,
97         TM2_SLIDER,
98         TM3_SLIDER
99     };
100 
101     static const char* menu_items[TOTAL_ITEMS];
102     static const char keys[TOTAL_ITEMS];
103 
first_time(int,char **)104     virtual void first_time(int /*argc*/, char** /*argv*/)
105     {
106         draw_f = &EllipticalArcToy::draw_menu;
107     }
108 
init_common()109     void init_common()
110     {
111         set_common_control_geometry = true;
112         set_control_geometry = true;
113 
114         double start_angle = (10.0/6.0) * M_PI;
115         double sweep_angle = (4.0/6.0) * M_PI;
116         double end_angle = start_angle + sweep_angle;
117         double rot_angle = (0.0/6.0) * M_PI;
118         double rx = 200;
119         double ry = 150;
120         double cx = 300;
121         double cy = 300;
122 
123         Point start_point( cx + rx * std::cos(start_angle),
124                            cy + ry * std::sin(start_angle) );
125         Point end_point( cx + rx * std::cos(end_angle),
126                          cy + ry * std::sin(end_angle) );
127 
128         bool large_arc = false;
129         bool sweep = true;
130 
131 
132         initial_point.pos = start_point;
133         final_point.pos = end_point;
134 
135         try
136         {
137             ea.set (initial_point.pos,
138                     rx, ry, rot_angle,
139                     large_arc, sweep,
140                     final_point.pos);
141         }
142         catch( RangeError e )
143         {
144             no_solution = true;
145             std::cerr << e.what() << std::endl;
146         }
147 
148         sliders.clear();
149         sliders.reserve(50);
150         sliders.push_back(Slider(0, 500, 0, ea.ray(X), "ray X"));
151         sliders.push_back(Slider(0, 500, 0, ea.ray(Y), "ray Y"));
152         sliders.push_back(Slider(0, 2*M_PI, 0, ea.rotationAngle(), "rot angle"));
153         sliders[ROT_ANGLE_SLIDER].formatter(&angle_formatter);
154 
155         toggles.clear();
156         toggles.reserve(50);
157         toggles.push_back(Toggle("Large Arc Flag", ea.largeArc()));
158         toggles.push_back(Toggle("Sweep Flag", ea.sweep()));
159 
160         handles.clear();
161         handles.push_back(&initial_point);
162         handles.push_back(&final_point);
163         handles.push_back(&(sliders[RX_SLIDER]));
164         handles.push_back(&(sliders[RY_SLIDER]));
165         handles.push_back(&(sliders[ROT_ANGLE_SLIDER]));
166         handles.push_back(&(toggles[LARGE_ARC_FLAG]));
167         handles.push_back(&(toggles[SWEEP_FLAG]));
168     }
169 
draw_common(cairo_t * cr,std::ostringstream * notify,int width,int height,bool,std::ostringstream * timer_stream=0)170     virtual void draw_common( cairo_t *cr, std::ostringstream *notify,
171                               int width, int height, bool /*save*/,
172                               std::ostringstream *timer_stream=0)
173     {
174         if(timer_stream == 0)
175             timer_stream = notify;
176         init_common_ctrl_geom(cr, width, height, notify);
177 
178         no_solution = false;
179         try
180         {
181             ea.set( initial_point.pos,
182                     sliders[0].value(),
183                     sliders[1].value(),
184                     sliders[2].value(),
185                     toggles[0].on,
186                     toggles[1].on,
187                     final_point.pos );
188         }
189         catch( RangeError e )
190         {
191             no_solution = true;
192             std::cerr << e.what() << std::endl;
193             return;
194         }
195 
196         degenerate = ea.isDegenerate();
197 
198         point_overlap = false;
199         if ( are_near(ea.initialPoint(), ea.finalPoint()) )
200         {
201             point_overlap = true;
202         }
203 
204         // calculate the center of the two possible ellipse supporting the arc
205         std::pair<Point,Point> centers
206             = calculate_ellipse_centers( ea.initialPoint(), ea.finalPoint(),
207                                          ea.ray(X), ea.ray(Y), ea.rotationAngle(),
208                                          ea.largeArc(), ea.sweep() );
209 
210 
211         // draw axes passing through the center of the ellipse supporting the arc
212         cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
213         cairo_set_line_width(cr, 0.5);
214         draw_axes(cr);
215 
216         // draw the 2 ellipse with rays rx, ry passing through
217         // the 2 given point and with the x-axis inclined of rot_angle
218         if ( !(are_near(ea.ray(X), 0) || are_near(ea.ray(Y), 0)) )
219         {
220             cairo_elliptiarc( cr,
221                               centers.first[X], centers.first[Y],
222                               ea.ray(X), ea.ray(Y),
223                               0, 2*M_PI,
224                               ea.rotationAngle() );
225             cairo_stroke(cr);
226             cairo_elliptiarc( cr,
227                               centers.second[X], centers.second[Y],
228                               ea.ray(X), ea.ray(Y),
229                               0, 2*M_PI,
230                               ea.rotationAngle() );
231             cairo_stroke(cr);
232         }
233 
234         // convert the elliptical arc to a sbasis path and draw it
235         D2<SBasis> easb = ea.toSBasis();
236         cairo_set_line_width(cr, 0.5);
237         cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
238         cairo_d2_sb(cr, easb);
239         cairo_stroke(cr);
240 
241         // draw initial and final point labels
242         draw_text(cr, ea.initialPoint() + Point(5, -15), "initial");
243         draw_text(cr, ea.finalPoint() + Point(5, 0), "final");
244         cairo_stroke(cr);
245 
246         // TODO re-enable this
247         //*notify << ea;
248     }
249 
250 
draw_comparison(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)251     void draw_comparison(cairo_t *cr, std::ostringstream *notify,
252                          int width, int height, bool save, std::ostringstream */*timer_stream*/)
253     {
254         draw_common(cr, notify, width, height, save);
255         if ( no_solution || point_overlap ) return;
256 
257         // draw the arc with cairo in order to make a visual comparison
258         cairo_set_line_width(cr, 1);
259         cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
260 
261         if (ea.isDegenerate())
262         {
263             cairo_move_to(cr, ea.initialPoint());
264             cairo_line_to(cr, ea.finalPoint());
265         }
266         else
267         {
268             if ( ea.sweep() )
269             {
270                 cairo_elliptiarc( cr,
271                                   ea.center(X), ea.center(Y),
272                                   ea.ray(X), ea.ray(Y),
273                                   ea.initialAngle(), ea.finalAngle(),
274                                   ea.rotationAngle() );
275             }
276             else
277             {
278                 cairo_elliptiarc( cr,
279                                   ea.center(X), ea.center(Y),
280                                   ea.ray(X), ea.ray(Y),
281                                   ea.finalAngle(), ea.initialAngle(),
282                                   ea.rotationAngle() );
283             }
284         }
285         cairo_stroke(cr);
286     }
287 
288 
init_portion()289     void init_portion()
290     {
291         init_common();
292 
293         from_t = 0;
294         to_t = 1;
295 
296         sliders.push_back( Slider(0, 1, 0, from_t, "from"));
297         sliders.push_back( Slider(0, 1, 0, to_t, "to"));
298 
299         handles.push_back(&(sliders[FROM_SLIDER]));
300         handles.push_back(&(sliders[TO_SLIDER]));
301     }
302 
draw_portion(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)303     void draw_portion(cairo_t *cr, std::ostringstream *notify,
304                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
305     {
306         draw_common(cr, notify, width, height, save);
307         init_portion_ctrl_geom(cr, notify, width, height);
308         if ( no_solution || point_overlap ) return;
309 
310         from_t = sliders[FROM_SLIDER].value();
311         to_t = sliders[TO_SLIDER].value();
312 
313         EllipticalArc* eapp
314             = static_cast<EllipticalArc*>(ea.portion(from_t, to_t));
315         EllipticalArc& eap = *eapp;
316 
317         cairo_set_line_width(cr, 0.8);
318         cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 1.0);
319         cairo_move_to(cr, eap.center(X), eap.center(Y));
320         cairo_line_to(cr, eap.initialPoint()[X], eap.initialPoint()[Y]);
321         cairo_move_to(cr, eap.center(X), eap.center(Y));
322         cairo_line_to(cr, eap.finalPoint()[X], eap.finalPoint()[Y]);
323         cairo_stroke(cr);
324         D2<SBasis> sub_arc = eap.toSBasis();
325         cairo_d2_sb(cr, sub_arc);
326         cairo_stroke(cr);
327 
328         delete eapp;
329 
330     }
331 
332 
init_reverse()333     void init_reverse()
334     {
335         init_common();
336         time = 0;
337 
338         sliders.push_back( Slider(0, 1, 0, time, "t"));
339         handles.push_back(&(sliders[T_SLIDER]));
340     }
341 
draw_reverse(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)342     void draw_reverse(cairo_t *cr, std::ostringstream *notify,
343                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
344     {
345         draw_common(cr, notify, width, height, save);
346         init_reverse_ctrl_geom(cr, notify, width, height);
347         if ( no_solution || point_overlap ) return;
348 
349 
350         time = sliders[T_SLIDER].value();
351 
352         EllipticalArc* eapp = static_cast<EllipticalArc*>(ea.reverse());
353         EllipticalArc& eap = *eapp;
354 
355         cairo_set_line_width(cr, 0.8);
356         cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.0);
357 
358         cairo_move_to(cr, eap.center(X), eap.center(Y));
359         cairo_line_to(cr, eap.valueAt(time,X), eap.valueAt(time,Y));
360         draw_circ(cr, eap.pointAt(time));
361         cairo_stroke(cr);
362         cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 1.0);
363         D2<SBasis> sub_arc = eap.toSBasis();
364         cairo_d2_sb(cr, sub_arc);
365         cairo_stroke(cr);
366 
367         delete eapp;
368 
369     }
370 
371 
init_np()372     void init_np()
373     {
374         init_common();
375         nph.pos = Point(10,10);
376         handles.push_back(&nph);
377     }
378 
draw_np(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)379     void draw_np(cairo_t *cr, std::ostringstream *notify,
380                  int width, int height, bool save, std::ostringstream */*timer_stream*/)
381     {
382         draw_common(cr, notify, width, height, save);
383         if ( no_solution || point_overlap ) return;
384 
385         std::vector<double> times = ea.allNearestTimes( nph.pos );
386         for ( unsigned int i = 0; i < times.size(); ++i )
387         {
388             cairo_move_to(cr,nph.pos);
389             cairo_line_to( cr, ea.pointAt(times[i]) );
390         }
391         cairo_stroke(cr);
392     }
393 
394 
init_derivative()395     void init_derivative()
396     {
397         init_common();
398         time = 0;
399 
400         sliders.push_back( Slider(0, 1, 0, time, "t"));
401         handles.push_back(&(sliders[T_SLIDER]));
402     }
403 
draw_derivative(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)404     void draw_derivative(cairo_t *cr, std::ostringstream *notify,
405                          int width, int height, bool save, std::ostringstream */*timer_stream*/)
406     {
407         draw_common(cr, notify, width, height, save);
408         init_reverse_ctrl_geom(cr, notify, width, height);
409         if ( no_solution || point_overlap ) return;
410 
411         time = sliders[T_SLIDER].value();
412 
413         Curve* der = ea.derivative();
414         Point p = ea.pointAt(time);
415         Point v = der->pointAt(time) + p;
416         delete der;
417 //      std::vector<Point> points = ea.pointAndDerivatives(time, 8);
418 //      Point p = points[0];
419 //      Point v = points[1] + p;
420         cairo_move_to(cr, p);
421         cairo_line_to(cr, v);
422         cairo_stroke(cr);
423     }
424 
425 
init_roots()426     void init_roots()
427     {
428         init_common();
429         ph.pos = Point(10,10);
430         toggles.push_back( Toggle("X/Y roots", true) );
431 
432         handles.push_back(&ph);
433         handles.push_back(&(toggles[X_Y_TOGGLE]));
434     }
435 
draw_roots(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)436     void draw_roots(cairo_t *cr, std::ostringstream *notify,
437                     int width, int height, bool save, std::ostringstream */*timer_stream*/)
438     {
439         draw_common(cr, notify, width, height, save);
440         init_roots_ctrl_geom(cr, notify, width, height);
441         if ( no_solution || point_overlap ) return;
442 
443         Dim2 DIM = toggles[X_Y_TOGGLE].on ? X : Y;
444 
445         Point p1[2] = { Point(ph.pos[X], -1000),
446                         Point(-1000, ph.pos[Y]) };
447         Point p2[2] = { Point(ph.pos[X], 1000),
448                         Point(1000, ph.pos[Y]) };
449         cairo_set_line_width(cr, 0.5);
450         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
451         cairo_move_to(cr, p1[DIM]);
452         cairo_line_to(cr, p2[DIM]);
453 
454         std::vector<double> times;
455         try
456         {
457             times = ea.roots(ph.pos[DIM], DIM);
458             *notify << "winding: " << ea.winding(ph.pos);
459         }
460         catch(Geom::Exception e)
461         {
462             std::cerr << e.what() << std::endl;
463         }
464         for ( unsigned int i = 0; i < times.size(); ++i )
465         {
466             draw_handle(cr, ea.pointAt(times[i]));
467         }
468         cairo_stroke(cr);
469     }
470 
471 
init_bounds()472     void init_bounds()
473     {
474         init_common();
475     }
476 
draw_bounds(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)477     void draw_bounds(cairo_t *cr, std::ostringstream *notify,
478                      int width, int height, bool save, std::ostringstream */*timer_stream*/)
479     {
480         draw_common(cr, notify, width, height, save);
481         if ( no_solution || point_overlap ) return;
482 
483 //      const char* msg[] = { "xmax", "xmin", "ymax", "ymin" };
484 
485         Rect bb = ea.boundsFast();
486 
487 //      for ( unsigned int i = 0; i < limits.size(); ++i )
488 //      {
489 //          std::cerr << "angle[" << i << "] = " <<  deg_from_rad(limits[i]) << std::endl;
490 //          Point extreme = ea.pointAtAngle(limits[i]);
491 //          draw_handle(cr, extreme );
492 //          draw_text(cr, extreme, msg[i]);
493 //      }
494         cairo_rectangle( cr, bb.left(), bb.top(), bb.width(), bb.height() );
495         cairo_stroke(cr);
496     }
497 
498 
init_fitting()499     void init_fitting()
500     {
501         init_common();
502     }
503 
draw_fitting(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)504     void draw_fitting(cairo_t * cr, std::ostringstream *notify,
505                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
506     {
507         draw_common(cr, notify, width, height, save);
508         if ( no_solution || point_overlap ) return;
509 
510         D2<SBasis> easb = ea.toSBasis();
511         try
512         {
513             EllipticalArc earc;
514             if (!arc_from_sbasis(earc, easb, 0.1, 5)) return;
515 
516             D2<SBasis> arc = earc.toSBasis();
517             arc[0] += Linear(50, 50);
518             cairo_d2_sb(cr, arc);
519             cairo_stroke(cr);
520         }
521         catch( RangeError e )
522         {
523             std::cerr << "conversion failure" << std::endl;
524             std::cerr << e.what() << std::endl;
525             return;
526         }
527     }
528 
init_transform()529     void init_transform()
530     {
531         init_common();
532 
533         double max = 4;
534         double min = -max;
535 
536         sliders.push_back( Slider(min, max, 0, 1, "TM0"));
537         sliders.push_back( Slider(min, max, 0, 0, "TM1"));
538         sliders.push_back( Slider(min, max, 0, 0, "TM2"));
539         sliders.push_back( Slider(min, max, 0, 1, "TM3"));
540 
541         handles.push_back(&(sliders[TM0_SLIDER]));
542         handles.push_back(&(sliders[TM1_SLIDER]));
543         handles.push_back(&(sliders[TM2_SLIDER]));
544         handles.push_back(&(sliders[TM3_SLIDER]));
545     }
546 
draw_transform(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)547     void draw_transform(cairo_t *cr, std::ostringstream *notify,
548                         int width, int height, bool save, std::ostringstream */*timer_stream*/)
549     {
550         draw_common(cr, notify, width, height, save);
551         init_transform_ctrl_geom(cr, notify, width, height);
552         if ( no_solution || point_overlap ) return;
553 
554         Affine TM(sliders[TM0_SLIDER].value(), sliders[TM1_SLIDER].value(),
555                   sliders[TM2_SLIDER].value(), sliders[TM3_SLIDER].value(),
556                   ea.center(X),                ea.center(Y));
557 
558         Affine tm( 1,            0,
559                    0,            1,
560                    -ea.center(X), -ea.center(Y) );
561 
562 
563         EllipticalArc* tea = static_cast<EllipticalArc*>(ea.transformed(tm));
564         EllipticalArc* eat = NULL;
565         eat = static_cast<EllipticalArc*>(tea->transformed(TM));
566         delete tea;
567         if (eat == NULL)
568         {
569             std::cerr << "elliptiarc transformation failed" << std::endl;
570             return;
571         }
572 
573         CairoPathSink ps(cr);
574 
575         //D2<SBasis> sb = eat->toSBasis();
576         cairo_set_line_width(cr, 0.8);
577         cairo_set_source_rgba(cr, 0.8, 0.1, 0.1, 1.0);
578         //cairo_d2_sb(cr, sb);
579         ps.feed(*eat);
580         cairo_stroke(cr);
581         delete eat;
582     }
583 
init_common_ctrl_geom(cairo_t *,int,int height,std::ostringstream *)584     void init_common_ctrl_geom(cairo_t* /*cr*/, int /*width*/, int height, std::ostringstream* /*notify*/)
585     {
586         if ( set_common_control_geometry )
587         {
588             set_common_control_geometry = false;
589 
590             sliders[RX_SLIDER].geometry(Point(50, height-120), 250);
591             sliders[RY_SLIDER].geometry(Point(50, height-85), 250);
592             sliders[ROT_ANGLE_SLIDER].geometry(Point(50, height-50), 180);
593 
594             toggles[LARGE_ARC_FLAG].bounds = Rect(Point(400, height-120), Point(540, height-95));
595             toggles[SWEEP_FLAG].bounds = Rect(Point(400, height-70), Point(520, height-45));
596         }
597     }
598 
init_portion_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)599     void init_portion_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
600     {
601         if ( set_control_geometry )
602         {
603             set_control_geometry = false;
604 
605             Point from_sp = Point(600, height - 120);
606             Point to_sp = from_sp + Point(0,45);
607             double from_to_len = 100;
608 
609             sliders[FROM_SLIDER].geometry(from_sp, from_to_len);
610             sliders[TO_SLIDER].geometry(to_sp, from_to_len);
611         }
612     }
613 
init_reverse_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)614     void init_reverse_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
615     {
616         if ( set_control_geometry )
617         {
618             set_control_geometry = false;
619 
620             Point t_sp = Point(600, height - 120);
621             double t_len = 200;
622 
623             sliders[T_SLIDER].geometry(t_sp, t_len);
624         }
625     }
626 
init_roots_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)627     void init_roots_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
628     {
629         if ( set_control_geometry )
630         {
631             set_control_geometry = false;
632             Point T(600, height - 120);
633             toggles[X_Y_TOGGLE].bounds = Rect( T, T + Point(100,25) );
634         }
635     }
636 
init_transform_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)637     void init_transform_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
638     {
639         if ( set_control_geometry )
640         {
641             set_control_geometry = false;
642 
643             Point sp = Point(600, height - 140);
644             Point op = Point(0, 30);
645             double len = 200;
646 
647             sliders[TM0_SLIDER].geometry(sp, len);
648             sliders[TM1_SLIDER].geometry(sp += op, len);
649             sliders[TM2_SLIDER].geometry(sp += op, len);
650             sliders[TM3_SLIDER].geometry(sp += op, len);
651         }
652     }
653 
init_menu()654     void init_menu()
655     {
656         handles.clear();
657         sliders.clear();
658         toggles.clear();
659     }
660 
draw_menu(cairo_t *,std::ostringstream * notify,int,int,bool,std::ostringstream *)661     void draw_menu( cairo_t * /*cr*/, std::ostringstream *notify,
662                     int /*width*/, int /*height*/, bool /*save*/,
663                     std::ostringstream */*timer_stream*/)
664     {
665         *notify << std::endl;
666         for (int i = SHOW_MENU; i < TOTAL_ITEMS; ++i)
667         {
668             *notify << "   " << keys[i] << " -  " <<  menu_items[i] << std::endl;
669         }
670     }
671 
key_hit(GdkEventKey * e)672     void key_hit(GdkEventKey *e)
673     {
674         char choice = std::toupper(e->keyval);
675         switch ( choice )
676         {
677             case 'A':
678                 init_menu();
679                 draw_f = &EllipticalArcToy::draw_menu;
680                 break;
681             case 'B':
682                 init_common();
683                 draw_f = &EllipticalArcToy::draw_common;
684                 break;
685             case 'C':
686                 init_common();
687                 draw_f = &EllipticalArcToy::draw_comparison;
688                 break;
689             case 'D':
690                 draw_f = &EllipticalArcToy::draw_menu;
691                 init_portion();
692                 draw_f = &EllipticalArcToy::draw_portion;
693                 break;
694             case 'E':
695                 init_reverse();
696                 draw_f = &EllipticalArcToy::draw_reverse;
697                 break;
698             case 'F':
699                 init_np();
700                 draw_f = &EllipticalArcToy::draw_np;
701                 break;
702             case 'G':
703                 init_derivative();
704                 draw_f = &EllipticalArcToy::draw_derivative;
705                 break;
706             case 'H':
707                 init_roots();
708                 draw_f = &EllipticalArcToy::draw_roots;
709                 break;
710             case 'I':
711                 init_bounds();
712                 draw_f = &EllipticalArcToy::draw_bounds;
713                 break;
714             case 'J':
715                 init_fitting();
716                 draw_f = &EllipticalArcToy::draw_fitting;
717                 break;
718             case 'K':
719                 init_transform();
720                 draw_f = &EllipticalArcToy::draw_transform;
721                 break;
722         }
723         redraw();
724     }
725 
726 
draw_axes(cairo_t * cr) const727     void draw_axes(cairo_t* cr) const
728     {
729         Point D(std::cos(ea.rotationAngle()), std::sin(ea.rotationAngle()));
730         Point Dx = (ea.ray(X) + 20) * D;
731         Point Dy = (ea.ray(Y) + 20) * D.cw();
732         Point C(ea.center(X),ea.center(Y));
733         Point LP = C - Dx;
734         Point RP = C + Dx;
735         Point UP = C - Dy;
736         Point DP = C + Dy;
737 
738         cairo_move_to(cr, LP[X], LP[Y]);
739         cairo_line_to(cr, RP[X], RP[Y]);
740         cairo_move_to(cr, UP[X], UP[Y]);
741         cairo_line_to(cr, DP[X], DP[Y]);
742         cairo_move_to(cr, 0, 0);
743         cairo_stroke(cr);
744     }
745 
cairo_elliptiarc(cairo_t * cr,double _cx,double _cy,double _rx,double _ry,double _sa,double _ea,double _ra=0) const746     void cairo_elliptiarc( cairo_t *cr,
747                            double _cx, double _cy,
748                            double _rx, double _ry,
749                            double _sa, double _ea,
750                            double _ra = 0
751                          ) const
752     {
753         double cos_rot_angle = std::cos(_ra);
754         double sin_rot_angle = std::sin(_ra);
755         cairo_matrix_t transform_matrix;
756         cairo_matrix_init( &transform_matrix,
757                            _rx * cos_rot_angle, _rx * sin_rot_angle,
758                           -_ry * sin_rot_angle, _ry * cos_rot_angle,
759                            _cx,                 _cy
760                          );
761         cairo_save(cr);
762         cairo_transform(cr, &transform_matrix);
763         cairo_arc(cr, 0, 0, 1, _sa, _ea);
764         cairo_restore(cr);
765     }
766 
767 
768     std::pair<Point,Point>
calculate_ellipse_centers(Point _initial_point,Point _final_point,double m_rx,double m_ry,double m_rot_angle,bool m_large_arc,bool m_sweep)769     calculate_ellipse_centers( Point _initial_point, Point _final_point,
770                                double m_rx,           double m_ry,
771                                double m_rot_angle,
772                                bool m_large_arc, bool m_sweep
773                              )
774     {
775         std::pair<Point,Point> result;
776         if ( _initial_point == _final_point )
777         {
778             result.first = result.second = _initial_point;
779             return result;
780         }
781 
782         m_rx = std::fabs(m_rx);
783         m_ry = std::fabs(m_ry);
784 
785         Point d = _initial_point - _final_point;
786 
787         if ( are_near(m_rx, 0) || are_near(m_ry, 0) )
788         {
789             result.first = result.second
790                 = middle_point(_initial_point, _final_point);
791             return result;
792         }
793 
794         double sin_rot_angle = std::sin(m_rot_angle);
795         double cos_rot_angle = std::cos(m_rot_angle);
796 
797 
798         Affine m( cos_rot_angle, -sin_rot_angle,
799                   sin_rot_angle, cos_rot_angle,
800                   0,             0              );
801 
802         Point p = (d / 2) * m;
803         double rx2 = m_rx * m_rx;
804         double ry2 = m_ry * m_ry;
805         double rxpy = m_rx * p[Y];
806         double rypx = m_ry * p[X];
807         double rx2py2 = rxpy * rxpy;
808         double ry2px2 = rypx * rypx;
809         double num = rx2 * ry2;
810         double den = rx2py2 + ry2px2;
811         assert(den != 0);
812         double rad = num / den;
813         Point c(0,0);
814         if (rad > 1)
815         {
816             rad -= 1;
817             rad = std::sqrt(rad);
818 
819             if (m_large_arc == m_sweep) rad = -rad;
820             c = rad * Point(rxpy / m_ry, -rypx / m_rx);
821 
822             m[1] = -m[1];
823             m[2] = -m[2];
824 
825             c = c * m;
826         }
827 
828         d = middle_point(_initial_point, _final_point);
829 
830         result.first = c + d;
831         result.second = -c + d;
832         return result;
833 
834     }
835 
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)836     virtual void draw( cairo_t *cr, std::ostringstream *notify,
837                        int width, int height, bool save, std::ostringstream *timer_stream)
838     {
839         (this->*draw_f)(cr, notify, width, height, save, timer_stream);
840         Toy::draw(cr, notify, width, height, save,timer_stream);
841     }
842 
843   public:
EllipticalArcToy()844     EllipticalArcToy() {}
845 
846   private:
847     typedef void (EllipticalArcToy::* draw_func_t) (cairo_t*, std::ostringstream*, int, int, bool, std::ostringstream*);
848     draw_func_t draw_f;
849     bool set_common_control_geometry;
850     bool set_control_geometry;
851     bool no_solution, point_overlap;
852     bool degenerate;
853     PointHandle initial_point, final_point;
854     PointHandle nph, ph;
855     std::vector<Toggle> toggles;
856     std::vector<Slider> sliders;
857     EllipticalArc ea;
858 
859     double from_t;
860     double to_t;
861     double time;
862 
863 };
864 
865 
866 const char* EllipticalArcToy::menu_items[] =
867 {
868     "show this menu",
869     "basic",
870     "comparison",
871     "portion, pointAt",
872     "reverse, valueAt",
873     "nearest points",
874     "derivative",
875     "roots",
876     "bounding box",
877     "fitting",
878     "transformation"
879 };
880 
881 const char EllipticalArcToy::keys[] =
882 {
883      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'
884 };
885 
886 
main(int argc,char ** argv)887 int main(int argc, char **argv)
888 {
889     init( argc, argv, new EllipticalArcToy(), 850, 780 );
890     return 0;
891 }
892 
893 
894 /*
895   Local Variables:
896   mode:c++
897   c-file-style:"stroustrup"
898   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
899   indent-tabs-mode:nil
900   fill-column:99
901   End:
902 */
903 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
904