1 /*
2  * Conic Section Toy
3  *
4  * Authors:
5  *      Marco Cecchetti <mrcekets at gmail.com>
6  *
7  * Copyright 2009  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 
34 #include <toys/path-cairo.h>
35 #include <toys/toy-framework-2.h>
36 
37 //#define CLIP_WITH_CAIRO_SUPPORT
38 #ifdef CLIP_WITH_CAIRO_SUPPORT
39     #include <2geom/conic_section_clipper_cr.h>
40 #endif
41 
42 
43 #include <2geom/conicsec.h>
44 #include <2geom/line.h>
45 
46 //#include <iomanip>
47 #include <optional>
48 
49 
50 using namespace Geom;
51 
52 
53 class ConicSectionToy : public Toy
54 {
55     enum menu_item_t
56     {
57         SHOW_MENU = 0,
58         TEST_VERTEX_FOCI,
59         TEST_FITTING,
60         TEST_ECCENTRICITY,
61         TEST_DEGENERATE,
62         TEST_ROOTS,
63         TEST_NEAREST_POINT,
64         TEST_BOUND,
65         TEST_CLIP,
66         TEST_TANGENT,
67         TEST_DUAL,
68         TOTAL_ITEMS // this one must be the last item
69     };
70 
71     static const char* menu_items[TOTAL_ITEMS];
72     static const char keys[TOTAL_ITEMS];
73 
first_time(int,char **)74     void first_time(int /*argc*/, char** /*argv*/) override
75     {
76         draw_f = &ConicSectionToy::draw_menu;
77     }
78 
79 
init_common()80     void init_common()
81     {
82         init_vertex_foci();
83         set_control_geometry = true;
84     }
85 
draw_common(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)86     void draw_common (cairo_t *cr, std::ostringstream *notify,
87                          int width, int height, bool save,
88                          std::ostringstream * timer_stream)
89     {
90         draw_vertex_foci(cr, notify, width, height, save, timer_stream);
91     }
92 
93 
94 /*
95  *  TEST VERTEX FOCI
96  */
init_vertex_foci()97     void init_vertex_foci()
98     {
99         set_common_control_geometry = true;
100         handles.clear();
101 
102         focus1.pos = Point(300, 300);
103         focus2.pos = Point(500, 300);
104         vertex.pos = Point(200, 300);
105 
106         parabola_toggle = Toggle("set/unset F2 to infinity", false);
107 
108         handles.push_back (&vertex);
109         handles.push_back (&focus1);
110         handles.push_back (&focus2);
111         handles.push_back (&parabola_toggle);
112     }
113 
draw_vertex_foci(cairo_t * cr,std::ostringstream * notify,int width,int height,bool,std::ostringstream *)114     void draw_vertex_foci (cairo_t *cr, std::ostringstream *notify,
115                          int width, int height, bool /*save*/,
116                          std::ostringstream * /*timer_stream*/)
117     {
118         init_vertex_foci_ctrl_geom(cr, notify, width, height);
119 
120 
121         if (!parabola_toggle.on )
122         {
123             if (focus2.pos == Point(infinity(),infinity()))
124                 focus2.pos = Point(m_width/2, m_height/2);
125             double df1f2 = distance (focus1.pos, focus2.pos);
126             if (df1f2 > 20 )
127             {
128                 Line axis(focus1.pos,focus2.pos);
129                 vertex.pos = projection (vertex.pos, axis);
130             }
131             else if (df1f2 > 1)
132             {
133                 Line axis(focus1.pos,vertex.pos);
134                 focus2.pos = projection (focus2.pos, axis);
135             }
136             else
137             {
138                 Line axis(focus1.pos,vertex.pos);
139                 focus2.pos = focus1.pos;
140             }
141         }
142         else
143         {
144             focus2.pos = Point(infinity(),infinity());
145         }
146 
147         cs.set (vertex.pos, focus1.pos, focus2.pos);
148         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
149         cairo_set_line_width (cr, 0.5);
150         draw (cr, cs, m_window);
151         cairo_stroke(cr);
152 
153         draw_label(cr, focus1, "F1");
154         if (!parabola_toggle.on)  draw_label(cr, focus2, "F2");
155         draw_label(cr, vertex, "V");
156         cairo_stroke(cr);
157 
158         *notify << cs.categorise() << ": " << cs << std::endl;
159     }
160 
init_vertex_foci_ctrl_geom(cairo_t *,std::ostringstream *,int width,int)161     void init_vertex_foci_ctrl_geom (cairo_t* /*cr*/,
162                                      std::ostringstream* /*notify*/,
163                                      int width, int /*height*/)
164     {
165         if ( set_common_control_geometry )
166         {
167             set_common_control_geometry = false;
168 
169             Point toggle_sp( width - 200, 10);
170             parabola_toggle.bounds = Rect (toggle_sp, toggle_sp + Point(190,30));
171         }
172     }
173 
174 
175 /*
176  *  TEST FITTING
177  */
init_fitting()178     void init_fitting()
179     {
180         set_common_control_geometry = true;
181         handles.clear();
182 
183         psh.pts.resize(5);
184         psh.pts[0] = Point(450, 250);
185         psh.pts[1] = Point(250, 100);
186         psh.pts[2] = Point(250, 400);
187         psh.pts[3] = Point(400, 320);
188         psh.pts[4] = Point(50, 250);
189 
190         fitting_slider.set (0, 5, 1, 0, "more handles");
191 
192         handles.push_back(&psh);
193         handles.push_back(&fitting_slider);
194     }
195 
draw_fitting(cairo_t * cr,std::ostringstream * notify,int width,int height,bool,std::ostringstream *)196     void draw_fitting (cairo_t *cr, std::ostringstream *notify,
197                          int width, int height, bool /*save*/,
198                          std::ostringstream * /*timer_stream*/)
199     {
200         init_fitting_ctrl_geom(cr, notify, width, height);
201 
202         size_t n = (size_t)(fitting_slider.value()) + 5;
203         if (n < psh.pts.size())
204         {
205             psh.pts.resize(n);
206         }
207         else if (n > psh.pts.size())
208         {
209             psh.push_back (std::fabs (width - 100) * uniform() + 50,
210                            std::fabs (height - 100) * uniform() + 50);
211         }
212 
213         cs.set (psh.pts);
214 
215         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
216         cairo_set_line_width (cr, 0.5);
217         draw (cr, cs, m_window);
218         cairo_stroke(cr);
219         *notify << cs.categorise() << ": " << cs << std::endl;
220     }
221 
init_fitting_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)222     void init_fitting_ctrl_geom (cairo_t* /*cr*/,
223                                  std::ostringstream* /*notify*/,
224                                  int /*width*/, int height)
225     {
226         if ( set_common_control_geometry )
227         {
228             set_common_control_geometry = false;
229             fitting_slider.geometry (Point(50, height - 50), 100);
230         }
231     }
232 
233 
234 /*
235  *  TEST ECCENTRICITY
236  */
init_eccentricity()237     void init_eccentricity()
238     {
239         set_common_control_geometry = true;
240         handles.clear();
241 
242         p1 = Point (100, 100);
243         p2 = Point (100, 400);
244         focus1 = Point (300, 250);
245 
246         eccentricity_slider.set (0, 3, 0, 1, "eccentricity");
247 
248         handles.push_back (&p1);
249         handles.push_back (&p2);
250         handles.push_back (&focus1);
251         handles.push_back (&eccentricity_slider);
252     }
253 
draw_eccentricity(cairo_t * cr,std::ostringstream * notify,int width,int height,bool,std::ostringstream *)254     void draw_eccentricity (cairo_t *cr, std::ostringstream *notify,
255                             int width, int height, bool /*save*/,
256                             std::ostringstream * /*timer_stream*/)
257     {
258         init_eccentricity_ctrl_geom(cr, notify, width, height);
259 
260         Line directrix (p1.pos, p2.pos);
261 
262         cs.set (focus1.pos, directrix, eccentricity_slider.value());
263 
264         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
265         cairo_set_line_width (cr, 0.5);
266         draw (cr, cs, m_window);
267         cairo_stroke(cr);
268 
269         draw_label (cr, focus1, "F");
270         draw_line (cr, directrix, m_window);
271         draw_label(cr, p1, "directrix");
272         cairo_stroke(cr);
273 
274         *notify << cs.categorise() << ": " << cs << std::endl;
275     }
276 
init_eccentricity_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)277     void init_eccentricity_ctrl_geom (cairo_t* /*cr*/,
278                                       std::ostringstream* /*notify*/,
279                                       int /*width*/, int height)
280     {
281         if ( set_common_control_geometry )
282         {
283             set_common_control_geometry = false;
284             eccentricity_slider.geometry (Point (10, height - 50), 300);
285         }
286     }
287 
288 
289 
290 /*
291  *  TEST DEGENERATE
292  */
init_degenerate()293     void init_degenerate()
294     {
295         set_common_control_geometry = true;
296         handles.clear();
297 
298         psh.pts.resize(4);
299         psh.pts[0] = Point(450, 250);
300         psh.pts[1] = Point(250, 100);
301         psh.pts[2] = Point(250, 400);
302         psh.pts[3] = Point(400, 320);
303 
304 
305 
306         handles.push_back(&psh);
307     }
308 
draw_degenerate(cairo_t * cr,std::ostringstream * notify,int width,int height,bool,std::ostringstream *)309     void draw_degenerate (cairo_t *cr, std::ostringstream *notify,
310                           int width, int height, bool /*save*/,
311                           std::ostringstream * /*timer_stream*/)
312     {
313         init_degenerate_ctrl_geom(cr, notify, width, height);
314 
315         Line l1 (psh.pts[0], psh.pts[1]);
316         Line l2 (psh.pts[2], psh.pts[3]);
317         cs.set (l1, l2);
318         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
319         cairo_set_line_width (cr, 0.5);
320         draw (cr, cs, m_window);
321         cairo_stroke(cr);
322 
323         *notify << cs.categorise() << ": " << cs << std::endl;
324     }
325 
init_degenerate_ctrl_geom(cairo_t *,std::ostringstream *,int,int)326     void init_degenerate_ctrl_geom (cairo_t* /*cr*/,
327                                     std::ostringstream* /*notify*/,
328                                     int /*width*/, int /*height*/)
329     {
330         if ( set_common_control_geometry )
331         {
332             set_common_control_geometry = false;
333         }
334     }
335 
336 
337 /*
338  *  TEST ROOTS
339  */
init_roots()340     void init_roots()
341     {
342         init_common();
343 
344         p1.pos = Point(180, 50);
345 
346         x_y_toggle = Toggle("X/Y roots", true);
347 
348         handles.push_back(&p1);
349         handles.push_back(&x_y_toggle);
350     }
351 
draw_roots(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)352     void draw_roots (cairo_t *cr, std::ostringstream *notify,
353                          int width, int height, bool save,
354                          std::ostringstream * timer_stream)
355     {
356         draw_common(cr, notify, width, height, save, timer_stream);
357         init_roots_ctrl_geom(cr, notify, width, height);
358 
359 
360         Dim2 DIM = x_y_toggle.on ? X : Y;
361         Line l(p1.pos, DIM * (-M_PI/2) + M_PI/2);
362 
363         cairo_set_line_width(cr, 0.2);
364         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
365         draw_line(cr, l, m_window);
366         cairo_stroke(cr);
367 
368         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
369         std::vector<double> values;
370         try
371         {
372             cs.roots(values, p1.pos[DIM], DIM);
373         }
374         catch(Geom::Exception e)
375         {
376             std::cerr << e.what() << std::endl;
377         }
378         for (double value : values)
379         {
380             Point p(value, value);
381             p[DIM] = p1.pos[DIM];
382             draw_handle(cr, p);
383         }
384         cairo_stroke(cr);
385 
386         *notify << "                 ";
387         for ( unsigned int i = 0; i < values.size(); ++i )
388         {
389             *notify << "v" << i << " = " << values[i] << "  ";
390         }
391     }
392 
init_roots_ctrl_geom(cairo_t *,std::ostringstream *,int width,int height)393     void init_roots_ctrl_geom (cairo_t* /*cr*/, std::ostringstream* /*notify*/,
394                                int width, int height)
395     {
396         if ( set_control_geometry )
397         {
398             set_control_geometry = false;
399 
400             Point T(width - 120, height - 60);
401             x_y_toggle.bounds = Rect( T, T + Point(100,25) );
402         }
403     }
404 
405 /*
406  *  TEST NEAREST POINT
407  */
408 
init_nearest_time()409     void init_nearest_time()
410     {
411         init_common();
412         p1.pos = Point(180, 50);
413         handles.push_back(&p1);
414     }
415 
draw_nearest_time(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)416     void draw_nearest_time (cairo_t *cr, std::ostringstream *notify,
417                              int width, int height, bool save,
418                              std::ostringstream * timer_stream)
419     {
420         draw_common(cr, notify, width, height, save, timer_stream);
421 
422         Point P;
423         try
424         {
425             P = cs.nearestTime (p1.pos);
426         }
427         catch (LogicalError e)
428         {
429             std::cerr << e.what() << std::endl;
430         }
431 
432 
433         cairo_set_source_rgba(cr, 0.8, 0.1, 0.1, 1.0);
434         draw_line_seg(cr, p1.pos, P);
435         cairo_stroke(cr);
436 
437         cairo_set_source_rgba(cr, 0.1, 0.1, 0.9, 1.0);
438         draw_handle(cr, P);
439         cairo_stroke(cr);
440         cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 1.0);
441         draw_label(cr, p1, "Q");
442         draw_text(cr, P + Point(5, 5), "P");
443         cairo_stroke(cr);
444     }
445 
446 /*
447  *  TEST BOUND
448  */
init_bound()449     void init_bound()
450     {
451         init_common();
452         p1.pos = Point(50, 200);
453         p2.pos = Point(50, 400);
454         p3.pos = Point(50, 500);
455         handles.push_back(&p1);
456         handles.push_back(&p2);
457         handles.push_back(&p3);
458     }
459 
draw_bound(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)460     void draw_bound (cairo_t *cr, std::ostringstream *notify,
461                          int width, int height, bool save,
462                          std::ostringstream * timer_stream)
463     {
464         draw_common(cr, notify, width, height, save, timer_stream);
465 
466         try
467         {
468             p1.pos = cs.nearestTime (p1.pos);
469             p2.pos = cs.nearestTime (p2.pos);
470             p3.pos = cs.nearestTime (p3.pos);
471         }
472         catch (LogicalError e)
473         {
474             std::cerr << e.what() << std::endl;
475         }
476 
477         Rect bound = cs.arc_bound (p1.pos, p2.pos, p3.pos);
478         cairo_set_source_rgba(cr, 0.8, 0.1, 0.1, 1.0);
479         cairo_set_line_width (cr, 0.5);
480         cairo_rectangle (cr, bound);
481         cairo_stroke(cr);
482 
483         cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 1.0);
484         draw_label (cr, p1, "initial");
485         draw_label (cr, p2, "inner");
486         draw_label (cr, p3, "final");
487         cairo_stroke(cr);
488 
489     }
490 
491 /*
492  *  TEST CLIP
493  */
init_clip()494     void init_clip()
495     {
496         init_common();
497     }
498 
draw_clip(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)499     void draw_clip (cairo_t *cr, std::ostringstream *notify,
500                          int width, int height, bool save,
501                          std::ostringstream * timer_stream)
502     {
503         draw_common(cr, notify, width, height, save, timer_stream);
504         //init_clip_ctrl_geom(cr, notify, width, height);
505 
506 
507         Rect R(Point (100, 100),Point (width-100, height-100));
508         std::vector<RatQuad> rq;
509 #ifdef  CLIP_WITH_CAIRO_SUPPORT
510         clipper_cr aclipper(cr, cs, R);
511         aclipper.clip (rq);
512 #else
513         clip (rq, cs, R);
514 #endif
515         cairo_set_source_rgba(cr, 0.8, 0.1, 0.1, 1.0);
516         cairo_set_line_width (cr, 0.5);
517         cairo_rectangle (cr, Rect (Point (100, 100),Point (width-100, height-100)));
518         for (auto & i : rq)
519         {
520             cairo_d2_sb (cr, i.toCubic().toSBasis());
521         }
522         cairo_stroke(cr);
523     }
524 
525 /*
526  *  TEST TANGENT
527  */
init_tangent()528     void init_tangent()
529     {
530         init_common();
531 
532         p1.pos = Point(180, 50);
533 
534         handles.push_back(&p1);
535     }
536 
draw_tangent(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)537     void draw_tangent (cairo_t *cr, std::ostringstream *notify,
538                          int width, int height, bool save,
539                          std::ostringstream * timer_stream)
540     {
541         draw_common(cr, notify, width, height, save, timer_stream);
542 
543         p1.pos = cs.nearestTime (p1.pos);
544         Line l = cs.tangent(p1.pos);
545 
546         draw_label (cr, p1, "P");
547         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
548         draw_line(cr, l, m_window);
549         cairo_stroke(cr);
550     }
551 
552 /*
553  *  TEST DUAL
554  */
init_dual()555     void init_dual()
556     {
557         init_common();
558     }
559 
draw_dual(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)560     void draw_dual (cairo_t *cr, std::ostringstream *notify,
561                          int width, int height, bool save,
562                          std::ostringstream * timer_stream)
563     {
564         draw_common(cr, notify, width, height, save, timer_stream);
565 
566         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
567         xAx dc = cs.dual();
568         // we need some trick to make the dual visible in the window
569         std::string dckind = dc.categorise();
570         std::optional<Point> T = dc.centre();
571         if (T)  dc = dc.translate (-*T);
572         dc = dc.scale (1e-5, 1e-5);
573         dc = dc.translate (Point(width/2, height/2));
574         draw (cr, dc, m_window);
575         cairo_stroke(cr);
576         *notify << "\n dual: " << dckind << ": " << dc;
577     }
578 
579 
draw_segment(cairo_t * cr,Point const & p1,Point const & p2)580     void draw_segment (cairo_t* cr, Point const& p1, Point const&  p2)
581     {
582         cairo_move_to(cr, p1);
583         cairo_line_to(cr, p2);
584     }
585 
draw(cairo_t * cr,xAx const & cs,const Rect & _window)586     void draw (cairo_t* cr, xAx const& cs, const Rect& _window)
587     {
588 
589         // offset
590         //Point O(400, 300);
591         Point O(0, 0);
592         std::vector<double> r1, r2;
593 
594         size_t N = (size_t)(2 *_window.width());
595         double dx = 0.5;//width / N;
596         //std::cout << "dx = " << dx << std::endl;
597         double x = _window.left() - O[X];
598         for (size_t i = 0; i < N; ++i, x += dx)
599         {
600             if (r1.empty())
601             {
602                 cs.roots(r1, x, X);
603                 if (r1.size() == 1)
604                 {
605                     r1.push_back(r1.front());
606                 }
607                 if (i != 0 && r1.size() == 2)
608                 {
609                     Point p1(x-dx, r1[0]);
610                     Point p2(x-dx, r1[1]);
611                     p1 += O; p2 += O;
612                     if (_window.contains(p1) && _window.contains(p2))
613                         draw_segment(cr, p1, p2);
614                 }
615                 continue;
616             }
617             cs.roots(r2, x, X);
618             if (r2.empty())
619             {
620                 Point p1(x-dx, r1[0]);
621                 Point p2(x-dx, r1[1]);
622                 p1 += O; p2 += O;
623                 if (_window.contains(p1) && _window.contains(p2))
624                     draw_segment(cr, p1, p2);
625                 r1.clear();
626                 continue;
627             }
628             if (r2.size() == 1)
629             {
630                 r2.push_back(r2.front());
631             }
632 
633             Point p1(x-dx, r1[0]);
634             Point p2(x, r2[0]);
635             p1 += O; p2 += O;
636             if (_window.contains(p1) && _window.contains(p2))
637                 draw_segment(cr, p1, p2);
638 
639             p1 = Point(x-dx, r1[1]) + O;
640             p2 = Point(x, r2[1]) + O;
641             if (_window.contains(p1) && _window.contains(p2))
642                 draw_segment(cr, p1, p2);
643 
644             using std::swap;
645             swap(r1, r2);
646         }
647     }
648 
draw_label(cairo_t * cr,PointHandle const & ph,const char * label)649     void draw_label(cairo_t* cr, PointHandle const& ph, const char* label)
650     {
651         draw_text(cr, ph.pos+op, label);
652     }
653 
654 //    void draw_label(cairo_t* cr, Line const& l, const char* label)
655 //    {
656 //        draw_text(cr, projection(Point(m_width/2-30, m_height/2-30), l)+op, label);
657 //    }
658 
init_menu()659     void init_menu()
660     {
661         handles.clear();
662     }
663 
draw_menu(cairo_t *,std::ostringstream * notify,int,int,bool,std::ostringstream *)664     void draw_menu( cairo_t * /*cr*/, std::ostringstream *notify,
665                     int /*width*/, int /*height*/, bool /*save*/,
666                     std::ostringstream */*timer_stream*/ )
667     {
668         *notify << std::endl;
669         for (int i = SHOW_MENU; i < TOTAL_ITEMS; ++i)
670         {
671             *notify << "   " << keys[i] << " -  " <<  menu_items[i] << std::endl;
672         }
673     }
674 
675 
key_hit(GdkEventKey * e)676     void key_hit(GdkEventKey *e) override
677     {
678         char choice = std::toupper(e->keyval);
679         switch ( choice )
680         {
681             case 'A':
682                 init_menu();
683                 draw_f = &ConicSectionToy::draw_menu;
684                 break;
685             case 'B':
686                 init_common();
687                 draw_f = &ConicSectionToy::draw_vertex_foci;
688                 break;
689             case 'C':
690                 init_fitting();
691                 draw_f = &ConicSectionToy::draw_fitting;
692                 break;
693             case 'D':
694                 init_eccentricity();
695                 draw_f = &ConicSectionToy::draw_eccentricity;
696                 break;
697             case 'E':
698                 init_degenerate();
699                 draw_f = &ConicSectionToy::draw_degenerate;
700                 break;
701             case 'F':
702                 init_roots();
703                 draw_f = &ConicSectionToy::draw_roots;
704                 break;
705             case 'G':
706                 init_nearest_time();
707                 draw_f = &ConicSectionToy::draw_nearest_time;
708                 break;
709             case 'H':
710                 init_bound();
711                 draw_f = &ConicSectionToy::draw_bound;
712                 break;
713             case 'K':
714                 init_clip();
715                 draw_f = &ConicSectionToy::draw_clip;
716                 break;
717             case 'J':
718                 init_tangent();
719                 draw_f = &ConicSectionToy::draw_tangent;
720                 break;
721             case 'I':
722                 init_dual();
723                 draw_f = &ConicSectionToy::draw_dual;
724                 break;
725 
726         }
727         redraw();
728     }
729 
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)730     void draw( cairo_t *cr, std::ostringstream *notify,
731                        int width, int height, bool save,
732                        std::ostringstream *timer_stream ) override
733     {
734         if (timer_stream == 0)  timer_stream = notify;
735         m_width = width;
736         m_height = height;
737         m_length = (m_width > m_height) ? m_width : m_height;
738         m_length *= 2;
739         m_window = Rect (Point (0, 0), Point (m_width, m_height));
740         (this->*draw_f)(cr, notify, width, height, save, timer_stream);
741         Toy::draw(cr, notify, width, height, save, timer_stream);
742     }
743 
744 public:
ConicSectionToy()745     ConicSectionToy()
746     {
747         op = Point(5,5);
748     }
749 
750 private:
751     typedef void (ConicSectionToy::* draw_func_t) (cairo_t*, std::ostringstream*, int, int, bool, std::ostringstream*);
752     draw_func_t draw_f;
753     bool set_common_control_geometry;
754     bool set_control_geometry;
755     Point op;
756     double m_width, m_height, m_length;
757     Rect m_window;
758 
759     xAx cs;
760     PointHandle vertex, focus1, focus2;
761     Toggle parabola_toggle;
762 
763     PointSetHandle psh;
764     Slider fitting_slider;
765 
766     PointHandle p1, p2, p3;
767     Toggle x_y_toggle;
768 
769     Slider eccentricity_slider;
770 };
771 
772 
773 const char* ConicSectionToy::menu_items[] =
774 {
775     "show this menu",
776     "vertex and foci",
777     "fitting",
778     "eccentricity",
779     "degenerate",
780     "roots",
781     "nearest point",
782     "bound",
783     "clip",
784     "tangent",
785     "dual"
786 };
787 
788 const char ConicSectionToy::keys[] =
789 {
790      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'K', 'J', 'I'
791 };
792 
793 
main(int argc,char ** argv)794 int main(int argc, char **argv)
795 {
796     //std::cout.precision(20);
797     init( argc, argv, new ConicSectionToy(), 800, 600 );
798     return 0;
799 }
800 
801 
802 
803 
804 /*
805   Local Variables:
806   mode:c++
807   c-file-style:"stroustrup"
808   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
809   indent-tabs-mode:nil
810   fill-column:99
811   End:
812 */
813 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
814