1 #include <2geom/d2.h>
2 #include <2geom/sbasis.h>
3 #include <2geom/sbasis-2d.h>
4 #include <2geom/bezier-to-sbasis.h>
5 #include <2geom/sbasis-geometric.h>
6 
7 #include <toys/path-cairo.h>
8 #include <toys/toy-framework-2.h>
9 
10 #include <2geom/angle.h>
11 
12 using std::vector;
13 using namespace Geom;
14 using namespace std;
15 
16 // Author: Johan Engelen, 2009
17 //
18 // Shows how to find the locations on a path where the derivative is parallel to a certain vector.
19 //-----------------------------------------------
20 
21 
angle_formatter(double angle)22 std::string angle_formatter(double angle)
23 {
24     return default_formatter(decimal_round(deg_from_rad(angle),2));
25 }
26 
27 
28 
29 class FindDerivatives : public Toy
30 {
31     enum menu_item_t
32     {
33         SHOW_MENU = 0,
34         TEST_CREATE,
35         TEST_PROJECTION,
36         TEST_ORTHO,
37         TEST_DISTANCE,
38         TEST_POSITION,
39         TEST_SEG_BISEC,
40         TEST_ANGLE_BISEC,
41         TEST_COLLINEAR,
42         TEST_INTERSECTIONS,
43         TEST_COEFFICIENTS,
44         TOTAL_ITEMS // this one must be the last item
45     };
46 
47     enum handle_label_t
48     {
49     };
50 
51     enum toggle_label_t
52     {
53     };
54 
55     enum slider_label_t
56     {
57         END_SHARED_SLIDERS = 0,
58         ANGLE_SLIDER = END_SHARED_SLIDERS,
59         A_COEFF_SLIDER = END_SHARED_SLIDERS,
60         B_COEFF_SLIDER,
61         C_COEFF_SLIDER
62     };
63 
64     static const char* menu_items[TOTAL_ITEMS];
65     static const char keys[TOTAL_ITEMS];
66 
67     PointSetHandle curve_handle;
68     PointHandle sample_point;
69 
first_time(int,char **)70     void first_time(int /*argc*/, char** /*argv*/) override
71     {
72         draw_f = &FindDerivatives::draw_menu;
73     }
74 
init_common()75     void init_common()
76     {
77         set_common_control_geometry = true;
78         set_control_geometry = true;
79 
80         sliders.clear();
81         toggles.clear();
82         handles.clear();
83     }
84 
85 
draw_common(cairo_t * cr,std::ostringstream * notify,int width,int height,bool)86     virtual void draw_common( cairo_t *cr, std::ostringstream *notify,
87                               int width, int height, bool /*save*/ )
88     {
89         init_common_ctrl_geom(cr, width, height, notify);
90     }
91 
92 
init_create()93     void init_create()
94     {
95         init_common();
96 
97         p1.pos = Point(400, 50);
98         p2.pos = Point(450, 450);
99         O.pos = Point(50, 400);
100 
101         sliders.emplace_back(0, 2*M_PI, 0, 0, "angle");
102         sliders[ANGLE_SLIDER].formatter(&angle_formatter);
103 
104         handles.push_back(&p1);
105         handles.push_back(&p2);
106         handles.push_back(&O);
107         handles.push_back(&(sliders[ANGLE_SLIDER]));
108     }
109 
draw_create(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)110     void draw_create(cairo_t *cr, std::ostringstream *notify,
111                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
112     {
113         draw_common(cr, notify, width, height, save);
114         init_create_ctrl_geom(cr, notify, width, height);
115 
116         Line l1(p1.pos, p2.pos);
117         Line l2(O.pos, sliders[ANGLE_SLIDER].value());
118 
119         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
120         cairo_set_line_width(cr, 0.3);
121         draw_line(cr, l1);
122         draw_line(cr, l2);
123         cairo_stroke(cr);
124 
125         draw_label(cr, p1, "P1");
126         draw_label(cr, p2, "P2");
127         draw_label(cr, O, "O");
128         draw_label(cr, l1, "L(P1,P2)");
129         draw_label(cr, l2, "L(O,angle)");
130     }
131 
132 
init_projection()133     void init_projection()
134     {
135         init_common();
136         p1.pos = Point(400, 50);
137         p2.pos = Point(450, 450);
138         p3.pos = Point(100, 250);
139         p4.pos = Point(200, 450);
140         O.pos = Point(50, 150);
141 
142         handles.push_back(&p1);
143         handles.push_back(&p2);
144         handles.push_back(&p3);
145         handles.push_back(&p4);
146         handles.push_back(&O);
147     }
148 
draw_projection(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)149     void draw_projection(cairo_t *cr, std::ostringstream *notify,
150                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
151     {
152         draw_common(cr, notify, width, height, save);
153 
154         Line l1(p1.pos, p2.pos);
155         LineSegment ls(p3.pos, p4.pos);
156 
157         Point np = projection(O.pos, l1);
158         LineSegment lsp = projection(ls, l1);
159 
160         cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
161         cairo_set_line_width(cr, 0.2);
162         draw_line(cr, l1);
163         draw_segment(cr, ls);
164         cairo_stroke(cr);
165 
166         cairo_set_line_width(cr, 0.3);
167         cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
168         draw_segment(cr, lsp);
169         draw_handle(cr, lsp[0]);
170         draw_handle(cr, lsp[1]);
171         cairo_stroke(cr);
172 
173         cairo_set_source_rgba(cr, 0.8, 0.0, 0.0, 1.0);
174         draw_circ(cr, np);
175         cairo_stroke(cr);
176 
177         cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
178         draw_label(cr, p1, "P1");
179         draw_label(cr, p2, "P2");
180         draw_label(cr, ls, "S");
181         draw_label(cr, lsp, "prj(S)");
182         draw_label(cr, O, "P");
183         draw_text(cr, np, "prj(P)");
184 
185         cairo_stroke(cr);
186     }
187 
init_derivative()188     void init_derivative() {
189         init_common();
190         handles.push_back(&curve_handle);
191         handles.push_back(&sample_point);
192         for(unsigned i = 0; i < 4; i++)
193             curve_handle.push_back(150+uniform()*300,150+uniform()*300);
194         sample_point.pos = Geom::Point(250,300);
195     }
196 
draw_derivative(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)197     void draw_derivative(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) {
198 
199         D2<SBasis> B = curve_handle.asBezier();
200 
201         cairo_set_line_width (cr, 1);
202         cairo_set_source_rgba (cr, 0., 0.5, 0., 1);
203         cairo_d2_sb(cr, B);
204         cairo_stroke(cr);
205 
206         Point vector = sample_point.pos - Geom::Point(400,400);
207         cairo_move_to(cr, Geom::Point(400,400));
208         cairo_line_to(cr, sample_point.pos);
209         cairo_set_source_rgba (cr, 0., 0., 0.5, 0.8);
210         cairo_stroke(cr);
211 
212         // How to find location of points with certain derivative along a path:
213         D2<SBasis> deriv = derivative(B);
214         SBasis dotp = dot(deriv, rot90(vector));
215         std::vector<double> sol = roots(dotp);
216         for (double i : sol) {
217             draw_handle(cr, B.valueAt(i));         // the solutions are in vector 'sol'
218         }
219 
220         cairo_set_source_rgba (cr, 0.5, 0.2, 0., 0.8);
221         cairo_stroke(cr);
222         Toy::draw(cr, notify, width, height, save,timer_stream);
223     }
224 
init_find_tangents()225     void init_find_tangents() {
226         init_common();
227         handles.push_back(&curve_handle);
228         handles.push_back(&sample_point);
229 
230         toggles.emplace_back(" tangent / normal ", false);
231         handles.push_back(&(toggles[0]));
232         for(unsigned i = 0; i < 4; i++)
233             curve_handle.push_back(150+uniform()*300,150+uniform()*300);
234         sample_point.pos = Geom::Point(250,300);
235         Point toggle_sp( 30, 30);
236         toggles[0].bounds = Rect( toggle_sp, toggle_sp + Point(200,25) );
237     }
238 
draw_find_tangents(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)239     void draw_find_tangents(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) {
240 
241         D2<SBasis> B = curve_handle.asBezier();
242 
243         cairo_set_line_width (cr, 1);
244         cairo_set_source_rgba (cr, 0., 0.5, 0., 1);
245         cairo_d2_sb(cr, B);
246         cairo_stroke(cr);
247 
248         std::vector<double> sol = toggles[0].on ?
249             find_tangents(sample_point.pos, B)
250             : find_normals(sample_point.pos, B);
251         for (double i : sol) {
252             draw_handle(cr, B.valueAt(i));         // the solutions are in vector 'sol'
253             draw_segment(cr, B.valueAt(i), sample_point.pos);
254         }
255 
256         cairo_set_source_rgba (cr, 0.5, 0.2, 0., 0.8);
257         cairo_stroke(cr);
258         Toy::draw(cr, notify, width, height, save,timer_stream);
259     }
260 
init_ortho()261     void init_ortho()
262     {
263         init_common();
264         p1.pos = Point(400, 50);
265         p2.pos = Point(450, 450);
266         p3.pos = Point(100, 50);
267         p4.pos = Point(150, 450);
268 
269         handles.push_back(&p1);
270         handles.push_back(&p2);
271         handles.push_back(&p3);
272         handles.push_back(&p4);
273     }
274 
draw_ortho(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream *)275     void draw_ortho(cairo_t *cr, std::ostringstream *notify,
276                       int width, int height, bool save, std::ostringstream */*timer_stream*/)
277     {
278         draw_common(cr, notify, width, height, save);
279 
280         Line l1(p1.pos, p2.pos);
281         Line l2 = make_orthogonal_line(p3.pos, l1);
282         Line l3 = make_parallel_line(p4.pos, l1);
283 
284         cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
285         cairo_set_line_width(cr, 0.3);
286         draw_line(cr, l1);
287         draw_line(cr, l2);
288         draw_line(cr, l3);
289         cairo_stroke(cr);
290 
291         draw_label(cr, p1, "P1");
292         draw_label(cr, p2, "P2");
293         draw_label(cr, p3, "O1");
294         draw_label(cr, p4, "O2");
295 
296         draw_label(cr, l1, "L");
297         draw_label(cr, l2, "L1 _|_ L");
298         draw_label(cr, l3, "L2 // L");
299 
300     }
301 
302 
303 
304 
init_common_ctrl_geom(cairo_t *,int,int,std::ostringstream *)305     void init_common_ctrl_geom(cairo_t* /*cr*/, int /*width*/, int /*height*/, std::ostringstream* /*notify*/)
306     {
307         if ( set_common_control_geometry )
308         {
309             set_common_control_geometry = false;
310         }
311     }
312 
init_create_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)313     void init_create_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
314     {
315         if ( set_control_geometry )
316         {
317             set_control_geometry = false;
318 
319             sliders[ANGLE_SLIDER].geometry(Point(50, height - 50), 180);
320         }
321     }
322 
init_coefficients_ctrl_geom(cairo_t *,std::ostringstream *,int,int height)323     void init_coefficients_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height)
324     {
325         if ( set_control_geometry )
326         {
327             set_control_geometry = false;
328 
329             sliders[A_COEFF_SLIDER].geometry(Point(50, height - 160), 400);
330             sliders[B_COEFF_SLIDER].geometry(Point(50, height - 110), 400);
331             sliders[C_COEFF_SLIDER].geometry(Point(50, height - 60), 400);
332         }
333     }
334 
335 
draw_segment(cairo_t * cr,Point const & p1,Point const & p2)336     void draw_segment(cairo_t* cr, Point const& p1, Point const&  p2)
337     {
338         cairo_move_to(cr, p1);
339         cairo_line_to(cr, p2);
340     }
341 
draw_segment(cairo_t * cr,Point const & p1,double angle,double length)342     void draw_segment(cairo_t* cr, Point const& p1, double angle, double length)
343     {
344         Point p2;
345         p2[X] = length * std::cos(angle);
346         p2[Y] = length * std::sin(angle);
347         p2 += p1;
348         draw_segment(cr, p1, p2);
349     }
350 
draw_segment(cairo_t * cr,LineSegment const & ls)351     void draw_segment(cairo_t* cr, LineSegment const& ls)
352     {
353         draw_segment(cr, ls[0], ls[1]);
354     }
355 
draw_ray(cairo_t * cr,Ray const & r)356     void draw_ray(cairo_t* cr, Ray const& r)
357     {
358         double angle = r.angle();
359         draw_segment(cr, r.origin(), angle, m_length);
360     }
361 
draw_line(cairo_t * cr,Line const & l)362     void draw_line(cairo_t* cr, Line const& l)
363     {
364         double angle = l.angle();
365         draw_segment(cr, l.origin(), angle, m_length);
366         draw_segment(cr, l.origin(), angle, -m_length);
367     }
368 
draw_label(cairo_t * cr,PointHandle const & ph,const char * label)369     void draw_label(cairo_t* cr, PointHandle const& ph, const char* label)
370     {
371         draw_text(cr, ph.pos+op, label);
372     }
373 
draw_label(cairo_t * cr,Line const & l,const char * label)374     void draw_label(cairo_t* cr, Line const& l, const char* label)
375     {
376         draw_text(cr, projection(Point(m_width/2-30, m_height/2-30), l)+op, label);
377     }
378 
draw_label(cairo_t * cr,LineSegment const & ls,const char * label)379     void draw_label(cairo_t* cr, LineSegment const& ls, const char* label)
380     {
381         draw_text(cr, middle_point(ls[0], ls[1])+op, label);
382     }
383 
draw_label(cairo_t * cr,Ray const & r,const char * label)384     void draw_label(cairo_t* cr, Ray const& r, const char* label)
385     {
386         Point prj = r.pointAt(r.nearestTime(Point(m_width/2-30, m_height/2-30)));
387         if (L2(r.origin() - prj) < 100)
388         {
389             prj = r.origin() + 100*r.vector();
390         }
391         draw_text(cr, prj+op, label);
392     }
393 
init_menu()394     void init_menu()
395     {
396         handles.clear();
397         sliders.clear();
398         toggles.clear();
399     }
400 
draw_menu(cairo_t *,std::ostringstream * notify,int,int,bool,std::ostringstream *)401     void draw_menu( cairo_t * /*cr*/, std::ostringstream *notify,
402                     int /*width*/, int /*height*/, bool /*save*/,
403                     std::ostringstream */*timer_stream*/ )
404     {
405         *notify << std::endl;
406         for (int i = SHOW_MENU; i < TOTAL_ITEMS; ++i)
407         {
408             *notify << "   " << keys[i] << " -  " <<  menu_items[i] << std::endl;
409         }
410     }
411 
key_hit(GdkEventKey * e)412     void key_hit(GdkEventKey *e) override
413     {
414         char choice = std::toupper(e->keyval);
415         switch ( choice )
416         {
417             case 'A':
418                 init_menu();
419                 draw_f = &FindDerivatives::draw_menu;
420                 break;
421             case 'B':
422                 init_derivative();
423                 draw_f = &FindDerivatives::draw_derivative;
424                 break;
425             case 'C':
426                 init_find_tangents();
427                 draw_f = &FindDerivatives::draw_find_tangents;
428                 break;
429             case 'D':
430                 init_ortho();
431                 draw_f = &FindDerivatives::draw_ortho;
432                 break;
433         }
434         redraw();
435     }
436 
draw(cairo_t * cr,std::ostringstream * notify,int width,int height,bool save,std::ostringstream * timer_stream)437     void draw( cairo_t *cr, std::ostringstream *notify,
438                        int width, int height, bool save, std::ostringstream *timer_stream) override
439     {
440         m_width = width;
441         m_height = height;
442         m_length = (m_width > m_height) ? m_width : m_height;
443         m_length *= 2;
444         (this->*draw_f)(cr, notify, width, height, save, timer_stream);
445         Toy::draw(cr, notify, width, height, save, timer_stream);
446     }
447 
448   public:
FindDerivatives()449     FindDerivatives()
450     {
451         op = Point(5,5);
452     }
453 
454   private:
455     typedef void (FindDerivatives::* draw_func_t) (cairo_t*, std::ostringstream*, int, int, bool, std::ostringstream*);
456     draw_func_t draw_f;
457     bool set_common_control_geometry;
458     bool set_control_geometry;
459     PointHandle p1, p2, p3, p4, p5, p6, O;
460     std::vector<Toggle> toggles;
461     std::vector<Slider> sliders;
462     Point op;
463     double m_width, m_height, m_length;
464 
465 }; // end class FindDerivatives
466 
467 
468 const char* FindDerivatives::menu_items[] =
469 {
470     "show this menu",
471     "derivative matching on curve",
472     "find normals",
473     "find tangents"
474 };
475 
476 const char FindDerivatives::keys[] =
477 {
478      'A', 'B', 'C', 'D'
479 };
480 
481 
482 
main(int argc,char ** argv)483 int main(int argc, char **argv)
484 {
485     init( argc, argv, new FindDerivatives());
486     return 0;
487 }
488 
489 /*
490   Local Variables:
491   mode:c++
492   c-file-style:"stroustrup"
493   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
494   indent-tabs-mode:nil
495   fill-column:99
496   End:
497 */
498 //vim:expandtab:shiftwidth = 4:tabstop = 8:softtabstop = 4:encoding = utf-8:textwidth = 99 :
499 
500 
501