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