1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <math.h>
24
25 #include <gschem.h>
26
27 #ifdef HAVE_LIBDMALLOC
28 #include <dmalloc.h>
29 #endif
30
31
32 /*! \brief Convert a x coordinate to mils.
33 * \par Function Description
34 * Convert a x coordinate to mils.
35 *
36 * \param [in] w_current The GSCHEM_TOPLEVEL object
37 * \param [in] val The x coordinate to convert
38 * \return The coordinate value in mils.
39 */
mil_x(GSCHEM_TOPLEVEL * w_current,int val)40 int mil_x (GSCHEM_TOPLEVEL *w_current, int val)
41 {
42 double i;
43 double fval;
44 int j;
45
46 fval = val;
47 i = fval * w_current->toplevel->page_current->to_world_x_constant +
48 w_current->toplevel->page_current->left;
49
50 #ifdef HAS_RINT
51 j = rint(i);
52 #else
53 j = i;
54 #endif
55
56 return(j);
57 }
58
59 /*! \brief Convert a y coordinate to mils
60 * \par Function Description
61 * Convert a y coordinate to mils
62 *
63 * \param [in] w_current The GSCHEM_TOPLEVEL object.
64 * \param [in] val The y coordinate to convert.
65 * \return The coordinate value in mils.
66 */
mil_y(GSCHEM_TOPLEVEL * w_current,int val)67 int mil_y(GSCHEM_TOPLEVEL *w_current, int val)
68 {
69 double i;
70 double fval;
71 int j;
72
73 fval = w_current->toplevel->height - val;
74 i = fval * w_current->toplevel->page_current->to_world_y_constant +
75 w_current->toplevel->page_current->top;
76
77 #ifdef HAS_RINT
78 j = rint(i);
79 #else
80 j = i;
81 #endif
82
83 return(j);
84 }
85
86 /*! \brief Convert a x coordinate to pixels.
87 * \par Function Description
88 * Convert a x coordinate to pixels.
89 *
90 * \param [in] w_current The GSCHEM_TOPLEVEL object
91 * \param [in] val The x coordinate to convert
92 * \return The coordinate value in pixels.
93 */
pix_x(GSCHEM_TOPLEVEL * w_current,int val)94 int pix_x (GSCHEM_TOPLEVEL *w_current, int val)
95 {
96
97 double i;
98 int j;
99
100 i = w_current->toplevel->page_current->to_screen_x_constant *
101 (double)(val - w_current->toplevel->page_current->left);
102
103 #ifdef HAS_RINT
104 j = rint(i);
105 #else
106 j = i;
107 #endif
108
109 /* this is a temp solution to fix the wrapping associated with */
110 /* X coords being greated/less than than 2^15 */
111 if (j >= 32768) {
112 j = 32767;
113 }
114 if (j <= -32768) {
115 j = -32767;
116 }
117
118 return(j);
119 }
120
121 /*! \brief Convert a y coordinate to pixels.
122 * \par Function Description
123 * Convert a y coordinate to pixels.
124 *
125 * \param [in] w_current The GSCHEM_TOPLEVEL object
126 * \param [in] val The y coordinate to convert
127 * \return The coordinate value in pixels.
128 */
pix_y(GSCHEM_TOPLEVEL * w_current,int val)129 int pix_y(GSCHEM_TOPLEVEL *w_current, int val)
130 {
131 double i;
132 int j;
133
134 i = w_current->toplevel->height -
135 (w_current->toplevel->page_current->to_screen_y_constant *
136 (double)(val - w_current->toplevel->page_current->top));
137
138 #ifdef HAS_RINT
139 j = rint(i);
140 #else
141 j = i;
142 #endif
143
144 /* this is a temp solution to fix the wrapping associated with */
145 /* X coords being greated/less than than 2^15 */
146 if (j >= 32768) {
147 j = 32767;
148 }
149 if (j <= -32768) {
150 j = -32767;
151 }
152
153 return(j);
154 }
155
156 /*! \brief Transform WORLD coordinates to SCREEN coordinates
157 * \par Function Description
158 * This function takes in WORLD x/y coordinates and
159 * transforms them to SCREEN x/y coordinates.
160 *
161 * \param [in] w_current The GSCHEM_TOPLEVEL object.
162 * \param [in] x The x coordinate in WORLD units.
163 * \param [in] y The y coordinate in WORLD units.
164 * \param [out] px The x coordinate in SCREEN units.
165 * \param [out] py The y coordinate in SCREEN units.
166 */
WORLDtoSCREEN(GSCHEM_TOPLEVEL * w_current,int x,int y,int * px,int * py)167 void WORLDtoSCREEN (GSCHEM_TOPLEVEL *w_current, int x, int y, int *px, int *py)
168 {
169 *px = pix_x (w_current, x);
170 *py = pix_y (w_current, y);
171 }
172
173 /*! \brief Transform WORLD coordinates to WORLD coordinates
174 * \par Function Description
175 * This function takes in SCREEN x/y coordinates and
176 * transforms them to WORLD x/y coordinates.
177 *
178 * \param [in] w_current The GSCHEM_TOPLEVEL object.
179 * \param [in] mx The x coordinate in SCREEN units.
180 * \param [in] my The y coordinate in SCREEN units.
181 * \param [out] x The x coordinate in WORLD units.
182 * \param [out] y The y coordinate in WORLD units.
183 * \note Question: why are we returning in x and y
184 * if this is SCREEN to WORLD shouldn't WORLD
185 * coordinates be returned in mx and my?
186 */
SCREENtoWORLD(GSCHEM_TOPLEVEL * w_current,int mx,int my,int * x,int * y)187 void SCREENtoWORLD (GSCHEM_TOPLEVEL *w_current, int mx, int my, int *x, int *y)
188 {
189 *x = mil_x (w_current, mx);
190 *y = mil_y (w_current, my);
191 }
192
193 /*! \brief Find the closest grid coordinate.
194 * \par Function Description
195 * This function snaps the current input coordinate to the
196 * closest grid coordinate.
197 *
198 * \param [in] w_current The GSCHEM_TOPLEVEL object.
199 * \param [in] input The coordinate to snap.
200 * \return The closest grid coordinate to the input.
201 */
snap_grid(GSCHEM_TOPLEVEL * w_current,int input)202 int snap_grid(GSCHEM_TOPLEVEL *w_current, int input)
203 {
204 int p, m, n;
205 int sign, value, snap_grid;
206
207 if (w_current->snap == SNAP_OFF ||
208 w_current->snap_size <= 0) {
209 return(input);
210 }
211
212 snap_grid = w_current->snap_size;
213
214 /* this code was inspired from killustrator, it's much simpler than mine */
215 sign = ( input < 0 ? -1 : 1 );
216 value = abs(input);
217
218 p = value / snap_grid;
219 m = value % snap_grid;
220 n = p * snap_grid;
221 if (m > snap_grid / 2)
222 n += snap_grid;
223
224 #if DEBUG
225 printf("p: %d\n", p);
226 printf("m: %d\n", m);
227 printf("m > snap_grid / 2: %d\n", (m > snap_grid / 2));
228 printf("n: %d\n", n);
229 printf("n*s: %d\n", n*sign);
230 #endif
231
232 return(sign*n);
233 }
234
235 /*! \brief Get absolute SCREEN coordinate.
236 * \par Function Description
237 * Get absolute SCREEN coordinate.
238 *
239 * \param [in] w_current The GSCHEM_TOPLEVEL object.
240 * \param [in] val The coordinate to convert.
241 * \return The converted SCREEN coordinate.
242 */
SCREENabs(GSCHEM_TOPLEVEL * w_current,int val)243 int SCREENabs(GSCHEM_TOPLEVEL *w_current, int val)
244 {
245 double f0,f1,f;
246
247 double i;
248 int j;
249
250 f0 = w_current->toplevel->page_current->left;
251 f1 = w_current->toplevel->page_current->right;
252 f = w_current->toplevel->width / (f1 - f0);
253 i = f * (double)(val);
254
255 #ifdef HAS_RINT
256 j = rint(i);
257 #else
258 j = i;
259 #endif
260
261 return(j);
262
263 }
264
265 /*! \brief Get absolute WORLD coordinate.
266 * \par Function Description
267 * Get absolute WORLD coordinate.
268 *
269 * \param [in] w_current The GSCHEM_TOPLEVEL object.
270 * \param [in] val The coordinate to convert.
271 * \return The converted WORLD coordinate.
272 */
WORLDabs(GSCHEM_TOPLEVEL * w_current,int val)273 int WORLDabs(GSCHEM_TOPLEVEL *w_current, int val)
274 {
275 double fw0,fw1,fw,fval;
276
277 double i;
278 int j;
279
280 fw1 = w_current->toplevel->page_current->right;
281 fw0 = w_current->toplevel->page_current->left;
282 fw = w_current->toplevel->width;
283 fval = val;
284 i = fval * (fw1 - fw0) / fw;
285
286 #ifdef HAS_RINT
287 j = rint(i);
288 #else
289 j = i;
290 #endif
291
292 return(j);
293 }
294
295
296 /*! \brief */
297 typedef struct st_halfspace HALFSPACE;
298
299 /*! \brief */
300 struct st_halfspace {
301 int left; /* these are booleans */
302 int top;
303 int right;
304 int bottom;
305 };
306
307 /* \note
308 * encode_halfspace and clip are part of the cohen-sutherland clipping
309 * algorithm. They are used to determine if an object is visible or not
310 */
311 /*! \brief Encode WORLD coordinates as halfspace matrix.
312 * \par Function Description
313 * This function takes a point and checks if it is in the bounds
314 * of the current TOPLEVEL object's page coordinates. It
315 * handles points with WORLD coordinates.
316 *
317 * \param [in] w_current The GSCHEM_TOPLEVEL object.
318 * \param [in] point The point in WORLD coordinates to be checked.
319 * \param [out] halfspace The created HALFSPACE structure.
320 *
321 * \warning halfspace must be allocated before this function is called
322 */
WORLDencode_halfspace(GSCHEM_TOPLEVEL * w_current,sPOINT * point,HALFSPACE * halfspace)323 static void WORLDencode_halfspace (GSCHEM_TOPLEVEL *w_current,
324 sPOINT *point, HALFSPACE *halfspace)
325 {
326 halfspace->left = point->x < w_current->toplevel->page_current->left;
327 halfspace->right = point->x > w_current->toplevel->page_current->right;
328 halfspace->bottom = point->y > w_current->toplevel->page_current->bottom;
329 halfspace->top = point->y < w_current->toplevel->page_current->top;
330 }
331
332 /*! \brief Calculate the cliping region for a set of coordinates.
333 * \par Function Description
334 * This function will check the provided set of coordinates to see if
335 * they fall within a clipping region. If they do the coordinates will
336 * be changed to reflect only the region no covered by the clipping window.
337 * All coordinates should be in WORLD units.
338 *
339 * \param [in] w_current The GSCHEM_TOPLEVEL object.
340 * \param [in,out] x1 x coordinate of the first screen point.
341 * \param [in,out] y1 y coordinate of the first screen point.
342 * \param [in,out] x2 x coordinate of the second screen point.
343 * \param [in,out] y2 y coordinate of the second screen point.
344 * \return TRUE if coordinates are now visible, FALSE otherwise.
345 */
WORLDclip_change(GSCHEM_TOPLEVEL * w_current,int * x1,int * y1,int * x2,int * y2)346 int WORLDclip_change (GSCHEM_TOPLEVEL *w_current,
347 int *x1, int *y1, int *x2, int *y2)
348 {
349 HALFSPACE half1, half2;
350 HALFSPACE tmp_half;
351 sPOINT tmp_point;
352 sPOINT point1, point2;
353 float slope;
354 int in1, in2, done;
355 int visible;
356 int w_l, w_t, w_r, w_b;
357
358 point1.x = *x1;
359 point1.y = *y1;
360 point2.x = *x2;
361 point2.y = *y2;
362
363 w_l = w_current->toplevel->page_current->left;
364 w_t = w_current->toplevel->page_current->top;
365 w_r = w_current->toplevel->page_current->right;
366 w_b = w_current->toplevel->page_current->bottom;
367
368 done = FALSE;
369 visible = FALSE;
370
371 do {
372 WORLDencode_halfspace (w_current, &point1, &half1);
373 WORLDencode_halfspace (w_current, &point2, &half2);
374
375 #if DEBUG
376 printf("starting loop\n");
377 printf("1 %d %d %d %d\n", half1.left, half1.top, half1.right, half1.bottom);
378 printf("2 %d %d %d %d\n", half2.left, half2.top, half2.right, half2.bottom);
379 #endif
380
381 in1 = (!half1.left) &&
382 (!half1.top) &&
383 (!half1.right) &&
384 (!half1.bottom);
385
386 in2 = (!half2.left) &&
387 (!half2.top) &&
388 (!half2.right) &&
389 (!half2.bottom);
390
391
392 if (in1 && in2) { /* trivally accept */
393 done = TRUE;
394 visible = TRUE;
395 } else if ( ((half1.left && half2.left) ||
396 (half1.right && half2.right)) ||
397 ((half1.top && half2.top) ||
398 (half1.bottom && half2.bottom)) ) {
399 done = TRUE; /* trivially reject */
400 visible = FALSE;
401 } else { /* at least one point outside */
402 if (in1) {
403 tmp_half = half1;
404 half1 = half2;
405 half2 = tmp_half;
406
407 tmp_point = point1;
408 point1 = point2;
409 point2 = tmp_point;
410 }
411
412 if (point2.x == point1.x) { /* vertical line */
413 if (half1.top) {
414 point1.y = w_t;
415 } else if (half1.bottom) {
416 point1.y = w_b;
417 }
418 } else { /* not a vertical line */
419
420 /* possible fix for alpha core dumping */
421 /* assume the object is visible */
422 if ((point2.x - point1.x) == 0) {
423 return(TRUE);
424 }
425
426 slope = (float) (point2.y - point1.y) /
427 (float) (point2.x - point1.x);
428
429 /* possible fix for alpha core dumping */
430 /* assume the object is visible */
431 if (slope == 0.0) {
432 return(TRUE);
433 }
434
435 if (half1.left) {
436 point1.y = point1.y +
437 (w_l - point1.x) * slope;
438 point1.x = w_l;
439 } else if (half1.right) {
440 point1.y = point1.y +
441 (w_r - point1.x) * slope;
442 point1.x = w_r;
443 } else if (half1.bottom) {
444 point1.x = point1.x +
445 (w_b - point1.y) / slope;
446 point1.y = w_b;
447 } else if (half1.top) {
448 point1.x = point1.x +
449 (w_t - point1.y) / slope;
450 point1.y = w_t;
451 }
452 } /* end of not a vertical line */
453 } /* end of at least one outside */
454 } while (!done);
455
456 /*printf("after: %d %d %d %d\n", point1.x, point1.y, point2.x, point2.y);*/
457 *x1 = point1.x;
458 *y1 = point1.y;
459 *x2 = point2.x;
460 *y2 = point2.y;
461 return(visible);
462 }
463
464 /*! \brief Check if a set of coordinates are within a clipping region
465 * \par Function Description
466 * This function will check if the given set of coordinates
467 * are within a clipping region. No action will be taken to change
468 * the coordinates.
469 *
470 * \param [in] w_current The GSCHEM_TOPLEVEL object.
471 * \param [in,out] x1 x coordinate of the first screen point.
472 * \param [in,out] y1 y coordinate of the first screen point.
473 * \param [in,out] x2 x coordinate of the second screen point.
474 * \param [in,out] y2 y coordinate of the second screen point.
475 * \return TRUE if coordinates are now visible, FALSE otherwise.
476 */
clip_nochange(GSCHEM_TOPLEVEL * w_current,int x1,int y1,int x2,int y2)477 int clip_nochange (GSCHEM_TOPLEVEL *w_current, int x1, int y1, int x2, int y2)
478 {
479 HALFSPACE half1, half2;
480 HALFSPACE tmp_half;
481 sPOINT tmp_point;
482 sPOINT point1, point2;
483 float slope;
484 int in1, in2, done;
485 int visible;
486 int w_l, w_t, w_r, w_b;
487
488 point1.x = x1;
489 point1.y = y1;
490 point2.x = x2;
491 point2.y = y2;
492
493 /*printf("before: %d %d %d %d\n", x1, y1, x2, y2);*/
494
495 w_l = w_current->toplevel->page_current->left;
496 w_t = w_current->toplevel->page_current->top;
497 w_r = w_current->toplevel->page_current->right;
498 w_b = w_current->toplevel->page_current->bottom;
499
500 done = FALSE;
501 visible = FALSE;
502
503 do {
504 WORLDencode_halfspace (w_current, &point1, &half1);
505 WORLDencode_halfspace (w_current, &point2, &half2);
506
507 #if DEBUG
508 printf("starting loop\n");
509 printf("1 %d %d %d %d\n", half1.left, half1.top, half1.right, half1.bottom);
510 printf("2 %d %d %d %d\n", half2.left, half2.top, half2.right, half2.bottom);
511 #endif
512
513 in1 = (!half1.left) &&
514 (!half1.top) &&
515 (!half1.right) &&
516 (!half1.bottom);
517
518 in2 = (!half2.left) &&
519 (!half2.top) &&
520 (!half2.right) &&
521 (!half2.bottom);
522
523
524 if (in1 && in2) { /* trivally accept */
525 done = TRUE;
526 visible = TRUE;
527 } else if ( ((half1.left && half2.left) ||
528 (half1.right && half2.right)) ||
529 ((half1.top && half2.top) ||
530 (half1.bottom && half2.bottom)) ) {
531 done = TRUE; /* trivially reject */
532 visible = FALSE;
533 } else { /* at least one point outside */
534 if (in1) {
535 tmp_half = half1;
536 half1 = half2;
537 half2 = tmp_half;
538
539 tmp_point = point1;
540 point1 = point2;
541 point2 = tmp_point;
542 }
543
544 if (point2.x == point1.x) { /* vertical line */
545 if (half1.top) {
546 point1.y = w_t;
547 } else if (half1.bottom) {
548 point1.y = w_b;
549 }
550 } else { /* not a vertical line */
551
552 /* possible fix for alpha core dumping */
553 /* assume the object is visible */
554 if ((point2.x - point1.x) == 0) {
555 return(TRUE);
556 }
557
558 slope = (float) (point2.y - point1.y) /
559 (float) (point2.x - point1.x);
560
561 /* possible fix for alpha core dumping */
562 /* assume the object is visible */
563 if (slope == 0.0) {
564 return(TRUE);
565 }
566
567 if (half1.left) {
568 point1.y = point1.y +
569 (w_l - point1.x) * slope;
570 point1.x = w_l;
571 } else if (half1.right) {
572 point1.y = point1.y +
573 (w_r - point1.x) * slope;
574 point1.x = w_r;
575 } else if (half1.bottom) {
576 point1.x = point1.x +
577 (w_b - point1.y) / slope;
578 point1.y = w_b;
579 } else if (half1.top) {
580 point1.x = point1.x +
581 (w_t - point1.y) / slope;
582 point1.y = w_t;
583 }
584 } /* end of not a vertical line */
585 } /* end of at least one outside */
586 } while (!done);
587
588 return(visible);
589 }
590
591 /*! \brief Check if a bounding box is visible on the screen.
592 * \par Function Description
593 * This function checks if a given bounding box is visible on the screen.
594 *
595 * WARNING: top and bottom are mis-named in world-coords,
596 * top is the smallest "y" value, and bottom is the largest.
597 * Be careful! This doesn't correspond to what you'd expect.
598 *
599 * \param [in] w_current The GSCHEM_TOPLEVEL object.
600 * \param [in] wleft Left coordinate of the bounding box.
601 * \param [in] wtop Top coordinate of the bounding box.
602 * \param [in] wright Right coordinate of the bounding box.
603 * \param [in] wbottom Bottom coordinate of the bounding box.
604 * \return TRUE if bounding box is visible, FALSE otherwise
605 */
visible(GSCHEM_TOPLEVEL * w_current,int wleft,int wtop,int wright,int wbottom)606 int visible (GSCHEM_TOPLEVEL *w_current,
607 int wleft, int wtop, int wright, int wbottom)
608 {
609 int visible=FALSE;
610
611 /* don't do object clipping if this is false */
612 if (!w_current->toplevel->object_clipping) {
613 return(TRUE);
614 }
615
616 visible = clip_nochange (w_current, wleft, wtop, wright, wtop);
617
618 #if DEBUG
619 printf("vis1 %d\n", visible);
620 #endif
621
622 if (!visible) {
623 visible = clip_nochange (w_current, wleft, wbottom, wright, wbottom);
624 } else {
625 return(visible);
626 }
627
628 #if DEBUG
629 printf("vis2 %d\n", visible);
630 #endif
631
632 if (!visible) {
633 visible = clip_nochange (w_current, wleft, wtop, wleft, wbottom);
634 } else {
635 return(visible);
636 }
637
638 #if DEBUG
639 printf("vis3 %d\n", visible);
640 #endif
641
642 if (!visible) {
643 visible = clip_nochange (w_current, wright, wtop, wright, wbottom);
644 } else {
645 return(visible);
646 }
647
648 #if DEBUG
649 printf("vis4 %d\n", visible);
650 #endif
651
652 #if DEBUG
653 printf("%d %d %d\n", wleft, w_current->toplevel->page_current->top, wright);
654 printf("%d %d %d\n", wtop, w_current->toplevel->page_current->top, wbottom);
655 printf("%d %d %d\n", wleft, w_current->toplevel->page_current->right, wright);
656 printf("%d %d %d\n", wtop, w_current->toplevel->page_current->bottom, wbottom);
657 #endif
658
659 /*
660 * now check to see if bounding box encompasses the entire viewport.
661 * We only need to test if one point on the screen clipping boundary
662 * is indide the bounding box of the object.
663 */
664 if (w_current->toplevel->page_current->left >= wleft &&
665 w_current->toplevel->page_current->left <= wright &&
666 w_current->toplevel->page_current->top >= wtop &&
667 w_current->toplevel->page_current->top <= wbottom ) {
668 visible = 1;
669 }
670
671 #if DEBUG
672 printf("vis5 %d\n", visible);
673 #endif
674
675 return(visible);
676 }
677
678
679 /*! \brief Rounds numbers by a power of 10.
680 * \par Function Description
681 * This function will round numbers using a power of 10 method.
682 * For example:
683 * 1235 rounds to 1000
684 * 670 rounds to 500
685 * 0.234 rounds to 0.2
686 * integer values would be enough if there are no numbers smaller than 1 (hw)
687 *
688 * \param [in] unrounded The number to be rounded.
689 * \return The rounded number.
690 */
691 /* rounds for example 1235 to 1000, 670 to 500, 0.234 to 0.2 ...
692 int would be enough if there are no numbers smaller 1 (hw)*/
round_5_2_1(double unrounded)693 double round_5_2_1(double unrounded)
694 {
695 int digits;
696 double betw_1_10;
697
698 /*only using the automatic cast */
699 digits = log10(unrounded);
700 /* creates numbers between 1 and 10 */
701 betw_1_10 = unrounded / pow(10,digits);
702
703 if (betw_1_10 < 1.5) {
704 return(pow(10,digits));
705 }
706 if (betw_1_10 > 1.4 && betw_1_10 < 3.5 ) {
707 return(2*pow(10,digits));
708 }
709 if (betw_1_10 > 3.4 && betw_1_10 < 7.5 ) {
710 return(5*pow(10,digits));
711 }
712 else {
713 return(10*pow(10,digits));
714 }
715 }
716