1 /*
2 * FIG : Facility for Interactive Generation of figures
3 * This part Copyright (c) 1999-2002 by Alexander Durner
4 *
5 * Any party obtaining a copy of these files is granted, free of charge, a
6 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
7 * nonexclusive right and license to deal in this software and documentation
8 * files (the "Software"), including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
10 * the Software, and to permit persons who receive copies from any such
11 * party to do so, with the only requirement being that the above copyright
12 * and this permission notice remain intact.
13 */
14
15 #include "fig.h"
16 #include "resources.h"
17 #include "object.h"
18 #include "mode.h"
19 #include "u_list.h"
20 #include "w_setup.h"
21 #include "w_zoom.h"
22
23 #include "u_smartsearch.h"
24
25 #include "u_geom.h"
26 #include "u_markers.h"
27 #include "u_search.h"
28
29 /* how close to user-selected location? */
30 #define TOLERANCE (zoomscale>1?2:(int)(2/zoomscale))
31
32 /* `singularities' */
33 #define SING_TOLERANCE (zoomscale>.5?1:(int)(.5/zoomscale))
34
35 /***************************************************************************/
36
37 Boolean smart_next_arc_found(int x, int y, int tolerance, int *px, int *py, int shift);
38 Boolean smart_next_ellipse_found(int x, int y, int tolerance, int *px, int *py, int shift);
39 Boolean smart_next_line_found(int x, int y, int tolerance, int *px, int *py, int shift);
40 Boolean smart_next_spline_found(int x, int y, int tolerance, int *px, int *py, int shift);
41 Boolean smart_next_text_found(int x, int y, int tolerance, int *px, int *py, int shift);
42 Boolean smart_next_compound_found(int x, int y, int tolerance, int *px, int *py, int shift);
43
44 void do_smart_object_search(int x, int y, unsigned int shift);
45
46 void smart_show_objecthighlight(void);
47 void smart_erase_objecthighlight(void);
48 void smart_toggle_objecthighlight(void);
49
50 /* exports: */
51 F_point smart_point1, smart_point2;
52
53 /* locals: */
54 static void (*manipulate) ();
55 static void (*handlerproc_left) ();
56 static void (*handlerproc_middle) ();
57 static void (*handlerproc_right) ();
58 static int type;
59 static long objectcount;
60 static long n;
61 static int csr_x, csr_y;
62
63 static F_arc *a;
64 static F_ellipse *e;
65 static F_line *l;
66 static F_spline *s;
67 static F_text *t;
68 static F_compound *c;
69
70 /***************************************************************************/
71
72 /* functions: */
73
74
75
76 void
init_smart_searchproc_left(void (* handlerproc)())77 init_smart_searchproc_left(void (*handlerproc) (/* ??? */))
78 {
79 handlerproc_left = handlerproc;
80 }
81
82 void
init_smart_searchproc_middle(void (* handlerproc)())83 init_smart_searchproc_middle(void (*handlerproc) (/* ??? */))
84 {
85 handlerproc_middle = handlerproc;
86 }
87
88 void
init_smart_searchproc_right(void (* handlerproc)())89 init_smart_searchproc_right(void (*handlerproc) (/* ??? */))
90 {
91 handlerproc_right = handlerproc;
92 }
93
94
95 void /* Shift Key Status from XEvent */
smart_object_search_left(int x,int y,unsigned int shift)96 smart_object_search_left(int x, int y, unsigned int shift)
97
98 {
99 manipulate = handlerproc_left;
100 do_smart_object_search(x, y, shift);
101 }
102
103 void /* Shift Key Status from XEvent */
smart_object_search_middle(int x,int y,unsigned int shift)104 smart_object_search_middle(int x, int y, unsigned int shift)
105 {
106 manipulate = handlerproc_middle;
107 do_smart_object_search(x, y, shift);
108 }
109
110 void /* Shift Key Status from XEvent */
smart_object_search_right(int x,int y,unsigned int shift)111 smart_object_search_right(int x, int y, unsigned int shift)
112 {
113 manipulate = handlerproc_right;
114 do_smart_object_search(x, y, shift);
115 }
116
117 /***************************************************************************/
118
119 static void
set_smart_points(int x1,int y1,int x2,int y2)120 set_smart_points(int x1, int y1, int x2, int y2)
121 {
122 smart_point1.x = x1;
123 smart_point1.y = y1;
124 smart_point2.x = x2;
125 smart_point2.y = y2;
126 }
127
128 static void
init_smart_search(void)129 init_smart_search(void)
130 {
131 if (highlighting)
132 smart_erase_objecthighlight();
133 else {
134 objectcount = 0;
135 if (ellipse_in_mask())
136 for (e = objects.ellipses; e != NULL; e = e->next)
137 objectcount++;
138 if (anyline_in_mask())
139 for (l = objects.lines; l != NULL; l = l->next)
140 if (validline_in_mask(l))
141 objectcount++;
142 if (anyspline_in_mask())
143 for (s = objects.splines; s != NULL; s = s->next)
144 if (validspline_in_mask(s))
145 objectcount++;
146 if (anytext_in_mask())
147 for (t = objects.texts; t != NULL; t = t->next)
148 if (validtext_in_mask(t))
149 objectcount++;
150 if (arc_in_mask())
151 for (a = objects.arcs; a != NULL; a = a->next)
152 objectcount++;
153 if (compound_in_mask())
154 for (c = objects.compounds; c != NULL; c = c->next)
155 objectcount++;
156 e = NULL;
157 type = O_ELLIPSE;
158 }
159 }
160
161 void /* Shift Key Status from XEvent */
do_smart_object_search(int x,int y,unsigned int shift)162 do_smart_object_search(int x, int y, unsigned int shift)
163 {
164 int px, py;
165 Boolean found = False;
166
167 init_smart_search();
168 for (n = 0; n < objectcount;) {
169 switch (type) {
170 case O_ELLIPSE:
171 found = smart_next_ellipse_found(x, y, TOLERANCE, &px, &py, shift);
172 break;
173 case O_POLYLINE:
174 found = smart_next_line_found(x, y, TOLERANCE, &px, &py, shift);
175 break;
176 case O_SPLINE:
177 found = smart_next_spline_found(x, y, TOLERANCE, &px, &py, shift);
178 break;
179 case O_TXT:
180 found = smart_next_text_found(x, y, TOLERANCE, &px, &py, shift);
181 break;
182 case O_ARC:
183 found = smart_next_arc_found(x, y, TOLERANCE, &px, &py, shift);
184 break;
185 case O_COMPOUND:
186 found = smart_next_compound_found(x, y, TOLERANCE, &px, &py, shift);
187 break;
188 }
189
190 if (found)
191 break;
192
193 switch (type) {
194 case O_ELLIPSE:
195 type = O_POLYLINE;
196 l = NULL;
197 break;
198 case O_POLYLINE:
199 type = O_SPLINE;
200 s = NULL;
201 break;
202 case O_SPLINE:
203 type = O_TXT;
204 t = NULL;
205 break;
206 case O_TXT:
207 type = O_ARC;
208 a = NULL;
209 break;
210 case O_ARC:
211 type = O_COMPOUND;
212 c = NULL;
213 break;
214 case O_COMPOUND:
215 type = O_ELLIPSE;
216 e = NULL;
217 break;
218 }
219 }
220 if (!found) { /* nothing found */
221 /* dummy values */
222 smart_point1.x = smart_point1.y = 0;
223 smart_point2.x = smart_point2.y = 0;
224 csr_x = x;
225 csr_y = y;
226 type = -1;
227 smart_show_objecthighlight();
228 } else if (shift) { /* show selected object */
229 smart_show_objecthighlight();
230 } else { /* user selected an object */
231 smart_erase_objecthighlight();
232 switch (type) {
233 case O_ELLIPSE:
234 manipulate(e, type, x, y, px, py);
235 break;
236 case O_POLYLINE:
237 manipulate(l, type, x, y, px, py);
238 break;
239 case O_SPLINE:
240 manipulate(s, type, x, y, px, py);
241 break;
242 case O_TXT:
243 manipulate(t, type, x, y, px, py);
244 break;
245 case O_ARC:
246 manipulate(a, type, x, y, px, py);
247 break;
248 case O_COMPOUND:
249 manipulate(c, type, x, y, px, py);
250 break;
251 }
252 }
253 }
254
255 /***************************************************************************/
256
257 Boolean
smart_next_arc_found(int x,int y,int tolerance,int * px,int * py,int shift)258 smart_next_arc_found(int x, int y, int tolerance, int *px, int *py, int shift)
259 {
260 float ax, ay;
261 int x1, y1, x2, y2;
262
263 if (!arc_in_mask())
264 return 0;
265 if (a == NULL)
266 a = last_arc(objects.arcs);
267 else if (shift)
268 a = prev_arc(objects.arcs, a);
269
270 for (; a != NULL; a = prev_arc(objects.arcs, a), n++) {
271 if (!close_to_arc(a, x, y, tolerance, &ax, &ay))
272 continue;
273 /* point found */
274 *px = x1 = round(ax);
275 *py = y1 = round(ay);
276 x2 = x1 + round(ay - a->center.y);
277 y2 = y1 - round(ax - a->center.x);
278 set_smart_points(x1, y1, x2, y2);
279 return 1;
280 }
281 return 0;
282 }
283
284 Boolean
smart_next_ellipse_found(int x,int y,int tolerance,int * px,int * py,int shift)285 smart_next_ellipse_found(int x, int y, int tolerance, int *px, int *py, int shift)
286 {
287 float ex, ey, vx, vy;
288 int x1, y1, x2, y2;
289
290 if (!ellipse_in_mask())
291 return (0);
292 if (e == NULL)
293 e = last_ellipse(objects.ellipses);
294 else if (shift)
295 e = prev_ellipse(objects.ellipses, e);
296 for (; e != NULL; e = prev_ellipse(objects.ellipses, e), n++) {
297 if (!close_to_ellipse(e, x, y, tolerance, &ex, &ey, &vx, &vy))
298 continue;
299 *px = round(ex);
300 *py = round(ey);
301 /* handle special case of very small ellipse */
302 if (fabs(ex - e->center.x) <= SING_TOLERANCE &&
303 fabs(ey - e->center.y) <= SING_TOLERANCE) {
304 x1 = x2 = *px;
305 y1 = y2 = *py;
306 }
307 else {
308 x1 = *px;
309 y1 = *py;
310 x2 = x1 + round(vx);
311 y2 = y1 + round(vy);
312 }
313 set_smart_points(x1, y1, x2, y2);
314 return 1;
315 }
316 return 0;
317 }
318
319 Boolean
smart_next_line_found(int x,int y,int tolerance,int * px,int * py,int shift)320 smart_next_line_found(int x, int y, int tolerance, int *px, int *py, int shift)
321 { /* return the pointer to lines object if the
322 * search is successful otherwise return
323 * NULL. The value returned via (px, py) is
324 * the closest point on the vector to point
325 * (x, y) */
326
327 int lx1, ly1, lx2, ly2;
328
329 if (!anyline_in_mask())
330 return (0);
331 if (l == NULL)
332 l = last_line(objects.lines);
333 else if (shift)
334 l = prev_line(objects.lines, l);
335
336 for (; l != NULL; l = prev_line(objects.lines, l)) {
337 if (validline_in_mask(l)) {
338 n++;
339 if (close_to_polyline(l, x, y, tolerance, SING_TOLERANCE, px, py,
340 &lx1, &ly1, &lx2, &ly2)) {
341 set_smart_points(lx1, ly1, lx2, ly2);
342 return 1;
343 }
344 }
345 }
346 return 0;
347 }
348
349 Boolean
smart_next_spline_found(int x,int y,int tolerance,int * px,int * py,int shift)350 smart_next_spline_found(int x, int y, int tolerance, int *px, int *py, int shift)
351 /* We call `close_to_spline' which uses HIGH_PRECISION.
352 Think about it.
353 */
354 {
355 int lx1, ly1, lx2, ly2;
356
357 if (!anyspline_in_mask())
358 return (0);
359 if (s == NULL)
360 s = last_spline(objects.splines);
361 else if (shift)
362 s = prev_spline(objects.splines, s);
363
364 for (; s != NULL; s = prev_spline(objects.splines, s)) {
365 if (validspline_in_mask(s)) {
366 n++;
367 if (close_to_spline(s, x, y, tolerance, px, py,
368 &lx1, &ly1, &lx2, &ly2)) {
369 set_smart_points(lx1, ly1, lx2, ly2);
370 return 1;
371 }
372 }
373 }
374 return 0;
375 }
376
377 /* actually, the following are not very smart */
378
379 Boolean
smart_next_text_found(int x,int y,int tolerance,int * px,int * py,int shift)380 smart_next_text_found(int x, int y, int tolerance, int *px, int *py, int shift)
381 {
382 int dum, tlength;
383
384 if (!anytext_in_mask())
385 return (0);
386 if (t == NULL)
387 t = last_text(objects.texts);
388 else if (shift)
389 t = prev_text(objects.texts, t);
390
391 for (; t != NULL; t = prev_text(objects.texts, t))
392 if (validtext_in_mask(t)) {
393 n++;
394 if (in_text_bound(t, x, y, &dum, False)) {
395 *px = x;
396 *py = y;
397 tlength = text_length(t);
398 set_smart_points(t->base_x, t->base_y,
399 t->base_x + round(tlength * cos((double)t->angle)),
400 t->base_y + round(tlength * sin((double)t->angle)));
401 return 1;
402 }
403 }
404 return 0;
405 }
406
407 Boolean
smart_next_compound_found(int x,int y,int tolerance,int * px,int * py,int shift)408 smart_next_compound_found(int x, int y, int tolerance, int *px, int *py, int shift)
409 {
410 float tol2;
411
412 if (!compound_in_mask())
413 return (0);
414 if (c == NULL)
415 c = last_compound(objects.compounds);
416 else if (shift)
417 c = prev_compound(objects.compounds, c);
418
419 tol2 = tolerance * tolerance;
420
421 for (; c != NULL; c = prev_compound(objects.compounds, c), n++) {
422 if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x,
423 c->secorner.y, x, y, tolerance, tol2, px, py)) {
424 set_smart_points(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x, c->secorner.y);
425 return 1;
426 }
427 else if (close_to_vector(c->secorner.x, c->secorner.y, c->nwcorner.x,
428 c->secorner.y, x, y, tolerance, tol2, px, py)) {
429 set_smart_points(c->secorner.x, c->secorner.y, c->nwcorner.x, c->secorner.y);
430 return 1;
431 }
432 else if (close_to_vector(c->secorner.x, c->secorner.y, c->secorner.x,
433 c->nwcorner.y, x, y, tolerance, tol2, px, py)) {
434 set_smart_points(c->secorner.x, c->secorner.y, c->secorner.x, c->nwcorner.y);
435 return 1;
436 }
437 else if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->secorner.x,
438 c->nwcorner.y, x, y, tolerance, tol2, px, py)) {
439 set_smart_points(c->nwcorner.x, c->nwcorner.y, c->secorner.x, c->nwcorner.y);
440 return 1;
441 }
442 }
443 return 0;
444 }
445
smart_show_objecthighlight(void)446 void smart_show_objecthighlight(void)
447 {
448 if (highlighting)
449 return;
450 highlighting = 1;
451 smart_toggle_objecthighlight();
452 }
453
smart_erase_objecthighlight(void)454 void smart_erase_objecthighlight(void)
455 {
456 if (!highlighting)
457 return;
458 highlighting = 0;
459 smart_toggle_objecthighlight();
460 if (type == -1) {
461 e = NULL;
462 type = O_ELLIPSE;
463 }
464 }
465
smart_toggle_objecthighlight(void)466 void smart_toggle_objecthighlight(void)
467 {
468 switch (type) {
469 case O_ELLIPSE:
470 toggle_ellipsehighlight(e);
471 break;
472 case O_POLYLINE:
473 toggle_linehighlight(l);
474 break;
475 case O_SPLINE:
476 toggle_splinehighlight(s);
477 break;
478 case O_TXT:
479 toggle_texthighlight(t);
480 break;
481 case O_ARC:
482 toggle_archighlight(a);
483 break;
484 case O_COMPOUND:
485 toggle_compoundhighlight(c);
486 break;
487 default:
488 toggle_csrhighlight(csr_x, csr_y);
489 }
490 }
491
492 void
smart_null_proc(void)493 smart_null_proc(void)
494 {
495 /* almost does nothing */
496 if (highlighting)
497 smart_erase_objecthighlight();
498 }
499