1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2007 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  *
7  * Any party obtaining a copy of these files is granted, free of charge, a
8  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
9  * nonexclusive right and license to deal in this software and documentation
10  * files (the "Software"), including without limitation the rights to use,
11  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
12  * the Software, and to permit persons who receive copies from any such
13  * party to do so, with the only requirement being that the above copyright
14  * and this permission notice remain intact.
15  *
16  */
17 
18 #include "e_rotate.h"
19 
20 #include <stddef.h>
21 #include <math.h>
22 
23 #include "resources.h"
24 #include "mode.h"
25 #include "object.h"
26 #include "paintop.h"
27 #include "d_text.h"
28 #include "e_flip.h"
29 #include "u_bound.h"
30 #include "u_draw.h"
31 #include "u_geom.h"
32 #include "u_create.h"
33 #include "u_list.h"
34 #include "u_markers.h"
35 #include "u_redraw.h"
36 #include "u_search.h"
37 #include "w_canvas.h"
38 #include "w_cursor.h"
39 #include "w_mousefun.h"
40 #include "w_msgpanel.h"
41 #include "xfig_math.h"
42 
43 
44 /* EXPORTS  */
45 
46 int		setcenter;
47 int		setcenter_x;
48 int		setcenter_y;
49 int		rotn_dirn;
50 float		act_rotnangle;
51 
52 /* LOCAL */
53 
54 static int	copy;
55 
56 static void	init_rotate(F_line *p, int type, int x, int y, int px, int py);
57 static void	set_unset_center(int x, int y);
58 static void	init_copynrotate(F_line *p, int type, int x,int y,int px,int py);
59 static void	rotate_selected(void);
60 static void	rotate_search(F_line *p, int type, int x, int y, int px, int py);
61 static void	init_rotateline(F_line *l, int px, int py);
62 static void	init_rotatetext(F_text *t, int px, int py);
63 static void	init_rotatearc (F_arc *a, int px, int py);
64 static void	init_rotateellipse (F_ellipse *e, int px, int py);
65 static void	init_rotatespline (F_spline *s, int px, int py);
66 static void	init_rotatecompound (F_compound *c, int px, int py);
67 static void	rotate_text (F_text *t, int x, int y);
68 static void	rotate_ellipse (F_ellipse *e, int x, int y);
69 static void	rotate_arc (F_arc *a, int x, int y);
70 static void	rotate_spline (F_spline *s, int x, int y);
71 static int	valid_rot_angle (F_compound *c);
72 static void	rotate_point (F_point *p, int x, int y);
73 static void	rotate_xy (int *orig_x, int *orig_y, int x, int y);
74 
75 
76 void
rotate_cw_selected(void)77 rotate_cw_selected(void)
78 {
79     rotn_dirn = 1;
80     /* erase any existing center */
81     if (setcenter)
82 	center_marker(setcenter_x, setcenter_y);
83     /* and any anchor */
84     if (setanchor)
85 	center_marker(setanchor_x, setanchor_y);
86     setcenter = 0;
87     setanchor = 0;
88     rotate_selected();
89 }
90 
91 void
rotate_ccw_selected(void)92 rotate_ccw_selected(void)
93 {
94     rotn_dirn = -1;
95     /* erase any existing center */
96     if (setcenter)
97 	center_marker(setcenter_x, setcenter_y);
98     /* and any anchor */
99     if (setanchor)
100 	center_marker(setanchor_x, setanchor_y);
101     setcenter = 0;
102     setanchor = 0;
103     rotate_selected();
104 }
105 
106 static void
rotate_selected(void)107 rotate_selected(void)
108 {
109     set_mousefun("rotate object", "copy & rotate", "set center",
110 			LOC_OBJ, LOC_OBJ, "set center");
111     canvas_kbd_proc = null_proc;
112     canvas_locmove_proc = null_proc;
113     canvas_ref_proc = null_proc;
114     init_searchproc_left(init_rotate);
115     init_searchproc_middle(init_copynrotate);
116     canvas_leftbut_proc = object_search_left;
117     canvas_middlebut_proc = object_search_middle;
118     canvas_rightbut_proc = set_unset_center;
119     set_cursor(pick15_cursor);
120     reset_action_on();
121 }
122 
123 static void
set_unset_center(int x,int y)124 set_unset_center(int x, int y)
125 {
126     if (setcenter) {
127       set_mousefun("rotate object", "copy & rotate", "set center",
128 			LOC_OBJ, LOC_OBJ, "set center");
129       draw_mousefun_canvas();
130       setcenter = 0;
131       center_marker(setcenter_x,setcenter_y);
132       /* second call to center_mark on same position deletes */
133     }
134     else {
135       set_mousefun("rotate object", "copy & rotate", "unset center",
136 			LOC_OBJ, LOC_OBJ, "unset center");
137       draw_mousefun_canvas();
138       setcenter = 1;
139       setcenter_x = x;
140       setcenter_y = y;
141       center_marker(setcenter_x,setcenter_y);
142     }
143 
144 }
145 
146 static void
init_rotate(F_line * p,int type,int x,int y,int px,int py)147 init_rotate(F_line *p, int type, int x, int y, int px, int py)
148 {
149     copy = 0;
150     act_rotnangle = cur_rotnangle;
151     if (setcenter)
152         rotate_search(p, type, x, y,setcenter_x,setcenter_y );
153       /* remember rotation center, e.g for multiple rotation*/
154     else
155         rotate_search(p, type, x, y, px, py);
156 }
157 
158 static void
init_copynrotate(F_line * p,int type,int x,int y,int px,int py)159 init_copynrotate(F_line *p, int type, int x, int y, int px, int py)
160 {
161     int		    i;
162 
163     copy = 1;
164     act_rotnangle = cur_rotnangle;
165     for ( i = 1; i <= cur_numcopies; act_rotnangle += cur_rotnangle, i++) {
166 	if (setcenter)
167 	    rotate_search(p, type, x, y,setcenter_x,setcenter_y );
168 	/* remember rotation center */
169 	else
170             rotate_search(p, type, x, y, px, py);
171     }
172 }
173 
174 static void
rotate_search(F_line * p,int type,int x,int y,int px,int py)175 rotate_search(F_line *p, int type, int x, int y, int px, int py)
176 {
177 	(void)x;
178 	(void)y;
179 
180     switch (type) {
181     case O_POLYLINE:
182 	cur_l = (F_line *) p;
183 	init_rotateline(cur_l, px, py);
184 	break;
185     case O_ARC:
186 	cur_a = (F_arc *) p;
187 	init_rotatearc(cur_a, px, py);
188 	break;
189     case O_ELLIPSE:
190 	cur_e = (F_ellipse *) p;
191 	init_rotateellipse(cur_e, px, py);
192 	break;
193     case O_SPLINE:
194 	cur_s = (F_spline *) p;
195 	init_rotatespline(cur_s, px, py);
196 	break;
197     case O_TXT:
198 	cur_t = (F_text *) p;
199 	init_rotatetext(cur_t, px, py);
200 	break;
201     case O_COMPOUND:
202 	cur_c = (F_compound *) p;
203 	init_rotatecompound(cur_c, px, py);
204 	break;
205     default:
206 	return;
207     }
208 }
209 
210 static void
init_rotateline(F_line * l,int px,int py)211 init_rotateline(F_line *l, int px, int py)
212 {
213     F_line	   *line;
214 
215     set_temp_cursor(wait_cursor);
216     line = copy_line(l);
217     rotate_line(line, px, py);
218     if (copy) {
219 	add_line(line);
220     } else {
221 	toggle_linemarker(l);
222 	draw_line(l, ERASE);
223 	change_line(l, line);
224     }
225     /* redisplay objects under this object before it was rotated */
226     redisplay_line(l);
227     /* and this line and any other objects on top */
228     redisplay_line(line);
229     reset_cursor();
230 }
231 
232 static void
init_rotatetext(F_text * t,int px,int py)233 init_rotatetext(F_text *t, int px, int py)
234 {
235     F_text	   *text;
236 
237     set_temp_cursor(wait_cursor);
238     text = copy_text(t);
239     rotate_text(text, px, py);
240     if (copy) {
241 	add_text(text);
242     } else {
243 	toggle_textmarker(t);
244 	draw_text(t, ERASE);
245 	change_text(t, text);
246     }
247     /* redisplay objects under this object before it was rotated */
248     redisplay_text(t);
249     /* and this text and any other objects on top */
250     redisplay_text(text);
251     reset_cursor();
252 }
253 
init_rotateellipse(F_ellipse * e,int px,int py)254 void init_rotateellipse(F_ellipse *e, int px, int py)
255 {
256     F_ellipse	   *ellipse;
257 
258     set_temp_cursor(wait_cursor);
259     ellipse = copy_ellipse(e);
260     rotate_ellipse(ellipse, px, py);
261     if (copy) {
262 	add_ellipse(ellipse);
263     } else {
264 	toggle_ellipsemarker(e);
265 	draw_ellipse(e, ERASE);
266 	change_ellipse(e, ellipse);
267     }
268     /* redisplay objects under this object before it was rotated */
269     redisplay_ellipse(e);
270     /* and this ellipse and any other objects on top */
271     redisplay_ellipse(ellipse);
272     reset_cursor();
273 }
274 
init_rotatearc(F_arc * a,int px,int py)275 void init_rotatearc(F_arc *a, int px, int py)
276 {
277     F_arc	   *arc;
278 
279     set_temp_cursor(wait_cursor);
280     arc = copy_arc(a);
281     rotate_arc(arc, px, py);
282     if (copy) {
283 	add_arc(arc);
284     } else {
285 	toggle_arcmarker(a);
286 	draw_arc(a, ERASE);
287 	change_arc(a, arc);
288     }
289     /* redisplay objects under this object before it was rotated */
290     redisplay_arc(a);
291     /* and this arc and any other objects on top */
292     redisplay_arc(arc);
293     reset_cursor();
294 }
295 
init_rotatespline(F_spline * s,int px,int py)296 void init_rotatespline(F_spline *s, int px, int py)
297 {
298     F_spline	   *spline;
299 
300     set_temp_cursor(wait_cursor);
301     spline = copy_spline(s);
302     rotate_spline(spline, px, py);
303     if (copy) {
304 	add_spline(spline);
305     } else {
306 	toggle_splinemarker(s);
307 	draw_spline(s, ERASE);
308 	change_spline(s, spline);
309     }
310     /* redisplay objects under this object before it was rotated */
311     redisplay_spline(s);
312     /* and this spline and any other objects on top */
313     redisplay_spline(spline);
314     reset_cursor();
315 }
316 
init_rotatecompound(F_compound * c,int px,int py)317 void init_rotatecompound(F_compound *c, int px, int py)
318 {
319     F_compound	   *compound;
320 
321     if (!valid_rot_angle(c)) {
322 	put_msg("Invalid rotation angle for this compound object");
323 	return;
324     }
325     set_temp_cursor(wait_cursor);
326     compound = copy_compound(c);
327     rotate_compound(compound, px, py);
328     if (copy) {
329 	add_compound(compound);
330     } else {
331 	toggle_compoundmarker(c);
332 	draw_compoundelements(c, ERASE);
333 	change_compound(c, compound);
334     }
335     /* redisplay objects under this object before it was rotated */
336     redisplay_compound(c);
337     /* and this compound and any other objects on top */
338     redisplay_compound(compound);
339     reset_cursor();
340 }
341 
342 void
rotate_line(F_line * l,int x,int y)343 rotate_line(F_line *l, int x, int y)
344 {
345     F_point	   *p;
346     int		    dx;
347 
348     /* for speed we treat 90 degrees as a special case */
349     if (act_rotnangle == 90.0) {
350 	for (p = l->points; p != NULL; p = p->next) {
351 	    dx = p->x - x;
352 	    p->x = x + rotn_dirn * (y - p->y);
353 	    p->y = y + rotn_dirn * dx;
354 	}
355     } else {
356 	for (p = l->points; p != NULL; p = p->next)
357 	    rotate_point(p, x, y);
358     }
359 
360 }
361 
rotate_figure(F_compound * f,int x,int y)362 void rotate_figure(F_compound *f, int x, int y)
363 {
364   float old_rotn_dirn, old_act_rotnangle;
365 
366   old_rotn_dirn = rotn_dirn;
367   old_act_rotnangle = act_rotnangle;
368   rotn_dirn = -1;
369   act_rotnangle = 90.0;
370   rotate_compound(f,x,y);
371   rotn_dirn = old_rotn_dirn;
372   act_rotnangle = old_act_rotnangle;
373 }
374 
rotate_spline(F_spline * s,int x,int y)375 void rotate_spline(F_spline *s, int x, int y)
376 {
377     F_point	   *p;
378     int		    dx;
379 
380     /* for speed we treat 90 degrees as a special case */
381     if (act_rotnangle == 90.0) {
382 	for (p = s->points; p != NULL; p = p->next) {
383 	    dx = p->x - x;
384 	    p->x = x + rotn_dirn * (y - p->y);
385 	    p->y = y + rotn_dirn * dx;
386 	}
387     } else {
388 	for (p = s->points; p != NULL; p = p->next)
389 	    rotate_point(p, x, y);
390     }
391 }
392 
rotate_text(F_text * t,int x,int y)393 void rotate_text(F_text *t, int x, int y)
394 {
395     int		    dx;
396 
397     if (act_rotnangle == 90.0) { /* treat 90 degs as special case for speed */
398 	dx = t->base_x - x;
399 	t->base_x = x + rotn_dirn * (y - t->base_y);
400 	t->base_y = y + rotn_dirn * dx;
401     } else {
402 	rotate_xy(&t->base_x, &t->base_y, x, y);
403     }
404     t->angle -= (float) (rotn_dirn * act_rotnangle * M_PI / 180.0);
405     if (t->angle < 0.0)
406 	t->angle += M_2PI;
407     else if (t->angle >= M_2PI - 0.001)
408 	t->angle -= M_2PI;
409     reload_text_fstruct(t);
410 }
411 
rotate_ellipse(F_ellipse * e,int x,int y)412 void rotate_ellipse(F_ellipse *e, int x, int y)
413 {
414     int		    dxc,dxs,dxe;
415 
416     if (act_rotnangle == 90.0) { /* treat 90 degs as special case for speed */
417 	dxc = e->center.x - x;
418 	dxs = e->start.x - x;
419 	dxe = e->end.x - x;
420 	e->center.x = x + rotn_dirn * (y - e->center.y);
421 	e->center.y = y + rotn_dirn * dxc;
422 	e->start.x = x + rotn_dirn * (y - e->start.y);
423 	e->start.y = y + rotn_dirn * dxs;
424 	e->end.x = x + rotn_dirn * (y - e->end.y);
425 	e->end.y = y + rotn_dirn * dxe;
426     } else {
427 	rotate_point((F_point *)&e->center, x, y);
428 	rotate_point((F_point *)&e->start, x, y);
429 	rotate_point((F_point *)&e->end, x, y);
430     }
431     e->angle -= (float) (rotn_dirn * act_rotnangle * M_PI / 180.0);
432     if (e->angle < 0.0)
433 	e->angle += M_2PI;
434     else if (e->angle >= M_2PI - 0.001)
435 	e->angle -= M_2PI;
436 }
437 
rotate_arc(F_arc * a,int x,int y)438 void rotate_arc(F_arc *a, int x, int y)
439 {
440     int		    dx;
441     F_pos	    p[3];
442     float	    xx, yy;
443 
444     /* for speed we treat 90 degrees as a special case */
445     if (act_rotnangle == 90.0) {
446 	dx = a->center.x - x;
447 	a->center.x = x + rotn_dirn * (y - a->center.y);
448 	a->center.y = y + rotn_dirn * dx;
449 	dx = a->point[0].x - x;
450 	a->point[0].x = x + rotn_dirn * (y - a->point[0].y);
451 	a->point[0].y = y + rotn_dirn * dx;
452 	dx = a->point[1].x - x;
453 	a->point[1].x = x + rotn_dirn * (y - a->point[1].y);
454 	a->point[1].y = y + rotn_dirn * dx;
455 	dx = a->point[2].x - x;
456 	a->point[2].x = x + rotn_dirn * (y - a->point[2].y);
457 	a->point[2].y = y + rotn_dirn * dx;
458     } else {
459 	p[0] = a->point[0];
460 	p[1] = a->point[1];
461 	p[2] = a->point[2];
462 	rotate_point((F_point *)&p[0], x, y);
463 	rotate_point((F_point *)&p[1], x, y);
464 	rotate_point((F_point *)&p[2], x, y);
465 	if (compute_arccenter(p[0], p[1], p[2], &xx, &yy)) {
466 	    a->point[0].x = p[0].x;
467 	    a->point[0].y = p[0].y;
468 	    a->point[1].x = p[1].x;
469 	    a->point[1].y = p[1].y;
470 	    a->point[2].x = p[2].x;
471 	    a->point[2].y = p[2].y;
472 	    a->center.x = xx;
473 	    a->center.y = yy;
474 	    a->direction = compute_direction(p[0], p[1], p[2]);
475 	}
476     }
477 }
478 
479 /* checks to see if the objects within c can be rotated by act_rotnangle */
480 
481 int
valid_rot_angle(F_compound * c)482 valid_rot_angle(F_compound *c)
483 {
484     F_line         *l;
485     F_compound     *c1;
486 
487     if (fabs(act_rotnangle) == 90.0 || fabs(act_rotnangle) == 180.0)
488 	return 1; /* always valid */
489     for (l = c->lines; l != NULL; l = l->next)
490 	if (l->type == T_ARCBOX || l->type == T_BOX)
491 	    return 0;
492     for (c1 = c->compounds; c1 != NULL; c1 = c1->next)
493 	if (!valid_rot_angle(c1))
494 	    return 0;
495     return 1;
496 }
497 
498 void
rotate_compound(F_compound * c,int x,int y)499 rotate_compound(F_compound *c, int x, int y)
500 {
501     F_line	   *l;
502     F_arc	   *a;
503     F_ellipse	   *e;
504     F_spline	   *s;
505     F_text	   *t;
506     F_compound	   *c1;
507 
508     for (l = c->lines; l != NULL; l = l->next)
509 	rotate_line(l, x, y);
510     for (a = c->arcs; a != NULL; a = a->next)
511 	rotate_arc(a, x, y);
512     for (e = c->ellipses; e != NULL; e = e->next)
513 	rotate_ellipse(e, x, y);
514     for (s = c->splines; s != NULL; s = s->next)
515 	rotate_spline(s, x, y);
516     for (t = c->texts; t != NULL; t = t->next)
517 	rotate_text(t, x, y);
518     for (c1 = c->compounds; c1 != NULL; c1 = c1->next)
519 	rotate_compound(c1, x, y);
520 
521     /*
522      * Make the bounding box exactly match the dimensions of the compound.
523      */
524     compound_bound(c, &c->nwcorner.x, &c->nwcorner.y,
525 		   &c->secorner.x, &c->secorner.y);
526 }
527 
rotate_point(F_point * p,int x,int y)528 void rotate_point(F_point *p, int x, int y)
529 {
530     /* rotate point p about coordinate (x, y) */
531     double	    dx, dy;
532     double	    cosa, sina, mag, theta;
533 
534     dx = p->x - x;
535     dy = y - p->y;
536     if (dx == 0 && dy == 0)
537 	return;
538 
539     theta = compute_angle(dx, dy);
540     theta -= (double) (rotn_dirn * act_rotnangle * M_PI / 180.0);
541     if (theta < 0.0)
542 	theta += M_2PI;
543     else if (theta >= M_2PI - 0.001)
544 	theta -= M_2PI;
545     mag = sqrt(dx * dx + dy * dy);
546     cosa = mag * cos(theta);
547     sina = mag * sin(theta);
548     p->x = round(x + cosa);
549     p->y = round(y - sina);
550 }
551 
rotate_xy(int * orig_x,int * orig_y,int x,int y)552 void rotate_xy(int *orig_x, int *orig_y, int x, int y)
553 {
554     /* rotate coord (orig_x, orig_y) about coordinate (x, y) */
555     double	    dx, dy;
556     double	    cosa, sina, mag, theta;
557 
558     dx = *orig_x - x;
559     dy = y - *orig_y;
560     if (dx == 0 && dy == 0)
561 	return;
562 
563     theta = compute_angle(dx, dy);
564     theta -= (double) (rotn_dirn * act_rotnangle * M_PI / 180.0);
565     if (theta < 0.0)
566 	theta += M_2PI;
567     else if (theta >= M_2PI - 0.001)
568 	theta -= M_2PI;
569     mag = sqrt(dx * dx + dy * dy);
570     cosa = mag * cos(theta);
571     sina = mag * sin(theta);
572     *orig_x = round(x + cosa);
573     *orig_y = round(y - sina);
574 }
575