a4ce51f9e55f215e0f682768e5eec4417c2a3a15
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / sketcher / sketcher.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include<stdio.h>
22 #include<stdint.h>
23 #include<math.h>
24 #include<string.h>
25
26 #include "arraylist.h"
27 #include "bccmodels.h"
28 #include "bccolors.h"
29 #include "clip.h"
30 #include "edlsession.h"
31 #include "filexml.h"
32 #include "sketcher.h"
33 #include "sketcherwindow.h"
34 #include "language.h"
35 #include "vframe.h"
36
37 void SketcherPoint::init(int id, int x, int y)
38 {
39         this->id = id;
40         this->x = x;    this->y = y;
41 }
42 SketcherPoint::SketcherPoint(int id)
43 {
44         init(id, 0, 0);
45 }
46 SketcherPoint::SketcherPoint(int id, int x, int y)
47 {
48         init(id, x, y);
49 }
50 SketcherPoint::~SketcherPoint()
51 {
52 }
53 SketcherPoint::SketcherPoint(SketcherPoint &pt)
54 {
55         copy_from(pt);
56 }
57 int SketcherPoint::equivalent(SketcherPoint &that)
58 {
59         return this->id == that.id &&
60                 this->x == that.x &&
61                 this->y == that.y ? 1 : 0;
62 }
63 void SketcherPoint::copy_from(SketcherPoint &that)
64 {
65         this->id = that.id;
66         this->x = that.x;    this->y = that.y;
67 }
68 void SketcherPoint::save_data(FileXML &output)
69 {
70         char point[BCSTRLEN];
71         sprintf(point,"/POINT_%d",id);
72         output.tag.set_title(point+1);
73         output.tag.set_property("X", x);
74         output.tag.set_property("Y", y);
75         output.append_tag();
76         output.tag.set_title(point+0);
77         output.append_tag();
78         output.append_newline();
79 }
80 void SketcherPoint::read_data(FileXML &input)
81 {
82         id = atoi(input.tag.get_title() + 6);
83         x = input.tag.get_property("X", 0.f);
84         y = input.tag.get_property("Y", 0.f);
85 }
86
87 void SketcherCurve::init(int id, int ty, int radius, int pen, int color)
88 {
89         this->id = id;
90         this->ty = ty;
91         this->radius = radius;
92         this->pen = pen;
93         this->color = color;
94 }
95 SketcherCurve::SketcherCurve(int id)
96 {
97         init(id, 0, 1, 0, BLACK);
98 }
99 SketcherCurve::SketcherCurve(int id, int ty,  int radius,int pen, int color)
100 {
101         init(id, ty, radius, pen, color);
102 }
103 SketcherCurve::~SketcherCurve()
104 {
105 }
106 SketcherCurve::SketcherCurve(SketcherCurve &cv)
107 {
108         copy_from(cv);
109 }
110 int SketcherCurve::equivalent(SketcherCurve &that)
111 {
112         if( this->id != that.id ) return 0;
113         if( this->ty != that.ty ) return 0;
114         if( this->radius != that.radius ) return 0;
115         if( this->pen != that.pen ) return 0;
116         if( this->color != that.color ) return 0;
117         int n = this->points.size();
118         if( n != that.points.size() ) return 0;
119         for( int i=0; i<n; ++i ) {
120                 if( !points[i]->equivalent(*that.points[i]) ) return 0;
121         }
122         return 1;
123 }
124 void SketcherCurve::copy_from(SketcherCurve &that)
125 {
126         this->id = that.id;
127         this->ty = that.ty;
128         this->radius = that.radius;
129         this->pen = that.pen;
130         this->color = that.color;
131         int m = points.size(), n = that.points.size();
132         while( m > n ) points.remove_object_number(--m);
133         while( m < n ) { points.append(new SketcherPoint());  ++m; }
134         for( int i=0; i<n; ++i ) points[i]->copy_from(*that.points[i]);
135 }
136 void SketcherCurve::save_data(FileXML &output)
137 {
138         this->ty = ty;
139         this->pen = pen;  this->color = color;
140         char curve[BCSTRLEN];
141         sprintf(curve,"/CURVE_%d",id);
142         output.tag.set_title(curve+1);
143         output.tag.set_property("TYPE", ty);
144         output.tag.set_property("RADIUS", radius);
145         output.tag.set_property("PEN", pen);
146         output.tag.set_property("COLOR", color);
147         output.append_tag();
148         output.append_newline();
149         for( int i=0,n=points.size(); i<n; ++i )
150                 points[i]->save_data(output);
151         output.tag.set_title(curve+0);
152         output.append_tag();
153         output.append_newline();
154 }
155 void SketcherCurve::read_data(FileXML &input)
156 {
157         id = atoi(input.tag.get_title() + 6);
158         ty = input.tag.get_property("TYPE", 0);
159         radius = input.tag.get_property("RADIUS", 1.);
160         pen = input.tag.get_property("PEN", 0);
161         color = input.tag.get_property("COLOR", BLACK);
162 }
163
164 int Sketcher::new_curve(int ty, int radius, int pen, int color)
165 {
166         SketcherCurves &curves = config.curves;
167         int k = curves.size(), id = 1;
168         for( int i=k; --i>=0; ) {
169                 int n = config.curves[i]->id;
170                 if( n >= id ) id = n + 1;
171         }
172         SketcherCurve *cv = new SketcherCurve(id, ty, radius, pen, color);
173         curves.append(cv);
174         config.cv_selected = k;
175         return k;
176 }
177
178 int Sketcher::new_curve()
179 {
180         return new_curve(0, 1, 0, BLACK);
181 }
182
183 int Sketcher::new_point(SketcherCurve *cv, int x, int y)
184 {
185         int k = cv->points.size(), id = 1;
186         for( int i=k; --i>=0; ) {
187                 int n = cv->points[i]->id;
188                 if( n >= id ) id = n + 1;
189         }
190         SketcherPoint *pt = new SketcherPoint(id, x, y);
191         cv->points.append(pt);
192         return k;
193 }
194
195 int Sketcher::new_point()
196 {
197         int ci = config.cv_selected;
198         if( ci < 0 || ci >= config.curves.size() )
199                 return -1;
200         SketcherCurve *cv = config.curves[ci];
201         EDLSession *session = get_edlsession();
202         int x = !session ? 0.f : session->output_w / 2.f;
203         int y = !session ? 0.f : session->output_h / 2.f;
204         return new_point(cv, x, y);
205 }
206
207 REGISTER_PLUGIN(Sketcher)
208
209 SketcherConfig::SketcherConfig()
210 {
211         drag = 1;
212         cv_selected = 0;
213         pt_selected = 0;
214 }
215 SketcherConfig::~SketcherConfig()
216 {
217 }
218
219 int SketcherConfig::equivalent(SketcherConfig &that)
220 {
221         if( this->drag != that.drag ) return 0;
222         if( this->cv_selected != that.cv_selected ) return 0;
223         if( this->pt_selected != that.pt_selected ) return 0;
224         if( this->curves.size() != that.curves.size() ) return 0;
225         for( int i=0, n=curves.size(); i<n; ++i ) {
226                 if( !curves[i]->equivalent(*that.curves[i]) ) return 0;
227         }
228         return 1;
229 }
230
231 void SketcherConfig::copy_from(SketcherConfig &that)
232 {
233         this->drag = that.drag;
234         this->cv_selected = that.cv_selected;
235         this->pt_selected = that.pt_selected;
236         int m = curves.size(), n = that.curves.size();
237         while( m > n ) curves.remove_object_number(--m);
238         while( m < n ) { curves.append(new SketcherCurve());  ++m; }
239         for( int i=0; i<n; ++i ) curves[i]->copy_from(*that.curves[i]);
240 }
241
242 void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next,
243                 long prev_frame, long next_frame, long current_frame)
244 {
245         this->cv_selected = prev.cv_selected;
246         this->pt_selected = prev.pt_selected;
247         this->drag = prev.drag;
248
249         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
250         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
251
252         curves.remove_all_objects();
253         int prev_cv_sz = prev.curves.size();
254         int next_cv_sz = next.curves.size();
255         for( int i=0; i<prev_cv_sz; ++i ) {
256                 SketcherCurve *pcv = prev.curves[i], *ncv = 0;
257                 SketcherCurve *cv = curves.append(new SketcherCurve());
258                 int k = next_cv_sz;  // associated by id in next
259                 while( --k >= 0 && pcv->id != (ncv=next.curves[k])->id );
260                 if( k >= 0 ) {
261                         cv->id = pcv->id;
262                         cv->ty = pcv->ty;
263                         cv->radius = pcv->radius;
264                         cv->pen = pcv->pen;
265                         cv->color = pcv->color;
266                         int prev_pt_sz = pcv->points.size(), next_pt_sz = ncv->points.size();
267                         for( int j=0; j<prev_pt_sz; ++j ) {
268                                 SketcherPoint &pt = *pcv->points[j], *nt = 0;
269                                 k = next_pt_sz;  // associated by id in next
270                                 while( --k >= 0 && pt.id != (nt=ncv->points[k])->id );
271                                 int x = pt.x, y = pt.y;
272                                 if( k >= 0 ) {
273                                         x = x * prev_scale + nt->x * next_scale;
274                                         y = y * prev_scale + nt->y * next_scale;
275                                 }
276                                 cv->points.append(new SketcherPoint(pt.id, x, y));
277                         }
278                 }
279                 else
280                         cv->copy_from(*pcv);
281         }
282 }
283
284 void SketcherConfig::limits()
285 {
286 }
287
288
289 Sketcher::Sketcher(PluginServer *server)
290  : PluginVClient(server)
291 {
292 }
293
294 Sketcher::~Sketcher()
295 {
296 }
297
298 const char* Sketcher::plugin_title() { return N_("Sketcher"); }
299 int Sketcher::is_realtime() { return 1; }
300
301 NEW_WINDOW_MACRO(Sketcher, SketcherWindow);
302 LOAD_CONFIGURATION_MACRO(Sketcher, SketcherConfig)
303
304 void Sketcher::save_data(KeyFrame *keyframe)
305 {
306         FileXML output;
307 // cause data to be stored directly in text
308         output.set_shared_output(keyframe->xbuf);
309
310         output.tag.set_title("SKETCHER");
311         output.tag.set_property("DRAG", config.drag);
312         output.tag.set_property("CURVE_SELECTED", config.cv_selected);
313         output.tag.set_property("POINT_SELECTED", config.pt_selected);
314         output.append_tag();
315         output.append_newline();
316         for( int i=0,n=config.curves.size(); i<n; ++i ) {
317                 config.curves[i]->save_data(output);
318         }
319         output.tag.set_title("/SKETCHER");
320         output.append_tag();
321         output.append_newline();
322         output.terminate_string();
323 }
324
325 void Sketcher::read_data(KeyFrame *keyframe)
326 {
327         FileXML input;
328         input.set_shared_input(keyframe->xbuf);
329         config.curves.remove_all_objects();
330         int result = 0;
331         SketcherCurve *cv = 0;
332
333         while( !(result=input.read_tag()) ) {
334                 if( input.tag.title_is("SKETCHER") ) {
335                         config.drag = input.tag.get_property("DRAG", config.drag);
336                         config.cv_selected = input.tag.get_property("CV_SELECTED", 0);
337                         config.pt_selected = input.tag.get_property("PT_SELECTED", 0);
338                 }
339                 else if( !strncmp(input.tag.get_title(),"CURVE_",6) ) {
340                         cv = new SketcherCurve();
341                         cv->read_data(input);
342                         config.curves.append(cv);
343                 }
344                 else if( !strncmp(input.tag.get_title(),"/CURVE_",7) )
345                         cv = 0;
346                 else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
347                         if( cv ) {
348                                 SketcherPoint *pt = new SketcherPoint();
349                                 pt->read_data(input);
350                                 cv->points.append(pt);
351                         }
352                         else
353                                 printf("Sketcher::read_data: no curve for point\n");
354                 }
355         }
356
357         if( !config.curves.size() ) {
358                 new_curve(0, 1, 0, BLACK);
359         }
360         config.limits();
361 }
362
363 void Sketcher::update_gui()
364 {
365         if( !thread ) return;
366         thread->window->lock_window("Sketcher::update_gui");
367         if( load_configuration() ) {
368                 SketcherWindow *window = (SketcherWindow*)thread->window;
369                 window->update_gui();
370                 window->flush();
371         }
372         thread->window->unlock_window();
373 }
374
375 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d)
376 {
377         int r = d/2+1, x = pt->x, y = pt->y;
378         vfrm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
379         vfrm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
380         vfrm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
381         vfrm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
382         vfrm->set_pixel_color(color);
383         vfrm->draw_x(pt->x, pt->y, d);
384 }
385 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color)
386 {
387         draw_point(vfrm, pt, color, bmax(w,h)/200 + 2);
388 }
389
390
391 int SketcherPenSquare::draw_pixel(int x, int y)
392 {
393         vfrm->draw_line(x-n, y, x+n, y);
394         for( int i=-n; i<n; ++i )
395                 vfrm->draw_line(x-n, y+i, x+n, y+i);
396         return 0;
397 }
398 int SketcherPenPlus::draw_pixel(int x, int y)
399 {
400         if( n > 1 ) {
401                 vfrm->draw_line(x-n, y, x+n, y);
402                 vfrm->draw_line(x, y-n, x, y+n);
403         }
404         else
405                 vfrm->draw_pixel(x, y);
406         return 0;
407 }
408 int SketcherPenSlant::draw_pixel(int x, int y)
409 {
410         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
411         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
412         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
413         return 0;
414 }
415 int SketcherPenXlant::draw_pixel(int x, int y)
416 {
417         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
418         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
419         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
420         vfrm->draw_line(x-n,   y-n,   x+n,   y+n);
421         vfrm->draw_line(x-n+1, y-n,   x+n+1, y+n);
422         vfrm->draw_line(x-n,   y-n+1, x+n,   y-n+1);
423         return 0;
424 }
425
426
427 VFrame *SketcherCurve::new_vpen(VFrame *out)
428 {
429         switch( pen ) {
430         case PEN_SQUARE: return new SketcherPenSquare(out, radius);
431         case PEN_PLUS:   return new SketcherPenPlus(out, radius);
432         case PEN_SLANT:  return new SketcherPenSlant(out, radius);
433         case PEN_XLANT:  return new SketcherPenXlant(out, radius);
434         }
435         return 0;
436 }
437
438 void SketcherCurve::draw_line(VFrame *out)
439 {
440         SketcherPoint *pt0 = points[0];
441         VFrame *vpen = new_vpen(out);
442         out->set_pixel_color(color);
443         int n = points.size();
444         if( n >= 2 ) {
445                 for( int pi=1; pi<n; ++pi ) {
446                         SketcherPoint *pt1 = points[pi];
447                         vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
448                         pt0 = pt1;
449                 }
450         }
451         else
452                 vpen->draw_pixel(pt0->x, pt0->y);
453         delete vpen;
454 }
455
456 /*
457 # python
458 from sympy import *
459 var("x,y, ax,ay, bx,by, cx,cy, dx,dy")
460
461 var("abdx,abdy, acdx,acdy, bddx,bddy, cddx,cddy");
462 abdx = bx-ax;  abdy = by-ay;
463 acdx = cx-ax;  acdy = cy-ay;
464 bddx = dx-bx;  bddy = dy-by;
465 cddx = dx-cx;  cddy = dy-cy;
466
467 var("xc,yc, xd,yd, sx,sy");
468 xc = (bx+dx)/2;  yc = (by+dy)/2;
469 xd = cx-xc;      yd = cy-yc;
470 ax = xc-xd;      ay = yc-yd;
471 # line thru b with slope (c-a) intersects line thru c with slope (d-b)
472 sx = solve(((x - bx) * acdy/acdx + by) - ((x - cx) * bddy/bddx + cy),x)
473 sy = solve(((y - by) * acdx/acdy + bx) - ((y - cy) * bddx/bddy + cx),y)
474
475 var("zx,zy, zdx,zdy, sx,sy, px,py, qx,qy");
476 # point z = (b+c)/2
477 zx = (bx+cx)/2;  zy = (by+cy)/2;
478 zdx = (abdx+cddx)/2;  zdy = (abdy+cddy)/2;
479 # line thru z with slope (d-a) intersects line thru b with slope (c-a)
480 px = solve(((x-zx)*zdy/zdx + zy) - ((x-bx) * acdy/acdx + by),x);
481 py = solve(((y-zy)*zdx/zdy + zx) - ((y-by) * acdx/acdy + bx),y);
482 # line thru z with slope (c-a + d-b)/2 intersects line thru c with slope (d-b)
483 qx = solve(((x-zx)*zdy/zdx + zy) - ((x-cx) * bddy/bddx + cy),x);
484 qy = solve(((y-zy)*zdx/zdy + zx) - ((y-cy) * bddx/bddy + cx),y);
485 */
486
487 static void smooth_sxy(
488         float ax, float ay, float bx, float by,
489         float cx, float cy, float dx, float dy,
490         float &sx, float &sy)
491 {
492         float acdx = cx-ax, acdy = cy-ay;
493         float bddx = dx-bx, bddy = dy-by;
494         float d = acdx*bddy - acdy*bddx;
495         if( fabsf(d) < 1 ) d = 1;
496         sx = (acdx*bddx*by - acdx*bddx*cy + acdx*bddy*cx - acdy*bddx*bx) / d;
497         sy = (acdx*bddy*by - acdy*bddx*cy - acdy*bddy*bx + acdy*bddy*cx) / d;
498         bclamp(sx, -4095.f, 4095.f);
499         bclamp(sy, -4095.f, 4095.f);
500 }
501
502 static void smooth_pxy(
503         float ax, float ay, float bx, float by,
504         float cx, float cy, float dx, float dy,
505         float &px, float &py)
506 {
507         float abdx = bx - ax, abdy = by - ay;
508         float acdx = cx - ax, acdy = cy - ay;
509         float cddx = dx - cx, cddy = dy - cy;
510         float d = (2*(abdx*acdy - abdy*acdx - acdx*cddy + acdy*cddx));
511         if( fabsf(d) < 1 ) d = 1;
512         px = (-abdx*acdx*by + abdx*acdx*cy + 2*abdx*acdy*bx - abdy*acdx*bx - abdy*acdx*cx -
513                 acdx*bx*cddy - acdx*by*cddx + acdx*cddx*cy - acdx*cddy*cx + 2*acdy*bx*cddx) / d;
514         py = (abdx*acdy*by + abdx*acdy*cy - 2*abdy*acdx*by + abdy*acdy*bx - abdy*acdy*cx -
515                  2*acdx*by*cddy + acdy*bx*cddy + acdy*by*cddx + acdy*cddx*cy - acdy*cddy*cx) / d;
516         bclamp(px, -4095.f, 4095.f);
517         bclamp(py, -4095.f, 4095.f);
518 }
519 static void smooth_qxy(
520         float ax, float ay, float bx, float by,
521         float cx, float cy, float dx, float dy,
522         float &qx, float &qy)
523 {
524         float abdx = bx - ax, abdy = by - ay;
525         float bddx = dx - bx, bddy = dy - by;
526         float cddx = dx - cx, cddy = dy - cy;
527         float d = (2*(abdx*bddy - abdy*bddx - bddx*cddy + bddy*cddx));
528         if( fabsf(d) < 1 ) d = 1;
529         qx = (abdx*bddx*by - abdx*bddx*cy + 2*abdx*bddy*cx - abdy*bddx*bx - abdy*bddx*cx -
530                 bddx*bx*cddy + bddx*by*cddx - bddx*cddx*cy - bddx*cddy*cx + 2*bddy*cddx*cx) / d;
531         qy = (abdx*bddy*by + abdx*bddy*cy - 2*abdy*bddx*cy - abdy*bddy*bx + abdy*bddy*cx -
532                 2*bddx*cddy*cy - bddy*bx*cddy + bddy*by*cddx + bddy*cddx*cy + bddy*cddy*cx) / d;
533         bclamp(qx, -4095.f, 4095.f);
534         bclamp(qy, -4095.f, 4095.f);
535 }
536
537
538 static int convex(float ax,float ay, float bx,float by,
539                   float cx,float cy, float dx,float dy)
540 {
541         float abdx = bx - ax, abdy = by - ay;
542         float acdx = cx - ax, acdy = cy - ay;
543         float bcdx = cx - bx, bcdy = cy - by;
544         float bddx = dx - bx, bddy = dy - by;
545         float abc = abdx*acdy - abdy*acdx;
546         float bcd = bcdx*bddy - bcdy*bddx;
547         float v = abc * bcd;
548         return !v ? 0 : v>0 ? 1 : -1;
549 }
550
551 void SketcherCurve::draw_smooth(VFrame *out)
552 {
553         VFrame *vpen = new_vpen(out);
554         out->set_pixel_color(color);
555         int n = points.size();
556         if( !n ) return;
557         if( n > 2 ) {
558                 SketcherPoint *pt0 = points[0], *pt1 = points[1], *pt2 = points[2];
559                 float bx = pt0->x, by = pt0->y;
560                 float cx = pt1->x, cy = pt1->y;
561                 float dx = pt2->x, dy = pt2->y;
562                 float xc = (bx+dx)/2.f, yc = (by+dy)/2.f;
563                 float xd = cx - xc, yd = cy - yc;
564                 float ax = xc - xd, ay = yc - yd;
565                 float sx, sy;
566                 for( int pi=0,n2=n-2; pi<n2; ++pi ) {
567                         float dx = points[pi+2]->x, dy = points[pi+2]->y;
568                         if( convex(ax,ay, bx,by, cx,cy, dx,dy) >= 0 ) {
569                                 smooth_sxy(ax,ay, bx,by, cx,cy, dx,dy, sx, sy);
570                                 vpen->draw_smooth(bx,by, sx,sy, cx,cy);
571                         }
572                         else {
573                                 float zx = (bx+cx)/2.f, zy = (by+cy)/2.f;
574                                 smooth_pxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
575                                 vpen->draw_smooth(bx,by, sx,sy, zx,zy);
576                                 smooth_qxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
577                                 vpen->draw_smooth(zx,zy, sx,sy, cx,cy);
578                         }
579                         ax = bx;  ay = by;
580                         bx = cx;  by = cy;
581                         cx = dx;  cy = dy;
582                 }
583                 xc = (ax+cx)/2.f; yc = (ay+cy)/2.f;
584                 xd = bx - xc, yd = by - yc;
585                 dx = xc - xd, dy = yc - yd;
586                 smooth_sxy(ax, ay, bx, by, cx, cy, dx, dy, sx, sy);
587                 vpen->draw_smooth(bx, by, sx, sy, cx, cy);
588         }
589         else if( n == 2 ) {
590                 SketcherPoint *pt0 = points[0], *pt1 = points[1];
591                 vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
592         }
593         else if( n > 0 ) {
594                 SketcherPoint *pt0 = points[0];
595                 vpen->draw_pixel(pt0->x, pt0->y);
596         }
597         delete vpen;
598 }
599
600 int Sketcher::process_realtime(VFrame *input, VFrame *output)
601 {
602         this->input = input;  this->output = output;
603         w = output->get_w();  h = output->get_h();
604         if( input != output ) output->transfer_from(input);
605
606         load_configuration();
607
608         for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
609                 SketcherCurve *cv = config.curves[ci];
610                 int m = cv->points.size();
611                 if( !m ) continue;
612                 switch( cv->ty ) {
613                 case TYP_OFF:
614                         break;
615                 case TYP_SKETCHER:
616                         cv->draw_line(output);
617                         break;
618                 case TYP_SMOOTH:
619                         cv->draw_smooth(output);
620                         break;
621                 }
622         }
623
624         if( config.drag ) {
625                 for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
626                         SketcherCurve *cv = config.curves[ci];
627                         for( int pi=0,m=cv->points.size(); pi<m; ++pi )
628                                 draw_point(output, cv->points[pi], cv->color);
629                 }
630         }
631
632         return 0;
633 }
634
635 void SketcherCurves::dump()
636 {
637         for( int i=0; i<size(); ++i ) {
638                 SketcherCurve *cv = get(i);
639                 printf("Curve %d, id=%d, ty=%s, r=%d, pen=%s, color=%02x%02x%02x\n",
640                         i, cv->id, cv_type[cv->ty], cv->radius, cv_pen[cv->pen],
641                         (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff);
642                 cv->points.dump();
643         }
644 }
645 void SketcherPoints::dump()
646 {
647         for( int i=0; i<size(); ++i ) {
648                 SketcherPoint *pt = get(i);
649                 printf("  Pt %d, id=%d, x=%d, y=%d\n", i, pt->id, pt->x, pt->y);
650         }
651 }
652