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