Fix 8 year-old segfault.
[geda-gaf/berndj.git] / libgeda / src / o_arc_basic.c
blobe1a25968ff79aa1b3aabd535bfa001cc0e0e10c6
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 /*! \file o_arc_basic.c
22 * \brief functions for the arc object
25 #include <config.h>
27 #include <stdio.h>
28 #include <math.h>
30 #include "libgeda_priv.h"
32 #ifdef HAVE_LIBDMALLOC
33 #include <dmalloc.h>
34 #endif
36 struct st_arc {
37 int x, y; /* world */
39 int width;
40 int height;
42 int start_angle;
43 int end_angle;
46 static OBJECT *o_arc_copy(TOPLEVEL *toplevel, OBJECT *o_current);
47 static void o_arc_recalc(OBJECT *o_current);
48 static void o_arc_grip_foreach(OBJECT *o,
49 gboolean (*fn)(OBJECT *o,
50 int grip_x, int grip_y,
51 enum grip_t whichone,
52 void *userdata),
53 void *userdata);
54 static int o_arc_grip_move(OBJECT *o, int whichone, int x, int y);
56 /*! Default setting for arc draw function. */
57 void (*arc_draw_func)() = NULL;
59 /*! \brief Create a minimal consistent-state arc.
60 * \par Function Description
61 * Creates an arc at a given position but does not add it to the object list.
63 * \param [in] toplevel The TOPLEVEL environment.
64 * \param [in] type The object type (OBJ_ARC).
65 * \param [in] color The "main" color of the arc.
66 * \param [in] x The world X coordinate of the arc's origin.
67 * \param [in] y The world Y coordinate of the arc's origin.
68 * \return A pointer to the newly created OBJECT.
70 OBJECT *o_arc_new_at_xy(TOPLEVEL *toplevel, char type, int color, int x, int y)
72 OBJECT *new_node;
73 ARC *arc;
75 /* create the object */
76 new_node = s_toplevel_new_object(toplevel, type, "arc");
77 new_node->color = color;
79 arc = g_malloc(sizeof (ARC));
80 new_node->arc = arc;
82 new_node->arc->x = x;
83 new_node->arc->y = y;
84 /* Placate valgrind. */
85 new_node->arc->width = 0;
86 new_node->arc->height = 0;
87 new_node->arc->start_angle = 0;
88 new_node->arc->end_angle = 0;
90 new_node->copy_func = &o_arc_copy;
91 new_node->bounds_recalc_func = o_arc_recalc;
92 new_node->draw_func = arc_draw_func;
93 new_node->psprint_func = &o_arc_print;
94 new_node->grip_foreach_func = &o_arc_grip_foreach;
95 new_node->grip_move_func = &o_arc_grip_move;
97 /* Default init */
98 o_set_line_options(new_node, END_NONE, TYPE_SOLID, 0, -1, -1);
99 o_set_fill_options(new_node, FILLING_HOLLOW, -1, -1, -1, -1, -1);
101 o_arc_recalc(new_node);
103 return new_node;
106 /*! \brief
107 * \par Function Description
108 * The function creates a new OBJECT of type arc.
110 * The arc is defined by its center in parameters x and y.
111 * The radius parameter specifies the radius of the arc. The start
112 * angle is given by start_angle and the end angle by end_angle.
113 * The line and fill type of the created arc are set to default.
115 * All dimensions are in world unit, except start_angle and
116 * end_angle in degrees.
118 * A new object of type OBJECT is allocated. Its type and color
119 * are initilized. The description of the arc characteristics
120 * are stored in a new ARC structure.
122 * Now fixed for world coordinates.
124 * \param [in] toplevel The TOPLEVEL object.
125 * \param [in] type
126 * \param [in] color
127 * \param [in] x
128 * \param [in] y
129 * \param [in] radius
130 * \param [in] start_angle
131 * \param [in] end_angle
132 * \return
134 OBJECT *o_arc_new(TOPLEVEL *toplevel,
135 char type, int color,
136 int x, int y, int radius, int start_angle, int end_angle)
138 OBJECT *new_node;
140 new_node = o_arc_new_at_xy(toplevel, type, color, x, y);
142 /*! \note
143 * The ARC structure is initialized with the parameters.
144 * A default initialization is performed for the line and
145 * fill type to avoid misunderstanding.
147 * The functions relative to the use of the object are sets.
150 /* World coordinates */
151 new_node->arc->width = 2 * radius;
152 new_node->arc->height = 2 * radius;
154 /* PB : must check the sign of start_angle, end_angle ... */
155 if(end_angle < 0) {
156 start_angle = start_angle + end_angle;
157 end_angle = -end_angle;
159 if(start_angle < 0) start_angle = 360 + start_angle;
161 new_node->arc->start_angle = start_angle;
162 new_node->arc->end_angle = end_angle;
164 /* new_node->graphical = arc; eventually */
166 return new_node;
169 /*! \brief
170 * \par Function Description
171 * This function creates a new object representing an arc.
173 * The values of the <B>o_current</B> pointed OBJECT are then copied to the new object.
175 * The arc, the line options are initialized whereas the fill options are
176 * initialized to passive values - as an arc can not be filled.
178 * \param [in] toplevel The TOPLEVEL object
179 * \param [in] o_current
180 * \return The new OBJECT
182 static OBJECT *o_arc_copy(TOPLEVEL *toplevel, OBJECT *o_current)
184 OBJECT *new_obj;
185 int color;
187 if (o_current->saved_color == -1) {
188 color = o_current->color;
189 } else {
190 color = o_current->saved_color;
193 new_obj = o_arc_new (toplevel, OBJ_ARC, color,
194 o_current->arc->x, o_current->arc->y,
195 o_current->arc->width / 2,
196 o_current->arc->start_angle,
197 o_current->arc->end_angle);
198 o_set_line_options(new_obj,
199 o_current->line_end, o_current->line_type,
200 o_current->line_width,
201 o_current->line_length, o_current->line_space);
202 o_set_fill_options(new_obj, FILLING_HOLLOW, -1, -1, -1, -1, -1);
204 return new_obj;
207 /*! \brief
208 * \par Function Description
209 * This function reads a formatted text buffer describing an arc
210 * in the gEDA file format and initializes the corresponding object.
211 * Depending on the version of the file format the data extraction is
212 * performed differently : currently pre-20000704 and 20000704 on one
213 * hand and post-20000704 file format version on the other hand are supported.
214 * The version is specified in string pointed by <B>fileformat_ver</B>.
216 * To get information on the various file formats have a
217 * look to the fileformats.html document.
219 * The object is initialized with the functions #o_set_line_options() and #o_set_fill_options().
220 * The second one is only used to put initialize unused values for an arc as an arc can not be filled.
222 * The arc is allocated initialized with the function #o_arc_new().
224 * A negative or null radius is not allowed.
226 * \param [in] toplevel The TOPLEVEL object.
227 * \param [in] buf
228 * \param [in] release_ver
229 * \param [in] fileformat_ver
230 * \return
232 OBJECT *o_arc_read(TOPLEVEL *toplevel, char buf[],
233 unsigned int release_ver, unsigned int fileformat_ver)
235 OBJECT *new_obj;
236 char type;
237 int x1, y1;
238 int radius;
239 int start_angle, end_angle;
240 int color;
241 int arc_width, arc_length, arc_space;
242 int arc_type;
243 int arc_end;
245 /*! \note
246 * Depending on the version of the file format used to describe this arc,
247 * the buffer is parsed differently. The unknown parameters of the less
248 * restrictive - the oldest - file format are set to common values
250 if(release_ver <= VERSION_20000704) {
251 sscanf(buf, "%c %d %d %d %d %d %d", &type,
252 &x1, &y1, &radius, &start_angle, &end_angle, &color);
254 arc_width = 0;
255 arc_end = END_NONE;
256 arc_type = TYPE_SOLID;
257 arc_space = -1;
258 arc_length= -1;
259 } else {
260 sscanf(buf, "%c %d %d %d %d %d %d %d %d %d %d %d", &type,
261 &x1, &y1, &radius, &start_angle, &end_angle, &color,
262 &arc_width, &arc_end, &arc_type, &arc_length, &arc_space);
266 /* Error check */
267 if (radius <= 0) {
268 s_log_message (_("Found a zero radius arc [ %c %d, %d, %d, %d, %d, %d ]\n"),
269 type, x1, y1, radius, start_angle, end_angle, color);
272 if (color < 0 || color > MAX_COLORS) {
273 s_log_message(_("Found an invalid color [ %s ]\n"), buf);
274 s_log_message(_("Setting color to WHITE\n"));
275 color = WHITE;
278 /* Allocation and initialization */
279 new_obj = o_arc_new(toplevel, OBJ_ARC, color,
280 x1, y1, radius, start_angle, end_angle);
281 o_set_line_options(new_obj,
282 arc_end, arc_type, arc_width, arc_length,
283 arc_space);
284 o_set_fill_options(new_obj, FILLING_HOLLOW, -1, -1, -1, -1, -1);
286 return new_obj;
289 /*! \brief
290 * \par Function Description
291 * This function formats a string in the buffer <B>*buff</B> to describe
292 * the arc object <B>*object</B>.
293 * It follows the post-20000704 release file format that handle the
294 * line type and filling options.
295 * A pointer to the new allocated and formated string is returned.
296 * The string must be freed at some point.
298 * \param [in] object
299 * \return
301 * \todo EEK! there is a nasty non-snap bug here!
302 * Basically the center isn't being snapped
303 * in complex objects only it seems...
305 char *o_arc_save(OBJECT *object)
307 int color;
308 int x, y, radius, start_angle, end_angle;
309 int arc_width, arc_length, arc_space;
310 char *buf;
311 OBJECT_END arc_end;
312 OBJECT_TYPE arc_type;
314 /* radius, center and angles of the arc */
315 radius = object->arc->width / 2;
316 x = object->arc->x;
317 y = object->arc->y;
318 start_angle = object->arc->start_angle;
319 end_angle = object->arc->end_angle;
321 /* line type parameters */
322 arc_width = object->line_width;
323 arc_end = object->line_end;
324 arc_type = object->line_type;
325 arc_length = object->line_length;
326 arc_space = object->line_space;
328 /* Save the right color */
329 if (object->saved_color == -1) {
330 color = object->color;
331 } else {
332 color = object->saved_color;
335 /* Describe a circle with post-20000704 file format */
336 buf = g_strdup_printf("%c %d %d %d %d %d %d %d %d %d %d %d", object->type,
337 x, y, radius, start_angle, end_angle, color,
338 arc_width, arc_end, arc_type, arc_length, arc_space);
340 return(buf);
343 /*! \brief
344 * \par Function Description
345 * This function applies a translation of (<B>dx</B>,<B>dy</B>)
346 * to the arc described in <B>*object</B>. <B>dx</B> and <B>dy</B> are in world unit.
348 * \param [in] dx
349 * \param [in] dy
350 * \param [in] object
352 void o_arc_translate_world(int dx, int dy, OBJECT *object)
354 if (object == NULL) {
355 return;
358 /* Do world coords */
359 object->arc->x = object->arc->x + dx;
360 object->arc->y = object->arc->y + dy;
363 /* Recalculate screen coords from new world coords */
364 o_arc_recalc(object);
367 /*! \brief
368 * \par Function Description
369 * This function rotates the world coordinates of an arc of an angle
370 * specified by <B>angle</B>. The center of the rotation is given by
371 * (<B>world_centerx</B>,<B>world_centery</B>).
373 * The arc is translated in order to put the center of the rotation
374 * on the origin. The center of the arc is then rotated of the angle
375 * specified by <B>angle</B>. The start angle of the arc is incremented by <B>angle</B>.
377 * The arc is finally back translated to its previous location on the page.
379 * <B>world_centerx</B> and <B>world_centery</B> are in world units, <B>angle</B> is in degrees.
381 * \param [in] world_centerx
382 * \param [in] world_centery
383 * \param [in] angle
384 * \param [in] object
386 void o_arc_rotate_world(int world_centerx, int world_centery, int angle,
387 OBJECT *object)
389 int x, y, newx, newy;
391 /* translate object to origin */
392 object->arc->x -= world_centerx;
393 object->arc->y -= world_centery;
395 /* get center, and rotate center */
396 x = object->arc->x;
397 y = object->arc->y;
398 if(angle % 90 == 0) {
399 rotate_point_90(x, y, angle % 360, &newx, &newy);
400 } else {
401 rotate_point(x, y, angle % 360, &newx, &newy);
403 object->arc->x = newx;
404 object->arc->y = newy;
406 /* apply rotation to angles */
407 object->arc->start_angle = (object->arc->start_angle + angle) % 360;
408 /* end_angle is unchanged as it is the sweep of the arc */
409 /* object->arc->end_angle = (object->arc->end_angle); */
411 /* translate object to its previous place */
412 object->arc->x += world_centerx;
413 object->arc->y += world_centery;
415 /* update the screen coords and the bounding box */
416 o_arc_recalc(object);
420 /*! \brief Mirror the WORLD coordinates of an ARC.
421 * \par Function Description
422 * This function mirrors the world coordinates of an arc.
423 * The symmetry axis is given by the vertical line going through the point (<B>world_centerx</B>,<B>world_centery</B>).
425 * The arc is translated in order to put the point (<B>world_centerx</B>,<B>world_centery</B>)
426 * on the origin. The center of the arc is then mirrored. The start angle of the arc
427 * and the sweep of the arc are also mirrored.
429 * The arc is finally back translated to its previous location on the page.
431 * \param [in] world_centerx
432 * \param [in] world_centery
433 * \param [in] object
435 void o_arc_mirror_world(int world_centerx, int world_centery,
436 OBJECT *object)
438 /* translate object to origin */
439 object->arc->x -= world_centerx;
440 object->arc->y -= world_centery;
442 /* get center, and mirror it (vertical mirror) */
443 object->arc->x = -object->arc->x;
444 object->arc->y = object->arc->y;
446 /* apply mirror to angles (vertical mirror) */
447 object->arc->start_angle = (180 - object->arc->start_angle) % 360;
448 /* pb20011125 - start_angle *MUST* be positive */
449 if(object->arc->start_angle < 0) object->arc->start_angle += 360;
450 object->arc->end_angle = -object->arc->end_angle;
452 /* translate object back to its previous position */
453 object->arc->x += world_centerx;
454 object->arc->y += world_centery;
456 /* update the screen coords and bounding box */
457 o_arc_recalc(object);
460 int o_arc_get_radius(OBJECT const *o_current)
462 return (o_current->arc->width / 2);
465 int o_arc_get_start_angle(OBJECT const *o_current)
467 return (o_current->arc->start_angle);
470 int o_arc_get_end_angle(OBJECT const *o_current)
472 return (o_current->arc->end_angle);
475 void o_arc_set_radius(OBJECT *o_current, int radius)
477 o_current->arc->width = radius * 2;
479 /* update the screen coords and bounding box */
480 o_arc_recalc(o_current);
483 void o_arc_set_start_angle(OBJECT *o_current, int angle)
485 o_current->arc->start_angle = angle;
487 /* update the screen coords and bounding box */
488 o_arc_recalc(o_current);
491 void o_arc_set_end_angle(OBJECT *o_current, int angle)
493 o_current->arc->end_angle = angle;
495 /* update the screen coords and bounding box */
496 o_arc_recalc(o_current);
499 /*! \brief
500 * \par Function Description
501 * This function recalculates internal parameters in screen units
502 * of an object containing an arc. The object is given as parameters <B>o_current</B>.
503 * The calculation is done according to the zoom factor detailed in the <B>toplevel</B>
504 * pointed structure.
505 * It also recalculates the <B>OBJECT</B> specific fields and the bounding box of the arc.
507 * The bounding box - in world units - is recalculated with the <B>world_get_arc_bounds()</B> function.
509 * \param [in] o_current
511 static void o_arc_recalc(OBJECT *o_current)
513 int left, right, top, bottom;
515 if (o_current->arc == NULL) {
516 return;
519 /* recalculates the bounding box */
520 world_get_arc_bounds(o_current, &left, &top, &right, &bottom);
521 o_current->w_left = left;
522 o_current->w_top = top;
523 o_current->w_right = right;
524 o_current->w_bottom = bottom;
525 o_current->w_bounds_valid = TRUE;
528 static void o_arc_grip_foreach(OBJECT *o,
529 gboolean (*fn)(OBJECT *o,
530 int grip_x, int grip_y,
531 enum grip_t whichone,
532 void *userdata),
533 void *userdata)
535 int centerx = o->arc->x;
536 int centery = o->arc->y;
537 int radius = o->arc->width / 2;
538 double start_angle = o->arc->start_angle * M_PI / 180;
539 double end_angle = start_angle + o->arc->end_angle * M_PI / 180;
541 const GRIP arc_grips[] = {
543 .x = centerx,
544 .y = centery,
545 .whichone = GRIP_ARC_CENTER,
548 .x = centerx,
549 .y = centery,
550 .whichone = GRIP_ARC_RADIUS,
553 .x = centerx + radius * cos(start_angle),
554 .y = centery + radius * sin(start_angle),
555 .whichone = GRIP_START_ANGLE,
558 .x = centerx + radius * cos(end_angle),
559 .y = centery + radius * sin(end_angle),
560 .whichone = GRIP_END_ANGLE,
562 { .whichone = GRIP_NONE }
565 s_basic_grip_foreach_helper(o, arc_grips, fn, userdata);
568 static int o_arc_grip_move(OBJECT *o, int whichone, int x, int y)
570 int retval = whichone;
572 switch (whichone) {
573 double radius, theta, r_cos_theta, r_sin_theta;
574 case GRIP_NONE:
575 o->arc->x = x;
576 o->arc->y = y;
577 o->arc->width = 0;
578 o->arc->height = 0;
579 o->arc->start_angle = 0;
580 o->arc->end_angle = 180;
581 retval = GRIP_ARC_RADIUS;
582 break;
584 case GRIP_ARC_CENTER:
585 o->arc->x = x;
586 o->arc->y = y;
587 retval = GRIP_ARC_RADIUS;
588 break;
589 case GRIP_ARC_RADIUS:
590 r_cos_theta = x - o->arc->x;
591 r_sin_theta = y - o->arc->y;
592 radius = sqrt(r_cos_theta*r_cos_theta + r_sin_theta*r_sin_theta);
593 o->arc->width = o->arc->height = rint(radius * 2);
594 retval = GRIP_START_ANGLE;
595 break;
596 case GRIP_START_ANGLE:
597 case GRIP_END_ANGLE:
598 r_cos_theta = x - o->arc->x;
599 r_sin_theta = y - o->arc->y;
600 radius = sqrt(r_cos_theta*r_cos_theta + r_sin_theta*r_sin_theta);
601 theta = atan2(r_sin_theta / radius, r_cos_theta / radius);
602 if (theta < 0) {
603 theta += 2*M_PI;
605 theta *= 180 / M_PI;
606 switch (whichone) {
607 case GRIP_START_ANGLE:
608 o->arc->start_angle = rint(theta);
609 retval = GRIP_END_ANGLE;
610 break;
611 case GRIP_END_ANGLE:
612 theta -= o->arc->start_angle;
613 if (theta < 0) {
614 theta += 360;
616 o->arc->end_angle = rint(theta);
617 retval = GRIP_NONE;
618 break;
620 break;
623 o_arc_recalc(o);
625 return retval;
628 /*! \brief
629 * \par Function Description
630 * This function calculates the smallest rectangle the arc can be drawn into.
631 * The <B>OBJECT</B> pointed by object is assumed to be an arc.
632 * The <B>left</B>, <B>top</B>, <B>right</B> and <B>bottom</B> pointed integers define
633 * this rectangle at the end of the function. It is expressed in world units.
634 * The process is divided into two steps : the first step is to calculate the
635 * coordinates of the two ends of the arc and the coordinates of the center.
636 * They forms a first rectangle but (depending on the start angle and the
637 * sweep of the arc) not the right.
639 * \param [in] toplevel The TOPLEVEL object.
640 * \param [in] object
641 * \param [out] left
642 * \param [out] top
643 * \param [out] right
644 * \param [out] bottom
646 void world_get_arc_bounds(OBJECT *object, int *left,
647 int *top, int *right, int *bottom)
649 int x1, y1, x2, y2, x3, y3;
650 int radius, start_angle, end_angle;
651 int i, angle;
652 int halfwidth;
654 halfwidth = object->line_width / 2;
656 radius = object->arc->width / 2;
657 start_angle = object->arc->start_angle % 360;
658 end_angle = object->arc->end_angle % 360;
660 x1 = object->arc->x;
661 y1 = object->arc->y;
662 x2 = x1 + radius * cos(start_angle * M_PI / 180);
663 y2 = y1 + radius * sin(start_angle * M_PI / 180);
664 x3 = x1 + radius * cos((start_angle + end_angle) * M_PI / 180);
665 y3 = y1 + radius * sin((start_angle + end_angle) * M_PI / 180);
667 *left = (x1 < x2) ? ((x1 < x3) ? x1 : x3) : ((x2 < x3) ? x2 : x3);
668 *right = (x1 > x2) ? ((x1 > x3) ? x1 : x3) : ((x2 > x3) ? x2 : x3);
669 *bottom = (y1 > y2) ? ((y1 > y3) ? y1 : y3) : ((y2 > y3) ? y2 : y3);
670 *top = (y1 < y2) ? ((y1 < y3) ? y1 : y3) : ((y2 < y3) ? y2 : y3);
672 /*! \note
673 * The previous rectangle is extended to the final one
674 * by checking whether the arc is over a main axis (vertical or horizontal).
675 * If so, the rectangle is extended in these directions.
677 * In the mirror mode, the sweep angle is negative. To get a
678 * CCW arc before this calculation we have to move the
679 * start angle to the end angle and reverse the sweep angle.
681 if (end_angle < 0) {
682 start_angle = (start_angle + end_angle + 360) % 360;
683 end_angle = -end_angle;
685 angle = ((int) (start_angle / 90)) * 90;
686 for(i = 0; i < 4; i++) {
687 angle = angle + 90;
688 if(angle < start_angle + end_angle) {
689 if(angle % 360 == 0) *right = x1 + radius;
690 if(angle % 360 == 90) *bottom = y1 + radius;
691 if(angle % 360 == 180) *left = x1 - radius;
692 if(angle % 360 == 270) *top = y1 - radius;
693 } else {
694 break;
698 /* This isn't strictly correct, but a 1st order approximation */
699 *left -= halfwidth;
700 *top -= halfwidth;
701 *right += halfwidth;
702 *bottom += halfwidth;
707 /*! \brief
708 * \par Function Description
709 * This function writes in a postscript file the arc described by
710 * the <B>o_current</B> pointed object.
711 * The postscript resulting file is described by the <B>fp</B> file pointer.
713 * Parameters of the arc are extracted from object pointed by <B>o_current</B>
714 * and formatted to suit future calls to specialized arc printing functions.
716 * \param [in] toplevel The TOPLEVEL object.
717 * \param [in] fp The postscript document to print to.
718 * \param [in] o_current
720 void o_arc_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
721 double scale, int unicode_count, gunichar *unicode_table)
723 int x, y, radius, start_angle, end_angle;
724 int color;
725 int arc_width, space, length;
726 void (*outl_func)(TOPLEVEL *toplevel, FILE *fp,
727 int x, int y, int radius,
728 int angle1, int angle2,
729 int color,
730 int arc_width, int length, int space) = NULL;
732 if (o_current == NULL) {
733 printf("got null in o_arc_print\n");
734 return;
737 x = o_current->arc->x;
738 y = o_current->arc->y;
739 radius = o_current->arc->width / 2;
740 start_angle = o_current->arc->start_angle;
741 end_angle = o_current->arc->end_angle;
742 color = o_current->color;
744 /*! \note
745 * Depending on the type of the line for this particular arc, the
746 * appropriate function is chosen among #o_arc_print_solid(),
747 * #o_arc_print_dotted(), #o_arc_print_dashed(), #o_arc_print_center() and #o_arc_print_phantom().
749 * The needed parameters for each of these types are extracted from the <B>o_current</B> object.
750 * Depending on the type, unused parameters are set to -1.
752 * In the eventuality of a length and/or space null, the arc is printed solid to avoid and
753 * endless loop produced by other functions.
756 #if 0 /* was causing arcs which are solid to be much thinner compared to */
757 /* lines, boxes, also of zero width */
758 if (o_current->line_width > 0) {
759 arc_width = o_current->line_width;
760 } else {
761 arc_width = 1;
763 #endif
764 arc_width = o_current->line_width; /* Added instead of above */
765 if(arc_width <=2) {
766 if(toplevel->line_style == THICK) {
767 arc_width=LINE_WIDTH;
768 } else {
769 arc_width=2;
773 length = o_current->line_length;
774 space = o_current->line_space;
776 switch(o_current->line_type) {
777 case(TYPE_SOLID):
778 length = -1; space = -1;
779 outl_func = o_arc_print_solid;
780 break;
782 case(TYPE_DOTTED):
783 length = -1;
784 outl_func = o_arc_print_dotted;
785 break;
787 case(TYPE_DASHED):
788 outl_func = o_arc_print_dashed;
789 break;
791 case(TYPE_CENTER):
792 outl_func = o_arc_print_center;
793 break;
795 case(TYPE_PHANTOM):
796 outl_func = o_arc_print_phantom;
797 break;
799 case(TYPE_ERASE):
800 /* Unused for now, print it solid */
801 length = -1; space = -1;
802 outl_func = o_arc_print_solid;
803 break;
806 if((space == 0) || (length == 0)) {
807 length = -1; space = -1;
808 outl_func = o_arc_print_solid;
811 (*outl_func)(toplevel, fp,
812 x, y, radius,
813 start_angle, end_angle,
814 color, arc_width, length, space);
818 /*! \brief
819 * \par Function Description
820 * This function prints an arc when a solid line type is required.
821 * The arc is defined by its center in <B>x</B> and <B>y</B>, its radius
822 * in <B>radius</B> and the start and end angles of the arc on the circle.
823 * The postscript file is defined by the file pointer <B>fp</B>.
825 * The parameters <B>length</B> and <B>space</B> are ignored
826 * whereas <B>arc_width</B> specifies the width of the printed line.
828 * All dimensions are in mils, except <B>angle1</B> and <B>angle2</B> in degrees.
830 * \param [in] toplevel The TOPLEVEL object.
831 * \param [in] fp FILE pointer to postscript document.
832 * \param [in] x
833 * \param [in] y
834 * \param [in] radius
835 * \param [in] angle1
836 * \param [in] angle2
837 * \param [in] color
838 * \param [in] arc_width
839 * \param [in] length
840 * \param [in] space
842 void o_arc_print_solid(TOPLEVEL *toplevel, FILE *fp,
843 int x, int y, int radius,
844 int angle1, int angle2,
845 int color,
846 int arc_width, int length, int space)
848 if (toplevel->print_color) {
849 f_print_set_color(fp, color);
852 /* PB/AVH inverting angle2 if < 0 and changing angle1 accordingly */
853 if (angle2 < 0) {
854 angle1 = angle1 + angle2;
855 angle2 = -angle2;
858 fprintf(fp, "%d %d %d %d %d %d darc\n",
859 x,y, radius, angle1, angle1 + angle2,
860 arc_width);
864 /*! \brief
865 * \par Function Description
866 * This function prints an arc when a dotted line type is required.
867 * The arc is defined by its center in <B>x</B> and <B>y</B>, its
868 * radius in <B>radius</B> and the start and end angles of the arc on the circle.
869 * The postscript file is defined by the file pointer <B>fp</B>.
870 * The parameter <B>length</B> is ignored whereas <B>arc_width</B> specifies
871 * the diameter of the dots of the printed line and <B>space</B> the distance
872 * between two dots.
874 * A negative value for <B>space</B> leads to an endless loop.
876 * All dimensions are in mils, except <B>angle1</B> and <B>angle2</B> in degrees.
878 * The function sets the color the line will be printed with.
880 * \param [in] toplevel The TOPLEVEL object.
881 * \param [in] fp FILE pointer to postscript document.
882 * \param [in] x
883 * \param [in] y
884 * \param [in] radius
885 * \param [in] angle1
886 * \param [in] angle2
887 * \param [in] color
888 * \param [in] arc_width
889 * \param [in] length
890 * \param [in] space
892 void o_arc_print_dotted(TOPLEVEL *toplevel, FILE *fp,
893 int x, int y, int radius,
894 int angle1, int angle2,
895 int color,
896 int arc_width, int length, int space)
898 int da, d;
901 if (toplevel->print_color) {
902 f_print_set_color(fp, color);
905 /*! \note
906 * Depending on the radius of the arc, the <B>space</B> parameter is
907 * changed into a small angle <B>da</B>.
908 * Starting from <B>angle1</B> - the start angle - the dots are printed
909 * along the arc by increments of this new angle.
911 * As <B>da</B> is rounded as an integer, it can take a null value which
912 * will make the function enter an endless loop. In such a case, the arc
913 * is printed solid. The <B>da</B> variable should never be negative
914 * except if <B>space</B> is negative.
917 /* Inverting angle2 if < 0 and changing angle1 accordingly */
918 /* the loop test assume that da > 0 */
919 if (angle2 < 0) {
920 angle1 = angle1 + angle2;
921 angle2 = -angle2;
923 da = (int) ((space * 180) / (M_PI * ((double) radius)));
925 /* If da or db too small for arc to be displayed as dotted,
926 draw a solid arc */
927 if (da <= 0) {
928 o_arc_print_solid(toplevel, fp,
929 x, y, radius,
930 angle1, angle2,
931 color,
932 arc_width, length, space);
933 return;
936 fprintf(fp,"[");
937 d = angle1;
938 while (d < (angle2 + angle1)) {
939 /*xa = ((double) x) + ((double) radius) * cos(d * M_PI / 180);
940 ya = ((double) y) + ((double) radius) * sin(d * M_PI / 180);
942 fprintf(fp,"[%d] ",d);
944 d = d + da;
946 fprintf(fp,"] %d %d %d %d dashedarc %% dotted\n",
947 x,y, radius, arc_width);
950 /*! \brief
951 * \par Function Description
952 * This function prints an arc when a dashed line type is required.
953 * The arc is defined by its center in <B>x</B> and <B>y</B>, its radius
954 * in <B>radius</B> and the start and end angles of the arc on the circle.
955 * The postscript file is defined by the file pointer <B>fp</B>.
956 * The parameter <B>arc_width</B> specifies the diameter of the dots of the printed line.
958 * A negative value for <B>space</B> or <B>length</B> leads to an endless loop.
960 * All dimensions are in mils, except <B>angle1</B> and <B>angle2</B> in degrees.
962 * The function sets the color the line will be printed with.
964 * \param [in] toplevel The TOPLEVEL object.
965 * \param [in] fp FILE pointer to postscript document.
966 * \param [in] x
967 * \param [in] y
968 * \param [in] radius
969 * \param [in] angle1
970 * \param [in] angle2
971 * \param [in] color
972 * \param [in] arc_width
973 * \param [in] length
974 * \param [in] space
976 void o_arc_print_dashed(TOPLEVEL *toplevel, FILE *fp,
977 int x, int y, int radius,
978 int angle1, int angle2,
979 int color,
980 int arc_width, int length, int space)
982 int da, db, a1, d;
984 if (toplevel->print_color) {
985 f_print_set_color(fp, color);
988 /*! \note
989 * Depending on the radius of the arc, the <B>space</B> (resp. <B>length</B>)
990 * parameter is changed into a small angle <B>da</B> (resp. <B>db</B>).
991 * Starting from <B>angle1</B> - the start angle - the dashes are printed
992 * along the arc by increments of these new angles.
994 * As <B>da</B> (resp. <B>db</B>) is rounded as an integer, it can take a
995 * null value which will make the function enter an endless loop. In such a case,
996 * the arc is printed solid. The <B>da</B> (resp. <B>db</B>) variable should never
997 * be negative except if <B>space</B> (resp. <B>length</B>) is negative.
999 * It prints as many dashes of length <B>length</B> as possible.
1002 /* Inverting angle2 if < 0 and changing angle1 accordingly */
1003 /* the loop test assume that da > 0 */
1004 if (angle2 < 0) {
1005 angle1 = angle1 + angle2;
1006 angle2 = -angle2;
1008 da = (int) ((length * 180) / (M_PI * ((double) radius)));
1009 db = (int) ((space * 180) / (M_PI * ((double) radius)));
1011 /* If da or db too small for arc to be displayed as dotted,
1012 draw a solid arc */
1013 if ((da <= 0) || (db <= 0)) {
1014 o_arc_print_solid(toplevel, fp,
1015 x, y, radius,
1016 angle1, angle2,
1017 color,
1018 arc_width, length, space);
1019 return;
1022 fprintf(fp,"[");
1023 d = angle1;
1024 while ((d + da + db) < (angle1 + angle2)) {
1025 a1 = d;
1026 d = d + da;
1028 fprintf(fp,"[%d %d] ",
1029 a1, a1+da);
1031 d = d + db;
1033 /*! \note
1034 * When the above condition is no more satisfied, then it is not
1035 * possible to print a dash of length <B>length</B> and the following <B>space</B>.
1036 * However it may be possible to print the complete dash or a shorter one.
1039 if ((d + da) < (angle1 + angle2)) {
1040 a1 = d;
1041 } else {
1042 a1 = d;
1045 fprintf(fp,"[%d %d] ",
1046 a1, a1+da);
1049 fprintf(fp,"] %d %d %d %d dashedarc %% dashed\n",
1050 x,y, radius, arc_width);
1053 /*! \brief
1054 * \par Function Description
1055 * This function prints an arc when a centered line type is required.
1056 * The arc is defined by its center in <B>x</B> and <B>y</B>, its radius in
1057 * <B>radius</B> and the start and end angles of the arc on the circle.
1058 * The postscript file is defined by the file pointer <B>fp</B>.
1059 * The parameter <B>arc_width</B> specifies the diameter of the dots and the width of the dashes of the printed line.
1061 * A negative value for <B>space</B> or <B>length</B> leads to an endless loop.
1063 * All dimensions are in mils, except <B>angle1</B> and <B>angle2</B> in degrees.
1065 * The function sets the color in which the line will be printed with.
1067 * \param [in] toplevel The TOPLEVEL object.
1068 * \param [in] fp FILE pointer to postscript document.
1069 * \param [in] x
1070 * \param [in] y
1071 * \param [in] radius
1072 * \param [in] angle1
1073 * \param [in] angle2
1074 * \param [in] color
1075 * \param [in] arc_width
1076 * \param [in] length
1077 * \param [in] space
1079 void o_arc_print_center(TOPLEVEL *toplevel, FILE *fp,
1080 int x, int y, int radius,
1081 int angle1, int angle2,
1082 int color,
1083 int arc_width, int length, int space)
1085 int da, db, a1, d;
1087 if (toplevel->print_color) {
1088 f_print_set_color(fp, color);
1091 /*! \note
1092 * Depending on the radius of the arc, the <B>space</B> (resp. <B>length</B>)
1093 * parameter is changed into a small angle <B>da</B> (resp. <B>db</B>).
1094 * Starting from <B>angle1</B> - the start angle - the dashes are printed
1095 * along the arc by increments of these new angles.
1097 * As <B>da</B> (resp. <B>db</B>) is rounded as an integer, it can take a null
1098 * value which will make the function enter an endless loop. In such a case,
1099 * the arc is printed solid. The <B>da</B> (resp. <B>db</B>) variable should never
1100 * be negative except if <B>space</B> (resp. <B>length</B>) is negative.
1102 * It prints as many sets of dash-dot as possible.
1105 /* Inverting angle2 if < 0 and changing angle1 accordingly */
1106 /* the loop test assume that da > 0 */
1107 if (angle2 < 0) {
1108 angle1 = angle1 + angle2;
1109 angle2 = -angle2;
1112 da = (int) ((length * 180) / (M_PI * ((double) radius)));
1113 db = (int) ((space * 180) / (M_PI * ((double) radius)));
1115 /* If da or db too small to be displayed, draw an arc */
1116 if ((da <= 0) || (db <= 0)) {
1117 o_arc_print_solid(toplevel, fp,
1118 x, y, radius,
1119 angle1, angle2,
1120 color,
1121 arc_width, length, space);
1122 return;
1125 fprintf(fp, "[");
1126 d = angle1;
1127 while ((d + da + 2 * db) < (angle1 + angle2)) {
1128 a1 = d;
1129 d = d + da;
1130 fprintf(fp,"[%d %d] ",(int) a1, (int) a1 + da);
1132 d = d + db;
1134 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1135 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1137 fprintf(fp,"[%d] ",d);
1138 d = d + db;
1140 /*! \note
1141 * When the above condition is no more satisfied, then it is not
1142 * possible to print a dash of length <B>length</B>. However two cases are possible :
1143 * <DL>
1144 * <DT>*</DT><DD>it is possible to print the dash and the dot
1145 * <DT>*</DT><DD>it is possible to print the dash or a part of the original dash
1146 * </DL>
1149 a1 = d;
1151 d = d + da;
1153 fprintf(fp,"[%d %d] ",(int) a1, (int) a1 + da);
1155 if ((d + db) < (angle1 + angle2)) {
1157 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1158 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1160 fprintf(fp,"[%d] ",d);
1163 fprintf(fp,"] %d %d %d %d dashedarc %% center\n",
1164 x,y, radius, arc_width);
1167 /*! \note
1168 * A dot is represented by a filled circle. Position of the circle is (<B>xa</B>, <B>ya</B>)
1169 * and its radius is the <B>arc_width</B> parameter.
1172 /*! \brief
1173 * \par Function Description
1174 * This function prints an arc when a phantom line type is required.
1175 * The arc is defined by its center in <B>x</B> and <B>y</B>, its radius
1176 * in <B>radius</B> and the start and end angles of the arc on the circle.
1177 * The postscript file is defined by the file pointer <B>fp</B>.
1178 * The parameter <B>arc_width</B> specifies the diameter of the dots and the width of the dashes of the printed line.
1180 * A negative value for <B>space</B> or <B>length</B> leads to an endless loop.
1182 * All dimensions are in mils, except <B>angle1</B> and <B>angle2</B> in degrees.
1184 * The function sets the color in which the line will be printed with.
1186 * \param [in] toplevel The TOPLEVEL object.
1187 * \param [in] fp FILE pointer to postscript document.
1188 * \param [in] x
1189 * \param [in] y
1190 * \param [in] radius
1191 * \param [in] angle1
1192 * \param [in] angle2
1193 * \param [in] color
1194 * \param [in] arc_width
1195 * \param [in] length
1196 * \param [in] space
1198 void o_arc_print_phantom(TOPLEVEL *toplevel, FILE *fp,
1199 int x, int y, int radius,
1200 int angle1, int angle2,
1201 int color,
1202 int arc_width, int length, int space)
1204 int da, db, a1, d;
1206 if (toplevel->print_color) {
1207 f_print_set_color(fp, color);
1210 /*! \note
1211 * Depending on the radius of the arc, the <B>space</B> (resp. <B>length</B>)
1212 * parameter is changed into a small angle <B>da</B> (resp. <B>db</B>).
1213 * Starting from <B>angle1</B> - the start angle - the dashes are printed
1214 * along the arc by increments of these new angles.
1216 * As <B>da</B> (resp. <B>db</B>) is rounded as an integer, it can take a
1217 * null value which will make the function enter an endless loop. In such
1218 * a case, the arc is printed solid. The <B>da</B> (resp. <B>db</B>) variable
1219 * should never be negative except if <B>space</B> (resp. <B>length</B>) is negative.
1221 * It prints as many sets of dash-dot-dot as possible.
1224 /* Inverting angle2 if < 0 and changing angle1 accordingly */
1225 /* the loop test assume that da > 0 */
1226 if (angle2 < 0) {
1227 angle1 = angle1 + angle2;
1228 angle2 = -angle2;
1230 da = (int) ((length * 180) / (((double) radius) * M_PI));
1231 db = (int) ((space * 180) / (((double) radius) * M_PI));
1233 /* If da or db too small for arc to be displayed as dotted,
1234 draw a solid arc */
1235 if ((da <= 0) || (db <= 0)) {
1236 o_arc_print_solid(toplevel, fp,
1237 x, y, radius,
1238 angle1, angle2,
1239 color,
1240 arc_width, length, space);
1241 return;
1244 fprintf(fp,"[");
1246 d = angle1;
1247 while ((d + da + 3 * db) < (angle1 + angle2)) {
1248 a1 = d;
1249 d = d + da;
1251 fprintf(fp,"[%d %d] ",(int) a1, (int) a1 + da);
1253 d = d + db;
1255 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1256 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1258 fprintf(fp,"[%d] ",d);
1260 d = d + db;
1263 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1264 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1266 fprintf(fp,"[%d] ",d);
1268 d = d + db;
1271 /*! \note
1272 * When the above condition is no more satisfied, then it is not
1273 * possible to print a dash of length <B>length</B>.
1274 * However three cases are possible :
1275 * <DL>
1276 * <DT>*</DT><DD>it is possible to print a dash and a dot and a dot
1277 * <DT>*</DT><DD>it is possible to print a dash and a dot
1278 * <DT>*</DT><DD>it is possible to print the dash or a part of the original dash
1279 * </DL>
1282 a1 = d;
1283 d = d + da;
1285 fprintf(fp,"[%d %d] ",(int) a1, (int) a1 + da);
1287 if ((d + db) < (angle1 + angle2)) {
1288 d = d + db;
1291 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1292 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1294 fprintf(fp,"[%d] ",d);
1297 if ((d + db) < (angle1 + angle2)) {
1298 d = d + db;
1301 xa = ((double) x) + ((double) radius) * cos(d * (M_PI / 180));
1302 ya = ((double) y) + ((double) radius) * sin(d * (M_PI / 180));
1305 fprintf(fp,"[%d] ",d);
1308 fprintf(fp,"] %d %d %d %d dashedarc %% phantom\n",
1309 x,y, radius, arc_width);
1312 /*! \brief Calculates the distance between the given point and the closest
1313 * point on the perimeter of the arc.
1315 * \param [in] arc the arc of the OBJECT
1316 * \param [in] x The x coordinate of the given point.
1317 * \param [in] y The y coordinate of the given point.
1318 * \return The shortest distance from the object to the point. With an
1319 * invalid parameter, this function returns G_MAXDOUBLE.
1321 gdouble o_arc_shortest_distance(ARC const *arc, gint x, gint y)
1323 gdouble radius;
1324 gdouble shortest_distance;
1326 if (arc == NULL) {
1327 g_critical("o_arc_shortest_distance(): arc == NULL\n");
1328 return G_MAXDOUBLE;
1331 radius = ((gdouble) arc->width) / 2.0;
1333 if ( o_arc_within_sweep(arc, x, y) ) {
1334 gdouble distance_to_center;
1335 gdouble dx;
1336 gdouble dy;
1338 dx = ((gdouble) x) - ((gdouble) arc->x);
1339 dy = ((gdouble) y) - ((gdouble) arc->y);
1341 distance_to_center = sqrt((dx*dx) + (dy*dy));
1343 shortest_distance = fabs(distance_to_center - radius);
1345 else {
1346 gdouble angle;
1347 gdouble distance_to_end0;
1348 gdouble distance_to_end1;
1349 gdouble dx;
1350 gdouble dy;
1352 angle = G_PI * ((gdouble) arc->start_angle ) / 180;
1354 dx = ((gdouble) x) - radius*cos(angle) - ((gdouble) arc->x);
1355 dy = ((gdouble) y) - radius*sin(angle) - ((gdouble) arc->y);
1357 distance_to_end0 = sqrt((dx*dx) + (dy*dy));
1359 angle += G_PI * ((gdouble) arc->end_angle ) / 180;
1361 dx = ((gdouble) x) - radius*cos(angle) - ((gdouble) arc->x);
1362 dy = ((gdouble) y) - radius*sin(angle) - ((gdouble) arc->y);
1364 distance_to_end1 = sqrt((dx*dx) + (dy*dy));
1366 shortest_distance = min(distance_to_end0, distance_to_end1);
1369 return shortest_distance;
1372 /*! \brief Determines if a point lies within the sweep of the arc.
1374 * \param [in] arc The arc of object
1375 * \param [in] x The x coordinate of the given point.
1376 * \param [in] y The y coordinate of the given point.
1377 * \return TRUE if the point lies within the sweep of the arc.
1378 * FALSE if the point lies outside the sweep of the arc. With an
1379 * invalid parameter, this function returns FALSE.
1381 gboolean o_arc_within_sweep(ARC const *arc, gint x, gint y)
1383 gdouble a0;
1384 gdouble a1;
1385 gdouble angle;
1386 gdouble dx;
1387 gdouble dy;
1389 if (arc == NULL) {
1390 g_critical("o_arc_within_sweep(): arc == NULL\n");
1391 return FALSE;
1394 dx = ((gdouble) x) - ((gdouble) arc->x);
1395 dy = ((gdouble) y) - ((gdouble) arc->y);
1397 angle = 180 * atan2(dy, dx) / G_PI;
1399 a0 = (gdouble) arc->start_angle;
1400 a1 = ((gdouble) arc->end_angle) + a0;
1402 while (angle < a0) {
1403 angle+=360;
1406 return (angle < a1);