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)
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
28 #ifdef HAVE_LIBDMALLOC
32 #define NUM_BEZIER_SEGMENTS 100
35 typedef void (*FILL_FUNC
) (GdkDrawable
*w
, GdkGC
*gc
, COLOR
*color
,
36 GSCHEM_TOPLEVEL
*w_currentm
, PATH
*path
,
38 gint angle1
, gint pitch1
, gint angle2
, gint pitch2
);
41 static void hint_coordinates (int x
, int y
, double *fx
, double *fy
, int width
)
43 double offset
= ((width
% 2) == 0) ? 0 : 0.5;
44 *fx
= (double)x
+ offset
;
45 *fy
= (double)y
+ offset
;
49 static void path_path (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*object
)
51 PATH
*path
= object
->path
;
54 int x1
, y1
, x2
, y2
, x3
, y3
;
55 double fx1
= 0.0, fy1
= 0.0;
56 double fx2
= 0.0, fy2
= 0.0;
57 double fx3
= 0.0, fy3
= 0.0;
59 line_width
= SCREENabs (w_current
, object
->line_width
);
60 if (line_width
<= 0) {
64 for (i
= 0; i
< path
->num_sections
; i
++) {
65 PATH_SECTION
*section
= &path
->sections
[i
];
67 switch (section
->code
) {
69 /* Two control point grips */
70 WORLDtoSCREEN (w_current
, section
->x1
, section
->y1
, &x1
, &y1
);
71 WORLDtoSCREEN (w_current
, section
->x2
, section
->y2
, &x2
, &y2
);
72 hint_coordinates (x1
, y1
, &fx1
, &fy1
, line_width
);
73 hint_coordinates (x2
, y2
, &fx2
, &fy2
, line_width
);
76 case PATH_MOVETO_OPEN
:
78 /* Destination point grip */
79 WORLDtoSCREEN (w_current
, section
->x3
, section
->y3
, &x3
, &y3
);
80 hint_coordinates (x3
, y3
, &fx3
, &fy3
, line_width
);
85 switch (section
->code
) {
87 cairo_close_path (w_current
->cr
);
89 case PATH_MOVETO_OPEN
:
90 cairo_move_to (w_current
->cr
, fx3
, fy3
);
93 cairo_curve_to (w_current
->cr
, fx1
, fy1
, fx2
, fy2
, fx3
, fy3
);
96 cairo_line_to (w_current
->cr
, fx3
, fy3
);
99 cairo_close_path (w_current
->cr
);
106 static PATH
*path_copy_modify (PATH
*path
, int dx
, int dy
,
107 int new_x
, int new_y
, int whichone
)
110 int x1
, y1
, x2
, y2
, x3
, y3
;
114 new_path
= g_malloc (sizeof (PATH
));
115 new_path
->sections
= g_malloc (path
->num_sections
* sizeof (PATH_SECTION
));
116 new_path
->num_sections
= path
->num_sections
;
117 new_path
->num_sections_max
= path
->num_sections
;
119 for (i
= 0; i
< path
->num_sections
; i
++) {
120 PATH_SECTION
*section
= &path
->sections
[i
];
121 PATH_SECTION
*new_section
= &new_path
->sections
[i
];
123 x1
= section
->x1
+ dx
; y1
= section
->y1
+ dy
;
124 x2
= section
->x2
+ dx
; y2
= section
->y2
+ dy
;
125 x3
= section
->x3
+ dx
; y3
= section
->y3
+ dy
;
127 switch (section
->code
) {
129 /* Two control point grips */
130 if (whichone
== grip_no
++) {
131 x1
= new_x
; y1
= new_y
;
133 if (whichone
== grip_no
++) {
134 x2
= new_x
; y2
= new_y
;
138 case PATH_MOVETO_OPEN
:
140 /* Destination point grip */
141 if (whichone
== grip_no
++) {
142 x3
= new_x
; y3
= new_y
;
148 new_section
->code
= section
->code
;
149 new_section
->x1
= x1
; new_section
->y1
= y1
;
150 new_section
->x2
= x2
; new_section
->y2
= y2
;
151 new_section
->x3
= x3
; new_section
->y3
= y3
;
157 /*! \brief Placeholder filling function.
158 * \par Function Description
159 * This function does nothing. It has the same prototype as all the
160 * filling functions. It prevent from making a difference between filling
161 * in function #o_path_draw().
163 * \param [in] w GdkDrawable to draw in.
164 * \param [in] gc GdkGC graphics context to draw on.
165 * \param [in] color Box fill color.
166 * \param [in] path The PATH object to draw
167 * \param [in] fill_width PATH pattern fill width.
168 * \param [in] angle1 1st angle for pattern.
169 * \param [in] pitch1 1st pitch for pattern.
170 * \param [in] angle2 2nd angle for pattern.
171 * \param [in] pitch2 2nd pitch for pattern.
173 static void o_path_fill_hollow (GdkDrawable
*w
, GdkGC
*gc
, COLOR
*color
,
174 GSCHEM_TOPLEVEL
*w_current
, PATH
*path
,
176 gint angle1
, gint pitch1
,
177 gint angle2
, gint pitch2
)
182 /*! \brief Fill inside of path with a solid pattern.
183 * \par Function Description
184 * This function fills the inside of the path with a solid pattern.
185 * Parameters <B>angle1</B>, <B>pitch1</B> and <B>angle2</B>,
186 * <B>pitch2</B> and <B>fill_width</B> are unused here but kept for compatibility
187 * with other path filling functions.
189 * \param [in] w GdkDrawable to draw in.
190 * \param [in] gc GdkGC graphics context to draw on.
191 * \param [in] color Box fill color.
192 * \param [in] path The PATH object to draw
193 * \param [in] fill_width PATH pattern fill width.
194 * \param [in] angle1 (unused)
195 * \param [in] pitch1 (unused)
196 * \param [in] angle2 (unused)
197 * \param [in] pitch2 (unused)
199 static void o_path_fill_fill (GdkDrawable
*w
, GdkGC
*gc
, COLOR
*color
,
200 GSCHEM_TOPLEVEL
*w_current
, PATH
*path
,
202 gint angle1
, gint pitch1
,
203 gint angle2
, gint pitch2
)
205 /* NOP: We'll fill it when we do the stroking */
208 /*! \brief Fill inside of path with single line pattern.
209 * \par Function Description
210 * This function fills the inside of the path with a pattern made of lines.
211 * The lines are drawn inside the path with an angle <B>angle1</B> from the
212 * horizontal. The distance between two of these lines is given by
213 * <B>pitch1</B> and their width by <B>fill_width</B>.
214 * Parameters <B>angle2</B> and <B>pitch2</B> are unused here but kept for
215 * compatbility with other path filling functions.
217 * \param [in] w GdkDrawable to draw in.
218 * \param [in] gc GdkGC graphics context to draw on.
219 * \param [in] color Box fill color.
220 * \param [in] path The PATH object to draw
221 * \param [in] fill_width PATH pattern fill width.
222 * \param [in] angle1 1st angle for pattern.
223 * \param [in] pitch1 1st pitch for pattern.
224 * \param [in] angle2 (unused)
225 * \param [in] pitch2 (unused)
227 static void o_path_fill_hatch (GdkDrawable
*w
, GdkGC
*gc
, COLOR
*color
,
228 GSCHEM_TOPLEVEL
*w_current
, PATH
*path
,
230 gint angle1
, gint pitch1
,
231 gint angle2
, gint pitch2
)
236 gschem_cairo_set_source_color (w_current
, color
);
238 lines
= g_array_new (FALSE
, FALSE
, sizeof (LINE
));
239 m_hatch_path (path
, angle1
, pitch1
, lines
);
241 for (i
=0; i
< lines
->len
; i
++) {
242 LINE
*line
= &g_array_index (lines
, LINE
, i
);
244 gschem_cairo_line (w_current
, END_NONE
, fill_width
, line
->x
[0], line
->y
[0],
245 line
->x
[1], line
->y
[1]);
247 gschem_cairo_stroke (w_current
, TYPE_SOLID
, END_NONE
, fill_width
, -1, -1);
249 g_array_free (lines
, TRUE
);
253 /*! \brief Fill inside of path with mesh pattern.
254 * \par Function Description
255 * This function fills the inside of the path with a pattern made of two
256 * sets of parallel lines in two directions. The first set is drawn inside
257 * the path with an angle <B>angle1</B> from the horizontal. The distance
258 * between two of these lines is given by <B>pitch1</B>.
259 * The second set is drawn inside the path with an angle <B>angle2</B> from
260 * the horizontal. The distance between two of these lines is given
263 * \param [in] w GdkDrawable to draw in.
264 * \param [in] gc GdkGC graphics context to draw on.
265 * \param [in] color Box fill color.
266 * \param [in] path The PATH object to draw
267 * \param [in] fill_width PATH pattern fill width.
268 * \param [in] angle1 1st angle for pattern.
269 * \param [in] pitch1 1st pitch for pattern.
270 * \param [in] angle2 2nd angle for pattern.
271 * \param [in] pitch2 2nd pitch for pattern.
273 static void o_path_fill_mesh (GdkDrawable
*w
, GdkGC
*gc
, COLOR
*color
,
274 GSCHEM_TOPLEVEL
*w_current
, PATH
*path
,
276 gint angle1
, gint pitch1
,
277 gint angle2
, gint pitch2
)
279 o_path_fill_hatch (w
, gc
, color
, w_current
, path
,
280 fill_width
, angle1
, pitch1
, -1, -1);
281 o_path_fill_hatch (w
, gc
, color
, w_current
, path
,
282 fill_width
, angle2
, pitch2
, -1, -1);
286 /*! \brief Draw a path on screen.
287 * \par Function Description
288 * This function is used to draw a path on screen. The path is described
289 * in the object which is referred by <B>o_current</B>. The path is displayed
290 * according to the current state, described in the GSCHEM_TOPLEVEL object pointed
291 * by <B>w_current</B>.
293 * \param [in] w_current The GSCHEM_TOPLEVEL object.
294 * \param [in] o_current The path OBJECT to draw.
296 void o_path_draw(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
298 TOPLEVEL
*toplevel
= w_current
->toplevel
;
299 PATH
*path
= o_current
->path
;
300 int angle1
, pitch1
, angle2
, pitch2
;
307 if (toplevel
->DONT_REDRAW
== 1)
310 angle1
= o_current
->fill_angle1
;
311 pitch1
= o_current
->fill_pitch1
;
312 angle2
= o_current
->fill_angle2
;
313 pitch2
= o_current
->fill_pitch2
;
315 switch(o_current
->fill_type
) {
317 angle1
= -1; angle2
= -1;
318 pitch1
= 1; pitch2
= 1;
319 /* this function is empty ! however if it do not use it we have to add
320 * a test before the call. Simply putting a return here instead is not
321 * possible as it would prevent any hollow path from having its grips
324 fill_func
= o_path_fill_hollow
;
328 angle1
= -1; angle2
= -1;
329 pitch1
= 1; pitch2
= 1;
330 fill_func
= o_path_fill_fill
;
334 fill_func
= o_path_fill_mesh
;
340 fill_func
= o_path_fill_hatch
;
345 angle1
= -1; angle2
= -1;
346 pitch1
= 1; pitch2
= 1;
347 fill_func
= o_path_fill_hollow
;
348 fprintf(stderr
, _("Unknown type for path (fill)!\n"));
351 if((pitch1
<= 0) || (pitch2
<= 0)) {
352 fill_func
= o_path_fill_fill
;
355 (*fill_func
) (w_current
->drawable
, w_current
->gc
,
356 o_drawing_color (w_current
, o_current
),
357 w_current
, path
, o_current
->fill_width
,
358 angle1
, pitch1
, angle2
, pitch2
);
360 path_path (w_current
, o_current
);
362 gschem_cairo_set_source_color (w_current
,
363 o_drawing_color (w_current
, o_current
));
365 if (o_current
->fill_type
== FILLING_FILL
)
366 cairo_fill_preserve (w_current
->cr
);
368 gschem_cairo_stroke (w_current
, o_current
->line_type
,
370 o_current
->line_width
,
371 o_current
->line_length
,
372 o_current
->line_space
);
374 if (o_current
->selected
&& w_current
->draw_grips
) {
375 o_path_draw_grips (w_current
, o_current
);
380 /*! \todo Finish function documentation
382 * \par Function Description
384 void o_path_invalidate_rubber (GSCHEM_TOPLEVEL
*w_current
)
386 PATH
*path
= w_current
->which_object
->path
;
387 int min_x
, min_y
, max_x
, max_y
;
388 int x1
, y1
, x2
, y2
, x3
, y3
;
389 int new_x
, new_y
, whichone
;
393 min_x
= G_MAXINT
; max_x
= G_MININT
;
394 min_y
= G_MAXINT
; max_y
= G_MININT
;
396 new_x
= w_current
->second_wx
;
397 new_y
= w_current
->second_wy
;
398 whichone
= w_current
->which_grip
;
400 for (i
= 0; i
< path
->num_sections
; i
++) {
401 PATH_SECTION
*section
= &path
->sections
[i
];
403 x1
= section
->x1
; y1
= section
->y1
;
404 x2
= section
->x2
; y2
= section
->y2
;
405 x3
= section
->x3
; y3
= section
->y3
;
407 switch (section
->code
) {
409 /* Two control point grips */
410 if (whichone
== grip_no
++) {
411 x1
= new_x
; y1
= new_y
;
413 if (whichone
== grip_no
++) {
414 x2
= new_x
; y2
= new_y
;
416 min_x
= MIN (min_x
, x1
); min_y
= MIN (min_y
, y1
);
417 max_x
= MAX (max_x
, x1
); max_y
= MAX (max_y
, y1
);
418 min_x
= MIN (min_x
, x2
); min_y
= MIN (min_y
, y2
);
419 max_x
= MAX (max_x
, x2
); max_y
= MAX (max_y
, y2
);
422 case PATH_MOVETO_OPEN
:
424 /* Destination point grip */
425 if (whichone
== grip_no
++) {
426 x3
= new_x
; y3
= new_y
;
428 min_x
= MIN (min_x
, x3
); min_y
= MIN (min_y
, y3
);
429 max_x
= MAX (max_x
, x3
); max_y
= MAX (max_y
, y3
);
435 WORLDtoSCREEN (w_current
, min_x
, max_y
, &x1
, &y1
);
436 WORLDtoSCREEN (w_current
, max_x
, min_y
, &x2
, &y2
);
437 o_invalidate_rect (w_current
, x1
, y1
, x2
, y2
);
441 /*! \brief Draw a path object after applying translation.
442 * \par Function Description
443 * This function is used to draw the path object described by
444 * <B>*o_current</B> after applying a translation on the two directions of
445 * <B>dx</B> and <B>dy</B> in world units.
447 * \param [in] w_current The GSCHEM_TOPLEVEL object.
448 * \param [in] dx Delta x coordinate for path.
449 * \param [in] dy Delta y coordinate for path.
450 * \param [in] o_current Line OBJECT to draw.
452 void o_path_draw_place (GSCHEM_TOPLEVEL
*w_current
, int dx
, int dy
, OBJECT
*o_current
)
456 g_return_if_fail (o_current
->path
!= NULL
);
458 /* Setup a fake object to pass the drawing routine */
459 object
.line_width
= 0; /* clamped to 1 pixel in circle_path */
460 object
.path
= path_copy_modify (o_current
->path
, dx
, dy
, 0, 0, -1);
462 path_path (w_current
, &object
);
463 g_free (object
.path
->sections
);
464 g_free (object
.path
);
466 gschem_cairo_set_source_color (w_current
,
467 x_color_lookup_dark (o_current
->color
));
468 gschem_cairo_stroke (w_current
, TYPE_SOLID
, END_NONE
, 0, -1, -1);
471 /*! \brief Start process to input a new path.
472 * \par Function Description
473 * This function starts the process of interactively adding a path to
476 * During all the process, the path is internally represented by the two
477 * ends of the path as (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>) and
478 * (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>).
480 * \param [in] w_current The GSCHEM_TOPLEVEL object.
481 * \param [in] w_x Current x coordinate of pointer in world units.
482 * \param [in] w_y Current y coordinate of pointer in world units.
484 void o_path_start(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
486 /* TODO: Implement support for drawing paths from within gschem */
490 /*! \brief End the input of a path.
491 * \par Function Description
492 * This function ends the process of interactively adding a path to the
495 * It first erases the last temporary path displayed, calculates the
496 * corresponding world coordinates of the two ends of the path and finally
497 * adds a new initialized path object to the list of object of the current
500 * \param [in] w_current The GSCHEM_TOPLEVEL object.
501 * \param [in] w_x (unused)
502 * \param [in] w_y (unused)
504 void o_path_end(GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
506 /* TODO: Implement support for drawing paths from within gschem */
510 /*! \brief Draw temporary path while dragging end.
511 * \par Function Description
512 * This function manages the erase/update/draw process of temporary path
513 * when modifying one end of the path.
514 * The path is described by four <B>*w_current</B> variables : the first end
515 * of the path is (<B>first_wx</B>,<B>first_wy</B>), the second end is
516 * (<B>second_wx</B>,<B>second_wy</B>).
517 * The first end is constant. The second end is updated to the (<B>w_x</B>,<B>w_y</B>).
519 * \param [in] w_current The GSCHEM_TOPLEVEL object.
520 * \param [in] w_x Current x coordinate of pointer in world units.
521 * \param [in] w_y Current y coordinate of pointer in world units.
523 void o_path_motion (GSCHEM_TOPLEVEL
*w_current
, int w_x
, int w_y
)
525 if (w_current
->rubber_visible
)
526 o_path_invalidate_rubber (w_current
);
528 w_current
->second_wx
= w_x
;
529 w_current
->second_wy
= w_y
;
531 o_path_invalidate_rubber (w_current
);
532 w_current
->rubber_visible
= 1;
536 /*! \brief Draw path from GSCHEM_TOPLEVEL object.
537 * \par Function Description
538 * This function draws a path with an exclusive or function over the sheet.
539 * The color of the box is <B>SELECT_COLOR</B>. The path is
540 * described by the two points (<B>w_current->first_wx</B>,
541 * <B>w_current->first_wy</B>) and (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>).
543 * \param [in] w_current The GSCHEM_TOPLEVEL object.
545 void o_path_draw_rubber (GSCHEM_TOPLEVEL
*w_current
)
549 /* Setup a fake object to pass the drawing routine */
550 object
.line_width
= 0; /* clamped to 1 pixel in circle_path */
551 object
.path
= path_copy_modify (w_current
->which_object
->path
, 0, 0,
552 w_current
->second_wx
,
553 w_current
->second_wy
, w_current
->which_grip
);
555 path_path (w_current
, &object
);
556 g_free (object
.path
->sections
);
557 g_free (object
.path
);
559 gschem_cairo_set_source_color (w_current
, x_color_lookup (SELECT_COLOR
));
560 gschem_cairo_stroke (w_current
, TYPE_SOLID
, END_SQUARE
, 0, -1, -1);
564 /*! \brief Draw lines between curve segment end-point and their control point.
566 * \par Function Description
567 * This function Draws lines between the end-points and respective
568 * control-points of curve segments in the path.
570 * \param [in] w_current The GSCHEM_TOPLEVEL object.
571 * \param [in] o_current The path OBJECT.
573 static void draw_control_lines (GSCHEM_TOPLEVEL
*w_current
,
576 TOPLEVEL
*toplevel
= w_current
->toplevel
;
579 int last_x
= 0, last_y
= 0;
580 PATH_SECTION
*section
;
583 if (toplevel
->override_color
!= -1 ) {
584 /* override : use the override_color instead */
585 color
= x_color_lookup (toplevel
->override_color
);
587 /* use the normal selection color */
588 color
= x_color_lookup_dark (SELECT_COLOR
);
591 /* set the color for the grip */
592 gschem_cairo_set_source_color (w_current
, color
);
594 for (i
= 0; i
< o_current
->path
->num_sections
; i
++) {
595 section
= &o_current
->path
->sections
[i
];
597 if (section
->code
!= PATH_END
) {
598 next_x
= section
->x3
;
599 next_y
= section
->y3
;
602 switch (section
->code
) {
604 /* Two control point grips */
605 gschem_cairo_line (w_current
, END_NONE
, 0,
606 last_x
, last_y
, section
->x1
, section
->y1
);
607 gschem_cairo_stroke (w_current
, TYPE_SOLID
, END_NONE
, 0, -1, -1);
609 gschem_cairo_line (w_current
, END_NONE
, 0,
610 next_x
, next_y
, section
->x2
, section
->y2
);
611 gschem_cairo_stroke (w_current
, TYPE_SOLID
, END_NONE
, 0, -1, -1);
615 case PATH_MOVETO_OPEN
:
627 /*! \brief Draw grip marks on path.
628 * \par Function Description
629 * This function draws the grips on the path object <B>o_current</B>.
631 * A path has a grip at each end.
633 * \param [in] w_current The GSCHEM_TOPLEVEL object.
634 * \param [in] o_current Line OBJECT to draw grip points on.
636 void o_path_draw_grips(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
638 PATH_SECTION
*section
;
641 if (w_current
->draw_grips
== FALSE
)
644 draw_control_lines (w_current
, o_current
);
646 for (i
= 0; i
< o_current
->path
->num_sections
; i
++) {
647 section
= &o_current
->path
->sections
[i
];
649 switch (section
->code
) {
651 /* Two control point grips */
652 o_grips_draw (w_current
, section
->x1
, section
->y1
);
653 o_grips_draw (w_current
, section
->x2
, section
->y2
);
656 case PATH_MOVETO_OPEN
:
658 /* Destination point grip */
659 o_grips_draw (w_current
, section
->x3
, section
->y3
);