1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
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
26 #ifdef HAVE_LIBDMALLOC
31 typedef void (*DRAW_FUNC
)( GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
32 GdkCapStyle cap
, gint x
, gint y
, gint radius
,
33 gint angle1
, gint angle2
,
34 gint arc_width
, gint length
, gint space
);
36 typedef void (*FILL_FUNC
)( GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
37 gint x
, gint y
, gint radius
,
38 gint fill_width
, gint angle1
, gint pitch1
,
39 gint angle2
, gint pitch2
);
41 /*! \brief Draw a circle on the screen.
42 * \par Function Description
43 * This function is used to draw a circle on screen. The circle is described
44 * by the OBJECT which is referred by <B>o_current</B>. The display is done
45 * according to the current state, given by the GSCHEM_TOPLEVEL object pointed by
48 * It first checks if the OBJECT pointed is valid or not. If not it
49 * returns and do not output anything. That should never happen though.
51 * \param [in] w_current The GSCHEM_TOPLEVEL object.
52 * \param [in] o_current Circle OBJECT to draw.
54 void o_circle_draw(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
56 TOPLEVEL
*toplevel
= w_current
->toplevel
;
57 PAGE
*page
= toplevel
->page_current
;
58 int wleft
, wright
, wtop
, wbottom
; /* world bounds */
59 int s_x
, s_y
, w_x
, w_y
;
61 int circle_width
, length
, space
;
62 int fill_width
, angle1
, pitch1
, angle2
, pitch2
;
63 GdkCapStyle circle_end
;
65 DRAW_FUNC draw_func
= NULL
;
68 if (o_current
->circle
== NULL
) {
73 * Get read to check for visibility of this line by using it's
76 world_get_single_object_bounds(o_current
, &wleft
, &wtop
, &wright
, &wbottom
);
78 if ( (toplevel
->DONT_REDRAW
== 1) ||
79 (!visible(page
, wleft
, wtop
, wright
, wbottom
)) ) {
84 printf("drawing circle\n\n");
88 * The draw of the circle is divided in two steps : first step is to draw
89 * the outline, the second is to draw the filling pattern inside (if any).
91 * Finally the function takes care of the grips.
93 if (toplevel
->override_color
!= -1 ) {
94 color
= x_get_color(toplevel
->override_color
);
96 color
= x_get_color(o_current
->color
);
99 radius
= SCREENabs(page
, o_circle_get_radius(o_current
));
102 * The values describing the line type are extracted from the
103 * <B>o_current</B> pointed structure. These are the width of the line, the
104 * field called length and the field called space and the desired end type
107 * Depending on the type of the line that has to be used to draw the circle
108 * the appropriate function is called. Values of space and length are
109 * adapted to the type of line. The possible functions are the following :
110 * #o_arc_draw_solid(), #o_arc_draw_dotted(), #o_arc_draw_dashed() and
111 * #o_arc_draw_phantom(). Therefore it reuses the code from arc primitive.
113 * The combination <B>length</B> == 0 and <B>space</B> == 0 is avoided as it lead
114 * to an endless loop in function called after. If such a case is encountered
115 * the circle is drawn as a solid circle independently of its initial type.
117 circle_width
= SCREENabs(page
, o_current
->line_width
);
118 if(circle_width
<= 0) {
122 length
= SCREENabs(page
, o_current
->line_length
);
123 space
= SCREENabs(page
, o_current
->line_space
);
125 switch(o_current
->line_end
) {
126 case END_NONE
: circle_end
= GDK_CAP_BUTT
; break;
127 case END_SQUARE
: circle_end
= GDK_CAP_PROJECTING
; break;
128 case END_ROUND
: circle_end
= GDK_CAP_ROUND
; break;
129 default: fprintf(stderr
, _("Unknown end for circle\n"));
130 circle_end
= GDK_CAP_BUTT
;
134 switch(o_current
->line_type
) {
138 draw_func
= o_arc_draw_solid
;
142 length
= -1; /* ..._draw_dotted only space used */
143 draw_func
= o_arc_draw_dotted
;
147 draw_func
= o_arc_draw_dashed
;
151 draw_func
= o_arc_draw_center
;
155 draw_func
= o_arc_draw_phantom
;
164 circle_width
= 0; /* just to be careful */
165 fprintf(stderr
, _("Unknown type for circle!\n"));
166 draw_func
= o_arc_draw_solid
;
170 if((length
== 0) || (space
== 0))
171 draw_func
= o_arc_draw_solid
;
173 s_basic_get_grip(o_current
, GRIP_CIRCLE_CENTER
, &w_x
, &w_y
);
174 WORLDtoSCREEN(page
, w_x
, w_y
, &s_x
, &s_y
);
176 (*draw_func
)(w_current
->backingstore
, w_current
->gc
, color
,
181 circle_width
, length
, space
);
184 * The values needed for the fill operation are taken from the
185 * <B>o_current</B> pointed OBJECT. It include the type of fill required, the
186 * width of the lines (if the fill use line) and angles and pitchs for hatch
189 * Once again the width of the line is important as if it is equal to 0 it
190 * may not be displayed. That is definitely not what we are looking for.
192 * Depending on the type of fill that has to be used inside the circle the
193 * right function is called. Values of <B>angle1</B>, <B>angle2</B>,
194 * <B>pitch1</B> and <B>pitch2</B> are adapted to the type of filling. The
195 * possible functions are the following : #o_circle_fill_hollow(),
196 * #o_circle_fill_fill(), #o_circle_fill_mesh() and #o_circle_fill_hatch().
198 * The combination <B>pitch1</B> <= 0 and <B>pitch2</B> <= 0 is avoided as it
199 * lead to an endless loop in function called after. It happens when the
200 * zoom factor is too small for two lines separated by the pitch to be
201 * distinct. If such a case is encountered the circle is filled hollow
204 fill_width
= SCREENabs(page
, o_current
->fill_width
);
205 if( fill_width
<= 0) {
209 angle1
= o_current
->fill_angle1
;
210 pitch1
= SCREENabs(page
, o_current
->fill_pitch1
);
211 angle2
= o_current
->fill_angle2
;
212 pitch2
= SCREENabs(page
, o_current
->fill_pitch2
);
214 switch(o_current
->fill_type
) {
216 angle1
= -1; angle2
= -1;
217 pitch1
= 1; pitch2
= 1;
219 * this function is empty ! however if it do not use it we have to add
220 * a test before the call. Simply putting a return here instead is not
221 * possible as it would prevent any hollow circle from having its grips
223 fill_func
= o_circle_fill_hollow
;
227 angle1
= -1; angle2
= -1;
228 pitch1
= 1; pitch2
= 1;
229 fill_func
= o_circle_fill_fill
;
233 fill_func
= o_circle_fill_mesh
;
239 fill_func
= o_circle_fill_hatch
;
244 angle1
= -1; angle2
= -1;
245 pitch1
= 1; pitch2
= 1;
246 fill_func
= o_circle_fill_hollow
;
247 fprintf(stderr
, _("Unknown type for circle (fill)!\n"));
250 if((pitch1
<= 0) || (pitch2
<= 0)) {
251 fill_func
= o_circle_fill_fill
;
254 (*fill_func
)(w_current
->backingstore
, w_current
->gc
, color
,
257 fill_width
, angle1
, pitch1
, angle2
, pitch2
);
260 printf("drawing circle\n");
264 /*! \brief Placeholder filling function.
265 * \par Function Description
266 * This function does nothing. It has the same prototype as all the filling
267 * functions. It prevent from making a difference between filling in function
270 * The unit for <B>width</B>, <B>pitch1</B> and <B>pitch2</B> is pixel and unit
271 * for <B>angle1</B> and <B>angle2</B> is degree.
273 * \param [in] w GdkDrawable to draw in.
274 * \param [in] gc GdkGC graphics context to draw on.
275 * \param [in] color Circle fill color.
276 * \param [in] x Center x coordinate of Circle.
277 * \param [in] y Center y coordinate of Circle.
278 * \param [in] radius Radius of Circle.
279 * \param [in] width Circle pattern fill width.
280 * \param [in] angle1 1st angle for pattern.
281 * \param [in] pitch1 1st pitch for pattern.
282 * \param [in] angle2 2nd angle for pattern.
283 * \param [in] pitch2 2nd pitch for pattern.
285 void o_circle_fill_hollow(GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
286 gint x
, gint y
, gint radius
,
288 gint angle1
, gint pitch1
, gint angle2
, gint pitch2
)
293 /*! \brief Fill inside of circle with a solid pattern.
294 * \par Function Description
295 * This function fills the inside of the circle with a solid pattern.
296 * Parameters <B>angle1</B>, <B>pitch1</B> and <B>angle2</B>, <B>pitch2</B>
297 * and <B>width</B> are unused here but kept for compatibility with other
298 * circle filling functions.
300 * The circle is described by the coordinates of its center and its radius.
301 * Please not that it is not the way GDK take it. Translation is made
304 * The unit for <B>width</B>, <B>pitch1</B> and <B>pitch2</B> is pixel and unit
305 * for <B>angle1</B> and <B>angle2</B> is degree.
307 * The solid fill is done with the #gdk_draw_arc() function and its
308 * parameters <B>filled</B> set. The circle is filled with the color
309 * <B>color</B> given as a parameter to the function.
311 * \param [in] w GdkDrawable to draw in.
312 * \param [in] gc GdkGC graphics context to draw on.
313 * \param [in] color Circle fill color.
314 * \param [in] x Center x coordinate of Circle.
315 * \param [in] y Center y coordinate of Circle.
316 * \param [in] radius Radius of Circle.
317 * \param [in] width (unused)
318 * \param [in] angle1 (unused)
319 * \param [in] pitch1 (unused)
320 * \param [in] angle2 (unused)
321 * \param [in] pitch2 (unused)
323 void o_circle_fill_fill(GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
324 gint x
, gint y
, gint radius
,
326 gint angle1
, gint pitch1
, gint angle2
, gint pitch2
)
328 gdk_gc_set_foreground(gc
, color
);
329 gdk_gc_set_line_attributes(gc
, 1, GDK_LINE_SOLID
,
330 GDK_CAP_BUTT
, GDK_JOIN_MITER
);
333 TRUE
, x
-radius
, y
-radius
, 2*radius
, 2*radius
, 0, FULL_CIRCLE
);
337 /*! \brief Fill inside of circle with single line pattern.
338 * \par Function Description
339 * This function fills the inside of the circle with a pattern made of lines.
340 * The lines are drawn inside the circle with an angle <B>angle1</B> from the
341 * horizontal. The distance between two of these lines is given by
342 * <B>pitch1</B> and their width by <B>width</B>.
343 * Parameters <B>angle2</B>, <B>pitch2</B> are unused here but kept for
344 * compatibility with other circle filling functions.
346 * The circle is described by the coordinates of its center and its radius.
347 * Please not that it is not the way GDK take it. Translation is made
350 * The unit for <B>width</B>, <B>pitch1</B> and <B>pitch2</B> is pixel and unit
351 * for <B>angle1</B> and <B>angle2</B> is degree.
353 * The only attribute of line here is its width from the parameter <B>width</B>.
355 * Negative or null values for <B>pitch1</B> are not allowed as it leads to
358 * \param [in] w GdkDrawable to draw in.
359 * \param [in] gc GdkGC graphics context to draw on.
360 * \param [in] color Circle fill color.
361 * \param [in] x Center x coordinate of Circle.
362 * \param [in] y Center y coordinate of Circle.
363 * \param [in] radius Radius of Circle.
364 * \param [in] width Circle pattern fill width.
365 * \param [in] angle1 1st angle for pattern.
366 * \param [in] pitch1 1st pitch for pattern.
367 * \param [in] angle2 (unused)
368 * \param [in] pitch2 (unused)
370 void o_circle_fill_hatch(GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
371 gint x
, gint y
, gint radius
,
373 gint angle1
, gint pitch1
, gint angle2
, gint pitch2
)
375 double x0
, y0
, x1
, y1
, x2
, y2
;
376 double cos_a_
, sin_a_
;
378 gdk_gc_set_line_attributes(gc
, width
, GDK_LINE_SOLID
,
379 GDK_CAP_BUTT
, GDK_JOIN_MITER
);
382 * The function use a matrix. Its elements are obtained from the sinus and
383 * cosinus of the angle <B>angle1</B>. It represent the rotation matrix that
384 * when applied to a point, rotate it of <B>angle1</B>.
386 cos_a_
= cos(((double) angle1
) * M_PI
/180);
387 sin_a_
= sin(((double) angle1
) * M_PI
/180);
390 * When drawing a line in a circle there is two intersections. It looks for
391 * the coordinates of one of these points when the line is horizontal.
392 * The second one can be easily obtained by symmetry in relation to the
393 * vertical axis going through the centre of the circle.
395 * These two points are then rotated of angle <B>angle1</B> using the
396 * elements of the rotation matrix previously computed.
398 * The corresponding line can be drawn providing that the coordinates
401 * These operations are repeated for every horizontal line that can fit
402 * in the upper half of the circle (using and incrementing the variable
406 while(y0
< (double) radius
) {
407 x0
= pow((double) radius
, 2) - pow(y0
, 2);
410 x1
= (x0
*cos_a_
- y0
*sin_a_
) + x
;
411 y1
= y
- (x0
*sin_a_
+ y0
*cos_a_
);
412 x2
= ((-x0
)*cos_a_
- y0
*sin_a_
) + x
;
413 y2
= y
- ((-x0
)*sin_a_
+ y0
*cos_a_
);
416 (int) x1
, (int) y1
, (int) x2
, (int) y2
);
419 * The function use the symmetry in relation to the centre of the circle.
420 * It avoid repetitive computation for the second half of the surface
423 x1
= (x0
*cos_a_
- (-y0
)*sin_a_
) + x
;
424 y1
= y
- (x0
*sin_a_
+ (-y0
)*cos_a_
);
425 x2
= ((-x0
)*cos_a_
- (-y0
)*sin_a_
) + x
;
426 y2
= y
- ((-x0
)*sin_a_
+ (-y0
)*cos_a_
);
428 gdk_draw_line(w
, gc
, (int) x1
, (int) y1
, (int) x2
, (int) y2
);
434 /*! \brief Fill inside of circle with mesh pattern.
435 * \par Function Description
436 * This function fills the inside of the circle with a pattern made of set
437 * of parallel lines in two directions. The first set is drawn inside the
438 * circle with an angle <B>angle1</B> from the horizontal. The distance between
439 * two of these lines is given by <B>pitch1</B>.
440 * The second set is drawn inside the circle with an angle <B>angle2</B> from
441 * the horizontal. The distance between two of these lines is given by
443 * Every lines have the same width given by <B>width</B>.
445 * The unit for <B>width</B>, <B>pitch1</B> and <B>pitch2</B> is pixel and unit
446 * for <B>angle1</B> and <B>angle2</B> is degree.
448 * This function simply makes two successive calls to the function
449 * #o_circle_fill_hatch() respectively with <B>angle1</B>, <B>pitch1</B> and
450 * <B>angle2</B>, <B>pitch2</B> for parameters.
452 * \param [in] w GdkDrawable to draw in.
453 * \param [in] gc GdkGC graphics context to draw on.
454 * \param [in] color Circle fill color.
455 * \param [in] x Center x coordinate of Circle.
456 * \param [in] y Center y coordinate of Circle.
457 * \param [in] radius Radius of Circle.
458 * \param [in] width Circle pattern fill width.
459 * \param [in] angle1 1st angle for pattern.
460 * \param [in] pitch1 1st pitch for pattern.
461 * \param [in] angle2 2nd angle for pattern.
462 * \param [in] pitch2 2nd pitch for pattern.
464 void o_circle_fill_mesh(GdkDrawable
*w
, GdkGC
*gc
, GdkColor
*color
,
465 gint x
, gint y
, gint radius
,
467 gint angle1
, gint pitch1
, gint angle2
, gint pitch2
)
469 o_circle_fill_hatch(w
, gc
, color
,
471 width
, angle1
, pitch1
, -1, -1);
472 o_circle_fill_hatch(w
, gc
, color
,
474 width
, angle2
, pitch2
, -1, -1);
479 /*! \todo Finish function documentation!!!
481 * \par Function Description
484 void o_circle_eraserubber(GSCHEM_TOPLEVEL
*w_current
)
486 o_draw_rubbersubject_xor(w_current
);
489 /*! \brief Draw a circle described by OBJECT with translation
490 * \par Function Description
491 * This function draws the circle object described by <B>*o_current</B>
492 * translated by the vector (<B>dx</B>,<B>dy</B>) with an xor-function over
494 * The translation vector is in world unit.
496 * The circle is displayed with the color of the object.
498 * \param [in] w_current The GSCHEM_TOPLEVEL object.
499 * \param [in] dx Delta x coordinate for circle.
500 * \param [in] dy Delta y coordinate for circle.
501 * \param [in] o_current Circle OBJECT to draw.
504 * add in offsets, get rid of global diffs_x,y
506 void o_circle_draw_xor(GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*o_current
)
508 TOPLEVEL
*toplevel
= w_current
->toplevel
;
509 PAGE
*page
= toplevel
->page_current
;
511 int screen_x1
, screen_y1
, screen_x2
, screen_y2
;
515 if (o_current
->circle
== NULL
) {
519 if (o_current
->saved_color
!= -1) {
520 color
= o_current
->saved_color
;
522 color
= o_current
->color
;
525 w_radius
= o_circle_get_radius(o_current
);
526 s_basic_get_grip(o_current
, GRIP_CIRCLE_CENTER
, &w_x
, &w_y
);
529 * Get bounding rectangle in screen coords. Converting the bounding box
530 * to screen coords is more immune to rounding errors than adding and
531 * subtracting radius in screen coords.
533 WORLDtoSCREEN(page
, w_x
- w_radius
+ dx
, w_y
+ w_radius
+ dy
,
534 &screen_x1
, &screen_y1
);
535 WORLDtoSCREEN(page
, w_x
+ w_radius
+ dx
, w_y
- w_radius
+ dy
,
536 &screen_x2
, &screen_y2
);
538 /* To draw be sure to setup width height */
539 gdk_gc_set_foreground(w_current
->outline_xor_gc
,
540 x_get_darkcolor(color
));
541 gdk_draw_arc(w_current
->backingstore
, w_current
->outline_xor_gc
,
543 screen_x1
, screen_y1
,
544 screen_x2
- screen_x1
, screen_y2
- screen_y1
,
547 /* backing store ? not appropriate here */
550 /*! \brief Start process to input a new circle.
551 * \par Function Description
552 * This function starts the process to input a new circle. Parameters for
553 * this circle are pu into/extracted from the <B>w_current</B> toplevel
555 * <B>w_x</B> and <B>w_y</B> are current coordinates of the mouse pointer in
558 * The first step of the circle input is to set the center of the arc.
559 * This center is kept in (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>).
561 * \param [in] w_current The GSCHEM_TOPLEVEL object.
562 * \param [in] w_x Current x coordinate of pointer in world units.
563 * \param [in] w_y Current y coordinate of pointer in world units.
565 void o_circle_start(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
567 w_current
->which_object
= o_circle_new_at_xy(w_current
->toplevel
, OBJ_CIRCLE
,
568 w_current
->graphic_color
, 0, 0);
569 w_current
->subject_is_new
= 1;
570 w_current
->which_grip
= s_basic_move_grip(w_current
->which_object
,
571 GRIP_NONE
, w_x
, w_y
);
573 /* start the rubberbanding process of the radius */
574 o_draw_rubbersubject_xor(w_current
);
575 w_current
->rubber_visible
= 1;
578 /*! \brief End the input of a circle.
579 * \par Function Description
580 * This function ends the input of the radius of the circle.
581 * The (<B>w_x</B>,<B>w_y</B>) point is taken as the other end of the radius
582 * segment, i.e. on the circle. The distance between this point and the
583 * center is the radius of the circle.
584 * <B>w_x</B> and <B>w_y</B> are in world coords.
586 * The center has previously been input and saved as
587 * (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>).
589 * The temporary circle drawn during the input of the radius is erased.
590 * A new object is allocated, initialized and linked in the object list.
591 * This new object is finally drawn.
593 * \param [in] w_current The GSCHEM_TOPLEVEL object.
594 * \param [in] w_x (unused)
595 * \param [in] w_y (unused)
597 void o_circle_end(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
599 TOPLEVEL
*toplevel
= w_current
->toplevel
;
601 g_assert( w_current
->inside_action
!= 0 );
603 /* erase the temporary circle */
604 o_draw_rubbersubject_xor(w_current
);
605 w_current
->rubber_visible
= 0;
607 s_basic_move_grip(w_current
->which_object
, w_current
->which_grip
, w_x
, w_y
);
609 if (w_current
->subject_is_new
) {
610 /* Link a permanent copy of the drawing subject into the object list. */
611 s_page_append(toplevel
->page_current
, w_current
->which_object
);
613 o_redraw_single(w_current
, w_current
->which_object
);
614 w_current
->which_object
= NULL
;
616 toplevel
->page_current
->CHANGED
= 1;
618 o_undo_savestate(w_current
, UNDO_ALL
);
621 /*! \brief Draw temporary circle while dragging edge.
622 * \par Function Description
623 * This function draws a circle according to its internal representation and
624 * allows the modification of its radius. The radius is updated according to
625 * the current mouse position in <B>w_x</B> and <B>w_y</B>.
626 * It draws a full circle and the horizontal segment of the radius in the
627 * right half of the circle.
629 * The previous temporary circle is erased, the radius is then computed and
630 * updated and finally a new temporary circle is drawn.
632 * The arc is internally described by :
634 * <DT>*</DT><DD>(<B>w_current->first_wx</B>,<B>w_current->first_wy</B>) as its
636 * <DT>*</DT><DD><B>w_current->distance</B> as its radius.
639 * \param [in] w_current The GSCHEM_TOPLEVEL object.
640 * \param [in] w_x Current x coordinate of pointer in world units.
641 * \param [in] w_y Current y coordinate of pointer in world units.
643 void o_circle_rubbercircle(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
645 g_assert( w_current
->inside_action
!= 0 );
647 /* erase the previous temporary circle if it is visible */
648 if (w_current
->rubber_visible
)
649 o_draw_rubbersubject_xor(w_current
);
651 /* Kick the currently moving grip to the new pointer location. */
652 s_basic_move_grip(w_current
->which_object
, w_current
->which_grip
, w_x
, w_y
);
654 /* draw the new temporary circle */
655 o_draw_rubbersubject_xor(w_current
);
656 w_current
->rubber_visible
=1;