1 /* spline.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 /* drawing of spline */
18 /*
19 * $Log: spline.c,v $
20 * Revision 1.3 2000/12/17 00:57:42 moz
21 * examples, filled open splines, highlight_objects
22 *
23 * Revision 1.2 2000/12/06 20:56:04 moz
24 * GPL stuff.
25 *
26 * Revision 1.1.1.1 2000/08/21 01:05:31 moz
27 *
28 *
29 * Revision 1.1.1.1 2000/07/19 22:45:30 moz
30 * CVS Import
31 *
32 * Revision 1.15 2000/03/15 00:16:15 moz
33 * Deal with disable_motion - zoom out point edit not fixed.
34 *
35 * Revision 1.14 2000/03/09 23:29:05 moz
36 * Joinstyle should always be JoinRound.
37 *
38 * Revision 1.13 2000/03/07 20:45:36 moz
39 * Compile fixes.
40 *
41 * Revision 1.12 2000/02/18 04:07:16 moz
42 * Free temporary cache contents.
43 *
44 * Revision 1.11 2000/01/29 21:01:56 moz
45 * Use get_nearest_point correctly.
46 *
47 * Revision 1.10 2000/01/28 16:44:22 moz
48 * Use edit_view to stop droppings on other views.
49 *
50 * Revision 1.9 2000/01/26 18:21:01 moz
51 * Can't use ++ in add_to_list() now.
52 *
53 * Revision 1.8 1999/11/15 02:04:37 moz
54 * Use rounded trig.
55 *
56 * Revision 1.7 1999/05/19 17:08:13 moz
57 * 1.0 Checkin.
58 *
59 * Revision 1.6 1999/04/27 19:02:19 moz
60 * Fixed coredump on ob==NULL
61 *
62 * Revision 1.5 1999/04/27 04:12:04 moz
63 * -Wall appeasement.
64 *
65 * Revision 1.4 1999/04/23 00:40:06 moz
66 * redraw_view_window change.
67 *
68 * Revision 1.3 1999/04/15 22:30:34 moz
69 * Can edit shape factor of start and finish when closed spline now.
70 *
71 * Revision 1.2 1999/04/15 22:19:15 moz
72 * Fixed final bbox calculation for interpolated closed splines.
73 *
74 * Revision 1.1 1999/03/30 00:06:02 moz
75 * Initial revision
76 *
77 */
78
79 #include <math.h>
80 #include "include/figurine.h"
81 #include "include/extern.h"
82
83 /* the spline model used is X-Spline, Blanc and Schlick */
84
85 XPoint xp[MAXPOINTS];
86 XPoint hp[MAXPOINTS];
87 static Boolean held = FALSE;
88 static ListEl *edited_point=NULL;
89 static View *edit_view=NULL;
90 static Object *cpoly=NULL;
91 static uint pcount=0;
92 static long orig_x=0;
93 static long orig_y=0;
94 static int ticket=0;
95
96 extern int disable_motion;
97
delete_cache_list(List points)98 void delete_cache_list(List points)
99 {
100 List l = points;
101
102 while (l!=NULL)
103 {
104 if (l->data!=NULL)
105 free(l->data);
106 l = l->next;
107 };
108
109 delete_list(points);
110 }
111
112 /* computes the curve given the control points */
113 List
compute_spline_segment(List points,Boolean closed)114 compute_spline_segment(List points, Boolean closed)
115 {
116 List l=NULL;
117 double step;
118 int k = 0;
119 ListEl *p0,*p1,*p2,*p3;
120
121 ticket=0;
122
123 if (points==NULL)
124 return NULL;
125
126 if (points->next==NULL)
127 return NULL;
128
129 if (points->next->next==NULL)
130 closed=FALSE; /* don't attempt to close a line */
131
132 /* so at least three control points */
133
134 /* this is a good compromise between looks and speed */
135 step=0.08;
136
137 if (!closed)
138 {
139 /* first control point used twice */
140
141 p0 = points;
142 p1 = points;
143 p2 = points->next;
144 if (p2->next==NULL)
145 p3 = p2;
146 else
147 p3 = p2->next;
148
149 for (k=0; ; k++)
150 {
151
152 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
153 if (p3->next==NULL)
154 break;
155 p0 = p1;
156 p1 = p2;
157 p2 = p3;
158 p3 = p3->next;
159 };
160
161 p0 = p1;
162 p1 = p2;
163 p2 = p3;
164 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
165 }
166 else
167 {
168 /* closed spline, loop the curve round */
169 p0 = points;
170 p1 = p0->next;
171 p2 = p1->next;
172 p3 = p2->next;
173
174 for (k=0 ; p3!=NULL ; k++)
175 {
176 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
177 p0 = p1;
178 p1 = p2;
179 p2 = p3;
180 p3 = p3->next;
181 };
182
183 p3 = points;
184
185 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
186
187 k++;
188 p0 = p1;
189 p1 = p2;
190 p2 = p3;
191 p3 = p3->next;
192 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
193 k++;
194 p0 = p1;
195 p1 = p2;
196 p2 = p3;
197 p3 = p3->next;
198 l = spline_compute(l, step, k, SPOINT(p0), SPOINT(p1), SPOINT(p2), SPOINT(p3), SPOINT(p1)->s, SPOINT(p2)->s);
199 };
200
201 return l;
202 }
203
204 /* just to make below a bit clear */
205 /* simple mean average of blending functions */
206 #define SPLINE_CALC(d) (blending[0]*p0->d + \
207 blending[1]*p1->d + \
208 blending[2]*p2->d + \
209 blending[3]*p3->d) / \
210 (blending[0] + blending[1] + blending[2] + blending[3])
211
212 /* three different blenders */
213 double
f_blend(double n,double d)214 f_blend(double n, double d)
215 {
216 double p = 2*d*d;
217 double u = n/d;
218 double u2 = u*u;
219
220 return (u*u2*(10-p + (2*p-15)*u + (6-p)*u2));
221 }
222
223 double
g_blend(double u,double q)224 g_blend(double u, double q)
225 {
226 return (u*(q + u*(2*q + u*(8-12*q + u*(14*q-11 + u*(4-5*q))))));
227 }
228
229 double
h_blend(double u,double q)230 h_blend(double u, double q)
231 {
232 double u2 = u*u;
233 return (u*(q + u*(2*q + u2*(-2*q - u*q))));
234 }
235
236 /* the next four functions determine the effect of the shape factor on the curve */
237 void
compute_negative_s1(double t,double s,double * b1,double * b2)238 compute_negative_s1(double t, double s, double *b1, double *b2)
239 {
240 *b1 = h_blend(-t, -s);
241 *b2 = g_blend(t, -s);
242 }
243
244 void
compute_negative_s2(double t,double s,double * b1,double * b2)245 compute_negative_s2(double t, double s, double *b1, double *b2)
246 {
247 *b1 = g_blend(1-t, -s);
248 *b2 = h_blend(t-1, -s);
249 }
250
251 void
compute_positive_s1(int k,double t,double s,double * b1,double * b2)252 compute_positive_s1(int k, double t, double s, double *b1, double *b2)
253 {
254 double Tk;
255
256 Tk = k + 1 + s;
257 *b1 = (t+k+1<Tk) ? f_blend(t+k+1-Tk, k-Tk) : 0.0;
258 Tk = k + 1 - s;
259 *b2 = f_blend(t+k+1-Tk, k+2-Tk);
260 }
261
262 void
compute_positive_s2(int k,double t,double s,double * b1,double * b2)263 compute_positive_s2(int k, double t, double s, double *b1, double *b2)
264 {
265 double Tk;
266
267 Tk = k + 2 + s;
268 *b1 = f_blend(t+k+1-Tk, k+1-Tk);
269 Tk = k + 2 - s;
270 *b2 = (t+k+1>Tk) ? f_blend(t+k+1-Tk, k+3-Tk) : 0.0;
271 }
272
273 /* compute one segment of the spline */
274 List
spline_compute(List l,double step,int k,SPoint * p0,SPoint * p1,SPoint * p2,SPoint * p3,double s1,double s2)275 spline_compute(List l, double step, int k, SPoint *p0, SPoint *p1, SPoint *p2, SPoint *p3, double s1, double s2)
276 {
277 VPoint *vp;
278 double blending[4];
279 double t;
280
281 for (t=0.0; t<1; t+=step)
282 {
283 if (s1 < 0 && s2 < 0)
284 {
285 compute_negative_s1(t, s1, &blending[0], &blending[2]);
286 compute_negative_s2(t, s2, &blending[1], &blending[3]);
287 }
288 else if (s1 < 0)
289 {
290 compute_negative_s1(t, s1, &blending[0], &blending[2]);
291 compute_positive_s2(k, t, s2, &blending[1], &blending[3]);
292 }
293 else if (s2 < 0)
294 {
295 compute_positive_s1(k, t, s1, &blending[0], &blending[2]);
296 compute_negative_s2(t, s2, &blending[1], &blending[3]);
297 }
298 else /* both positive or =0 */
299 {
300 compute_positive_s1(k, t, s1, &blending[0], &blending[2]);
301 compute_positive_s2(k, t, s2, &blending[1], &blending[3]);
302 }
303
304 vp = (VPoint *)malloc(sizeof(VPoint));
305 vp->x = SPLINE_CALC(x);
306 vp->y = SPLINE_CALC(y);
307 vp->derried = FALSE;
308 l = add_to_list(l, (ulong)ticket, 0, (void *)vp);
309 ticket++;
310 }
311
312 return l;
313 }
314
315 void
spline_button(BEvent * bev,View * view)316 spline_button(BEvent *bev, View *view)
317 {
318 Object *ob;
319 List l;
320 SPoint *p1;
321
322 if (bev->button==Button2)
323 {
324 return;
325 };
326
327 if (bev->button==Button3 && state.busy_drawing)
328 {
329 /* cancel the current line */
330 if (!held)
331 XUngrabPointer(display,CurrentTime);
332
333 toggle_spline_line(view, bev->x, bev->y);
334
335 if (cpoly->ob.spline.points->next==NULL)
336 {
337 view->doc->o = trash_object(view->doc->o, &view->doc->lo, cpoly);
338 kill_object(cpoly);
339 cpoly = NULL;
340 };
341
342 if (cpoly!=NULL)
343 {
344 /* must set final shape factor to 0.0 and redraw */
345 l = cpoly->ob.spline.points;
346 while (l->next!=NULL)
347 l = l->next;
348
349 if (!cpoly->ob.spline.closed)
350 SPOINT(l)->s = 0.0;
351 else
352 SPOINT(l)->s = view->current_sfactor;
353
354 delete_cache_list(cpoly->ob.spline.cache);
355 cpoly->ob.spline.cache=NULL;
356
357 /* we need to recalc and redisplay in some situations */
358 store_redraw_object(cpoly);
359 cpoly->ob.spline.cache = compute_spline_segment(cpoly->ob.spline.points,cpoly->ob.spline.closed);
360 recalc_polyline_bbox(cpoly, TRUE);
361 view->doc->o = change_bbox(view->doc->o, cpoly);
362 send_stored_redraw_object(view,cpoly);
363 };
364
365 state.busy_drawing = FALSE;
366 edit_view=NULL;
367 held = FALSE;
368 cpoly = NULL;
369 pcount = 0;
370
371 }
372 else if (bev->button==Button1)
373 {
374 long x,y;
375
376 if (!P_IN_DOC(bev->x, bev->y, view) && state.busy_drawing)
377 {
378 /* ok, cancel the particular line we're drawing */
379 state.busy_drawing = FALSE;
380 pcount = 0;
381 cpoly = NULL;
382
383 if (!held)
384 XUngrabPointer(display,CurrentTime);
385 else
386 held = FALSE;
387
388 toggle_spline_line(view, bev->x, bev->y);
389 edit_view=NULL;
390 }
391 else
392 {
393 switch (bev->type)
394 {
395 case BUTTON_HELD:
396
397 if (state.busy_drawing)
398 {
399
400 }
401 else
402 {
403 Object *rob=NULL;
404 /* start drawing */
405 state.busy_drawing = TRUE;
406 held = TRUE;
407 edit_view=view;
408 if (state.shift_down &&
409 get_nearest_point(view,view->doc->o, XP2D(bev->x,view),YP2D(bev->y,view),&x,&y,&rob))
410 {
411 if (view->guide_lines && view->guide_lines_displayed)
412 {
413 toggle_guidelines(view);
414 view->guide_lines_displayed = FALSE;
415 };
416 orig_x = x;
417 orig_y = y;
418 bev->x = XD2P(x,view);
419 bev->y = YD2P(y,view);
420 mouse_x = bev->x;
421 mouse_y = bev->y;
422 XWarpPointer(display, None, view->draw_window->win, 0, 0, 1, 1,
423 bev->x,bev->y);
424 }
425 else
426 {
427 if (view->gridon)
428 {
429 orig_x = GXP2D(bev->x,view);
430 orig_y = GYP2D(bev->y,view);
431 }
432 else
433 {
434 orig_x = XP2D(bev->x,view);
435 orig_y = YP2D(bev->y,view);
436 };
437 };
438
439 toggle_spline_line(view, bev->x, bev->y);
440 /* add start of spline control points ! */
441 ob = (Object *)malloc(sizeof(Object));
442 p1 = (SPoint *)malloc(sizeof(SPoint));
443 p1->x = 0;
444 p1->y = 0;
445 p1->derried = rob!=NULL;
446 if (!view->closed)
447 p1->s = 0.0; /* angular to starting point if open */
448 else
449 p1->s = view->current_sfactor;
450 ob->bbox.x1 = orig_x;
451 ob->bbox.y1 = orig_y;
452 ob->bbox.x2 = orig_x;
453 ob->bbox.y2 = orig_y;
454 ob->ls = view->linestyle;
455 ob->lw = view->linewidth;
456 ob->es = view->endstyle;
457 ob->js = JoinRound;
458 if (view->barrow_on && !view->closed)
459 ob->barrow = make_arrow(view->barrow);
460 else
461 ob->barrow = NULL;
462 if (view->farrow_on && !view->closed)
463 ob->farrow = make_arrow(view->farrow);
464 else
465 ob->farrow = NULL;
466 ob->colour = view->colour;
467 if (view->fillon)
468 ob->fs = view->fillstyle;
469 else
470 ob->fs = NONE;
471 ob->fillcolour = view->fillcolour;
472 ob->type = SPLINE;
473 ob->ticket = ob_ticket++;
474 ob->derries = NULL;
475 ob->depth = view->doc->ob_depth--;
476 ob->ob.spline.closed = view->closed;
477
478 ob->ob.spline.points = create_list(0,0,(void *)p1);
479 ob->ob.spline.cache = NULL;
480 cpoly = ob;
481 pcount = 1;
482
483 register_undo(UNDO_PASTE,ob,view->doc);
484 if (rob!=NULL)
485 add_derry(rob,ob,(VPoint *)p1);
486 view->doc->o = add_object(view->doc->o, &view->doc->lo, ob);
487
488 };
489
490 break; /* BUTTON_HELD */
491
492 case BUTTON_CLICKED:
493 case BUTTON_RELEASED:
494
495 if (held)
496 {
497 XGrabPointer(display, view->draw_window->win, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask
498 | Button1MotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
499 }
500 else
501 {
502 VRegion bb;
503 Object *rob=NULL;
504
505 toggle_spline_line(view, bev->x, bev->y);
506
507 /* add line to existing object */
508 bb.x1 = orig_x;
509 bb.y1 = orig_y;
510 if (state.shift_down && get_nearest_point(view,view->doc->o, XP2D(bev->x,view),YP2D(bev->y,view),&x,&y,&rob))
511 {
512 if (view->guide_lines && view->guide_lines_displayed)
513 {
514 toggle_guidelines(view);
515 view->guide_lines_displayed = FALSE;
516 };
517 bb.x2 = x;
518 bb.y2 = y;
519 bev->x = XD2P(x,view);
520 bev->y = YD2P(y,view);
521 mouse_x = bev->x;
522 mouse_y = bev->y;
523 XWarpPointer(display, None, view->draw_window->win, 0, 0, 1, 1,
524 bev->x,bev->y);
525 }
526 else
527 {
528 if (view->gridon)
529 {
530 bb.x2 = GXP2D(bev->x,view);
531 bb.y2 = GYP2D(bev->y,view);
532 }
533 else
534 {
535 bb.x2 = XP2D(bev->x,view);
536 bb.y2 = YP2D(bev->y,view);
537 };
538 };
539
540 p1 = (SPoint *)malloc(sizeof(SPoint));
541
542 p1->x = bb.x2;
543 p1->y = bb.y2;
544 p1->derried = rob!=NULL;
545 p1->s = view->current_sfactor;
546 orig_x = p1->x;
547 orig_y = p1->y;
548 normalise_rectangle(&bb.x1,&bb.y1,&bb.x2, &bb.y2);
549 l = cpoly->ob.spline.points;
550
551 /* unrelavitise coordinates before bb merge */
552 while (l != NULL)
553 {
554 ((SPoint *)l->data)->x = ((SPoint *)l->data)->x+cpoly->bbox.x1;
555 ((SPoint *)l->data)->y = ((SPoint *)l->data)->y+cpoly->bbox.y1;
556 l = l->next;
557 };
558
559 cpoly->bbox = merge_boxes(cpoly->bbox,bb);
560 cpoly->ob.spline.points = add_to_list(cpoly->ob.spline.points, pcount, 0, (void *)p1);
561 pcount++;
562
563 l = cpoly->ob.spline.points;
564 while (l != NULL)
565 {
566 ((SPoint *)l->data)->x = ((SPoint *)l->data)->x-cpoly->bbox.x1;
567 ((SPoint *)l->data)->y = ((SPoint *)l->data)->y-cpoly->bbox.y1;
568 l = l->next;
569 };
570
571 delete_cache_list(cpoly->ob.spline.cache);
572 cpoly->ob.spline.cache = compute_spline_segment(cpoly->ob.spline.points,FALSE);
573 recalc_polyline_bbox(cpoly, TRUE);
574 view->doc->o = change_bbox(view->doc->o, cpoly);
575 bb.x2++; bb.y2++;
576
577 if (rob!=NULL)
578 add_derry(rob,cpoly,(VPoint *)p1);
579 send_redraw_object(view,cpoly);
580 /* carry on drawing */
581 toggle_spline_line(view,bev->x, bev->y);
582
583 };
584
585 held = FALSE;
586 break;
587 };
588 };
589 };
590 }
591
592 void
toggle_spline_line(View * view,int x,int y)593 toggle_spline_line(View *view, int x, int y)
594 {
595 VLine *pl;
596 VLine line;
597 Boolean clipped;
598
599 if (edit_view!=view)
600 return;
601
602 line.x1 = XD2P(orig_x,view);
603 line.y1 = YD2P(orig_y,view);
604
605 if (view->gridon)
606 {
607 line.x2 = XD2P(GXP2D(x,view),view);
608 line.y2 = YD2P(GYP2D(y,view),view);
609 }
610 else
611 {
612 line.x2 = x;
613 line.y2 = y;
614 };
615
616 pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 0);
617
618 if (pl!=NULL)
619 XDrawLine(display,view->draw_window->win, blackxorgc, pl->x1, pl->y1, pl->x2, pl->y2);
620 }
621
622 void
spline_point_button(BEvent * bev,View * v)623 spline_point_button(BEvent *bev, View *v)
624 {
625 if (bev->type==BUTTON_HELD)
626 {
627 if (!state.editing_point && !is_in_bbox(XP2D(bev->x,v),YP2D(bev->y,v),
628 v->edited_object->bbox.x1-P2D(HANDLE_PIXEL_SIZE,v),
629 v->edited_object->bbox.y1-P2D(HANDLE_PIXEL_SIZE,v),
630 v->edited_object->bbox.x2+P2D(HANDLE_PIXEL_SIZE,v),
631 v->edited_object->bbox.y2+P2D(HANDLE_PIXEL_SIZE,v)))
632 {
633 if (state.editing_point)
634 {
635 toggle_point_spline(v,bev->x,bev->y);
636 state.editing_point = FALSE;
637 };
638 send_redraw_object(v,v->edited_object->ob);
639 v->edited_object = NULL;
640 }
641 else if (!state.editing_point)
642 {
643 double near;
644 ListEl *n;
645
646 n = polyline_nearest_point(v->edited_object->ob->ob.spline.points,
647 XP2D(bev->x,v)-v->edited_object->ob->bbox.x1,
648 YP2D(bev->y,v)-v->edited_object->ob->bbox.y1, &near);
649
650 if (D2P(near,v)<POINTER_JUMP_SIZE)
651 {
652 XWarpPointer(display, None, v->draw_window->win, 0, 0, 1, 1,
653 XD2P(SPOINT(n)->x+v->edited_object->ob->bbox.x1,v),
654 YD2P(SPOINT(n)->y+v->edited_object->ob->bbox.y1,v));
655 state.editing_point = TRUE;
656 edited_point = n;
657 toggle_point_spline(v,bev->x, bev->y);
658 orig_x = bev->x;
659 orig_y = bev->y;
660 };
661 }
662 else if (bev->button == Button3)
663 {
664 toggle_point_spline(v,bev->x, bev->y);
665 state.editing_point = FALSE;
666 };
667 }
668 else
669 { /* CLICKED or RELEASED */
670 Object *rob=NULL;
671 Object *s=NULL;
672 long x,y;
673
674 /* if released, place it and redraw object */
675 if (bev->type == BUTTON_RELEASED && state.editing_point && P_IN_DOC(bev->x,bev->y,v))
676 {
677 register_undo(UNDO_OB_PROP,v->edited_object->ob,v->doc);
678 toggle_point_spline(v,bev->x, bev->y);
679 if (SPOINT(edited_point)->derried)
680 remove_derry(v,v->edited_object->ob,(VPoint *)SPOINT(edited_point));
681
682 if (!state.control_down)
683 {
684 if (state.shift_down &&
685 get_nearest_point(v,v->doc->o, XP2D(bev->x,v),YP2D(bev->y,v),&x,&y,&rob))
686 {
687 if (v->guide_lines && v->guide_lines_displayed)
688 {
689 toggle_guidelines(v);
690 v->guide_lines_displayed = FALSE;
691 };
692 SPOINT(edited_point)->x = x-v->edited_object->ob->bbox.x1;
693 SPOINT(edited_point)->y = y-v->edited_object->ob->bbox.y1;
694 bev->x = XD2P(x,v);
695 bev->y = YD2P(y,v);
696 mouse_x = bev->x;
697 mouse_y = bev->y;
698 XWarpPointer(display, None, v->draw_window->win, 0, 0, 1, 1,
699 bev->x,bev->y);
700 add_derry(rob,v->edited_object->ob,(VPoint *)SPOINT(edited_point));
701 SPOINT(edited_point)->derried = TRUE;
702 }
703 else
704 {
705 if (v->gridon)
706 {
707 SPOINT(edited_point)->x = GXP2D(bev->x, v)-v->edited_object->ob->bbox.x1;
708 SPOINT(edited_point)->y = GYP2D(bev->y, v)-v->edited_object->ob->bbox.y1;
709 }
710 else
711 {
712 SPOINT(edited_point)->x = XP2D(bev->x, v)-v->edited_object->ob->bbox.x1;
713 SPOINT(edited_point)->y = YP2D(bev->y, v)-v->edited_object->ob->bbox.y1;
714 };
715 };
716 }
717 else if (!(edited_point->prev==NULL || edited_point->next==NULL)
718 || v->edited_object->ob->ob.spline.closed)
719 {
720 double f;
721 long sx, sy;
722
723 sx = min(P2D(50,v),max(P2D(-50,v),XP2D(bev->x, v) - ((SPOINT(edited_point)->x)+v->edited_object->ob->bbox.x1)));
724 sy = min(P2D(50,v),max(P2D(-50,v),YP2D(bev->y, v) - ((SPOINT(edited_point)->y)+v->edited_object->ob->bbox.y1)));
725 f = (double)max(abs(sx),abs(sy));
726 /* minus shape factor if "behind" point */
727 if (sx<0 || sy<0)
728 f = -f;
729 f = f/P2D(50,v);
730 if (abs(f)<0.2)
731 f = 0.0; /* angular */
732 SPOINT(edited_point)->s = f;
733 };
734
735
736 store_redraw_object(v->edited_object->ob);
737 delete_cache_list(v->edited_object->ob->ob.spline.cache);
738 v->edited_object->ob->ob.spline.cache = compute_spline_segment(v->edited_object->ob->ob.spline.points,v->edited_object->ob->ob.spline.closed);
739 recalc_polyline_bbox(v->edited_object->ob,TRUE);
740 s = v->edited_object->ob;
741 v->doc->o = change_bbox(v->doc->o, v->edited_object->ob);
742 v->edited_object = get_object_node(v->doc->o,s);
743 send_stored_redraw_object(v,v->edited_object->ob);
744 state.editing_point = FALSE;
745 }
746 else if (state.editing_point)
747 {
748 toggle_point_spline(v,bev->x,bev->y);
749 state.editing_point = FALSE;
750 };
751 };
752 }
753
754 void
spline_point_motion(View * v,long x,long y)755 spline_point_motion(View *v, long x, long y)
756 {
757 long tx, ty;
758
759 /* if drawing, resize as appropriate */
760 if (state.editing_point)
761 {
762 Object *rob=NULL;
763 int test=-1,test2=-1; /* func returns -1 if no change */
764
765 /* complex logic for after a zoom */
766 if (!disable_motion)
767 toggle_point_spline(v,orig_x,orig_y);
768 else if (disable_motion==1)
769 disable_motion--;
770 else
771 {
772 disable_motion--;
773 return;
774 };
775
776 if (state.shift_down && get_nearest_point(v,v->doc->o, XP2D(x,v),YP2D(y,v),&tx,&ty,&rob))
777 {
778 if (v->guide_lines && v->guide_lines_displayed)
779 {
780 toggle_guidelines(v);
781 v->guide_lines_displayed = FALSE;
782 };
783 mouse_x = XD2P(tx,v);
784 mouse_y = YD2P(ty,v);
785 x = mouse_x;
786 y = mouse_y;
787 XWarpPointer(display, None, v->draw_window->win, 0, 0, 1, 1,
788 mouse_x,mouse_y);
789 };
790
791 orig_x = x;
792 orig_y = y;
793
794 if ( x < MOVE_BOUNDARY) /* move left */
795 test = nudge_ruler_x(v->ruler_x_window, v, LEFT);
796 else if ( x >= ((int)v->draw_window->w)-MOVE_BOUNDARY) /* move right */
797 test = nudge_ruler_x(v->ruler_x_window, v, RIGHT);
798
799 if ( y < MOVE_BOUNDARY) /* move up */
800 test2 = nudge_ruler_y(v->ruler_y_window, v, UP);
801 else if ( y >= ((int)v->draw_window->h)-MOVE_BOUNDARY) /* move down */
802 test2 = nudge_ruler_y(v->ruler_y_window, v, DOWN);
803
804 if (test==-1 && test2==-1) /* i.e. not scrolled */
805 {
806 if (!disable_motion)
807 toggle_point_spline(v, x, y);
808 else
809 disable_motion--;
810 }
811 else
812 {
813 redraw_view_window(v);
814 state.rubberon=FALSE;
815 toggle_point_spline(v,mouse_x,mouse_y);
816 };
817 };
818 }
819
820 void
toggle_point_spline(View * v,long x,long y)821 toggle_point_spline(View *v, long x, long y)
822 {
823 List l=NULL,l2,l3;
824 SPoint p,pprev,pnext,pprevprev,pnextnext;
825 Boolean clipped = FALSE;
826 int xpp;
827 VLine line; VLine *pl;
828
829 /* we only need to recompute the relevant section of the spline 2 control points either way */
830 if (edited_point->prev!=NULL)
831 {
832 if (edited_point->prev->prev!=NULL)
833 {
834 pprevprev = *SPOINT(edited_point->prev->prev);
835 pprevprev.x += v->edited_object->ob->bbox.x1;
836 pprevprev.y += v->edited_object->ob->bbox.y1;
837 l = add_to_list(l,0,0,(void *)&pprevprev);
838 };
839
840 pprev = *SPOINT(edited_point->prev);
841 pprev.x += v->edited_object->ob->bbox.x1;
842 pprev.y += v->edited_object->ob->bbox.y1;
843 l = add_to_list(l,1,0,(void *)&pprev);
844 };
845
846 if (!state.control_down)
847 {
848 if (v->gridon)
849 {
850 p.x = GXP2D(x,v);
851 p.y = GYP2D(y,v);
852 }
853 else
854 {
855 p.x = XP2D(x,v);
856 p.y = YP2D(y,v);
857 };
858
859 p.s = SPOINT(edited_point)->s;
860 }
861 else if (!(edited_point->prev==NULL || edited_point->next==NULL)
862 || v->edited_object->ob->ob.spline.closed)
863 {
864 double f;
865 p.x = SPOINT(edited_point)->x+v->edited_object->ob->bbox.x1;
866 p.y = SPOINT(edited_point)->y+v->edited_object->ob->bbox.y1;
867 f = min(P2D(50,v),max(P2D(-50,v),XP2D(x, v) - p.x));
868 f = f/P2D(50,v);
869 if (abs(f)<0.2)
870 f = 0.0; /* angular */
871 p.s = f;
872 }
873 else
874 {
875 p.x = SPOINT(edited_point)->x+v->edited_object->ob->bbox.x1;
876 p.y = SPOINT(edited_point)->y+v->edited_object->ob->bbox.y1;
877 p.s = SPOINT(edited_point)->s;
878 };
879
880
881 l = add_to_list(l,2,0,(void *)&p);
882
883 if (edited_point->next!=NULL)
884 {
885 pnext = *SPOINT(edited_point->next);
886 pnext.x += v->edited_object->ob->bbox.x1;
887 pnext.y += v->edited_object->ob->bbox.y1;
888 l = add_to_list(l,3,0,(void *)&pnext);
889 if (edited_point->next->next!=NULL)
890 {
891 pnextnext = *SPOINT(edited_point->next->next);
892 pnextnext.x += v->edited_object->ob->bbox.x1;
893 pnextnext.y += v->edited_object->ob->bbox.y1;
894 l = add_to_list(l,4,0,(void *)&pnextnext);
895 };
896 };
897
898 l2 = compute_spline_segment(l,FALSE);
899
900 xpp=0;
901 l3 = l2;
902 line.x1 = XD2P(POINT(l3)->x,v);
903 line.y1 = YD2P(POINT(l3)->y,v);
904 xp[xpp].x = line.x1;
905 xp[xpp].y = line.y1;
906 xpp++;
907
908 while (l3->next!=NULL)
909 {
910 line.x1 = XD2P(POINT(l3)->x,v);
911 line.y1 = YD2P(POINT(l3)->y,v);
912 line.x2 = XD2P(POINT(l3->next)->x,v);
913 line.y2 = YD2P(POINT(l3->next)->y,v);
914 pl = clip_line(0,0,(long)v->draw_window->w,(long)v->draw_window->h, &line, &clipped, 1);
915 if (pl!=NULL)
916 {
917 xp[xpp].x = pl->x2;
918 xp[xpp].y = pl->y2;
919 xpp++;
920 };
921
922 if (clipped || xpp==MAXPOINTS)
923 {
924 XDrawLines(display,v->draw_window->win, blackxorgc, xp, xpp, CoordModeOrigin);
925 /* continue from last line */
926 xp[0].x = XD2P(POINT(l3->next)->x,v);
927 xp[0].y = YD2P(POINT(l3->next)->y,v);
928 xpp=1;
929 };
930 l3 = l3->next;
931 };
932
933 if (xpp!=0)
934 XDrawLines(display,v->draw_window->win, blackxorgc, xp, xpp, CoordModeOrigin);
935
936 /* don't need to delete contents, auto vars in scope */
937 delete_list(l);
938
939 delete_cache_list(l2);
940 }
941
942 void
draw_spline(Object * ob,View * view,GC gc,long x,long y,double rx,double ry,double angle)943 draw_spline(Object *ob, View *view, GC gc, long x, long y, double rx, double ry, double angle)
944 {
945 int xpp=0,hpp;
946 List l;
947 VLine line;
948 VLine *pl;
949 Boolean clipped=FALSE;
950
951 if (ob->ob.spline.cache==NULL)
952 {
953 if (cpoly!=ob) /* ie. not drawing this */
954 ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,ob->ob.spline.closed);
955 else
956 ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,FALSE);
957 };
958 if (ob->ob.spline.cache==NULL)
959 return;
960
961 if (angle!=0.0)
962 {
963 double sina, cosa;
964 long cx,cy;
965
966 cosa = cosround(angle);
967 sina = sinround(angle);
968 cx = (ob->bbox.x2-ob->bbox.x1)/2;
969 cy = (ob->bbox.y2-ob->bbox.y1)/2;
970
971 xpp=0;
972 l = ob->ob.spline.cache;
973 line.x1 = XD2P(x,view) + D2P(cx,view) + (D2P((cosa*(POINT(l)->x-cx)),view) - D2P((sina*(POINT(l)->y-cy)),view));
974 line.y1 = YD2P(y,view) + D2P(cy,view) + (D2P((sina*(POINT(l)->x-cx)),view) + D2P((cosa*(POINT(l)->y-cy)),view));
975 xp[xpp].x = line.x1;
976 xp[xpp].y = line.y1;
977 xpp++;
978
979 while (l->next!=NULL)
980 {
981 line.x1 = XD2P(x,view) + D2P(cx,view) + (D2P((cosa*(POINT(l)->x-cx)),view) - D2P((sina*(POINT(l)->y-cy)),view));
982 line.y1 = YD2P(y,view) + D2P(cy,view) + (D2P((sina*(POINT(l)->x-cx)),view) + D2P((cosa*(POINT(l)->y-cy)),view));
983 line.x2 = XD2P(x,view) + D2P(cx,view) + (D2P((cosa*(POINT(l->next)->x-cx)),view) - D2P((sina*(POINT(l->next)->y-cy)),view));
984 line.y2 = YD2P(y,view) + D2P(cy,view) + (D2P((sina*(POINT(l->next)->x-cx)),view) + D2P((cosa*(POINT(l->next)->y-cy)),view));
985 pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2*ob->lw);
986 if (pl!=NULL)
987 {
988 xp[xpp].x = pl->x2;
989 xp[xpp].y = pl->y2;
990 xpp++;
991 };
992
993 if (clipped || xpp==MAXPOINTS)
994 {
995 XDrawLines(display,view->draw_window->win, gc, xp, xpp, CoordModeOrigin);
996 /* continue from last line */
997 xp[0].x = XD2P(x,view) + D2P(cx,view) + (D2P((cosa*(POINT(l->next)->x-cx)),view) - D2P((sina*(POINT(l->next)->y-cy)),view));
998 xp[0].y = YD2P(y,view) + D2P(cy,view) + (D2P((sina*(POINT(l->next)->x-cx)),view) + D2P((cosa*(POINT(l->next)->y-cy)),view));
999
1000 xpp=1;
1001 };
1002 l = l->next;
1003 };
1004
1005 if (xpp!=0)
1006 XDrawLines(display,view->draw_window->win, gc, xp, xpp, CoordModeOrigin);
1007
1008 if (ob!=cpoly && ob->fs!=NONE && gc!=blackxorgc)
1009 XFillPolygon(display,view->draw_window->win, fillgc, xp, xpp, Complex, CoordModeOrigin);
1010
1011 if (ob->ob.spline.closed)
1012 {
1013 /* last little line */
1014 line.x1 = xp[xpp-1].x;
1015 line.y1 = xp[xpp-1].y;
1016 line.x2 = XD2P(x,view) + D2P(cx,view) + (D2P((cosa*(POINT(ob->ob.spline.cache)->x-cx)),view) - D2P((sina*(POINT(ob->ob.spline.cache)->y-cy)),view));
1017 line.y2 = YD2P(y,view) + D2P(cy,view) + (D2P((sina*(POINT(ob->ob.spline.cache)->x-cx)),view) + D2P((cosa*(POINT(ob->ob.spline.cache)->y-cy)),view));
1018 pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2*ob->lw);
1019 if (pl!=NULL)
1020 XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1021 };
1022 }
1023 else
1024 {
1025 if (state.tied_corner!=NOTSCALING)
1026 corner_magic(ob, &x, &y, rx, ry);
1027
1028 rx = abs(rx);
1029 ry = abs(ry);
1030
1031 l = ob->ob.spline.cache;
1032 hpp=0;
1033 while (l!=NULL && hpp<MAXPOINTS)
1034 {
1035 hp[hpp].x = XD2P(R(POINT(l)->x,rx)+x,view);
1036 hp[hpp].y = YD2P(R(POINT(l)->y,ry)+y,view);
1037 hpp++;
1038 l = l->next;
1039 };
1040
1041 if (ob!=cpoly && ob->fs!=NONE && gc!=blackxorgc)
1042 XFillPolygon(display,view->draw_window->win,fillgc, hp, hpp, Complex, CoordModeOrigin);
1043
1044 if (gc==blackxorgc || ob->lw!=0)
1045 {
1046 xpp=0;
1047 l = ob->ob.spline.cache;
1048 line.x1 = XD2P(R(POINT(l)->x,rx)+x,view);
1049 line.y1 = YD2P(R(POINT(l)->y,ry)+y,view);
1050 xp[xpp].x = line.x1;
1051 xp[xpp].y = line.y1;
1052 xpp++;
1053
1054 while (l->next!=NULL)
1055 {
1056 line.x1 = XD2P(R(POINT(l)->x,rx)+x,view);
1057 line.y1 = YD2P(R(POINT(l)->y,ry)+y,view);
1058 line.x2 = XD2P(R(POINT(l->next)->x,rx)+x,view);
1059 line.y2 = YD2P(R(POINT(l->next)->y,ry)+y,view);
1060 pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2*ob->lw);
1061 if (pl!=NULL)
1062 {
1063 xp[xpp].x = pl->x2;
1064 xp[xpp].y = pl->y2;
1065 xpp++;
1066 };
1067
1068 if (clipped || xpp==MAXPOINTS)
1069 {
1070 XDrawLines(display,view->draw_window->win, gc, xp, xpp, CoordModeOrigin);
1071 /* continue from last line */
1072 xp[0].x = XD2P(R(POINT(l->next)->x,rx)+x,view);
1073 xp[0].y = YD2P(R(POINT(l->next)->y,ry)+y,view);
1074 xpp=1;
1075 };
1076 l = l->next;
1077 };
1078
1079 if (xpp!=0)
1080 XDrawLines(display,view->draw_window->win, gc, xp, xpp, CoordModeOrigin);
1081
1082 if (ob->ob.spline.closed)
1083 {
1084 /* last little line */
1085 line.x1 = xp[xpp-1].x;
1086 line.y1 = xp[xpp-1].y;
1087 line.x2 = XD2P(R(POINT(ob->ob.spline.cache)->x,rx)+x,view);
1088 line.y2 = YD2P(R(POINT(ob->ob.spline.cache)->y,ry)+y,view);
1089 pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2*ob->lw);
1090 if (pl!=NULL)
1091 XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1092 }
1093 else
1094 {
1095 if (ob->barrow!=NULL && rx==1.0 && ry==1.0 && cpoly!=ob)
1096 {
1097 long x1,y1,x2,y2;
1098
1099 x2 = XD2P(POINT(ob->ob.spline.points)->x+x,view);
1100 y2 = YD2P(POINT(ob->ob.spline.points)->y+y,view);
1101 if (ob->ob.spline.points->next!=NULL)
1102 {
1103 x1 = XD2P(POINT(ob->ob.spline.points->next)->x+x,view);
1104 y1 = YD2P(POINT(ob->ob.spline.points->next)->y+y,view);
1105 }
1106 else
1107 {
1108 x1 = XD2P(POINT(ob->ob.spline.points)->x+x,view);
1109 y1 = YD2P(POINT(ob->ob.spline.points)->y+y,view);
1110 };
1111 draw_arrow(view,gc,ob->barrow,x1,y1,x2,y2);
1112 };
1113
1114 if (ob->farrow!=NULL && rx==1.0 && ry==1.0 && cpoly!=ob)
1115 {
1116 /* init to zero to keep -Wall happy */
1117 long x1=0,y1=0,x2,y2;
1118
1119 l = ob->ob.spline.points;
1120
1121 while (l->next!=NULL)
1122 {
1123 if (l->next->next==NULL)
1124 {
1125 x1 = XD2P(POINT(l)->x+x,view);
1126 y1 = YD2P(POINT(l)->y+y,view);
1127 };
1128 l = l->next;
1129 };
1130
1131 x2 = XD2P(POINT(l)->x+x,view);
1132 y2 = YD2P(POINT(l)->y+y,view);
1133
1134 draw_arrow(view,gc,ob->farrow,x1,y1,x2,y2);
1135 };
1136 };
1137 };
1138
1139
1140 /* handles after everything else */
1141 if ((state.current_icon==SPLINEICON && state.busy_drawing && ob==cpoly) ||
1142 (view->edited_object!=NULL && view->edited_object->ob == ob))
1143 {
1144 l = ob->ob.spline.points;
1145 while (l!=NULL)
1146 {
1147 if (P_IN_DOC(XD2P(R(SPOINT(l)->x,rx)+x,view),YD2P(R(SPOINT(l)->y,ry)+y,view),view))
1148 draw_handle(view, XD2P(R(SPOINT(l)->x,rx)+x,view),YD2P(R(SPOINT(l)->y,ry)+y,view));
1149 l=l->next;
1150 };
1151 };
1152 };
1153 }
1154