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