1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1999 Alexander Larsson
3  *
4  * beziershape.c - code to help implement bezier shapes
5  * Copyright (C) 2000 James Henstridge
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #include <config.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h> /* memcpy() */
28 
29 #include "beziershape.h"
30 #include "message.h"
31 #include "diarenderer.h"
32 
33 #define HANDLE_BEZMAJOR  (HANDLE_CUSTOM1)
34 #define HANDLE_LEFTCTRL  (HANDLE_CUSTOM2)
35 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
36 
37 enum change_type {
38   TYPE_ADD_POINT,
39   TYPE_REMOVE_POINT
40 };
41 
42 /* Invariant:
43    # of handles = 3*(numpoints-1)
44    # of connections = 2*(numpoints-1) + 1 (main point)
45    For historical reasons, the main point is the last cp.
46  */
47 struct BezPointChange {
48   ObjectChange obj_change;
49 
50   enum change_type type;
51   int applied;
52 
53   BezPoint point;
54   BezCornerType corner_type;
55   int pos;
56 
57   /* owning ref when not applied for ADD_POINT
58    * owning ref when applied for REMOVE_POINT */
59   Handle *handle1, *handle2, *handle3;
60   ConnectionPoint *cp1, *cp2;
61 };
62 
63 struct CornerChange {
64   ObjectChange obj_change;
65   /* Only one kind of corner_change */
66   int applied;
67 
68   Handle *handle;
69   /* Old places when SET_CORNER_TYPE is applied */
70   Point point_left, point_right;
71   BezCornerType old_type, new_type;
72 };
73 
74 static ObjectChange *
75 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
76 			BezPoint *point, BezCornerType corner_type,
77 			int segment,
78 			Handle *handle1, Handle *handle2, Handle *handle3,
79 			ConnectionPoint *cp1, ConnectionPoint *cp2);
80 static ObjectChange *
81 beziershape_create_corner_change(BezierShape *bezier, Handle *handle,
82 			Point *point_left, Point *point_right,
83 			BezCornerType old_corner_type,
84 			BezCornerType new_corner_type);
85 
86 static void new_handles_and_connections(BezierShape *bezier, int num_points);
87 
setup_handle(Handle * handle,int handle_id)88 static void setup_handle(Handle *handle, int handle_id)
89 {
90   handle->id = handle_id;
91   handle->type =
92     (handle_id == HANDLE_BEZMAJOR) ?
93     HANDLE_MAJOR_CONTROL :
94     HANDLE_MINOR_CONTROL;
95   handle->connect_type = HANDLE_NONCONNECTABLE;
96   handle->connected_to = NULL;
97 }
98 
99 
get_handle_nr(BezierShape * bezier,Handle * handle)100 static int get_handle_nr(BezierShape *bezier, Handle *handle)
101 {
102   int i = 0;
103   for (i = 0; i < bezier->object.num_handles; i++) {
104     if (bezier->object.handles[i] == handle)
105       return i;
106   }
107   return -1;
108 }
109 
110 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
111 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
112 
113 ObjectChange *
beziershape_move_handle(BezierShape * bezier,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)114 beziershape_move_handle(BezierShape *bezier, Handle *handle,
115 			Point *to, ConnectionPoint *cp,
116 			HandleMoveReason reason, ModifierKeys modifiers)
117 {
118   int handle_nr, comp_nr, next_nr, prev_nr;
119   Point delta, pt;
120 
121   delta = *to;
122   point_sub(&delta, &handle->pos);
123 
124   handle_nr = get_handle_nr(bezier, handle);
125   comp_nr = get_comp_nr(handle_nr);
126   next_nr = comp_nr + 1;
127   prev_nr = comp_nr - 1;
128   if (comp_nr == bezier->numpoints - 1)
129     next_nr = 1;
130   if (comp_nr == 1)
131     prev_nr = bezier->numpoints - 1;
132 
133   switch(handle->id) {
134   case HANDLE_BEZMAJOR:
135     if (comp_nr == bezier->numpoints - 1) {
136       bezier->points[comp_nr].p3 = *to;
137       bezier->points[0].p1 = bezier->points[0].p3 = *to;
138       point_add(&bezier->points[comp_nr].p2, &delta);
139       point_add(&bezier->points[1].p1, &delta);
140     } else {
141       bezier->points[comp_nr].p3 = *to;
142       point_add(&bezier->points[comp_nr].p2, &delta);
143       point_add(&bezier->points[comp_nr+1].p1, &delta);
144     }
145     break;
146   case HANDLE_LEFTCTRL:
147     bezier->points[comp_nr].p2 = *to;
148     switch (bezier->corner_types[comp_nr]) {
149     case BEZ_CORNER_SYMMETRIC:
150       pt = bezier->points[comp_nr].p3;
151       point_sub(&pt, &bezier->points[comp_nr].p2);
152       point_add(&pt, &bezier->points[comp_nr].p3);
153       bezier->points[next_nr].p1 = pt;
154       break;
155     case BEZ_CORNER_SMOOTH: {
156       real len;
157 
158       pt = bezier->points[next_nr].p1;
159       point_sub(&pt, &bezier->points[comp_nr].p3);
160       len = point_len(&pt);
161 
162       pt = bezier->points[comp_nr].p3;
163       point_sub(&pt, &bezier->points[comp_nr].p2);
164       if (point_len(&pt) > 0)
165 	point_normalize(&pt);
166       else {
167 	pt.x = 1.0; pt.y = 0.0;
168       }
169       point_scale(&pt, len);
170       point_add(&pt, &bezier->points[comp_nr].p3);
171       bezier->points[next_nr].p1 = pt;
172       break;
173     }
174     case BEZ_CORNER_CUSP:
175       /* no mirror point movement required */
176       break;
177     }
178     break;
179   case HANDLE_RIGHTCTRL:
180     bezier->points[comp_nr].p1 = *to;
181     switch (bezier->corner_types[prev_nr]) {
182     case BEZ_CORNER_SYMMETRIC:
183       pt = bezier->points[prev_nr].p3;
184       point_sub(&pt, &bezier->points[comp_nr].p1);
185       point_add(&pt, &bezier->points[prev_nr].p3);
186       bezier->points[prev_nr].p2 = pt;
187       break;
188     case BEZ_CORNER_SMOOTH: {
189       real len;
190 
191       pt = bezier->points[prev_nr].p2;
192       point_sub(&pt, &bezier->points[prev_nr].p3);
193       len = point_len(&pt);
194 
195       pt = bezier->points[prev_nr].p3;
196       point_sub(&pt, &bezier->points[comp_nr].p1);
197       if (point_len(&pt) > 0)
198 	point_normalize(&pt);
199       else {
200 	pt.x = 1.0; pt.y = 0.0;
201       }
202       point_scale(&pt, len);
203       point_add(&pt, &bezier->points[prev_nr].p3);
204       bezier->points[prev_nr].p2 = pt;
205       break;
206     }
207     case BEZ_CORNER_CUSP:
208       /* no mirror point movement required */
209       break;
210     }
211     break;
212   default:
213     message_error("Internal error in beziershape_move_handle.");
214     break;
215   }
216   return NULL;
217 }
218 
219 ObjectChange*
beziershape_move(BezierShape * bezier,Point * to)220 beziershape_move(BezierShape *bezier, Point *to)
221 {
222   Point p;
223   int i;
224 
225   p = *to;
226   point_sub(&p, &bezier->points[0].p1);
227 
228   bezier->points[0].p1 = bezier->points[0].p3 = *to;
229   for (i = 1; i < bezier->numpoints; i++) {
230     point_add(&bezier->points[i].p1, &p);
231     point_add(&bezier->points[i].p2, &p);
232     point_add(&bezier->points[i].p3, &p);
233   }
234 
235   return NULL;
236 }
237 
238 int
beziershape_closest_segment(BezierShape * bezier,Point * point,real line_width)239 beziershape_closest_segment(BezierShape *bezier, Point *point, real line_width)
240 {
241   Point last;
242   int i;
243   real dist = G_MAXDOUBLE;
244   int closest;
245 
246   closest = 0;
247   last = bezier->points[0].p1;
248   /* the first point is just move-to so there is no need to consider p2,p3 of it */
249   for (i = 1; i < bezier->numpoints; i++) {
250     real new_dist = distance_bez_seg_point(&last, &bezier->points[i].p1,
251 			&bezier->points[i].p2, &bezier->points[i].p3,
252 			line_width, point);
253     if (new_dist < dist) {
254       dist = new_dist;
255       closest = i;
256     }
257     last = bezier->points[i].p3;
258   }
259   return closest;
260 }
261 
262 Handle *
beziershape_closest_handle(BezierShape * bezier,Point * point)263 beziershape_closest_handle(BezierShape *bezier, Point *point)
264 {
265   int i, hn;
266   real dist = G_MAXDOUBLE;
267   Handle *closest = NULL;
268 
269   for (i = 1, hn = 0; i < bezier->numpoints; i++, hn++) {
270     real new_dist;
271 
272     new_dist = distance_point_point( point, &bezier->points[i].p1);
273     if (new_dist < dist) {
274       dist = new_dist;
275       closest = bezier->object.handles[hn];
276     }
277     hn++;
278 
279     new_dist = distance_point_point( point, &bezier->points[i].p2);
280     if (new_dist < dist) {
281       dist = new_dist;
282       closest = bezier->object.handles[hn];
283     }
284 
285     hn++;
286     new_dist = distance_point_point( point, &bezier->points[i].p3);
287     if (new_dist < dist) {
288       dist = new_dist;
289       closest = bezier->object.handles[hn];
290     }
291   }
292   return closest;
293 }
294 
295 Handle *
beziershape_closest_major_handle(BezierShape * bezier,Point * point)296 beziershape_closest_major_handle(BezierShape *bezier, Point *point)
297 {
298   Handle *closest = beziershape_closest_handle(bezier, point);
299   int pos = get_major_nr(get_handle_nr(bezier, closest));
300 
301   if (pos == 0)
302     pos = bezier->numpoints - 1;
303   return bezier->object.handles[3*pos - 1];
304 }
305 
306 real
beziershape_distance_from(BezierShape * bezier,Point * point,real line_width)307 beziershape_distance_from(BezierShape *bezier, Point *point, real line_width)
308 {
309   return distance_bez_shape_point(bezier->points, bezier->numpoints,
310 				  line_width, point);
311 }
312 
313 static void
add_handles(BezierShape * bezier,int pos,BezPoint * point,BezCornerType corner_type,Handle * handle1,Handle * handle2,Handle * handle3,ConnectionPoint * cp1,ConnectionPoint * cp2)314 add_handles(BezierShape *bezier, int pos, BezPoint *point,
315 	    BezCornerType corner_type, Handle *handle1,
316 	    Handle *handle2, Handle *handle3,
317 	    ConnectionPoint *cp1, ConnectionPoint *cp2)
318 {
319   int i, next;
320   DiaObject *obj;
321 
322   g_assert(pos >= 1);
323   g_assert(pos <= bezier->numpoints);
324 
325   obj = (DiaObject *)bezier;
326   bezier->numpoints++;
327   next = pos + 1;
328   if (pos == bezier->numpoints - 1)
329     next = 1;
330   bezier->points = g_realloc(bezier->points,
331 			     bezier->numpoints * sizeof(BezPoint));
332   bezier->corner_types = g_realloc(bezier->corner_types,
333 				   bezier->numpoints * sizeof(BezCornerType));
334 
335   for (i = bezier->numpoints - 1; i > pos; i--) {
336     bezier->points[i] = bezier->points[i-1];
337     bezier->corner_types[i] =bezier->corner_types[i-1];
338   }
339   bezier->points[pos] = *point;
340   bezier->points[pos].p1 = bezier->points[next].p1;
341   bezier->points[next].p1 = point->p1;
342   if (pos == bezier->numpoints - 1)
343     bezier->points[0].p1 = bezier->points[0].p3 = bezier->points[pos].p3;
344   bezier->corner_types[pos] = corner_type;
345   object_add_handle_at((DiaObject*)bezier, handle1, 3*pos-3);
346   object_add_handle_at((DiaObject*)bezier, handle2, 3*pos-2);
347   object_add_handle_at((DiaObject*)bezier, handle3, 3*pos-1);
348   object_add_connectionpoint_at((DiaObject *)bezier, cp1, 2*pos-2);
349   object_add_connectionpoint_at((DiaObject *)bezier, cp2, 2*pos-1);
350 }
351 
352 static void
remove_handles(BezierShape * bezier,int pos)353 remove_handles(BezierShape *bezier, int pos)
354 {
355   int i;
356   DiaObject *obj;
357   Handle *old_handle1, *old_handle2, *old_handle3;
358   ConnectionPoint *old_cp1, *old_cp2;
359   Point tmppoint;
360   Point controlvector;
361   controlvector.x=0;
362   controlvector.y=0;
363 
364   g_assert(pos > 0);
365   g_assert(pos < bezier->numpoints);
366 
367   obj = (DiaObject *)bezier;
368 
369   /* delete the points */
370   bezier->numpoints--;
371   tmppoint = bezier->points[pos].p1;
372   if (pos == bezier->numpoints) {
373     controlvector = bezier->points[pos-1].p3;
374     point_sub(&controlvector, &bezier->points[pos].p1);
375   }
376   for (i = pos; i < bezier->numpoints; i++) {
377     bezier->points[i] = bezier->points[i+1];
378     bezier->corner_types[i] = bezier->corner_types[i+1];
379   }
380   bezier->points[pos].p1 = tmppoint;
381   if (pos == bezier->numpoints) {
382     /* If this was the last point, we also need to move points[0] and
383        the control point in points[1]. */
384     bezier->points[0].p1 = bezier->points[bezier->numpoints-1].p3;
385     bezier->points[1].p1 = bezier->points[0].p1;
386     point_sub(&bezier->points[1].p1, &controlvector);
387   }
388   bezier->points = g_realloc(bezier->points,
389 			     bezier->numpoints * sizeof(BezPoint));
390   bezier->corner_types = g_realloc(bezier->corner_types,
391 				   bezier->numpoints * sizeof(BezCornerType));
392 
393   old_handle1 = obj->handles[3*pos-3];
394   old_handle2 = obj->handles[3*pos-2];
395   old_handle3 = obj->handles[3*pos-1];
396   object_remove_handle(&bezier->object, old_handle1);
397   object_remove_handle(&bezier->object, old_handle2);
398   object_remove_handle(&bezier->object, old_handle3);
399   old_cp1 = obj->connections[2*pos-2];
400   old_cp2 = obj->connections[2*pos-1];
401   object_remove_connectionpoint(&bezier->object, old_cp1);
402   object_remove_connectionpoint(&bezier->object, old_cp2);
403 }
404 
405 
406 /* Add a point by splitting segment into two, putting the new point at
407  'point' or, if NULL, in the middle */
408 ObjectChange *
beziershape_add_segment(BezierShape * bezier,int segment,Point * point)409 beziershape_add_segment(BezierShape *bezier, int segment, Point *point)
410 {
411   BezPoint realpoint;
412   BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
413   Handle *new_handle1, *new_handle2, *new_handle3;
414   ConnectionPoint *new_cp1, *new_cp2;
415   Point startpoint;
416   Point other;
417 
418   if (segment != 1)
419     startpoint = bezier->points[segment-1].p3;
420   else
421     startpoint = bezier->points[0].p1;
422   other = bezier->points[segment].p3;
423   if (point == NULL) {
424     realpoint.p1.x = (startpoint.x + other.x)/6;
425     realpoint.p1.y = (startpoint.y + other.y)/6;
426     realpoint.p2.x = (startpoint.x + other.x)/3;
427     realpoint.p2.y = (startpoint.y + other.y)/3;
428     realpoint.p3.x = (startpoint.x + other.x)/2;
429     realpoint.p3.y = (startpoint.y + other.y)/2;
430   } else {
431     realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
432     realpoint.p2.y = point->y+(startpoint.y-other.y)/6;
433 
434     realpoint.p3 = *point;
435     /* this really goes into the next segment ... */
436     realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
437     realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
438   }
439   realpoint.type = BEZ_CURVE_TO;
440 
441   new_handle1 = g_new0(Handle,1);
442   new_handle2 = g_new0(Handle,1);
443   new_handle3 = g_new0(Handle,1);
444   setup_handle(new_handle1, HANDLE_RIGHTCTRL);
445   setup_handle(new_handle2, HANDLE_LEFTCTRL);
446   setup_handle(new_handle3, HANDLE_BEZMAJOR);
447   new_cp1 = g_new0(ConnectionPoint, 1);
448   new_cp2 = g_new0(ConnectionPoint, 1);
449   new_cp1->object = &bezier->object;
450   new_cp2->object = &bezier->object;
451   add_handles(bezier, segment, &realpoint, corner_type,
452 	      new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
453   return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
454 					 &realpoint, corner_type, segment,
455 					 new_handle1, new_handle2, new_handle3,
456 					 new_cp1, new_cp2);
457 }
458 
459 ObjectChange *
beziershape_remove_segment(BezierShape * bezier,int pos)460 beziershape_remove_segment(BezierShape *bezier, int pos)
461 {
462   Handle *old_handle1, *old_handle2, *old_handle3;
463   ConnectionPoint *old_cp1, *old_cp2;
464   BezPoint old_point;
465   BezCornerType old_ctype;
466   int next = pos+1;
467 
468   g_assert(pos > 0);
469   g_assert(bezier->numpoints > 2);
470   g_assert(pos < bezier->numpoints);
471 
472   if (pos == bezier->numpoints - 1)
473     next = 1;
474 
475   old_handle1 = bezier->object.handles[3*pos-3];
476   old_handle2 = bezier->object.handles[3*pos-2];
477   old_handle3 = bezier->object.handles[3*pos-1];
478   old_point = bezier->points[pos];
479   /* remember the old contro point of following bezpoint */
480   old_point.p1 = bezier->points[next].p1;
481   old_ctype = bezier->corner_types[pos];
482 
483   old_cp1 = bezier->object.connections[2*pos-2];
484   old_cp2 = bezier->object.connections[2*pos-1];
485 
486   object_unconnect((DiaObject *)bezier, old_handle1);
487   object_unconnect((DiaObject *)bezier, old_handle2);
488   object_unconnect((DiaObject *)bezier, old_handle3);
489 
490   remove_handles(bezier, pos);
491 
492   beziershape_update_data(bezier);
493 
494   return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
495 					 &old_point, old_ctype, pos,
496 					 old_handle1, old_handle2, old_handle3,
497 					 old_cp1, old_cp2);
498 }
499 
500 static void
beziershape_straighten_corner(BezierShape * bez,int comp_nr)501 beziershape_straighten_corner(BezierShape *bez, int comp_nr) {
502   int next_nr;
503 
504   if (comp_nr == 0) comp_nr = bez->numpoints - 1;
505   next_nr = comp_nr + 1;
506   if (comp_nr == bez->numpoints - 1)
507     next_nr = 1;
508   /* Neat thing would be to have the kind of straigthening depend on
509      which handle was chosen:  Mid-handle does average, other leaves that
510      handle where it is. */
511   bez->points[0].p3 = bez->points[0].p1;
512   switch (bez->corner_types[comp_nr]) {
513   case BEZ_CORNER_SYMMETRIC: {
514     Point pt1, pt2;
515 
516     pt1 = bez->points[comp_nr].p3;
517     point_sub(&pt1, &bez->points[comp_nr].p2);
518     pt2 = bez->points[comp_nr].p3;
519     point_sub(&pt2, &bez->points[next_nr].p1);
520     point_scale(&pt2, -1.0);
521     point_add(&pt1, &pt2);
522     point_scale(&pt1, 0.5);
523     pt2 = pt1;
524     point_scale(&pt1, -1.0);
525     point_add(&pt1, &bez->points[comp_nr].p3);
526     point_add(&pt2, &bez->points[comp_nr].p3);
527     bez->points[comp_nr].p2 = pt1;
528     bez->points[next_nr].p1 = pt2;
529     beziershape_update_data(bez);
530   }
531   break;
532   case BEZ_CORNER_SMOOTH: {
533     Point pt1, pt2;
534     real len1, len2;
535     pt1 = bez->points[comp_nr].p3;
536     point_sub(&pt1, &bez->points[comp_nr].p2);
537     pt2 = bez->points[comp_nr].p3;
538     point_sub(&pt2, &bez->points[next_nr].p1);
539     len1 = point_len(&pt1);
540     len2 = point_len(&pt2);
541     point_scale(&pt2, -1.0);
542     if (len1 > 0)
543       point_normalize(&pt1);
544     if (len2 > 0)
545       point_normalize(&pt2);
546     point_add(&pt1, &pt2);
547     point_scale(&pt1, 0.5);
548     pt2 = pt1;
549     point_scale(&pt1, -len1);
550     point_add(&pt1, &bez->points[comp_nr].p3);
551     point_scale(&pt2, len2);
552     point_add(&pt2, &bez->points[comp_nr].p3);
553     bez->points[comp_nr].p2 = pt1;
554     bez->points[next_nr].p1 = pt2;
555     beziershape_update_data(bez);
556   }
557     break;
558   case BEZ_CORNER_CUSP:
559     break;
560   }
561   bez->points[0].p1 = bez->points[0].p3;
562 }
563 
564 ObjectChange *
beziershape_set_corner_type(BezierShape * bez,Handle * handle,BezCornerType corner_type)565 beziershape_set_corner_type(BezierShape *bez, Handle *handle,
566 			    BezCornerType corner_type)
567 {
568   Handle *mid_handle = NULL;
569   Point old_left, old_right;
570   int old_type;
571   int handle_nr, comp_nr;
572 
573   handle_nr = get_handle_nr(bez, handle);
574 
575   switch (handle->id) {
576   case HANDLE_BEZMAJOR:
577     mid_handle = handle;
578     break;
579   case HANDLE_LEFTCTRL:
580     handle_nr++;
581     if (handle_nr == bez->object.num_handles) handle_nr = 0;
582     mid_handle = bez->object.handles[handle_nr];
583     break;
584   case HANDLE_RIGHTCTRL:
585     handle_nr--;
586     if (handle_nr < 0) handle_nr = bez->object.num_handles - 1;
587     mid_handle = bez->object.handles[handle_nr];
588     break;
589   default:
590     g_assert_not_reached();
591     break;
592   }
593 
594   comp_nr = get_major_nr(handle_nr);
595 
596   old_type = bez->corner_types[comp_nr];
597   old_left = bez->points[comp_nr].p2;
598   if (comp_nr == bez->numpoints - 1)
599     old_right = bez->points[1].p1;
600   else
601     old_right = bez->points[comp_nr+1].p1;
602 
603 #if 0
604   g_message("Setting corner type on segment %d to %s", comp_nr,
605 	    corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
606 	    corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
607 #endif
608   bez->corner_types[comp_nr] = corner_type;
609   if (comp_nr == 0)
610     bez->corner_types[bez->numpoints-1] = corner_type;
611   else if (comp_nr == bez->numpoints - 1)
612     bez->corner_types[0] = corner_type;
613 
614   beziershape_straighten_corner(bez, comp_nr);
615 
616   return beziershape_create_corner_change(bez, mid_handle, &old_left,
617 					  &old_right, old_type, corner_type);
618 }
619 
620 void
beziershape_update_data(BezierShape * bezier)621 beziershape_update_data(BezierShape *bezier)
622 {
623   int i;
624   Point last;
625   Point minp, maxp;
626 
627   DiaObject *obj = &bezier->object;
628 
629   /* handle the case of whole points array update (via set_prop) */
630   if (3*(bezier->numpoints-1) != obj->num_handles ||
631       2*(bezier->numpoints-1) + 1 != obj->num_connections) {
632     object_unconnect_all(obj); /* too drastic ? */
633 
634     /* delete the old ones */
635     for (i = 0; i < obj->num_handles; i++)
636       g_free(obj->handles[i]);
637     g_free(obj->handles);
638     for (i = 0; i < obj->num_connections; i++)
639       g_free(obj->connections[i]);
640     g_free(obj->connections);
641 
642     obj->num_handles = 3*(bezier->numpoints-1);
643     obj->handles = g_new(Handle*, obj->num_handles);
644     obj->num_connections = 2*(bezier->numpoints-1) + 1;
645     obj->connections = g_new(ConnectionPoint *, obj->num_connections);
646 
647     new_handles_and_connections(bezier, bezier->numpoints);
648 
649     bezier->corner_types = g_realloc(bezier->corner_types, bezier->numpoints*sizeof(BezCornerType));
650     for (i = 0; i < bezier->numpoints; i++)
651       bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
652   }
653 
654   /* Update handles: */
655   bezier->points[0].p3 = bezier->points[0].p1;
656   for (i = 1; i < bezier->numpoints; i++) {
657     obj->handles[3*i-3]->pos = bezier->points[i].p1;
658     obj->handles[3*i-2]->pos = bezier->points[i].p2;
659     obj->handles[3*i-1]->pos = bezier->points[i].p3;
660   }
661 
662   /* Update connection points: */
663   last = bezier->points[0].p1;
664   for (i = 1; i < bezier->numpoints; i++) {
665     Point slopepoint1, slopepoint2;
666     slopepoint1 = bezier->points[i].p1;
667     point_sub(&slopepoint1, &last);
668     point_scale(&slopepoint1, .5);
669     point_add(&slopepoint1, &last);
670     slopepoint2 = bezier->points[i].p2;
671     point_sub(&slopepoint2, &bezier->points[i].p3);
672     point_scale(&slopepoint2, .5);
673     point_add(&slopepoint2, &bezier->points[i].p3);
674 
675     obj->connections[2*i-2]->pos = last;
676     obj->connections[2*i-2]->directions =
677       find_slope_directions(last, bezier->points[i].p1);
678     obj->connections[2*i-1]->pos.x =
679       (last.x + 3*bezier->points[i].p1.x + 3*bezier->points[i].p2.x +
680        bezier->points[i].p3.x)/8;
681     obj->connections[2*i-1]->pos.y =
682       (last.y + 3*bezier->points[i].p1.y + 3*bezier->points[i].p2.y +
683        bezier->points[i].p3.y)/8;
684     obj->connections[2*i-1]->directions =
685       find_slope_directions(slopepoint1, slopepoint2);
686     last = bezier->points[i].p3;
687   }
688 
689   /* Find the middle of the object (or some approximation at least) */
690   minp = maxp = bezier->points[0].p1;
691   for (i = 1; i < bezier->numpoints; i++) {
692     Point p = bezier->points[i].p3;
693     if (p.x < minp.x) minp.x = p.x;
694     if (p.x > maxp.x) maxp.x = p.x;
695     if (p.y < minp.y) minp.y = p.y;
696     if (p.y > maxp.y) maxp.y = p.y;
697   }
698   obj->connections[obj->num_connections-1]->pos.x = (minp.x + maxp.x) / 2;
699   obj->connections[obj->num_connections-1]->pos.y = (minp.y + maxp.y) / 2;
700   obj->connections[obj->num_connections-1]->directions = DIR_ALL;
701 }
702 
703 void
beziershape_update_boundingbox(BezierShape * bezier)704 beziershape_update_boundingbox(BezierShape *bezier)
705 {
706   ElementBBExtras *extra;
707   PolyBBExtras pextra;
708 
709   g_assert(bezier != NULL);
710 
711   extra = &bezier->extra_spacing;
712   pextra.start_trans = pextra.end_trans =
713     pextra.start_long = pextra.end_long = 0;
714   pextra.middle_trans = extra->border_trans;
715 
716   polybezier_bbox(&bezier->points[0],
717                   bezier->numpoints,
718                   &pextra, TRUE,
719                   &bezier->object.bounding_box);
720 }
721 
722 void
beziershape_simple_draw(BezierShape * bezier,DiaRenderer * renderer,real width)723 beziershape_simple_draw(BezierShape *bezier, DiaRenderer *renderer, real width)
724 {
725   BezPoint *points;
726 
727   g_assert(bezier != NULL);
728   g_assert(renderer != NULL);
729 
730   points = &bezier->points[0];
731 
732   DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
733   DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
734   DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
735   DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
736 
737   DIA_RENDERER_GET_CLASS(renderer)->fill_bezier(renderer, points, bezier->numpoints,&color_white);
738   DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bezier->numpoints,&color_black);
739 }
740 
741 void
beziershape_draw_control_lines(BezierShape * bez,DiaRenderer * renderer)742 beziershape_draw_control_lines(BezierShape *bez, DiaRenderer *renderer)
743 {
744   Color line_colour = {0.0, 0.0, 0.6};
745   Point startpoint;
746   int i;
747 
748   /* setup renderer ... */
749   DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
750   DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
751   DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
752   DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
753   DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
754 
755   startpoint = bez->points[0].p1;
756   for (i = 1; i < bez->numpoints; i++) {
757     DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
758                              &line_colour);
759     DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
760                              &line_colour);
761     startpoint = bez->points[i].p3;
762   }
763 }
764 
765 static void
new_handles_and_connections(BezierShape * bezier,int num_points)766 new_handles_and_connections(BezierShape *bezier, int num_points)
767 {
768   DiaObject *obj;
769   int i;
770 
771   obj = &bezier->object;
772 
773   for (i = 0; i < num_points-1; i++) {
774     obj->handles[3*i] = g_new0(Handle,1);
775     obj->handles[3*i+1] = g_new0(Handle,1);
776     obj->handles[3*i+2] = g_new0(Handle,1);
777 
778     obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
779     obj->handles[3*i]->connected_to = NULL;
780     obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
781     obj->handles[3*i]->id = HANDLE_RIGHTCTRL;
782 
783     obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
784     obj->handles[3*i+1]->connected_to = NULL;
785     obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
786     obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
787 
788     obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
789     obj->handles[3*i+2]->connected_to = NULL;
790     obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
791     obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;
792 
793     obj->connections[2*i] = g_new0(ConnectionPoint, 1);
794     obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
795     obj->connections[2*i]->object = obj;
796     obj->connections[2*i+1]->object = obj;
797     obj->connections[2*i]->flags = 0;
798     obj->connections[2*i+1]->flags = 0;
799   }
800 
801   /** Main point */
802   obj->connections[obj->num_connections-1] = g_new0(ConnectionPoint, 1);
803   obj->connections[obj->num_connections-1]->object = obj;
804   obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
805 }
806 
807 void
beziershape_init(BezierShape * bezier,int num_points)808 beziershape_init(BezierShape *bezier, int num_points)
809 {
810   DiaObject *obj;
811   int i;
812 
813   obj = &bezier->object;
814 
815   object_init(obj, 3*(num_points-1), 2*(num_points-1) + 1);
816 
817   bezier->numpoints = num_points;
818 
819   bezier->points = g_new(BezPoint, num_points);
820   bezier->points[0].type = BEZ_MOVE_TO;
821   bezier->corner_types = g_new(BezCornerType, num_points);
822   for (i = 1; i < num_points; i++) {
823     bezier->points[i].type = BEZ_CURVE_TO;
824     bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
825   }
826 
827   new_handles_and_connections(bezier, num_points);
828 
829   /* The points are not assigned at this point, so don't try to use
830      them */
831   /*  beziershape_update_data(bezier);*/
832 }
833 
834 
835 /** This function does *not* set up handles */
836 void
beziershape_set_points(BezierShape * bez,int num_points,BezPoint * points)837 beziershape_set_points(BezierShape *bez, int num_points, BezPoint *points)
838 {
839   int i;
840 
841   bez->numpoints = num_points;
842 
843   if (bez->points)
844     g_free(bez->points);
845 
846   bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
847 
848   for (i=0;i<bez->numpoints;i++) {
849     bez->points[i] = points[i];
850   }
851 }
852 
853 void
beziershape_copy(BezierShape * from,BezierShape * to)854 beziershape_copy(BezierShape *from, BezierShape *to)
855 {
856   int i;
857   DiaObject *toobj, *fromobj;
858 
859   toobj = &to->object;
860   fromobj = &from->object;
861 
862   object_copy(fromobj, toobj);
863 
864   to->numpoints = from->numpoints;
865 
866   to->points = g_new(BezPoint, to->numpoints);
867   to->corner_types = g_new(BezCornerType, to->numpoints);
868 
869   for (i = 0; i < to->numpoints; i++) {
870     to->points[i] = from->points[i];
871     to->corner_types[i] = from->corner_types[i];
872   }
873 
874   for (i = 0; i < toobj->num_handles; i++) {
875     toobj->handles[i] = g_new0(Handle,1);
876     setup_handle(toobj->handles[i], fromobj->handles[i]->id);
877   }
878   for (i = 0; i < toobj->num_connections; i++) {
879     toobj->connections[i] = g_new0(ConnectionPoint, 1);
880     toobj->connections[i]->object = &to->object;
881     toobj->connections[i]->flags = fromobj->connections[i]->flags;
882   }
883 
884   memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
885   beziershape_update_data(to);
886 }
887 
888 void
beziershape_destroy(BezierShape * bezier)889 beziershape_destroy(BezierShape *bezier)
890 {
891   int i;
892   Handle **temp_handles;
893   ConnectionPoint **temp_cps;
894 
895   /* Need to store these temporary since object.handles is
896      freed by object_destroy() */
897   temp_handles = g_new(Handle *, bezier->object.num_handles);
898   for (i = 0; i < bezier->object.num_handles; i++)
899     temp_handles[i] = bezier->object.handles[i];
900 
901   temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
902   for (i = 0; i < bezier->object.num_connections; i++)
903     temp_cps[i] = bezier->object.connections[i];
904 
905   object_destroy(&bezier->object);
906 
907   for (i = 0; i < bezier->object.num_handles; i++)
908     g_free(temp_handles[i]);
909   g_free(temp_handles);
910 
911   for (i = 0; i < bezier->object.num_connections; i++)
912     g_free(temp_cps[i]);
913   g_free(temp_cps);
914 
915   g_free(bezier->points);
916   g_free(bezier->corner_types);
917 }
918 
919 
920 void
beziershape_save(BezierShape * bezier,ObjectNode obj_node)921 beziershape_save(BezierShape *bezier, ObjectNode obj_node)
922 {
923   int i;
924   AttributeNode attr;
925 
926   object_save(&bezier->object, obj_node);
927 
928   attr = new_attribute(obj_node, "bez_points");
929 
930   data_add_point(attr, &bezier->points[0].p1);
931   for (i = 1; i < bezier->numpoints; i++) {
932     data_add_point(attr, &bezier->points[i].p1);
933     data_add_point(attr, &bezier->points[i].p2);
934     if (i < bezier->numpoints - 1)
935       data_add_point(attr, &bezier->points[i].p3);
936   }
937 
938   attr = new_attribute(obj_node, "corner_types");
939   for (i = 0; i < bezier->numpoints; i++)
940     data_add_enum(attr, bezier->corner_types[i]);
941 }
942 
943 void
beziershape_load(BezierShape * bezier,ObjectNode obj_node)944 beziershape_load(BezierShape *bezier, ObjectNode obj_node)
945 {
946   int i;
947   AttributeNode attr;
948   DataNode data;
949 
950   DiaObject *obj = &bezier->object;
951 
952   object_load(obj, obj_node);
953 
954   attr = object_find_attribute(obj_node, "bez_points");
955 
956   if (attr != NULL)
957     bezier->numpoints = attribute_num_data(attr) / 3 + 1;
958   else
959     bezier->numpoints = 0;
960 
961   object_init(obj, 3 * (bezier->numpoints - 1),
962 	      2 * (bezier->numpoints - 1) + 1);
963 
964   data = attribute_first_data(attr);
965   if (bezier->numpoints != 0) {
966     bezier->points = g_new(BezPoint, bezier->numpoints);
967     bezier->points[0].type = BEZ_MOVE_TO;
968     data_point(data, &bezier->points[0].p1);
969     bezier->points[0].p3 = bezier->points[0].p1;
970     data = data_next(data);
971 
972     for (i = 1; i < bezier->numpoints; i++) {
973       bezier->points[i].type = BEZ_CURVE_TO;
974       data_point(data, &bezier->points[i].p1);
975       data = data_next(data);
976       data_point(data, &bezier->points[i].p2);
977       data = data_next(data);
978       if (i < bezier->numpoints - 1) {
979 	data_point(data, &bezier->points[i].p3);
980 	data = data_next(data);
981       } else
982 	bezier->points[i].p3 = bezier->points[0].p1;
983     }
984   }
985 
986   bezier->corner_types = g_new(BezCornerType, bezier->numpoints);
987   attr = object_find_attribute(obj_node, "corner_types");
988   if (!attr || attribute_num_data(attr) != bezier->numpoints) {
989     for (i = 0; i < bezier->numpoints; i++)
990       bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
991   } else {
992     data = attribute_first_data(attr);
993     for (i = 0; i < bezier->numpoints; i++) {
994       bezier->corner_types[i] = data_enum(data);
995       data = data_next(data);
996     }
997   }
998 
999   for (i = 0; i < bezier->numpoints - 1; i++) {
1000     obj->handles[3*i] = g_new0(Handle,1);
1001     obj->handles[3*i+1] = g_new0(Handle,1);
1002     obj->handles[3*i+2]   = g_new0(Handle,1);
1003 
1004     setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
1005     setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
1006     setup_handle(obj->handles[3*i+2],   HANDLE_BEZMAJOR);
1007   }
1008   for (i = 0; i < obj->num_connections; i++) {
1009     obj->connections[i] = g_new0(ConnectionPoint, 1);
1010     obj->connections[i]->object = obj;
1011   }
1012   obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
1013 
1014   beziershape_update_data(bezier);
1015 }
1016 
1017 static void
beziershape_point_change_free(struct BezPointChange * change)1018 beziershape_point_change_free(struct BezPointChange *change)
1019 {
1020   if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
1021        (change->type==TYPE_REMOVE_POINT && change->applied) ){
1022     g_free(change->handle1);
1023     g_free(change->handle2);
1024     g_free(change->handle3);
1025     g_free(change->cp1);
1026     g_free(change->cp2);
1027     change->handle1 = NULL;
1028     change->handle2 = NULL;
1029     change->handle3 = NULL;
1030     change->cp1 = NULL;
1031     change->cp2 = NULL;
1032   }
1033 }
1034 
1035 static void
beziershape_point_change_apply(struct BezPointChange * change,DiaObject * obj)1036 beziershape_point_change_apply(struct BezPointChange *change, DiaObject *obj)
1037 {
1038   change->applied = 1;
1039   switch (change->type) {
1040   case TYPE_ADD_POINT:
1041     add_handles((BezierShape *)obj, change->pos, &change->point,
1042 		change->corner_type,
1043 		change->handle1, change->handle2, change->handle3,
1044 		change->cp1, change->cp2);
1045     break;
1046   case TYPE_REMOVE_POINT:
1047     object_unconnect(obj, change->handle1);
1048     object_unconnect(obj, change->handle2);
1049     object_unconnect(obj, change->handle3);
1050     remove_handles((BezierShape *)obj, change->pos);
1051     break;
1052   }
1053 }
1054 
1055 static void
beziershape_point_change_revert(struct BezPointChange * change,DiaObject * obj)1056 beziershape_point_change_revert(struct BezPointChange *change, DiaObject *obj)
1057 {
1058   switch (change->type) {
1059   case TYPE_ADD_POINT:
1060     remove_handles((BezierShape *)obj, change->pos);
1061     break;
1062   case TYPE_REMOVE_POINT:
1063     add_handles((BezierShape *)obj, change->pos, &change->point,
1064 		change->corner_type,
1065 		change->handle1, change->handle2, change->handle3,
1066 		change->cp1, change->cp2);
1067     break;
1068   }
1069   change->applied = 0;
1070 }
1071 
1072 static ObjectChange *
beziershape_create_point_change(BezierShape * bezier,enum change_type type,BezPoint * point,BezCornerType corner_type,int pos,Handle * handle1,Handle * handle2,Handle * handle3,ConnectionPoint * cp1,ConnectionPoint * cp2)1073 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
1074 				BezPoint *point, BezCornerType corner_type,
1075 				int pos,
1076 				Handle *handle1, Handle *handle2,
1077 				Handle *handle3,
1078 				ConnectionPoint *cp1, ConnectionPoint *cp2)
1079 {
1080   struct BezPointChange *change;
1081 
1082   change = g_new(struct BezPointChange, 1);
1083 
1084   change->obj_change.apply =
1085     (ObjectChangeApplyFunc)beziershape_point_change_apply;
1086   change->obj_change.revert =
1087     (ObjectChangeRevertFunc)beziershape_point_change_revert;
1088   change->obj_change.free =
1089     (ObjectChangeFreeFunc)beziershape_point_change_free;
1090 
1091   change->type = type;
1092   change->applied = 1;
1093   change->point = *point;
1094   change->corner_type = corner_type;
1095   change->pos = pos;
1096   change->handle1 = handle1;
1097   change->handle2 = handle2;
1098   change->handle3 = handle3;
1099   change->cp1 = cp1;
1100   change->cp2 = cp2;
1101 
1102   return (ObjectChange *)change;
1103 }
1104 
1105 static void
beziershape_corner_change_apply(struct CornerChange * change,DiaObject * obj)1106 beziershape_corner_change_apply(struct CornerChange *change, DiaObject *obj)
1107 {
1108   BezierShape *bez = (BezierShape *)obj;
1109   int handle_nr = get_handle_nr(bez, change->handle);
1110   int comp_nr = get_major_nr(handle_nr);
1111 
1112   beziershape_straighten_corner(bez, comp_nr);
1113 
1114   bez->corner_types[comp_nr] = change->new_type;
1115   if (comp_nr == 0)
1116     bez->corner_types[bez->numpoints-1] = change->new_type;
1117   if (comp_nr == bez->numpoints - 1)
1118     bez->corner_types[0] = change->new_type;
1119 
1120   change->applied = 1;
1121 }
1122 
1123 static void
beziershape_corner_change_revert(struct CornerChange * change,DiaObject * obj)1124 beziershape_corner_change_revert(struct CornerChange *change, DiaObject *obj)
1125 {
1126   BezierShape *bez = (BezierShape *)obj;
1127   int handle_nr = get_handle_nr(bez, change->handle);
1128   int comp_nr = get_major_nr(handle_nr);
1129 
1130   bez->points[comp_nr].p2 = change->point_left;
1131   if (comp_nr == bez->numpoints - 1)
1132     bez->points[1].p1 = change->point_right;
1133   else
1134     bez->points[comp_nr+1].p1 = change->point_right;
1135   bez->corner_types[comp_nr] = change->old_type;
1136   if (comp_nr == 0)
1137     bez->corner_types[bez->numpoints-1] = change->new_type;
1138   if (comp_nr == bez->numpoints - 1)
1139     bez->corner_types[0] = change->new_type;
1140 
1141   change->applied = 0;
1142 }
1143 
1144 static ObjectChange *
beziershape_create_corner_change(BezierShape * bez,Handle * handle,Point * point_left,Point * point_right,BezCornerType old_corner_type,BezCornerType new_corner_type)1145 beziershape_create_corner_change(BezierShape *bez, Handle *handle,
1146 				 Point *point_left, Point *point_right,
1147 				 BezCornerType old_corner_type,
1148 				 BezCornerType new_corner_type)
1149 {
1150   struct CornerChange *change;
1151 
1152   change = g_new(struct CornerChange, 1);
1153 
1154   change->obj_change.apply =
1155     (ObjectChangeApplyFunc)beziershape_corner_change_apply;
1156   change->obj_change.revert =
1157     (ObjectChangeRevertFunc)beziershape_corner_change_revert;
1158   change->obj_change.free = (ObjectChangeFreeFunc)NULL;
1159 
1160   change->old_type = old_corner_type;
1161   change->new_type = new_corner_type;
1162   change->applied = 1;
1163 
1164   change->handle = handle;
1165   change->point_left = *point_left;
1166   change->point_right = *point_right;
1167 
1168   return (ObjectChange *)change;
1169 }
1170