1 /*
2  * Line Toy
3  *
4  * Copyright 2008  Marco Cecchetti <mrcekets at gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  */
29 
30 
31 #include <2geom/line.h>
32 #include <toys/path-cairo.h>
33 #include <toys/toy-framework-2.h>
34 
35 #include <2geom/angle.h>
36 
37 #include <vector>
38 #include <string>
39 #include <optional>
40 
41 using namespace Geom;
42 
43 
44 
angle_formatter(double angle)45 std::string angle_formatter(double angle)
46 {
47     return default_formatter(decimal_round(deg_from_rad(angle),2));
48 }
49 
50 
51 
52 class LineToy : public Toy
53 {
54     enum menu_item_t
55     {
56         SHOW_MENU = 0,
57         TEST_CREATE,
58         TEST_PROJECTION,
59         TEST_ORTHO,
60         TEST_DISTANCE,
61         TEST_POSITION,
62         TEST_SEG_BISEC,
63         TEST_ANGLE_BISEC,
64         TEST_COLLINEAR,
65         TEST_INTERSECTIONS,
66         TEST_COEFFICIENTS,
67         TEST_SEGMENT_INSIDE,
68         TOTAL_ITEMS // this one must be the last item
69     };
70 
71     enum handle_label_t
72     {
73     };
74 
75     enum toggle_label_t
76     {
77     };
78 
79     enum slider_label_t
80     {
81         END_SHARED_SLIDERS = 0,
82         ANGLE_SLIDER = END_SHARED_SLIDERS,
83         A_COEFF_SLIDER = END_SHARED_SLIDERS,
84         B_COEFF_SLIDER,
85         C_COEFF_SLIDER
86     };
87 
88     static const char* menu_items[TOTAL_ITEMS];
89     static const char keys[TOTAL_ITEMS];
90 
first_time(int,char **)91     virtual void first_time(int /*argc*/, char** /*argv*/)
92     {
93         draw_f = &LineToy::draw_menu;
94     }
95 
init_common()96     void init_common()
97     {
98         set_common_control_geometry = true;
99         set_control_geometry = true;
100 
101         sliders.clear();
102         toggles.clear();
103         handles.clear();
104     }
105 
106 
draw_common(cairo_t * cr,std::ostringstream * notify,int width,int height,bool)107     virtual void draw_common( cairo_t *cr, std::ostringstream *notify,
108                               int width, int height, bool /*save*/ )
109     {
110         init_common_ctrl_geom(cr, width, height, notify);
111     }
112 
113 
init_create()114     void init_create()
115     {
116         init_common();
117 
118         p1.pos = Point(400, 50);
119         p2.pos = Point(450, 450);
120         O.pos = Point(50, 400);
121 
122         sliders.push_back(Slider(0, 2*M_PI, 0, 0, "angle"));
123         sliders[ANGLE_SLIDER].formatter(&angle_formatter);
124 
125         handles.push_back(&p1);
126         handles.push_back(&p2);
127         handles.push_back(&O);
128         handles.push_back(&(sliders[ANGLE_SLIDER]));
129     }
130 
draw_create(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)131     void draw_create(cairo_t *cr, std::ostringstream *notify,
132                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
133     {
134         draw_common(cr, notify, width, height, save);
135         init_create_ctrl_geom(cr, notify, width, height);
136 
137         Line l1(p1.pos, p2.pos);
138         Line l2(O.pos, sliders[ANGLE_SLIDER].value());
139 
140         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
141         cairo_set_line_width(cr, 0.3);
142         draw_line(cr, l1);
143         draw_line(cr, l2);
144         cairo_stroke(cr);
145 
146         draw_label(cr, p1, "P1");
147         draw_label(cr, p2, "P2");
148         draw_label(cr, O, "O");
149         draw_label(cr, l1, "L(P1,P2)");
150         draw_label(cr, l2, "L(O,angle)");
151     }
152 
153 
init_projection()154     void init_projection()
155     {
156         init_common();
157         p1.pos = Point(400, 50);
158         p2.pos = Point(450, 450);
159         p3.pos = Point(100, 250);
160         p4.pos = Point(200, 450);
161         O.pos = Point(50, 150);
162 
163         handles.push_back(&p1);
164         handles.push_back(&p2);
165         handles.push_back(&p3);
166         handles.push_back(&p4);
167         handles.push_back(&O);
168     }
169 
draw_projection(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)170     void draw_projection(cairo_t *cr, std::ostringstream *notify,
171                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
172     {
173         draw_common(cr, notify, width, height, save);
174 
175         Line l1(p1.pos, p2.pos);
176         LineSegment ls(p3.pos, p4.pos);
177 
178         Point np = projection(O.pos, l1);
179         LineSegment lsp = projection(ls, l1);
180 
181         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
182         cairo_set_line_width(cr, 0.2);
183         draw_line(cr, l1);
184         draw_segment(cr, ls);
185         cairo_stroke(cr);
186 
187         cairo_set_line_width(cr, 0.3);
188         cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
189         draw_segment(cr, lsp);
190         draw_handle(cr, lsp[0]);
191         draw_handle(cr, lsp[1]);
192         cairo_stroke(cr);
193 
194         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
195         draw_circ(cr, np);
196         cairo_stroke(cr);
197 
198         cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
199         draw_label(cr, p1, "P1");
200         draw_label(cr, p2, "P2");
201         draw_label(cr, ls, "S");
202         draw_label(cr, lsp, "prj(S)");
203         draw_label(cr, O, "P");
204         draw_text(cr, np, "prj(P)");
205 
206         cairo_stroke(cr);
207     }
208 
209 
init_ortho()210     void init_ortho()
211     {
212         init_common();
213         p1.pos = Point(400, 50);
214         p2.pos = Point(450, 450);
215         p3.pos = Point(100, 50);
216         p4.pos = Point(150, 450);
217 
218         handles.push_back(&p1);
219         handles.push_back(&p2);
220         handles.push_back(&p3);
221         handles.push_back(&p4);
222     }
223 
draw_ortho(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)224     void draw_ortho(cairo_t *cr, std::ostringstream *notify,
225                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
226     {
227         draw_common(cr, notify, width, height, save);
228 
229         Line l1(p1.pos, p2.pos);
230         Line l2 = make_orthogonal_line(p3.pos, l1);
231         Line l3 = make_parallel_line(p4.pos, l1);
232 
233         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
234         cairo_set_line_width(cr, 0.3);
235         draw_line(cr, l1);
236         draw_line(cr, l2);
237         draw_line(cr, l3);
238         cairo_stroke(cr);
239 
240         draw_label(cr, p1, "P1");
241         draw_label(cr, p2, "P2");
242         draw_label(cr, p3, "O1");
243         draw_label(cr, p4, "O2");
244 
245         draw_label(cr, l1, "L");
246         draw_label(cr, l2, "L1 _|_ L");
247         draw_label(cr, l3, "L2 // L");
248 
249     }
250 
251 
init_distance()252     void init_distance()
253     {
254         init_common();
255         p1.pos = Point(400, 50);
256         p2.pos = Point(450, 450);
257         p3.pos = Point(100, 250);
258         p4.pos = Point(200, 450);
259         p5.pos = Point(50, 150);
260         p6.pos = Point(480, 60);
261         O.pos = Point(300, 300);
262 
263         handles.push_back(&p1);
264         handles.push_back(&p2);
265         handles.push_back(&p3);
266         handles.push_back(&p4);
267         handles.push_back(&p5);
268         handles.push_back(&p6);
269         handles.push_back(&O);
270     }
271 
draw_distance(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)272     void draw_distance(cairo_t *cr, std::ostringstream *notify,
273                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
274     {
275         draw_common(cr, notify, width, height, save);
276 
277         Line l1(p1.pos, p2.pos);
278         LineSegment ls(p3.pos, p4.pos);
279         Ray r1(p5.pos, p6.pos);
280 
281         Point q1 = l1.pointAt(l1.nearestTime(O.pos));
282         Point q2 = ls.pointAt(ls.nearestTime(O.pos));
283         Point q3 = r1.pointAt(r1.nearestTime(O.pos));
284 
285         double d1 = distance(O.pos, l1);
286         double d2 = distance(O.pos, ls);
287         double d3 = distance(O.pos, r1);
288 
289         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
290         cairo_set_line_width(cr, 0.3);
291         draw_line(cr, l1);
292         draw_segment(cr, ls);
293         draw_ray(cr, r1);
294         cairo_stroke(cr);
295 
296 
297         draw_label(cr, l1, "L");
298         draw_label(cr, ls, "S");
299         draw_label(cr, r1, "R");
300         draw_label(cr, O, "P");
301         cairo_stroke(cr);
302 
303         cairo_set_source_rgba(cr, 0.5, 0.5, 0.8, 1.0);
304         cairo_set_line_width(cr, 0.2);
305         draw_segment(cr, O.pos, q1);
306         draw_segment(cr, O.pos, q2);
307         draw_segment(cr, O.pos, q3);
308         cairo_stroke(cr);
309 
310         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
311         cairo_set_line_width(cr, 0.3);
312         draw_handle(cr, q1);
313         draw_handle(cr, q2);
314         draw_handle(cr, q3);
315         cairo_stroke(cr);
316 
317         *notify << "  distance(P,L) = " << d1 << std::endl;
318         *notify << "  distance(P,S) = " << d2 << std::endl;
319         *notify << "  distance(P,R) = " << d3 << std::endl;
320     }
321 
322 
init_position()323     void init_position()
324     {
325         init_common();
326         p1.pos = Point(400, 50);
327         p2.pos = Point(450, 450);
328         p3.pos = Point(100, 50);
329         p4.pos = Point(150, 450);
330 
331         handles.push_back(&p1);
332         handles.push_back(&p2);
333         handles.push_back(&p3);
334         handles.push_back(&p4);
335     }
336 
draw_position(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)337     void draw_position(cairo_t *cr, std::ostringstream *notify,
338                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
339     {
340         draw_common(cr, notify, width, height, save);
341 
342         Line l1(p1.pos, p2.pos);
343         Line l2(p3.pos, p4.pos);
344 
345         bool b1 = are_same(l1, l2, 0.01);
346         bool b2 = are_parallel(l1, l2, 0.01);
347         bool b3 = are_orthogonal(l1, l2, 0.01);
348 
349         double a = angle_between(l1,l2);
350 
351         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
352         cairo_set_line_width(cr, 0.3);
353         draw_line(cr, l1);
354         draw_line(cr, l2);
355         cairo_stroke(cr);
356 
357         draw_label(cr, l1, "A");
358         draw_label(cr, l2, "B");
359         cairo_stroke(cr);
360 
361         if (b1)
362         {
363             *notify << "  A is coincident with B" << std::endl;
364         }
365         else if (b2)
366         {
367             *notify << "  A is parallel to B" << std::endl;
368         }
369         else if (b3)
370         {
371             *notify << "  A is orthogonal to B" << std::endl;
372         }
373         else
374         {
375             *notify << "  A is incident with B:  angle(A,B) = " << angle_formatter(a) << std::endl;
376         }
377     }
378 
379 
init_seg_bisec()380     void init_seg_bisec()
381     {
382         init_common();
383         p1.pos = Point(100, 50);
384         p2.pos = Point(150, 450);
385 
386         handles.push_back(&p1);
387         handles.push_back(&p2);
388     }
389 
draw_seg_bisec(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)390     void draw_seg_bisec(cairo_t *cr, std::ostringstream *notify,
391                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
392     {
393         draw_common(cr, notify, width, height, save);
394 
395         LineSegment ls(p1.pos, p2.pos);
396         Line l = make_bisector_line(ls);
397         Point M = middle_point(p1.pos, p2.pos);
398 
399         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
400         cairo_set_line_width(cr, 0.3);
401         draw_label(cr, p1, "P");
402         draw_label(cr, p2, "Q");
403         draw_label(cr, M, "M");
404         draw_segment(cr, ls);
405         draw_line(cr, l);
406         cairo_stroke(cr);
407 
408         draw_label(cr, l, "axis");
409         cairo_stroke(cr);
410     }
411 
412 
init_angle_bisec()413     void init_angle_bisec()
414     {
415         init_common();
416         p1.pos = Point(100, 50);
417         p2.pos = Point(150, 450);
418         O.pos = Point(50, 200);
419 
420         handles.push_back(&p1);
421         handles.push_back(&p2);
422         handles.push_back(&O);
423     }
424 
draw_angle_bisec(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)425     void draw_angle_bisec(cairo_t *cr, std::ostringstream *notify,
426                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
427     {
428         draw_common(cr, notify, width, height, save);
429 
430         Ray r1(O.pos, p1.pos);
431         Ray r2(O.pos, p2.pos);
432         Ray rb = make_angle_bisector_ray(r1, r2);
433 
434         double a1 = angle_between(r1,rb);
435         double a2 = angle_between(rb,r2);
436 
437         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
438         cairo_set_line_width(cr, 0.3);
439         draw_label(cr, O, "O");
440         draw_ray(cr, r1);
441         draw_ray(cr, r2);
442         draw_ray(cr, rb);
443         cairo_stroke(cr);
444 
445         draw_label(cr, r1, "R1");
446         draw_label(cr, r2, "R2");
447         draw_label(cr, rb, "bisector ray");
448 
449         *notify << "  angle(R1, bisector ray) = " << angle_formatter(a1)
450                 << "    angle(bisector ray, R2) = " << angle_formatter(a2)
451                 << std::endl;
452     }
453 
454 
init_collinear()455     void init_collinear()
456     {
457         init_common();
458         p1.pos = Point(100, 50);
459         p2.pos = Point(450, 450);
460         p3.pos = Point(400, 50);
461 
462         handles.push_back(&p1);
463         handles.push_back(&p2);
464         handles.push_back(&p3);
465     }
466 
draw_collinear(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)467     void draw_collinear(cairo_t *cr, std::ostringstream *notify,
468                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
469     {
470         draw_common(cr, notify, width, height, save);
471 
472         Point A = p1.pos;
473         Point B = p2.pos;
474         Point C = p3.pos;
475 
476         LineSegment AB(A, B);
477         LineSegment BC(B, C);
478         Line l(A, C);
479 
480         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
481         cairo_set_line_width(cr, 0.3);
482         draw_label(cr, p1, "A");
483         draw_label(cr, p2, "B");
484         draw_label(cr, p3, "C");
485         cairo_stroke(cr);
486 
487 
488         double turn = cross(C, B) - cross(C, A) + cross(B, A);
489         //*notify << "  turn: " << turn << std::endl;
490 
491         bool collinear = are_collinear(A, B, C, 200);
492         if (collinear)
493         {
494             cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
495             cairo_set_line_width(cr, 0.3);
496             draw_line(cr, l);
497             cairo_stroke(cr);
498             *notify << "  A,B,C are collinear!" << std::endl;
499         }
500         else
501         {
502             cairo_set_source_rgba(cr, 0.5, 0.5, 0.8, 1.0);
503             cairo_set_line_width(cr, 0.2);
504             draw_segment(cr, AB);
505             draw_segment(cr, BC);
506             cairo_stroke(cr);
507             if (turn < 0)
508                 *notify << "  A,B,C is a left turn:  " << turn << std::endl;
509             else
510                 *notify << "  A,B,C is a right turn: " << turn << std::endl;
511         }
512 
513     }
514 
515 
init_intersections()516     void init_intersections()
517     {
518         init_common();
519         p1.pos = Point(400, 50);
520         p2.pos = Point(450, 450);
521         p3.pos = Point(100, 250);
522         p4.pos = Point(200, 450);
523         p5.pos = Point(50, 150);
524         p6.pos = Point(480, 60);
525 
526         handles.push_back(&p1);
527         handles.push_back(&p2);
528         handles.push_back(&p3);
529         handles.push_back(&p4);
530         handles.push_back(&p5);
531         handles.push_back(&p6);
532     }
533 
draw_intersections(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)534     void draw_intersections(cairo_t *cr, std::ostringstream *notify,
535                             int width, int height, bool save, std::ostringstream */*timer_stream*/)
536     {
537         draw_common(cr, notify, width, height, save);
538 
539         Line l1(p1.pos, p2.pos);
540         Ray r1(p3.pos, p4.pos);
541         LineSegment s1(p5.pos, p6.pos);
542 
543         std::vector<ShapeIntersection> cl1r1 = l1.intersect(r1);
544         std::vector<ShapeIntersection> cl1s1 = l1.intersect(s1);
545         std::vector<ShapeIntersection> cr1s1 = Line(r1).intersect(s1);
546         filter_ray_intersections(cr1s1, true, false);
547 
548         std::vector<Point> ip;
549 
550         if (!cl1r1.empty()) {
551             ip.push_back(l1.pointAt(cl1r1[0].first));
552         }
553         if (!cl1s1.empty()) {
554             ip.push_back(l1.pointAt(cl1s1[0].first));
555         }
556         if (!cr1s1.empty()) {
557             ip.push_back(r1.pointAt(cr1s1[0].first));
558         }
559 
560         cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.0);
561         cairo_set_line_width(cr, 0.3);
562         draw_line(cr, l1);
563         draw_ray(cr, r1);
564         draw_segment(cr, s1);
565         cairo_stroke(cr);
566 
567 
568         draw_label(cr, l1, "L");
569         draw_label(cr, r1, "R");
570         draw_label(cr, s1, "S");
571         cairo_stroke(cr);
572 
573         std::string label("A");
574         cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
575         cairo_set_line_width(cr, 0.5);
576         for (unsigned int i = 0; i < ip.size(); ++i)
577         {
578             draw_handle(cr, ip[i]);
579             draw_text(cr, ip[i]+op, label.c_str());
580             label[0] += 1;
581         }
582         cairo_stroke(cr);
583 
584     }
585 
586 
init_coefficients()587     void init_coefficients()
588     {
589         init_common();
590         p1.pos = Point(400, 50);
591         p2.pos = Point(450, 450);
592 
593         Line l(p1.pos, p2.pos);
594         std::vector<double> coeff = l.coefficients();
595         sliders.push_back( Slider(-1, 1, 0, coeff[0], "A"));
596         sliders.push_back( Slider(-1, 1, 0, coeff[1], "B"));
597         sliders.push_back( Slider(-500, 500, 0, coeff[2], "C"));
598 
599         handles.push_back(&p1);
600         handles.push_back(&p2);
601         handles.push_back(&(sliders[A_COEFF_SLIDER]));
602         handles.push_back(&(sliders[B_COEFF_SLIDER]));
603         handles.push_back(&(sliders[C_COEFF_SLIDER]));
604     }
605 
draw_coefficients(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)606     void draw_coefficients(cairo_t *cr, std::ostringstream *notify,
607                             int width, int height, bool save, std::ostringstream */*timer_stream*/)
608     {
609         draw_common(cr, notify, width, height, save);
610         init_coefficients_ctrl_geom(cr, notify, width, height);
611 
612         Line l1(p1.pos, p2.pos);
613         std::vector<double> coeff1 = l1.coefficients();
614         Line l2(sliders[A_COEFF_SLIDER].value(),
615                 sliders[B_COEFF_SLIDER].value(),
616                 sliders[C_COEFF_SLIDER].value());
617 
618         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
619         cairo_set_line_width(cr, 0.8);
620         draw_line(cr, l1);
621         cairo_stroke(cr);
622 
623         cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
624         cairo_set_line_width(cr, 0.4);
625         draw_line(cr, l2);
626         cairo_stroke(cr);
627 
628         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
629         draw_label(cr, p1, "P");
630         draw_label(cr, p2, "Q");
631         //draw_label(cr, l1, "L(P,Q)");
632         cairo_stroke(cr);
633 
634         cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0);
635         draw_label(cr, l2, "L(A, B, C)");
636         cairo_stroke(cr);
637 
638         *notify << "  L(P,Q):  a = " << coeff1[0]
639                 << ", b = " << coeff1[1]
640                 << ", c = " << coeff1[2] << std::endl;
641 
642     }
643 
init_segment_inside()644     void init_segment_inside()
645     {
646         init_common();
647         p1.pos = Point(200, 300);
648         p2.pos = Point(400, 200);
649         p3.pos = Point(250, 200);
650         p4.pos = Point(350, 300);
651 
652         handles.push_back(&p1);
653         handles.push_back(&p2);
654         handles.push_back(&p3);
655         handles.push_back(&p4);
656     }
657 
draw_segment_inside(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)658     void draw_segment_inside(cairo_t *cr, std::ostringstream *notify,
659                              int width, int height, bool save, std::ostringstream */*timer_stream*/)
660     {
661         draw_common(cr, notify, width, height, save);
662         Line l(p1.pos, p2.pos);
663         Rect r(p3.pos, p4.pos);
664 
665         cairo_set_source_rgba(cr, 0, 0, 0, 1);
666         cairo_set_line_width(cr, 0.5);
667         draw_line(cr, l);
668         cairo_stroke(cr);
669 
670         cairo_set_source_rgba(cr, 0, 0, 1, 1);
671         cairo_rectangle(cr, r);
672         cairo_stroke(cr);
673 
674         std::optional<LineSegment> seg = l.clip(r);
675         if (seg) {
676             cairo_set_source_rgba(cr, 1, 0, 0, 1);
677             draw_line_seg(cr, seg->initialPoint(), seg->finalPoint());
678             cairo_stroke(cr);
679         }
680     }
681 
682 
init_common_ctrl_geom(cairo_t *,int,int,std::ostringstream *)683     void init_common_ctrl_geom(cairo_t* /*cr*/, int /*width*/, int /*height*/, std::ostringstream* /*notify*/)
684     {
685         if ( set_common_control_geometry )
686         {
687             set_common_control_geometry = false;
688         }
689     }
690 
init_create_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)691     void init_create_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
692     {
693         if ( set_control_geometry )
694         {
695             set_control_geometry = false;
696 
697             sliders[ANGLE_SLIDER].geometry(Point(50, height - 50), 180);
698         }
699     }
700 
init_coefficients_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)701     void init_coefficients_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
702     {
703         if ( set_control_geometry )
704         {
705             set_control_geometry = false;
706 
707             sliders[A_COEFF_SLIDER].geometry(Point(50, height - 160), 400);
708             sliders[B_COEFF_SLIDER].geometry(Point(50, height - 110), 400);
709             sliders[C_COEFF_SLIDER].geometry(Point(50, height - 60), 400);
710         }
711     }
712 
713 
draw_segment(cairo_t * cr,Point const & p1,Point const & p2)714     void draw_segment(cairo_t* cr, Point const& p1, Point const&  p2)
715     {
716         cairo_move_to(cr, p1);
717         cairo_line_to(cr, p2);
718     }
719 
draw_segment(cairo_t * cr,Point const & p1,double angle,double length)720     void draw_segment(cairo_t* cr, Point const& p1, double angle, double length)
721     {
722         Point p2;
723         p2[X] = length * std::cos(angle);
724         p2[Y] = length * std::sin(angle);
725         p2 += p1;
726         draw_segment(cr, p1, p2);
727     }
728 
draw_segment(cairo_t * cr,LineSegment const & ls)729     void draw_segment(cairo_t* cr, LineSegment const& ls)
730     {
731         draw_segment(cr, ls[0], ls[1]);
732     }
733 
draw_ray(cairo_t * cr,Ray const & r)734     void draw_ray(cairo_t* cr, Ray const& r)
735     {
736         double angle = r.angle();
737         draw_segment(cr, r.origin(), angle, m_length);
738     }
739 
draw_line(cairo_t * cr,Line const & l)740     void draw_line(cairo_t* cr, Line const& l)
741     {
742         double angle = l.angle();
743         draw_segment(cr, l.origin(), angle, m_length);
744         draw_segment(cr, l.origin(), angle, -m_length);
745     }
746 
draw_label(cairo_t * cr,PointHandle const & ph,const char * label)747     void draw_label(cairo_t* cr, PointHandle const& ph, const char* label)
748     {
749         draw_text(cr, ph.pos+op, label);
750     }
751 
draw_label(cairo_t * cr,Line const & l,const char * label)752     void draw_label(cairo_t* cr, Line const& l, const char* label)
753     {
754         draw_text(cr, projection(Point(m_width/2-30, m_height/2-30), l)+op, label);
755     }
756 
draw_label(cairo_t * cr,LineSegment const & ls,const char * label)757     void draw_label(cairo_t* cr, LineSegment const& ls, const char* label)
758     {
759         draw_text(cr, middle_point(ls[0], ls[1])+op, label);
760     }
761 
draw_label(cairo_t * cr,Ray const & r,const char * label)762     void draw_label(cairo_t* cr, Ray const& r, const char* label)
763     {
764         Point prj = r.pointAt(r.nearestTime(Point(m_width/2-30, m_height/2-30)));
765         if (L2(r.origin() - prj) < 100)
766         {
767             prj = r.origin() + 100*r.vector();
768         }
769         draw_text(cr, prj+op, label);
770     }
771 
init_menu()772     void init_menu()
773     {
774         handles.clear();
775         sliders.clear();
776         toggles.clear();
777     }
778 
draw_menu(cairo_t *,std::ostringstream * notify,int,int,bool,std::ostringstream *)779     void draw_menu( cairo_t * /*cr*/, std::ostringstream *notify,
780                     int /*width*/, int /*height*/, bool /*save*/,
781                     std::ostringstream */*timer_stream*/ )
782     {
783         *notify << std::endl;
784         for (int i = SHOW_MENU; i < TOTAL_ITEMS; ++i)
785         {
786             *notify << "   " << keys[i] << " -  " <<  menu_items[i] << std::endl;
787         }
788     }
789 
key_hit(GdkEventKey * e)790     void key_hit(GdkEventKey *e)
791     {
792         char choice = std::toupper(e->keyval);
793         switch ( choice )
794         {
795             case 'A':
796                 init_menu();
797                 draw_f = &LineToy::draw_menu;
798                 break;
799             case 'B':
800                 init_create();
801                 draw_f = &LineToy::draw_create;
802                 break;
803             case 'C':
804                 init_projection();
805                 draw_f = &LineToy::draw_projection;
806                 break;
807             case 'D':
808                 init_ortho();
809                 draw_f = &LineToy::draw_ortho;
810                 break;
811             case 'E':
812                 init_distance();
813                 draw_f = &LineToy::draw_distance;
814                 break;
815             case 'F':
816                 init_position();
817                 draw_f = &LineToy::draw_position;
818                 break;
819             case 'G':
820                 init_seg_bisec();
821                 draw_f = &LineToy::draw_seg_bisec;
822                 break;
823             case 'H':
824                 init_angle_bisec();
825                 draw_f = &LineToy::draw_angle_bisec;
826                 break;
827             case 'I':
828                 init_collinear();
829                 draw_f = &LineToy::draw_collinear;
830                 break;
831             case 'J':
832                 init_intersections();
833                 draw_f = &LineToy::draw_intersections;
834                 break;
835             case 'K':
836                 init_coefficients();
837                 draw_f = &LineToy::draw_coefficients;
838                 break;
839             case 'L':
840                 init_segment_inside();
841                 draw_f = &LineToy::draw_segment_inside;
842         }
843         redraw();
844     }
845 
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)846     virtual void draw( cairo_t *cr, std::ostringstream *notify,
847                        int width, int height, bool save, std::ostringstream *timer_stream)
848     {
849         m_width = width;
850         m_height = height;
851         m_length = (m_width > m_height) ? m_width : m_height;
852         m_length *= 2;
853         (this->*draw_f)(cr, notify, width, height, save, timer_stream);
854         Toy::draw(cr, notify, width, height, save, timer_stream);
855     }
856 
857   public:
LineToy()858     LineToy()
859     {
860         op = Point(5,5);
861     }
862 
863   private:
864     typedef void (LineToy::* draw_func_t) (cairo_t*, std::ostringstream*, int, int, bool, std::ostringstream*);
865     draw_func_t draw_f;
866     bool set_common_control_geometry;
867     bool set_control_geometry;
868     PointHandle p1, p2, p3, p4, p5, p6, O;
869     std::vector<Toggle> toggles;
870     std::vector<Slider> sliders;
871     Point op;
872     double m_width, m_height, m_length;
873 
874 }; // end class LineToy
875 
876 
877 const char* LineToy::menu_items[] =
878 {
879     "show this menu",
880     "line generation",
881     "projection on a line",
882     "make orthogonal/parallel",
883     "distance",
884     "position",
885     "segment bisector",
886     "angle bisector",
887     "collinear",
888     "intersection",
889     "coefficients",
890     "segment inside"
891 };
892 
893 const char LineToy::keys[] =
894 {
895      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'
896 };
897 
898 
899 
main(int argc,char ** argv)900 int main(int argc, char **argv)
901 {
902     init( argc, argv, new LineToy());
903     return 0;
904 }
905 
906 
907 /*
908   Local Variables:
909   mode:c++
910   c-file-style:"stroustrup"
911   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
912   indent-tabs-mode:nil
913   fill-column:99
914   End:
915 */
916 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
917