e3b61b192e15f85f030c4b69d80660c95a011ba0
[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 pty, int x, int y)
38 {
39         this->id = id;  this->pty = pty;
40         this->x = x;    this->y = y;
41 }
42 SketcherPoint::SketcherPoint(int id)
43 {
44         init(id, PTY_LINE, 0, 0);
45 }
46 SketcherPoint::SketcherPoint(int id, int pty, int x, int y)
47 {
48         init(id, pty, 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->pty == that.pty &&
61                 this->x == that.x &&
62                 this->y == that.y ? 1 : 0;
63 }
64 void SketcherPoint::copy_from(SketcherPoint &that)
65 {
66         this->id = that.id;  this->pty = that.pty;
67         this->x = that.x;    this->y = that.y;
68 }
69 void SketcherPoint::save_data(FileXML &output)
70 {
71         char point[BCSTRLEN];
72         sprintf(point,"/POINT_%d",id);
73         output.tag.set_title(point+1);
74         output.tag.set_property("TYPE", pty);
75         output.tag.set_property("X", x);
76         output.tag.set_property("Y", y);
77         output.append_tag();
78         output.tag.set_title(point+0);
79         output.append_tag();
80         output.append_newline();
81 }
82 void SketcherPoint::read_data(FileXML &input)
83 {
84         id = atoi(input.tag.get_title() + 6);
85         pty = input.tag.get_property("TYPE", PTY_OFF);
86         x = input.tag.get_property("X", 0.f);
87         y = input.tag.get_property("Y", 0.f);
88         bclamp(pty, 0, PTY_SZ-1);
89 }
90
91 void SketcherCurve::init(int id, int pen, int radius, int color)
92 {
93         this->id = id;
94         this->radius = radius;
95         this->pen = pen;
96         this->color = color;
97 }
98 SketcherCurve::SketcherCurve(int id)
99 {
100         init(id, 1, PTY_LINE, CV_COLOR);
101 }
102 SketcherCurve::SketcherCurve(int id, int pen, int radius, int color)
103 {
104         init(id, pen, radius, color);
105 }
106 SketcherCurve::~SketcherCurve()
107 {
108 }
109 SketcherCurve::SketcherCurve(SketcherCurve &cv)
110 {
111         copy_from(cv);
112 }
113 int SketcherCurve::equivalent(SketcherCurve &that)
114 {
115         if( this->id != that.id ) return 0;
116         if( this->pen != that.pen ) return 0;
117         if( this->radius != that.radius ) return 0;
118         if( this->color != that.color ) return 0;
119         int n = this->points.size();
120         if( n != that.points.size() ) return 0;
121         for( int i=0; i<n; ++i ) {
122                 if( !points[i]->equivalent(*that.points[i]) ) return 0;
123         }
124         return 1;
125 }
126 void SketcherCurve::copy_from(SketcherCurve &that)
127 {
128         this->id = that.id;
129         this->pen = that.pen;
130         this->radius = that.radius;
131         this->color = that.color;
132         int m = points.size(), n = that.points.size();
133         while( m > n ) points.remove_object_number(--m);
134         while( m < n ) { points.append(new SketcherPoint());  ++m; }
135         for( int i=0; i<n; ++i ) points[i]->copy_from(*that.points[i]);
136 }
137 void SketcherCurve::save_data(FileXML &output)
138 {
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("PEN", pen);
144         output.tag.set_property("RADIUS", radius);
145         output.tag.set_property("COLOR", color);
146         output.append_tag();
147         output.append_newline();
148         for( int i=0,n=points.size(); i<n; ++i )
149                 points[i]->save_data(output);
150         output.tag.set_title(curve+0);
151         output.append_tag();
152         output.append_newline();
153 }
154 void SketcherCurve::read_data(FileXML &input)
155 {
156         id = atoi(input.tag.get_title() + 6);
157         pen = input.tag.get_property("PEN", PTY_OFF);
158         radius = input.tag.get_property("RADIUS", 1.);
159         color = input.tag.get_property("COLOR", CV_COLOR);
160         bclamp(pen, 0, PEN_SZ-1);
161 }
162
163 int Sketcher::new_curve(int pen, int radius, int color)
164 {
165         SketcherCurves &curves = config.curves;
166         int k = curves.size(), id = 1;
167         for( int i=k; --i>=0; ) {
168                 int n = config.curves[i]->id;
169                 if( n >= id ) id = n + 1;
170         }
171         SketcherCurve *cv = new SketcherCurve(id, pen, radius, color);
172         curves.append(cv);
173         config.cv_selected = k;
174         return k;
175 }
176
177 int Sketcher::new_curve()
178 {
179         return new_curve(PEN_XLANT, 1, CV_COLOR);
180 }
181
182 int Sketcher::new_point(SketcherCurve *cv, int pty, int x, int y, int idx)
183 {
184         int id = 1;
185         for( int i=cv->points.size(); --i>=0; ) {
186                 int n = cv->points[i]->id;
187                 if( n >= id ) id = n + 1;
188         }
189         SketcherPoint *pt = new SketcherPoint(id, pty, x, y);
190         int n = cv->points.size();
191         if( idx < 0 || idx > n ) idx = n;
192         cv->points.insert(pt, idx);
193         return idx;
194 }
195
196 int Sketcher::new_point(int idx)
197 {
198         int ci = config.cv_selected;
199         if( ci < 0 || ci >= config.curves.size() )
200                 return -1;
201         SketcherCurve *cv = config.curves[ci];
202         EDLSession *session = get_edlsession();
203         int x = !session ? 0.f : session->output_w / 2.f;
204         int y = !session ? 0.f : session->output_h / 2.f;
205         return new_point(cv, PTY_LINE, x, y, idx);
206 }
207
208 REGISTER_PLUGIN(Sketcher)
209
210 SketcherConfig::SketcherConfig()
211 {
212         drag = 1;
213         cv_selected = 0;
214         pt_selected = 0;
215 }
216 SketcherConfig::~SketcherConfig()
217 {
218 }
219
220 int SketcherConfig::equivalent(SketcherConfig &that)
221 {
222         if( this->drag != that.drag ) return 0;
223         if( this->cv_selected != that.cv_selected ) return 0;
224         if( this->pt_selected != that.pt_selected ) return 0;
225         if( this->curves.size() != that.curves.size() ) return 0;
226         for( int i=0, n=curves.size(); i<n; ++i ) {
227                 if( !curves[i]->equivalent(*that.curves[i]) ) return 0;
228         }
229         return 1;
230 }
231
232 void SketcherConfig::copy_from(SketcherConfig &that)
233 {
234         this->drag = that.drag;
235         this->cv_selected = that.cv_selected;
236         this->pt_selected = that.pt_selected;
237         int m = curves.size(), n = that.curves.size();
238         while( m > n ) curves.remove_object_number(--m);
239         while( m < n ) { curves.append(new SketcherCurve());  ++m; }
240         for( int i=0; i<n; ++i ) curves[i]->copy_from(*that.curves[i]);
241 }
242
243 void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next,
244                 long prev_frame, long next_frame, long current_frame)
245 {
246         this->cv_selected = prev.cv_selected;
247         this->pt_selected = prev.pt_selected;
248         this->drag = prev.drag;
249
250         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
251         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
252
253         curves.remove_all_objects();
254         int prev_cv_sz = prev.curves.size();
255         int next_cv_sz = next.curves.size();
256         for( int i=0; i<prev_cv_sz; ++i ) {
257                 SketcherCurve *pcv = prev.curves[i], *ncv = 0;
258                 SketcherCurve *cv = curves.append(new SketcherCurve());
259                 int k = next_cv_sz;  // associated by id in next
260                 while( --k >= 0 && pcv->id != (ncv=next.curves[k])->id );
261                 if( k >= 0 ) {
262                         cv->id = pcv->id;
263                         cv->pen = pcv->pen;
264                         cv->radius = pcv->radius;
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, pt.pty, 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("CV_SELECTED", config.cv_selected);
313         output.tag.set_property("PT_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();
359         config.limits();
360 }
361
362 void Sketcher::update_gui()
363 {
364         if( !thread ) return;
365         thread->window->lock_window("Sketcher::update_gui");
366         if( load_configuration() ) {
367                 SketcherWindow *window = (SketcherWindow*)thread->window;
368                 window->update_gui();
369                 window->flush();
370         }
371         thread->window->unlock_window();
372 }
373
374 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d)
375 {
376         int r = d/2+1, x = pt->x, y = pt->y;
377         vfrm->set_pixel_color(color);
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->draw_x(pt->x, pt->y, d);
383 }
384 void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color)
385 {
386         draw_point(vfrm, pt, color, bmax(w,h)/200 + 2);
387 }
388
389
390 int SketcherPenSquare::draw_pixel(int x, int y)
391 {
392         vfrm->draw_line(x-n, y, x+n, y);
393         for( int i=-n; i<n; ++i )
394                 vfrm->draw_line(x-n, y+i, x+n, y+i);
395         return 0;
396 }
397 int SketcherPenPlus::draw_pixel(int x, int y)
398 {
399         if( n > 1 ) {
400                 vfrm->draw_line(x-n, y, x+n, y);
401                 vfrm->draw_line(x, y-n, x, y+n);
402         }
403         else
404                 vfrm->draw_pixel(x, y);
405         return 0;
406 }
407 int SketcherPenSlant::draw_pixel(int x, int y)
408 {
409         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
410         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
411         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
412         return 0;
413 }
414 int SketcherPenXlant::draw_pixel(int x, int y)
415 {
416         vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
417         vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
418         vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
419         vfrm->draw_line(x-n,   y-n,   x+n,   y+n);
420         vfrm->draw_line(x-n+1, y-n,   x+n+1, y+n);
421         vfrm->draw_line(x-n,   y-n+1, x+n,   y-n+1);
422         return 0;
423 }
424
425
426 VFrame *SketcherCurve::new_vpen(VFrame *out)
427 {
428         switch( pen ) {
429         case PEN_SQUARE: return new SketcherPenSquare(out, radius);
430         case PEN_PLUS:   return new SketcherPenPlus(out, radius);
431         case PEN_SLANT:  return new SketcherPenSlant(out, radius);
432         case PEN_XLANT:  return new SketcherPenXlant(out, radius);
433         }
434         return 0;
435 }
436
437 static int intersects_at(float &x, float &y,
438                 float ax,float ay, float bx, float by, float cx,float cy,  // line slope ab thru c
439                 float dx,float dy, float ex, float ey, float fx,float fy, // line slope de thru f
440                 float mx=0)
441 {
442         float badx = bx - ax, bady = by - ay;
443         float eddx = ex - dx, eddy = ey - dy;
444         float d = badx*eddy - bady*eddx;
445         int ret = 0;
446         if( fabsf(d) < 1 ) { ret = 1;  d = signbit(d) ? -1 : 1; }
447         x = (badx*cy*eddx - badx*eddx*fy + badx*eddy*fx - bady*cx*eddx) / d;
448         y = (badx*cy*eddy - bady*cx*eddy - bady*eddx*fy + bady*eddy*fx) / d;
449         if( mx > 0 ) { bclamp(x, -mx,mx);  bclamp(y, -mx,mx); }
450         return ret;
451 }
452
453 static void smooth_axy(float &ax, float &ay,
454         float bx, float by, float cx, float cy, float dx, float dy)
455 {
456 //middle of bd reflected around ctr
457 // point ctr = b+d/2, dv=c-ctr, a=ctr-dv;
458         float xc = (bx+dx)*.5f, yc = (by+dy)*.5f;
459         float xd = cx - xc, yd = cy - yc;
460         ax = xc - xd;  ay = yc - yd;
461 }
462 static void smooth_dxy(float &dx, float &dy,
463         float ax, float ay, float bx, float by, float cx, float cy)
464 {
465 //middle of ac reflected around ctr
466 // point ctr = a+c/2, dv=c-ctr, d=ctr-dv;
467         float xc = (ax+cx)*.5f, yc = (ay+cy)*.5f;
468         float xd = bx - xc, yd = by - yc;
469         dx = xc - xd;  dy = yc - yd;
470 }
471
472 #if 0
473 static int convex(float ax,float ay, float bx,float by,
474                   float cx,float cy, float dx,float dy)
475 {
476         float abdx = bx-ax, abdy = by-ay;
477         float acdx = cx-ax, acdy = cy-ay;
478         float bcdx = cx-bx, bcdy = cy-by;
479         float bddx = dx-bx, bddy = dy-by;
480         float abc = abdx*acdy - abdy*acdx;
481         float bcd = bcdx*bddy - bcdy*bddx;
482         float v = abc * bcd;
483         return !v ? 0 : v>0 ? 1 : -1;
484 }
485 #endif
486
487 void SketcherCurve::draw(VFrame *out)
488 {
489         const float fmx = 16383;
490         VFrame *vpen = new_vpen(out);
491         out->set_pixel_color(color);
492         int n = points.size();
493         if( !n ) return;
494         if( n > 2 ) {
495                 int n2 = n - 2;
496                 SketcherPoint *pt0 = points[0];
497                 SketcherPoint *pt1 = points[1];
498                 SketcherPoint *pt2 = points[2];
499                 float ax,ay, bx,by, cx,cy, dx,dy, sx,sy;
500                 bx = pt0->x;  by = pt0->y;
501                 cx = pt1->x;  cy = pt1->y;
502                 dx = pt2->x;  dy = pt2->y;
503                 smooth_axy(ax,ay, bx,by, cx,cy, dx,dy);
504                 for( int pi=0; pi<n2; ++pi ) {
505                         int pty = points[pi]->pty;
506                         dx = points[pi+2]->x;  dy = points[pi+2]->y;
507                         switch( pty ) {
508                         case PTY_LINE:
509                                 vpen->draw_line(bx, by, cx, cy);
510                                 break;
511                         case PTY_CURVE: {
512                                 // s = ac thru b x bd thru c
513                                 intersects_at(sx,sy, ax,ay,cx,cy,bx,by, bx,by,dx,dy,cx,cy,fmx);
514                                 vpen->draw_smooth(bx,by, sx,sy, cx,cy);
515                                 break; }
516                         }
517                         ax = bx;  ay = by;
518                         bx = cx;  by = cy;
519                         cx = dx;  cy = dy;
520                 }
521                 switch( points[n2]->pty ) {
522                 case PTY_LINE:
523                         vpen->draw_line(bx, by, cx, cy);
524                         break;
525                 case PTY_CURVE: {
526                         smooth_dxy(dx,dy, ax,ay, bx,by, cx,cy);
527                         intersects_at(sx,sy, ax,ay,cx,cy,bx,by, bx,by,dx,dy,cx,cy,fmx);
528                         vpen->draw_smooth(bx,by, sx,sy, cx,cy);
529                         break; }
530                 }
531         }
532         else if( n == 2 ) {
533                 SketcherPoint *pt0 = points[0], *pt1 = points[1];
534                 vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
535         }
536         else if( n > 0 ) {
537                 SketcherPoint *pt0 = points[0];
538                 vpen->draw_pixel(pt0->x, pt0->y);
539         }
540         delete vpen;
541 }
542
543 int Sketcher::process_realtime(VFrame *input, VFrame *output)
544 {
545         this->input = input;  this->output = output;
546         w = output->get_w();  h = output->get_h();
547         if( input != output ) output->transfer_from(input);
548
549         load_configuration();
550
551         for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
552                 SketcherCurve *cv = config.curves[ci];
553                 int m = cv->points.size();
554                 if( !m || cv->pen == PTY_OFF ) continue;
555                 cv->draw(output);
556         }
557
558         if( config.drag ) {
559                 for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
560                         SketcherCurve *cv = config.curves[ci];
561                         for( int pi=0,m=cv->points.size(); pi<m; ++pi ) {
562                                 int color = ci==config.cv_selected && pi==config.pt_selected ?
563                                         RED : cv->color ; 
564                                 draw_point(output, cv->points[pi], color);
565                         }
566                 }
567         }
568
569         return 0;
570 }
571
572 void SketcherCurves::dump()
573 {
574         for( int i=0; i<size(); ++i ) {
575                 SketcherCurve *cv = get(i);
576                 printf("Curve %d, id=%d, pen=%s, r=%d, color=%02x%02x%02x\n",
577                         i, cv->id, cv_pen[cv->pen], cv->radius,
578                         (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff);
579                 cv->points.dump();
580         }
581 }
582 void SketcherPoints::dump()
583 {
584         for( int i=0; i<size(); ++i ) {
585                 SketcherPoint *pt = get(i);
586                 printf("  Pt %d, id=%d, pty=%s, x=%d, y=%d\n",
587                         i, pt->id, pt_type[pt->pty], pt->x, pt->y);
588         }
589 }
590