1 /* object.c */
2 /* COPYRIGHT (C) 2000 THE VICTORIA UNIVERSITY OF MANCHESTER and John Levon
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by the Free
5 * Software Foundation; either version 2 of the License, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15 * Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17 /* functions for dealing with Objects */
18 /*
19 * $Log: object.c,v $
20 * Revision 1.4 2000/12/17 00:57:42 moz
21 * examples, filled open splines, highlight_objects
22 *
23 * Revision 1.3 2000/12/06 20:56:03 moz
24 * GPL stuff.
25 *
26 * Revision 1.2 2000/08/21 01:30:31 moz
27 * Re-calculate cache on spline enlarge to avoid low-res artefacts.
28 *
29 * Revision 1.1.1.1 2000/08/21 01:05:31 moz
30 *
31 *
32 * Revision 1.1.1.1 2000/07/19 22:45:30 moz
33 * CVS Import
34 *
35 * Revision 1.43 2000/03/13 01:50:12 moz
36 * Fixed add_object0() bug.
37 *
38 * Revision 1.42 2000/03/12 23:33:49 moz
39 * Fixed stupid typo reversing text lines !
40 *
41 * Revision 1.41 2000/03/12 04:13:40 moz
42 * Derry to node ellipses correctly.
43 *
44 * Revision 1.40 2000/03/11 19:10:15 moz
45 * Fix calc to recalc_text_box().
46 *
47 * Revision 1.39 2000/03/11 18:19:50 moz
48 * Adjust text box position to stay static on scale.
49 *
50 * Revision 1.38 2000/03/06 02:28:42 moz
51 * Compile fixes.
52 *
53 * Revision 1.37 2000/02/23 02:20:41 moz
54 * Was forgetting to NULL polyline.pic !.
55 *
56 * Revision 1.36 2000/02/21 03:53:13 moz
57 * Backed out debugging message.
58 *
59 * Revision 1.35 2000/02/18 21:14:15 moz
60 * polyline.pic fix.
61 * Some compile fixes.
62 *
63 * Revision 1.34 2000/02/18 04:06:27 moz
64 * Was accessing freed mem in remove_derry() ...
65 *
66 * Revision 1.33 2000/02/18 03:04:57 moz
67 * kill_object() should free derries.
68 *
69 * Revision 1.32 2000/02/17 22:25:56 moz
70 * Backed out debugging change.
71 *
72 * Revision 1.31 2000/02/04 02:17:38 moz
73 * kill_object() should free arcellipse arrows.
74 *
75 * Revision 1.30 2000/02/04 01:54:59 moz
76 * switch_icons() needs view
77 *
78 * Revision 1.29 2000/01/29 23:51:37 moz
79 * Small fix.
80 *
81 * Revision 1.28 2000/01/29 04:13:16 moz
82 * Removed more wrong v_error().
83 *
84 * Revision 1.27 2000/01/29 03:58:37 moz
85 * Default for get_nearest_point isn't an error.
86 *
87 * Revision 1.26 2000/01/26 18:50:23 moz
88 * kill_object should free arrows.
89 *
90 * Revision 1.25 2000/01/26 18:19:53 moz
91 * Changes to allow FIG_DEBUG_LIST_ALLOC.
92 *
93 * Revision 1.24 1999/11/29 20:34:40 moz
94 * BBSIZE() renamed to avoid IRIX param.h namespace clash.
95 *
96 * Revision 1.23 1999/11/15 02:06:34 moz
97 * Use rounded trig.
98 * Pick smallest object under cursor.
99 *
100 * Revision 1.22 1999/08/17 00:35:24 moz
101 * Fix malloc_node.
102 *
103 * Revision 1.21 1999/08/08 20:55:05 moz
104 * From clean up of structs.
105 *
106 * Revision 1.20 1999/07/29 20:44:35 moz
107 * Make sure rottext resize rubber box is redrawn by apply_scale...().
108 *
109 * Revision 1.19 1999/07/04 15:19:49 moz
110 * Only draw scaled compounds when finished.
111 *
112 * Revision 1.18 1999/07/04 03:13:59 moz
113 * Changes to derrying to an ellipse.
114 *
115 * Revision 1.17 1999/05/31 23:05:18 moz
116 * Spline curves contribute interpolated/angular points to attachment.
117 *
118 * Revision 1.16 1999/05/22 02:52:56 moz
119 * Correctly recalculate a scaled compound's bounding box.
120 *
121 * Revision 1.15 1999/05/19 17:08:37 moz
122 * 1.0 Checkin.
123 *
124 * Revision 1.14 1999/05/05 00:38:05 moz
125 * Derry to arcellipses.
126 *
127 * Revision 1.13 1999/05/04 20:09:57 moz
128 * Remove derries on rotate and scale.
129 *
130 * Revision 1.12 1999/05/04 20:01:45 moz
131 * Move targetee.
132 *
133 * Revision 1.11 1999/05/04 15:50:17 moz
134 * Merged in duplicate_object() and kill_object().
135 *
136 * Revision 1.10 1999/05/03 05:54:49 moz
137 * Scale text in compound.
138 *
139 * Revision 1.9 1999/04/27 16:58:26 moz
140 * Set text angle to zero if close (3 degrees).
141 *
142 * Revision 1.8 1999/04/27 16:55:59 moz
143 * Flip object.
144 *
145 * Revision 1.7 1999/04/27 04:11:33 moz
146 * Use calc_text_outer_box.
147 * -Wall appeasement.
148 *
149 * Revision 1.6 1999/04/26 19:59:55 moz
150 * move_object function added.
151 *
152 * Revision 1.5 1999/04/25 19:39:37 moz
153 * Rotate text's bounding box.
154 *
155 * Revision 1.4 1999/04/22 22:16:46 moz
156 * Don't register undos for scaling subobjects.
157 *
158 * Revision 1.3 1999/04/22 20:55:18 moz
159 * Rescale compounds within compounds correctly.
160 *
161 * Revision 1.2 1999/04/22 16:16:44 moz
162 * Imperfect fix for scaling arc circles.
163 *
164 * Revision 1.1 1999/03/30 00:05:37 moz
165 * Initial revision
166 *
167 */
168
169 #include <math.h>
170 #include <string.h>
171 #include "include/figurine.h"
172 #include "include/extern.h"
173
174 Tree malloc_node(void);
175
176 /* this calculates the correct position for a scaled object with a tied corner */
177 /* given a topleft corner, returns a new one */
178 void
corner_magic(Object * ob,long * x,long * y,double rx,double ry)179 corner_magic(Object *ob, long *x, long *y, double rx, double ry)
180 {
181 switch (state.tied_corner)
182 {
183 case TOPLEFT:
184 break;
185
186 case TOPRIGHT:
187 *x = *x + (ob->bbox.x2-ob->bbox.x1) - R(ob->bbox.x2-ob->bbox.x1,abs(rx));
188 break;
189
190 case BOTTOMLEFT:
191 *y = *y + (ob->bbox.y2-ob->bbox.y1) - R(ob->bbox.y2-ob->bbox.y1,abs(ry));
192 break;
193
194 case BOTTOMRIGHT:
195 *x = *x + (ob->bbox.x2-ob->bbox.x1) - R(ob->bbox.x2-ob->bbox.x1,abs(rx));
196 *y = *y + (ob->bbox.y2-ob->bbox.y1) - R(ob->bbox.y2-ob->bbox.y1,abs(ry));
197 break;
198
199 default:
200 v_error(state.tied_corner);
201 break;
202 };
203
204 }
205
206 Tree
malloc_node(void)207 malloc_node(void)
208 {
209 return (Tree)malloc(sizeof(Node));
210 }
211
212 /* add an object to object tree and list */
213 Tree
add_object(Tree t,List * l,Object * ob)214 add_object(Tree t, List *l, Object *ob)
215 {
216 if ((t=add_object0(t,ob))!=NULL)
217 *l = add_to_list(*l,(ulong)ob,ob->type,(void *)ob);
218 return t;
219 }
220
221 /* insert an object in object tree */
222 Tree
add_object0(Tree t,Object * ob)223 add_object0(Tree t, Object *ob)
224 {
225 Tree tp,tp2;
226
227 if (t==NULL) /* empty tree */
228 {
229 tp = malloc_node();
230 if (tp==NULL)
231 return NULL;
232
233 tp->left = NULL;
234 tp->right = NULL;
235 tp->ob = ob;
236 tp->bbox = ob->bbox;
237 tp->up = NULL;
238
239 return tp;
240 }
241 else if (t->ob!=NULL) /* node */
242 {
243 tp = malloc_node();
244 if (tp==NULL)
245 return NULL;
246 tp->left = NULL;
247 tp->right = NULL;
248 tp->ob = ob;
249 tp->bbox = ob->bbox;
250
251 tp2 = malloc_node();
252 if (tp2==NULL)
253 return NULL;
254 tp2->left = t; /* this is used in algorithm ! */
255 tp2->right = tp;
256 tp2->up = NULL;
257 tp->up = tp2;
258 t->up = tp2;
259 tp2->ob = NULL;
260 tp2->bbox = merge_boxes(tp2->left->bbox, tp2->right->bbox);
261
262 return tp2;
263 }
264 else
265 {
266 /* not at a node yet */
267 /* if no recursing */
268 if (!is_contained_v(t->left->bbox,ob->bbox) && !is_contained_v(t->right->bbox,ob->bbox))
269 {
270 tp = malloc_node();
271 if (tp==NULL)
272 return NULL;
273 tp->left = NULL;
274 tp->right = NULL;
275 tp->ob = ob;
276 tp->bbox = ob->bbox;
277
278 tp2 = malloc_node();
279 if (tp2==NULL)
280 return NULL;
281
282 if ( (FIG_BBSIZE(merge_boxes(t->left->bbox,ob->bbox))-FIG_BBSIZE(t->left->bbox)) <
283 (FIG_BBSIZE(merge_boxes(t->right->bbox,ob->bbox))-FIG_BBSIZE(t->right->bbox)) )
284 {
285 tp2->left = t->left;
286 tp2->right = tp;
287 tp->up = tp2;
288 t->left->up = tp2;
289 tp2->ob = NULL;
290 tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
291 t->left = tp2;
292 tp2->up = t;
293 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
294 }
295 else
296 {
297 tp2->left = t->right;
298 tp2->right = tp;
299 tp->up = tp2;
300 t->right->up = tp2;
301 tp2->ob = NULL;
302 tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
303 t->right = tp2;
304 tp2->up = t;
305 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
306 };
307
308 return t;
309 }
310 else if (!is_contained_v(t->left->bbox,t->right->bbox) &&
311 !is_contained_v(t->right->bbox,t->left->bbox))
312 {
313 /* now ob is contained with in left or right, and they are disjointish, so recurse down */
314 if (is_contained_v(t->left->bbox,ob->bbox))
315 {
316 t->left = add_object0(t->left,ob);
317 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
318 t->left->up = t;
319 return t;
320 }
321 else
322 {
323 t->right = add_object0(t->right,ob);
324 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
325 t->right->up = t;
326 return t;
327 };
328 }
329 else if (is_contained_v(t->left->bbox,t->right->bbox))
330 {
331 /* now right is inside left, so join right with ob */
332 if (is_contained_v(t->right->bbox,ob->bbox))
333 {
334 t->right = add_object0(t->right,ob);
335 t->bbox = merge_boxes(t->left->bbox, t->right->bbox);
336 t->right->up = t;
337 return t;
338 }
339 else
340 {
341 tp = malloc_node();
342 if (tp==NULL)
343 return NULL;
344 tp->left = NULL;
345 tp->right = NULL;
346 tp->ob = ob;
347 tp->bbox = ob->bbox;
348 tp2 = malloc_node();
349 if (tp2==NULL)
350 return NULL;
351 tp2->left = tp;
352 tp2->right = t->right;
353 tp->up = tp2;
354 t->right->up = tp2;
355 tp2->ob = NULL;
356 tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
357 t->right = tp2;
358 tp2->up = t;
359 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
360 return t;
361 };
362 }
363 else
364 {
365 /* now left is inside right, so join left with ob */
366 if (is_contained_v(t->left->bbox,ob->bbox))
367 {
368 t->left = add_object0(t->left,ob);
369 t->bbox = merge_boxes(t->left->bbox, t->right->bbox);
370 t->left->up = t;
371 return t;
372 }
373 else
374 {
375 tp = malloc_node();
376 if (tp==NULL)
377 return NULL;
378 tp->left = NULL;
379 tp->right = NULL;
380 tp->ob = ob;
381 tp->bbox = ob->bbox;
382 tp2 = malloc_node();
383 if (tp2==NULL)
384 return NULL;
385 tp2->left = tp;
386 tp2->right = t->left;
387 tp->up = tp2;
388 t->left->up = tp2;
389 tp2->ob = NULL;
390 tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
391 t->left = tp2;
392 tp2->up = t;
393 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
394 return t;
395 };
396
397 };
398 };
399
400 }
401
402 /* return leaf of tree t containing ob */
403 Node *
get_object_node(Tree t,Object * ob)404 get_object_node(Tree t, Object *ob)
405 {
406 Node *t2;
407
408 if (t==NULL)
409 return NULL;
410
411 if (t->left==NULL && t->right==NULL)
412 {
413 if (ob==t->ob)
414 return t;
415 else
416 return NULL;
417 };
418
419 if ((t2=get_object_node(t->left,ob))!=NULL)
420 return t2;
421
422 if ((t2=get_object_node(t->right,ob))!=NULL)
423 return t2;
424
425 return NULL;
426 }
427
428 /* find an object by its ticket value */
429 /* this is used for derrying support */
430 /* node ellipses must be special-cased */
431 Object *
get_object_by_ticket(Tree t,ulong ticket)432 get_object_by_ticket(Tree t, ulong ticket)
433 {
434 Object *ob;
435
436 if (t==NULL)
437 return NULL;
438
439 if (t->left==NULL && t->right==NULL)
440 {
441 if (ticket==t->ob->ticket)
442 return t->ob;
443 else if (t->ob->type==TEXT && t->ob->ob.text.node && ticket==t->ob->ob.text.ellipse->ticket)
444 return t->ob->ob.text.ellipse;
445 else
446 return NULL;
447 };
448
449 if ((ob=get_object_by_ticket(t->left,ticket))!=NULL)
450 return ob;
451
452 if ((ob=get_object_by_ticket(t->right,ticket))!=NULL)
453 return ob;
454
455 return NULL;
456 }
457
458 /* take object off tree and list */
459 Tree
trash_object(Tree t,List * lo,Object * ob)460 trash_object(Tree t, List *lo, Object *ob)
461 {
462 t = delete_object(t,ob);
463 *lo = delete_from_list(*lo, (ulong)ob);
464 return t;
465 }
466
467 /* take object off tree */
468 Tree
delete_object(Tree t,Object * ob)469 delete_object(Tree t, Object *ob)
470 {
471 Node *a;
472
473 if (t==NULL)
474 return NULL;
475
476 if (t->left==NULL && t->right==NULL)
477 {
478 if (t->ob==ob)
479 {
480 free(t);
481 return NULL;
482 }
483 else
484 return t;
485 };
486
487 if (t->left->ob!=NULL)
488 {
489 /* left of tree */
490 if (t->left->ob==ob)
491 {
492 a = t->right;
493 t->right->up = NULL;
494 free(t->left);
495 free(t);
496 return a;
497 };
498 };
499
500 if (t->right->ob!=NULL)
501 {
502 /* right of tree */
503 if (t->right->ob==ob)
504 {
505 a = t->left;
506 t->left->up = NULL;
507 free(t->right);
508 free(t);
509 return a;
510 };
511 };
512
513 if (t->left->ob==NULL)
514 {
515 t->left = delete_object(t->left,ob);
516 t->left->up = t;
517 };
518
519 if (t->right->ob==NULL)
520 {
521 t->right = delete_object(t->right,ob);
522 t->right->up = t;
523 };
524
525 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
526 return t;
527 }
528
529 /* for small bbox changes, just propogate changes upwards */
530 void
resize_bbox(Tree t,Object * ob)531 resize_bbox(Tree t, Object *ob)
532 {
533 if (t==NULL)
534 return;
535
536 if (t->left==NULL && t->right==NULL)
537 {
538 if (t->ob->ticket==ob->ticket)
539 t->bbox = ob->bbox;
540
541 return;
542 }
543 else
544 {
545 resize_bbox(t->left,ob);
546
547 resize_bbox(t->right,ob);
548
549 t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
550 return;
551 };
552 }
553
554 /* for bbox changes, we need to reinsert to maintain a good tree */
555 Tree
change_bbox(Tree t,Object * ob)556 change_bbox(Tree t, Object *ob)
557 {
558 t = delete_object(t,ob);
559 t = add_object0(t,ob);
560 return t;
561 }
562
563 Node *
object_at_point(View * v,Tree t,long x,long y)564 object_at_point(View *v, Tree t, long x, long y)
565 {
566 List match=NULL;
567 List l;
568 Node *best=NULL;
569 ulong bsize=ULONG_MAX;
570
571 match = object_at_point0(match, v, t, x, y);
572 l = match;
573
574 while (match!=NULL)
575 {
576 if (FIG_BBSIZE(((Node *)match->data)->ob->bbox) < bsize)
577 {
578 bsize = FIG_BBSIZE(((Node *)match->data)->ob->bbox);
579 best = (Node *)match->data;
580 };
581 match=match->next;
582 };
583
584 delete_list(l);
585 return best;
586
587
588 }
589
590 /* return object at document coords x,y */
591 List
object_at_point0(List match,View * v,Tree t,long x,long y)592 object_at_point0(List match, View *v, Tree t, long x, long y)
593 {
594 if (t==NULL)
595 return match;
596
597 if (t->left==NULL && t->right==NULL)
598 {
599 if (is_in_bbox(x,y,t->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
600 t->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
601 t->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
602 t->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
603 {
604 match = add_to_list(match,0,0,(void *)t);
605 return match;
606 }
607 else
608 return match;
609 }
610 else
611 {
612 if (is_in_bbox(x,y,t->left->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
613 t->left->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
614 t->left->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
615 t->left->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
616 match = object_at_point0(match, v, t->left,x,y);
617
618 if (is_in_bbox(x,y,t->right->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
619 t->right->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
620 t->right->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
621 t->right->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
622 match = object_at_point0(match, v, t->right,x,y);
623
624 return match;
625
626 };
627 }
628
629 /* return object at pixel coords x,y */
630 Node *
object_at_point_p(View * v,Tree t,long x,long y)631 object_at_point_p(View *v, Tree t, long x, long y)
632 {
633 return object_at_point(v, t, XP2D(x,v), YP2D(y,v));
634 }
635
636 /* area coherence test: should we change object selection ? */
637 Boolean
still_same_object(Node * n,long x,long y)638 still_same_object(Node *n, long x, long y)
639 {
640 if (n==NULL)
641 return FALSE;
642
643 if (!is_in_bbox(x,y,n->bbox.x1,n->bbox.y1,n->bbox.x2,n->bbox.y2))
644 return FALSE;
645
646 if (n->up==NULL) /* only one object in tree ! */
647 return TRUE;
648
649 if (n->up->right!=n && n->up->right!=NULL && is_in_bbox(x,y,n->up->right->bbox.x1,n->up->right->bbox.y1,n->up->right->bbox.x2,n->up->right->bbox.y2))
650 return FALSE;
651
652 if (n->up->left!=n && n->up->left!=NULL && is_in_bbox(x,y,n->up->left->bbox.x1,n->up->left->bbox.y1,n->up->left->bbox.x2,n->up->left->bbox.y2))
653 return FALSE;
654
655 return TRUE;
656 }
657
658 Boolean
still_same_object_p(View * v,Node * n,long x,long y)659 still_same_object_p(View *v, Node *n, long x, long y)
660 {
661 return still_same_object(n,XP2D(x,v),YP2D(y,v));
662 }
663
664 /* return depth-sorted list of objects that intersect bb */
665 List
intersecting_objects(Tree t,VRegion bb,List l)666 intersecting_objects(Tree t, VRegion bb, List l)
667 {
668 List l2;
669
670 if (t==NULL)
671 return l;
672 else if (t->left==NULL && t->right==NULL) /* leaf node */
673 {
674 if (intersects_v(bb,t->bbox))
675 {
676 /* why like this ? for FIG_DEBUG_LIST_ALLOC */
677 l2 = add_to_list(l,ULONG_MAX-t->ob->depth,0,(void *)t->ob);
678 return l2;
679 }
680 else
681 return l;
682 }
683 else
684 {
685 if (t->left!=NULL && intersects_v(bb,t->left->bbox))
686 l = intersecting_objects(t->left,bb,l);
687
688 if (t->right!=NULL && intersects_v(bb,t->right->bbox))
689 return intersecting_objects(t->right,bb,l);
690
691 return l;
692 };
693
694 }
695
696 /* return depth-sorted list of objects that intersect cx,cy */
697 List
intersecting_objects_point(Tree t,long cx,long cy,List l)698 intersecting_objects_point(Tree t, long cx, long cy, List l)
699 {
700 List l2;
701
702 if (t==NULL)
703 return l;
704 else if (t->left==NULL && t->right==NULL) /* leaf node */
705 {
706 if (is_in_object(cx,cy,t->ob))
707 {
708 /* why like this ? for FIG_DEBUG_LIST_ALLOC */
709 l2 = add_to_list(l,0,0,(void *)t->ob);
710 return l2;
711 }
712 else
713 return l;
714 }
715 else
716 {
717 if (t->left!=NULL && is_in_bbox(cx,cy,t->left->bbox.x1,t->left->bbox.y1,t->left->bbox.x2,t->left->bbox.y2))
718 l = intersecting_objects_point(t->left,cx,cy,l);
719
720 if (t->right!=NULL && is_in_bbox(cx,cy,t->right->bbox.x1,t->right->bbox.y1,t->right->bbox.x2,t->right->bbox.y2))
721 return intersecting_objects_point(t->right,cx,cy,l);
722
723 return l;
724 };
725 }
726
727 /* free all mem held by ob */
728 void
kill_object(Object * ob)729 kill_object(Object *ob)
730 {
731 List l,l2;
732
733 switch (ob->type)
734 {
735 case TEXT:
736 if (ob->ob.text.node && ob->ob.text.ellipse!=NULL)
737 free(ob->ob.text.ellipse);
738
739 l = ob->ob.text.lines;
740
741 while (l!=NULL)
742 {
743 l2 = TEXTLINE(l)->sections;
744 while (l2!=NULL)
745 {
746 free(TEXTSEC(l2));
747 l2 = l2->next;
748 };
749 delete_list(TEXTLINE(l)->sections);
750 free(TEXTLINE(l));
751 l = l->next;
752 };
753 delete_list(ob->ob.text.lines);
754 l=ob->derries;
755 while (l!=NULL)
756 {
757 free(DERRY(l));
758 l=l->next;
759 };
760 delete_list(ob->derries);
761 free(ob);
762 break;
763
764 case POLYLINE:
765 case POLYGON:
766 l = ob->ob.polyline.points;
767
768 while (l!=NULL)
769 {
770 free (POINT(l));
771 l = l->next;
772 };
773
774 if (ob->ob.polyline.pic)
775 free (ob->ob.polyline.pic);
776
777 if (ob->farrow)
778 free(ob->farrow);
779 if (ob->barrow)
780 free(ob->barrow);
781
782 delete_list(ob->ob.polyline.points);
783 l=ob->derries;
784 while (l!=NULL)
785 {
786 free(DERRY(l));
787 l=l->next;
788 };
789 delete_list(ob->derries);
790 free(ob);
791 break;
792
793 case SPLINE:
794 case ARC:
795 l = ob->ob.spline.points;
796 while (l!=NULL)
797 {
798 free(SPOINT(l));
799 l = l->next;
800 };
801
802 delete_list(ob->ob.spline.points);
803
804 l = ob->ob.spline.cache;
805 while (l!=NULL)
806 {
807 free(POINT(l));
808 l = l->next;
809 };
810
811 if (ob->farrow)
812 free(ob->farrow);
813 if (ob->barrow)
814 free(ob->barrow);
815
816 l=ob->derries;
817 while (l!=NULL)
818 {
819 free(DERRY(l));
820 l=l->next;
821 };
822 delete_list(ob->derries);
823 delete_list(ob->ob.spline.cache);
824 free(ob);
825 break;
826
827 case ARCELLIPSE:
828 if (ob->farrow)
829 free(ob->farrow);
830 if (ob->barrow)
831 free(ob->barrow);
832 l=ob->derries;
833 while (l!=NULL)
834 {
835 free(DERRY(l));
836 l=l->next;
837 };
838 delete_list(ob->derries);
839 free(ob);
840 break;
841
842 case ELLIPSE:
843 case ROUNDBOX:
844 l=ob->derries;
845 while (l!=NULL)
846 {
847 free(DERRY(l));
848 l=l->next;
849 };
850 delete_list(ob->derries);
851 free(ob);
852 break;
853
854 case COMPOUND:
855
856 l = ob->ob.compound.obs;
857 while (l!=NULL)
858 {
859 kill_object(OB(l));
860 l = l->next;
861 };
862 delete_list(ob->ob.compound.obs);
863 l=ob->derries;
864 while (l!=NULL)
865 {
866 free(DERRY(l));
867 l=l->next;
868 };
869 delete_list(ob->derries);
870 free(ob);
871 break;
872
873 default:
874 v_error(ob->type);
875 break;
876 };
877 }
878
879 /* duplicate an object. the ticket value is altered */
880 Object *
duplicate_object(Object * ob)881 duplicate_object(Object *ob)
882 {
883 List l,l2;
884 Object *nob;
885 TextLine *tl;
886 TextSection *ts;
887 VPoint *p;
888 SPoint *sp;
889 uint c=0;
890 uint c2=0;
891
892 nob = (Object *)malloc(sizeof(Object));
893
894 nob->type = ob->type;
895 nob->ticket = ob_ticket++;
896 nob->derries = NULL;
897 nob->depth = ob->depth;
898 nob->colour = ob->colour;
899 nob->fillcolour = ob->fillcolour;
900 nob->ls = ob->ls;
901 nob->lw = ob->lw;
902 nob->es = ob->es;
903 nob->js = ob->js;
904 nob->fs = ob->fs;
905 nob->dash = ob->dash;
906 nob->gap = ob->gap;
907 nob->bbox = ob->bbox;
908 if (ob->farrow!=NULL)
909 nob->farrow = make_arrow(*ob->farrow);
910 else
911 nob->farrow = NULL;
912 if (ob->barrow!=NULL)
913 nob->barrow = make_arrow(*ob->barrow);
914 else
915 nob->barrow = NULL;
916
917 switch (ob->type)
918 {
919 case TEXT:
920 if (ob->ob.text.node && ob->ob.text.ellipse!=NULL)
921 nob->ob.text.ellipse = (void *)duplicate_object(ob->ob.text.ellipse);
922
923 nob->ob.text.node = ob->ob.text.node;
924 nob->ob.text.bbox = ob->ob.text.bbox;
925 nob->ob.text.angle = ob->ob.text.angle;
926 nob->ob.text.lines = NULL;
927 l = ob->ob.text.lines;
928
929 while (l!=NULL)
930 {
931 tl = (TextLine *)malloc(sizeof(TextLine));
932 tl->y = TEXTLINE(l)->y;
933 tl->h = TEXTLINE(l)->h;
934 tl->just = TEXTLINE(l)->just;
935 tl->sections = NULL;
936 l2 = TEXTLINE(l)->sections;
937 while (l2!=NULL)
938 {
939 ts = (TextSection *)malloc(sizeof(TextSection));
940
941 ts->num = TEXTSEC(l2)->num;
942 ts->size = TEXTSEC(l2)->size;
943 strcpy(ts->text,TEXTSEC(l2)->text);
944 ts->x = TEXTSEC(l2)->x;
945 ts->y = TEXTSEC(l2)->y;
946 ts->w = TEXTSEC(l2)->w;
947 ts->h = TEXTSEC(l2)->h;
948 tl->sections = add_to_list(tl->sections, c,0,(void *)ts);
949 c++;
950
951 l2 = l2->next;
952 };
953 nob->ob.text.lines = add_to_list(nob->ob.text.lines,c2,0, (void *)tl);
954 c2++;
955 l = l->next;
956 };
957 break;
958
959 case POLYLINE:
960 case POLYGON:
961 l = ob->ob.polyline.points;
962
963 if (ob->ob.polyline.pic)
964 {
965 nob->ob.polyline.pic = malloc(strlen(ob->ob.polyline.pic)+1);
966 strcpy(nob->ob.polyline.pic, ob->ob.polyline.pic);
967 }
968 else
969 nob->ob.polyline.pic = NULL;
970
971 nob->ob.polyline.points = NULL;
972
973 while (l!=NULL)
974 {
975 p = (VPoint *)malloc(sizeof(VPoint));
976
977 p->x = POINT(l)->x;
978 p->y = POINT(l)->y;
979 p->derried = FALSE;
980 nob->ob.polyline.points = add_to_list(nob->ob.polyline.points,c,0,(void *)p);
981 c++;
982
983 l = l->next;
984 };
985 break;
986
987 case ROUNDBOX:
988 nob->ob.roundbox.radius = ob->ob.roundbox.radius;
989 break;
990
991 case SPLINE:
992 case ARC:
993 nob->ob.spline.closed = ob->ob.spline.closed;
994 nob->ob.spline.cache = NULL;
995 nob->ob.spline.points = NULL;
996
997 l = ob->ob.spline.points;
998 while (l!=NULL)
999 {
1000 sp = (SPoint *)malloc(sizeof(SPoint));
1001
1002 sp->x = SPOINT(l)->x;
1003 sp->y = SPOINT(l)->y;
1004 sp->derried = FALSE;
1005 sp->s = SPOINT(l)->s;
1006 nob->ob.spline.points = add_to_list(nob->ob.spline.points,c,0,(void *)sp);
1007 c++;
1008 l = l->next;
1009 };
1010 break;
1011
1012 case ELLIPSE:
1013 nob->ob.ellipse.centre.x = ob->ob.ellipse.centre.x;
1014 nob->ob.ellipse.centre.y = ob->ob.ellipse.centre.y;
1015 nob->ob.ellipse.xradius = ob->ob.ellipse.xradius;
1016 nob->ob.ellipse.yradius = ob->ob.ellipse.yradius;
1017 break;
1018
1019 case ARCELLIPSE:
1020 nob->ob.arcellipse.centre.x = ob->ob.arcellipse.centre.x;
1021 nob->ob.arcellipse.centre.y = ob->ob.arcellipse.centre.y;
1022 nob->ob.arcellipse.xradius = ob->ob.arcellipse.xradius;
1023 nob->ob.arcellipse.yradius = ob->ob.arcellipse.yradius;
1024 nob->ob.arcellipse.start = ob->ob.arcellipse.start;
1025 nob->ob.arcellipse.end = ob->ob.arcellipse.end;
1026 nob->ob.arcellipse.open = ob->ob.arcellipse.open;
1027 break;
1028
1029 case COMPOUND:
1030 nob->ob.compound.obs = NULL;
1031
1032 l = ob->ob.compound.obs;
1033 while (l!=NULL)
1034 {
1035 Object *tob;
1036 tob = duplicate_object(OB(l));
1037 nob->ob.compound.obs = add_to_list(nob->ob.compound.obs,ULONG_MAX-OB(l)->depth,0,(void *)tob);
1038 l = l->next;
1039 };
1040 break;
1041
1042 default:
1043 v_error(ob->type);
1044 break;
1045 };
1046
1047 return nob;
1048 }
1049
1050 /* move an object by document offset dx,dy */
1051 void
move_object(View * view,Object * ob,long dx,long dy)1052 move_object(View *view, Object *ob, long dx, long dy)
1053 {
1054 List l;
1055
1056 if (view->gridon)
1057 {
1058 dx = snap(view->grid_x,ob->bbox.x1+dx) - ob->bbox.x1;
1059 dy = snap(view->grid_y,ob->bbox.y1+dy) - ob->bbox.y1;
1060 };
1061
1062 ob->bbox.x1 += dx;
1063 ob->bbox.y1 += dy;
1064 ob->bbox.x2 += dx;
1065 ob->bbox.y2 += dy;
1066
1067 if (ob->type==COMPOUND)
1068 compound_move(view,ob,ob->bbox.x1-dx,ob->bbox.y1-dy,ob->bbox.x1,ob->bbox.y1);
1069 else if (ob->type==TEXT)
1070 {
1071 ob->ob.text.bbox.x1 += dx;
1072 ob->ob.text.bbox.y1 += dy;
1073 ob->ob.text.bbox.x2 += dx;
1074 ob->ob.text.bbox.y2 += dy;
1075 };
1076
1077 if (ob->type==POLYLINE || ob->type==POLYGON)
1078 {
1079 l=ob->ob.polyline.points;
1080 while (l!=NULL)
1081 {
1082 if (POINT(l)->derried)
1083 {
1084 /* derries to somewhere, so should stay where it is */
1085 POINT(l)->x -= dx;
1086 POINT(l)->y -= dy;
1087 recalc_polyline_bbox(ob, FALSE);
1088 view->doc->o = change_bbox(view->doc->o, ob);
1089 };
1090 l=l->next;
1091 };
1092 }
1093 else if (ob->type==SPLINE || ob->type==ARC)
1094 {
1095 l=ob->ob.spline.points;
1096 while (l!=NULL)
1097 {
1098 if (SPOINT(l)->derried)
1099 {
1100 /* derries to somewhere, so should stay where it is */
1101 SPOINT(l)->x -= dx;
1102 SPOINT(l)->y -= dy;
1103 delete_cache_list(ob->ob.spline.cache);
1104 ob->ob.spline.cache=NULL;
1105 ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,ob->ob.spline.closed);
1106 recalc_polyline_bbox(ob, TRUE);
1107 view->doc->o = change_bbox(view->doc->o, ob);
1108 };
1109 l=l->next;
1110 };
1111 };
1112
1113 view->doc->o = change_bbox(view->doc->o, ob);
1114 view->selected_object = get_object_node(view->doc->o,ob);
1115 view->highlighted_object = view->selected_object;
1116 switch_icons(view);
1117
1118 /* if derries on object, they need to be moved */
1119 if (ob->type==TEXT && ob->ob.text.node && ob->ob.text.ellipse->derries!=NULL)
1120 derry_move(view,ob->ob.text.ellipse,ob->ob.text.ellipse->bbox.x1-dx,ob->ob.text.ellipse->bbox.y1-dy);
1121 else if (ob->derries!=NULL)
1122 derry_move(view,ob,ob->bbox.x1-dx,ob->bbox.y1-dy);
1123 }
1124
1125 /* flip object ob horizontally */
1126 void
flip_object_x(View * view,Object * ob)1127 flip_object_x(View *view, Object *ob)
1128 {
1129 flip_object_x0(view,ob,TRUE);
1130 }
1131
1132 void
flip_object_x0(View * view,Object * ob,Boolean needs_undo)1133 flip_object_x0(View *view, Object *ob,Boolean needs_undo)
1134 {
1135 List l;
1136 long dx;
1137 long tx;
1138 double t;
1139
1140 if (needs_undo)
1141 {
1142 register_undo(UNDO_OB_PROP, ob, view->doc);
1143 store_redraw_object(ob);
1144 };
1145
1146 dx = ob->bbox.x2-ob->bbox.x1;
1147
1148 switch(ob->type)
1149 {
1150 case POLYLINE:
1151 case POLYGON:
1152 l = ob->ob.polyline.points;
1153 while (l!=NULL)
1154 {
1155 POINT(l)->x = dx - POINT(l)->x;
1156 l = l->next;
1157 };
1158 break;
1159
1160 case SPLINE:
1161 case ARC:
1162 l = ob->ob.spline.points;
1163 while (l!=NULL)
1164 {
1165 SPOINT(l)->x = dx - SPOINT(l)->x;
1166 l = l->next;
1167 };
1168 l = ob->ob.spline.cache;
1169 while (l!=NULL)
1170 {
1171 POINT(l)->x = dx - POINT(l)->x;
1172 l = l->next;
1173 };
1174 break;
1175
1176 case ARCELLIPSE:
1177 if (ob->ob.arcellipse.start>ob->ob.arcellipse.end)
1178 {
1179 t = ob->ob.arcellipse.start;
1180 ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1181 ob->ob.arcellipse.end = t;
1182 };
1183 ob->ob.arcellipse.start = ((180*64.0)/PI) * flip_angle_x((ob->ob.arcellipse.start*PI)/(64.0*180));
1184 ob->ob.arcellipse.end = ((180*64.0)/PI) * flip_angle_x((ob->ob.arcellipse.end*PI)/(64.0*180));
1185 t = ob->ob.arcellipse.start;
1186 ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1187 ob->ob.arcellipse.end = t;
1188 ob->ob.arcellipse.end += 360*64;
1189 break;
1190
1191 case COMPOUND:
1192 l = ob->ob.compound.obs;
1193 while (l!=NULL)
1194 {
1195 OB(l)->bbox.x1 -= ob->bbox.x1;
1196 tx = dx - OB(l)->bbox.x1;
1197 OB(l)->bbox.x2 -= ob->bbox.x1;
1198 OB(l)->bbox.x1 = dx - OB(l)->bbox.x2;
1199 OB(l)->bbox.x2 = tx;
1200 OB(l)->bbox.x1 += ob->bbox.x1;
1201 OB(l)->bbox.x2 += ob->bbox.x1;
1202 flip_object_x0(view,OB(l),FALSE);
1203 l = l->next;
1204 };
1205 break;
1206
1207 default:
1208 /* not handled */
1209 break;
1210 };
1211
1212 if (needs_undo)
1213 send_stored_redraw_object(view, ob);
1214 }
1215
1216 /* flip object ob vertically */
1217 void
flip_object_y(View * view,Object * ob)1218 flip_object_y(View *view, Object *ob)
1219 {
1220 flip_object_y0(view,ob,TRUE);
1221 }
1222
1223 void
flip_object_y0(View * view,Object * ob,Boolean needs_undo)1224 flip_object_y0(View *view, Object *ob,Boolean needs_undo)
1225 {
1226 List l;
1227 long dy;
1228 long ty;
1229 double t;
1230
1231 if (needs_undo)
1232 {
1233 register_undo(UNDO_OB_PROP, ob, view->doc);
1234 store_redraw_object(ob);
1235 };
1236
1237 dy = ob->bbox.y2-ob->bbox.y1;
1238
1239 switch(ob->type)
1240 {
1241 case POLYLINE:
1242 case POLYGON:
1243 l = ob->ob.polyline.points;
1244 while (l!=NULL)
1245 {
1246 POINT(l)->y = dy - POINT(l)->y;
1247 l = l->next;
1248 };
1249 break;
1250
1251 case SPLINE:
1252 case ARC:
1253 l = ob->ob.spline.points;
1254 while (l!=NULL)
1255 {
1256 SPOINT(l)->y = dy - SPOINT(l)->y;
1257 l = l->next;
1258 };
1259 l = ob->ob.spline.cache;
1260 while (l!=NULL)
1261 {
1262 POINT(l)->y = dy - POINT(l)->y;
1263 l = l->next;
1264 };
1265 break;
1266
1267 case ARCELLIPSE:
1268 ob->ob.arcellipse.start = ((180*64.0)/PI) * flip_angle_y((ob->ob.arcellipse.start*PI)/(64.0*180));
1269 ob->ob.arcellipse.end = ((180*64.0)/PI) * flip_angle_y((ob->ob.arcellipse.end*PI)/(64.0*180));
1270 t = ob->ob.arcellipse.start;
1271 ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1272 ob->ob.arcellipse.end = t;
1273 ob->ob.arcellipse.end += 360*64;
1274 break;
1275
1276 case COMPOUND:
1277 l = ob->ob.compound.obs;
1278 while (l!=NULL)
1279 {
1280 OB(l)->bbox.y1 -= ob->bbox.y1;
1281 ty = dy - OB(l)->bbox.y1;
1282 OB(l)->bbox.y2 -= ob->bbox.y1;
1283 OB(l)->bbox.y1 = dy - OB(l)->bbox.y2;
1284 OB(l)->bbox.y2 = ty;
1285 OB(l)->bbox.y1 += ob->bbox.y1;
1286 OB(l)->bbox.y2 += ob->bbox.y1;
1287 flip_object_y0(view,OB(l),FALSE);
1288 l = l->next;
1289 };
1290 break;
1291
1292 default:
1293 /* not handled */
1294 break;
1295 };
1296
1297 if (needs_undo)
1298 send_stored_redraw_object(view, ob);
1299 }
1300
1301 /* rotate an object by angle */
1302 void
apply_rotate_to_object(View * view,Object * ob,double angle)1303 apply_rotate_to_object(View *view, Object *ob, double angle)
1304 {
1305 double cosa, sina;
1306 long cx,cy;
1307 List l;
1308 long x,y;
1309 Object *s;
1310
1311
1312 register_undo(UNDO_OB_PROP, ob, view->doc);
1313
1314 cosa = cosround(angle);
1315 sina = sinround(angle);
1316 cx = (ob->bbox.x2-ob->bbox.x1)/2;
1317 cy = (ob->bbox.y2-ob->bbox.y1)/2;
1318
1319 SET_CLIP_WINDOW(handlegc, view->draw_window);
1320
1321 store_redraw_object(ob);
1322
1323 switch(ob->type)
1324 {
1325 case TEXT:
1326 ob->ob.text.angle = ob->ob.text.angle+angle;
1327 /* if it's a small rotation, default to 0.0 */
1328 if (abs(ob->ob.text.angle)<((3*PI)/180))
1329 ob->ob.text.angle=0.0;
1330
1331 calc_text_outer_box_adjust(ob,0,0);
1332
1333 s = view->selected_object->ob;
1334 view->doc->o = change_bbox(view->doc->o, ob);
1335 view->selected_object = get_object_node(view->doc->o, s);
1336 view->highlighted_object = view->selected_object;
1337 break;
1338
1339 case POLYLINE:
1340 case POLYGON:
1341 l = ob->ob.polyline.points;
1342
1343 while (l!=NULL)
1344 {
1345 x = cx+(cosa*(POINT(l)->x-cx))-(sina*(POINT(l)->y-cy));
1346 y = cy+(sina*(POINT(l)->x-cx))+(cosa*(POINT(l)->y-cy));
1347 POINT(l)->x = x;
1348 POINT(l)->y = y;
1349 /* remove if derry */
1350 if (POINT(l)->derried)
1351 remove_derry(view,ob,POINT(l));
1352 l=l->next;
1353 };
1354
1355 recalc_polyline_bbox(ob, FALSE);
1356 s = view->selected_object->ob;
1357 view->doc->o = change_bbox(view->doc->o, ob);
1358 view->selected_object = get_object_node(view->doc->o, s);
1359 view->highlighted_object = view->selected_object;
1360 break;
1361
1362 case SPLINE:
1363 case ARC:
1364 l = ob->ob.spline.points;
1365
1366 while (l!=NULL)
1367 {
1368 x = cx+(cosa*(SPOINT(l)->x-cx))-(sina*(SPOINT(l)->y-cy));
1369 y = cy+(sina*(SPOINT(l)->x-cx))+(cosa*(SPOINT(l)->y-cy));
1370 SPOINT(l)->x = x;
1371 SPOINT(l)->y = y;
1372 /* remove if derry */
1373 if (POINT(l)->derried)
1374 remove_derry(view,ob,POINT(l));
1375 l=l->next;
1376 };
1377
1378 if (!ob->ob.spline.cache)
1379 ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points, ob->ob.spline.closed);
1380
1381 l = ob->ob.spline.cache;
1382
1383 while (l!=NULL)
1384 {
1385 x = cx+(cosa*(POINT(l)->x-cx))-(sina*(POINT(l)->y-cy));
1386 y = cy+(sina*(POINT(l)->x-cx))+(cosa*(POINT(l)->y-cy));
1387 POINT(l)->x = x;
1388 POINT(l)->y = y;
1389 l=l->next;
1390 };
1391
1392 recalc_polyline_bbox(ob, TRUE);
1393 s = view->selected_object->ob;
1394 view->doc->o = change_bbox(view->doc->o, ob);
1395 view->selected_object = get_object_node(view->doc->o, s);
1396 view->highlighted_object = view->selected_object;
1397 break;
1398
1399 default:
1400 /* object type not handled */
1401 break;
1402 };
1403
1404 send_stored_redraw_object(view, ob);
1405 }
1406
1407 /* scale a compound object */
1408 /* must be treated differently to pass a centre of rotation */
1409 void
apply_scale_to_compound(View * view,Object * ob,double x,double y,long x1,long y1)1410 apply_scale_to_compound(View *view, Object *ob, double x, double y, long x1, long y1)
1411 {
1412 List l;
1413 Object *s;
1414
1415 l = ob->ob.compound.obs;
1416
1417 ob->bbox.x2 = ob->bbox.x1+1;
1418 ob->bbox.y2 = ob->bbox.y1+1;
1419
1420 while (l!=NULL)
1421 {
1422 if (OB(l)->type==TEXT)
1423 {
1424 OB(l)->ob.text.bbox.x1 = x1 + R(OB(l)->ob.text.bbox.x1-x1,x);
1425 OB(l)->ob.text.bbox.y1 = y1 + R(OB(l)->ob.text.bbox.y1-y1,y);
1426 OB(l)->ob.text.bbox.x2 = x1 + R(OB(l)->ob.text.bbox.x2-x1,x);
1427 OB(l)->ob.text.bbox.y2 = y1 + R(OB(l)->ob.text.bbox.y2-y1,y);
1428 }
1429 else
1430 {
1431 OB(l)->bbox.x1 = x1 + R(OB(l)->bbox.x1-x1,x);
1432 OB(l)->bbox.y1 = y1 + R(OB(l)->bbox.y1-y1,y);
1433 OB(l)->bbox.x2 = x1 + R(OB(l)->bbox.x2-x1,x);
1434 OB(l)->bbox.y2 = y1 + R(OB(l)->bbox.y2-y1,y);
1435 };
1436 if (OB(l)->type==COMPOUND)
1437 apply_scale_to_compound(view,OB(l),x,y,x1,y1);
1438 else
1439 apply_scale_to_object(view,OB(l),x,y,FALSE);
1440 ob->bbox = merge_boxes(ob->bbox,OB(l)->bbox);
1441 view->doc->o = trash_object(view->doc->o,&view->doc->lo,OB(l));
1442 l = l->next;
1443 };
1444
1445 s = view->selected_object->ob;
1446 view->doc->o = change_bbox(view->doc->o, ob);
1447 view->selected_object = get_object_node(view->doc->o, s);
1448 view->highlighted_object = view->selected_object;
1449 }
1450
1451 /* scale object ob by ratios x and y */
1452 void
apply_scale_to_object(View * view,Object * ob,double x,double y,Boolean part_of_compound)1453 apply_scale_to_object(View *view, Object *ob, double x, double y, Boolean part_of_compound)
1454 {
1455 List l,l2;
1456 Object *s;
1457 long ox=0,oy=0;
1458
1459 x = abs(x);
1460 y = abs(y);
1461
1462 if (part_of_compound)
1463 {
1464 register_undo(UNDO_OB_PROP, ob, view->doc);
1465 /* ugh ! */
1466 if (ob->type==TEXT)
1467 {
1468 VRegion bb;
1469 bb.x1 = ob->bbox.x1; bb.y1 = ob->bbox.y1;
1470 bb.x2 = ob->bbox.x1 + R(ob->bbox.x2-ob->bbox.x1,x);
1471 bb.y2 = ob->bbox.y1 + R(ob->bbox.y2-ob->bbox.y1,y);
1472 ob->bbox = merge_boxes(ob->bbox,bb);
1473 };
1474 store_redraw_object(ob);
1475 };
1476
1477 corner_magic(ob, &ob->bbox.x1, &ob->bbox.y1, x, y);
1478 switch(ob->type)
1479 {
1480 case TEXT:
1481 if (ob->ob.text.angle!=0.0)
1482 {
1483 double sina,cosa;
1484 long cx,cy;
1485 sina = sinround(ob->ob.text.angle);
1486 cosa = cosround(ob->ob.text.angle);
1487 cx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
1488 cy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
1489 ox = cx+(cosa*(ob->ob.text.bbox.x1-cx))-(sina*(ob->ob.text.bbox.y1-cy));
1490 oy = cy+(sina*(ob->ob.text.bbox.x1-cx))+(cosa*(ob->ob.text.bbox.y1-cy));
1491 };
1492
1493 corner_magic(ob, &ob->ob.text.bbox.x1, &ob->ob.text.bbox.y1, x, y);
1494 (x<y) ? (y=x) : (x=y);
1495 if (part_of_compound)
1496 {
1497 ob->ob.text.bbox.x2 = R(ob->ob.text.bbox.x2-ob->ob.text.bbox.x1,x);
1498 ob->ob.text.bbox.x2 += ob->ob.text.bbox.x1;
1499 ob->ob.text.bbox.y2 = R(ob->ob.text.bbox.y2-ob->bbox.y1,y);
1500 ob->ob.text.bbox.y2 += ob->ob.text.bbox.y1;
1501 };
1502 l = ob->ob.text.lines;
1503 while (l!=NULL)
1504 {
1505 l2 = TEXTLINE(l)->sections;
1506 while (l2!=NULL)
1507 {
1508 TEXTSEC(l2)->size=y*TEXTSEC(l2)->size;
1509 l2=l2->next;
1510 };
1511 l=l->next;
1512 };
1513 recalc_text_box(view,ob,FALSE,FALSE);
1514 if (ob->ob.text.angle!=0.0)
1515 {
1516 double sina,cosa;
1517 long cx,cy;
1518 long nx,ny;
1519 sina = sinround(ob->ob.text.angle);
1520 cosa = cosround(ob->ob.text.angle);
1521 cx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
1522 cy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
1523 nx = cx+(cosa*(ob->ob.text.bbox.x1-cx))-(sina*(ob->ob.text.bbox.y1-cy));
1524 ny = cy+(sina*(ob->ob.text.bbox.x1-cx))+(cosa*(ob->ob.text.bbox.y1-cy));
1525 ob->bbox.x1 -= nx - ox;
1526 ob->bbox.y1 -= ny - oy;
1527 ob->bbox.x2 -= nx - ox;
1528 ob->bbox.y2 -= ny - oy;
1529 ob->ob.text.bbox.x1 -= nx - ox;
1530 ob->ob.text.bbox.y1 -= ny - oy;
1531 ob->ob.text.bbox.x2 -= nx - ox;
1532 ob->ob.text.bbox.y2 -= ny - oy;
1533 };
1534 s = view->selected_object->ob;
1535 view->doc->o = change_bbox(view->doc->o, ob);
1536 view->selected_object = get_object_node(view->doc->o, s);
1537 view->highlighted_object = view->selected_object;
1538 break;
1539
1540 case ROUNDBOX:
1541 if (part_of_compound)
1542 {
1543 ob->bbox.x2 = R(ob->bbox.x2-ob->bbox.x1,x);
1544 ob->bbox.x2 += ob->bbox.x1;
1545 ob->bbox.y2 = R(ob->bbox.y2-ob->bbox.y1,y);
1546 ob->bbox.y2 += ob->bbox.y1;
1547 };
1548 s = view->selected_object->ob;
1549 view->doc->o = change_bbox(view->doc->o, ob);
1550 view->selected_object = get_object_node(view->doc->o, s);
1551 view->highlighted_object = view->selected_object;
1552 break;
1553
1554 case ELLIPSE:
1555 ob->ob.ellipse.centre.x = R(ob->ob.ellipse.centre.x,x);
1556 ob->ob.ellipse.centre.y = R(ob->ob.ellipse.centre.y,y);
1557 ob->ob.ellipse.xradius = R(ob->ob.ellipse.xradius,x);
1558 ob->ob.ellipse.yradius = R(ob->ob.ellipse.yradius,y);
1559 ob->bbox.x2 = ob->bbox.x1 + ob->ob.ellipse.centre.x + ob->ob.ellipse.xradius;
1560 ob->bbox.y2 = ob->bbox.y1 + ob->ob.ellipse.centre.y + ob->ob.ellipse.yradius;
1561 s = view->selected_object->ob;
1562 view->doc->o = change_bbox(view->doc->o, ob);
1563 view->selected_object = get_object_node(view->doc->o, s);
1564 view->highlighted_object = view->selected_object;
1565 break;
1566
1567 case ARCELLIPSE:
1568 /* must constrain to circular shape */
1569 /* this works very badly in compounds, but there doesn't
1570 seem to be a better way to ensure arc is contained */
1571 (x<y) ? (y=x) : (x=y);
1572 ob->ob.arcellipse.centre.x = R(ob->ob.arcellipse.centre.x,x);
1573 ob->ob.arcellipse.centre.y = R(ob->ob.arcellipse.centre.y,y);
1574 ob->ob.arcellipse.xradius = R(ob->ob.arcellipse.xradius,x);
1575 ob->ob.arcellipse.yradius = R(ob->ob.arcellipse.yradius,y);
1576 ob->bbox.x2 = ob->bbox.x1 + ob->ob.arcellipse.centre.x + ob->ob.arcellipse.xradius;
1577 ob->bbox.y2 = ob->bbox.y1 + ob->ob.arcellipse.centre.y + ob->ob.arcellipse.yradius;
1578 s = view->selected_object->ob;
1579 view->doc->o = change_bbox(view->doc->o, ob);
1580 view->selected_object = get_object_node(view->doc->o, s);
1581 view->highlighted_object = view->selected_object;
1582 break;
1583
1584 case POLYGON:
1585 case POLYLINE:
1586 l = ob->ob.polyline.points;
1587
1588 while (l!=NULL)
1589 {
1590 POINT(l)->x = R(POINT(l)->x,x);
1591 POINT(l)->y = R(POINT(l)->y,y);
1592 if (POINT(l)->derried)
1593 remove_derry(view,ob,POINT(l));
1594 l = l->next;
1595 };
1596
1597 recalc_polyline_bbox(ob, FALSE);
1598 s = view->selected_object->ob;
1599 view->doc->o = change_bbox(view->doc->o, ob);
1600 view->selected_object = get_object_node(view->doc->o, s);
1601 view->highlighted_object = view->selected_object;
1602 break;
1603
1604 case SPLINE:
1605 case ARC:
1606 l = ob->ob.spline.points;
1607
1608 while (l!=NULL)
1609 {
1610 SPOINT(l)->x = R(SPOINT(l)->x,x);
1611 SPOINT(l)->y = R(SPOINT(l)->y,y);
1612 if (POINT(l)->derried)
1613 remove_derry(view,ob,POINT(l));
1614 l = l->next;
1615 };
1616
1617 delete_cache_list(ob->ob.spline.cache);
1618 ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,ob->ob.spline.closed);
1619 recalc_polyline_bbox(ob, TRUE);
1620 s = view->selected_object->ob;
1621 view->doc->o = change_bbox(view->doc->o, ob);
1622 view->selected_object = get_object_node(view->doc->o, s);
1623 view->highlighted_object = view->selected_object;
1624 break;
1625
1626 case COMPOUND:
1627 apply_scale_to_compound(view,ob,x,y,ob->bbox.x1,ob->bbox.y1);
1628 break;
1629
1630 default:
1631 v_error(ob->type);
1632 break;
1633 };
1634
1635 if (part_of_compound)
1636 send_stored_redraw_object(view, ob);
1637 }
1638
1639 /* request for a derry. Search through objects intersecting this point
1640 and pick out the contributed point that is closest. The caller then
1641 sets up the connection with the returned value *rob */
1642 Boolean
get_nearest_point(View * view,Tree t,long cx,long cy,long * x,long * y,Object ** rob)1643 get_nearest_point(View *view, Tree t, long cx, long cy, long *x,long *y,Object **rob)
1644 {
1645 List l=NULL;
1646 List l2;
1647 List points;
1648 VRegion bb;
1649 double rcx, rcy;
1650 double tx,ty;
1651 double ax,ay;
1652 double bx,by;
1653 long ix, iy;
1654 Object *ob;
1655 double theta;
1656 double theta2;
1657 Boolean found=FALSE;
1658 double nearest=600000000.0; /* far away */
1659
1660 if (t==NULL)
1661 return FALSE;
1662
1663 /* look thirty pixels in every direction */
1664 bb.x1 = cx - P2D(30,view);
1665 bb.y1 = cy - P2D(30,view);
1666 bb.x2 = cx + P2D(30,view);
1667 bb.y2 = cy + P2D(30,view);
1668 l = intersecting_objects(t,bb,l);
1669 l2=l;
1670 while (l!=NULL)
1671 {
1672 ob = OB(l);
1673
1674 switch (ob->type)
1675 {
1676 case POLYLINE:
1677 case POLYGON:
1678 rcx = cx - ob->bbox.x1;
1679 rcy = cy - ob->bbox.y1;
1680
1681 points = ob->ob.polyline.points;
1682
1683 while (points!=NULL)
1684 {
1685 if (sqrt((double)((POINT(points)->x-rcx)*(POINT(points)->x-rcx))+((POINT(points)->y-rcy)*(POINT(points)->y-rcy)))
1686 < nearest)
1687 {
1688 nearest = sqrt((double)((POINT(points)->x-rcx)*(POINT(points)->x-rcx))+((POINT(points)->y-rcy)*(POINT(points)->y-rcy)));
1689 *x = ob->bbox.x1 + POINT(points)->x;
1690 *y = ob->bbox.y1 + POINT(points)->y;
1691 found = TRUE;
1692 *rob = ob;
1693 };
1694 points = points->next;
1695 };
1696 break;
1697
1698 case SPLINE:
1699 case ARC:
1700 if (ob->ob.spline.closed)
1701 break;
1702
1703 rcx = cx - ob->bbox.x1;
1704 rcy = cy - ob->bbox.y1;
1705
1706 points = ob->ob.spline.points;
1707
1708 while (points!=NULL)
1709 {
1710 /* only jump if curve is near point */
1711 /* ideally, this should take account of when curve passes through,
1712 but has an approximated shape factor (three-point straight line) */
1713 if (SPOINT(points)->s<SHAPE_ATTACH_THRESHOLD &&
1714 sqrt((double)((SPOINT(points)->x-rcx)*(SPOINT(points)->x-rcx))+((SPOINT(points)->y-rcy)*(SPOINT(points)->y-rcy)))
1715 < nearest)
1716 {
1717 nearest = sqrt((double)((SPOINT(points)->x-rcx)*(SPOINT(points)->x-rcx))+((SPOINT(points)->y-rcy)*(SPOINT(points)->y-rcy)));
1718 *x = ob->bbox.x1 + SPOINT(points)->x;
1719 *y = ob->bbox.y1 + SPOINT(points)->y;
1720 found = TRUE;
1721 *rob = ob;
1722 };
1723 points = points->next;
1724 };
1725 break;
1726
1727 case TEXT:
1728 if (!ob->ob.text.node)
1729 break;
1730 else
1731 {
1732 long xr,yr;
1733 Object *eob=ob->ob.text.ellipse;
1734
1735 /* only attempt to derry to node's ellipse */
1736 xr = eob->ob.ellipse.xradius;
1737 yr = eob->ob.ellipse.yradius;
1738
1739 theta = atan2((((double)yr)/xr)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1740 (((double)yr)/xr)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1741
1742
1743 tx = xr * cosround(theta);
1744 ty = yr * sinround(theta);
1745 ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1746 iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1747 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1748 {
1749 /* kludge factor */
1750 /* this is to attempt to determine whether we are on the ellipse line or not already */
1751 /* it's far from perfect */
1752 theta2 = atan2((((double)yr)/xr)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1753 (((double)yr)/xr)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1754 ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1755 ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1756 bx = (double)xr;
1757 by = (double)yr;
1758 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1759 {
1760 *x = ix;
1761 *y = iy;
1762 }
1763 else
1764 {
1765 *x = cx;
1766 *y = cy;
1767 };
1768 found = TRUE;
1769 *rob = eob;
1770 nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1771 };
1772 };
1773 break;
1774
1775 case ARCELLIPSE:
1776 theta = atan2((((double)ob->ob.arcellipse.yradius)/ob->ob.ellipse.xradius)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1777 (((double)ob->ob.arcellipse.yradius)/ob->ob.ellipse.xradius)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1778
1779 tx = ob->ob.arcellipse.xradius * cosround(theta);
1780 ty = ob->ob.arcellipse.yradius * sinround(theta);
1781 ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1782 iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1783 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1784 {
1785 /* kludge factor */
1786 /* this is to attempt to determine whether we are on the ellipse line or not already */
1787 /* it's far from perfect */
1788 theta2 = atan2((((double)ob->ob.arcellipse.yradius)/ob->ob.arcellipse.xradius)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1789 (((double)ob->ob.arcellipse.yradius)/ob->ob.arcellipse.xradius)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1790 ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1791 ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1792 bx = (double)ob->ob.arcellipse.xradius;
1793 by = (double)ob->ob.arcellipse.yradius;
1794 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1795 {
1796 *x = ix;
1797 *y = iy;
1798 }
1799 else
1800 {
1801 *x = cx;
1802 *y = cy;
1803 };
1804 *rob = ob;
1805 found = TRUE;
1806 nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1807 };
1808 break;
1809
1810 case ELLIPSE:
1811 theta = atan2((((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1812 (((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1813
1814 tx = ob->ob.ellipse.xradius * cosround(theta);
1815 ty = ob->ob.ellipse.yradius * sinround(theta);
1816 ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1817 iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1818 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1819 {
1820 /* kludge factor */
1821 /* this is to attempt to determine whether we are on the ellipse line or not already */
1822 /* it's far from perfect */
1823 theta2 = atan2((((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1824 (((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1825 ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1826 ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1827 bx = (double)ob->ob.ellipse.xradius;
1828 by = (double)ob->ob.ellipse.yradius;
1829 if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1830 {
1831 *x = ix;
1832 *y = iy;
1833 }
1834 else
1835 {
1836 *x = cx;
1837 *y = cy;
1838 };
1839 *rob = ob;
1840 found = TRUE;
1841 nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1842 };
1843 break;
1844
1845 default:
1846 /* not an error, just an uninteresting object */
1847 break;
1848 };
1849
1850 l = l->next;
1851 };
1852 delete_list(l2);
1853 return found;
1854 }
1855
1856 /* Adds a derry to the host object */
1857 /* the point is flagged as derried in the source object */
1858 void
add_derry(Object * ob,Object * src,VPoint * point)1859 add_derry(Object *ob, Object *src, VPoint *point)
1860 {
1861 Derry *d;
1862
1863 d = (Derry *)malloc(sizeof(Derry));
1864
1865 d->ticket = src->ticket;
1866 d->point = point;
1867
1868 ob->derries = add_to_list(ob->derries,src->ticket,0,(void *)d);
1869 }
1870
1871 /* to move all derryees */
1872 void
derry_move(View * v,Object * ob,long oldx,long oldy)1873 derry_move(View *v,Object *ob,long oldx,long oldy)
1874 {
1875 long x,y;
1876 long dx,dy;
1877 Object *nob=NULL;
1878 List l;
1879
1880 dx = ob->bbox.x1-oldx;
1881 dy = ob->bbox.y1-oldy;
1882
1883 l=ob->derries;
1884
1885 while (l!=NULL)
1886 {
1887 nob = get_object_by_ticket(v->doc->o,DERRY(l)->ticket);
1888 if (nob!=NULL)
1889 {
1890 store_redraw_object(nob);
1891 x = nob->bbox.x1 + DERRY(l)->point->x;
1892 y = nob->bbox.y1 + DERRY(l)->point->y;
1893
1894 x += dx;
1895 y += dy;
1896 DERRY(l)->point->x = x - nob->bbox.x1;
1897 DERRY(l)->point->y = y - nob->bbox.y1;
1898 if (nob->type==SPLINE || nob->type==ARC)
1899 {
1900 if (nob->type==ARC)
1901 {
1902 SPoint *p1,*p2,*p3;
1903 short a,b;
1904 p1 = SPOINT(nob->ob.spline.points);
1905 p2 = SPOINT(nob->ob.spline.points->next);
1906 p3 = SPOINT(nob->ob.spline.points->next->next);
1907 bent_midpoint(p1->x, p1->y,p3->x,p3->y, 0.261799388, &a, &b);
1908 p2->x = a;
1909 p2->y = b;
1910 };
1911 delete_cache_list(nob->ob.spline.cache);
1912 nob->ob.spline.cache = compute_spline_segment(nob->ob.spline.points,nob->ob.spline.closed);
1913 };
1914 recalc_polyline_bbox(nob,(nob->type==SPLINE || nob->type==ARC));
1915 v->doc->o = change_bbox(v->doc->o, nob);
1916 send_stored_redraw_object(v,nob);
1917 };
1918
1919 l = l->next;
1920 };
1921 }
1922
1923 /* remove a no-longer-valid attachment */
1924 void
remove_derry(View * v,Object * ob,VPoint * p)1925 remove_derry(View *v,Object *ob, VPoint *p)
1926 {
1927 List l,l2;
1928 Derry *d;
1929 Object *nob=NULL;
1930
1931 l = v->doc->lo;
1932
1933 while (l!=NULL)
1934 {
1935 if (OB(l)->derries!=NULL)
1936 {
1937 l2 = OB(l)->derries;
1938 while (l2!=NULL)
1939 {
1940 nob = get_object_by_ticket(v->doc->o,DERRY(l2)->ticket);
1941 if (nob==ob && DERRY(l2)->point==p)
1942 {
1943 d = DERRY(l2);
1944 OB(l)->derries = delete_from_list_v(OB(l)->derries,l2->data);
1945 free(d);
1946 l2 = OB(l)->derries;
1947 }
1948 else
1949 l2 = l2->next;
1950 };
1951 };
1952 l = l->next;
1953 };
1954 p->derried=FALSE;
1955 }
1956