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