Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / o_path.c
blob682bda53ff24b0d503551a1e6a1c594f1a76d76a
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
20 #include <config.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include <cairo.h>
26 #include "gschem.h"
28 #ifdef HAVE_LIBDMALLOC
29 #include <dmalloc.h>
30 #endif
32 #define NUM_BEZIER_SEGMENTS 100
35 typedef void (*FILL_FUNC) (GdkDrawable *w, GdkGC *gc, COLOR *color,
36 GSCHEM_TOPLEVEL *w_currentm, PATH *path,
37 gint fill_width,
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;
52 int line_width;
53 int i;
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) {
61 line_width = 1;
64 for (i = 0; i < path->num_sections; i++) {
65 PATH_SECTION *section = &path->sections[i];
67 switch (section->code) {
68 case PATH_CURVETO:
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);
74 /* Fall through */
75 case PATH_MOVETO:
76 case PATH_MOVETO_OPEN:
77 case PATH_LINETO:
78 /* Destination point grip */
79 WORLDtoSCREEN (w_current, section->x3, section->y3, &x3, &y3);
80 hint_coordinates (x3, y3, &fx3, &fy3, line_width);
81 case PATH_END:
82 break;
85 switch (section->code) {
86 case PATH_MOVETO:
87 cairo_close_path (w_current->cr);
88 /* fall-through */
89 case PATH_MOVETO_OPEN:
90 cairo_move_to (w_current->cr, fx3, fy3);
91 break;
92 case PATH_CURVETO:
93 cairo_curve_to (w_current->cr, fx1, fy1, fx2, fy2, fx3, fy3);
94 break;
95 case PATH_LINETO:
96 cairo_line_to (w_current->cr, fx3, fy3);
97 break;
98 case PATH_END:
99 cairo_close_path (w_current->cr);
100 break;
106 static PATH *path_copy_modify (PATH *path, int dx, int dy,
107 int new_x, int new_y, int whichone)
109 PATH *new_path;
110 int x1, y1, x2, y2, x3, y3;
111 int i;
112 int grip_no = 0;
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) {
128 case PATH_CURVETO:
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;
136 /* Fall through */
137 case PATH_MOVETO:
138 case PATH_MOVETO_OPEN:
139 case PATH_LINETO:
140 /* Destination point grip */
141 if (whichone == grip_no++) {
142 x3 = new_x; y3 = new_y;
144 case PATH_END:
145 break;
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;
153 return new_path;
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,
175 gint fill_width,
176 gint angle1, gint pitch1,
177 gint angle2, gint pitch2)
179 /* NOP */
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,
201 gint fill_width,
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,
229 gint fill_width,
230 gint angle1, gint pitch1,
231 gint angle2, gint pitch2)
233 int i;
234 GArray *lines;
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
261 * by <B>pitch2</B>.
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,
275 gint fill_width,
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;
301 FILL_FUNC fill_func;
303 if (path == NULL) {
304 return;
307 if (toplevel->DONT_REDRAW == 1)
308 return;
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) {
316 case FILLING_HOLLOW:
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
322 * drawn
324 fill_func = o_path_fill_hollow;
325 break;
327 case FILLING_FILL:
328 angle1 = -1; angle2 = -1;
329 pitch1 = 1; pitch2 = 1;
330 fill_func = o_path_fill_fill;
331 break;
333 case FILLING_MESH:
334 fill_func = o_path_fill_mesh;
335 break;
337 case FILLING_HATCH:
338 angle2 = -1;
339 pitch2 = 1;
340 fill_func = o_path_fill_hatch;
341 break;
343 case FILLING_VOID:
344 default:
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,
369 o_current->line_end,
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
381 * \brief
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;
390 int grip_no = 0;
391 int i;
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) {
408 case PATH_CURVETO:
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);
420 /* Fall through */
421 case PATH_MOVETO:
422 case PATH_MOVETO_OPEN:
423 case PATH_LINETO:
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);
430 case PATH_END:
431 break;
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)
454 OBJECT object;
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
474 * the current sheet.
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
493 * current sheet.
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
498 * sheet.
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)
547 OBJECT object;
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,
574 OBJECT *o_current)
576 TOPLEVEL *toplevel = w_current->toplevel;
577 int i;
578 int next_x, next_y;
579 int last_x = 0, last_y = 0;
580 PATH_SECTION *section;
581 COLOR *color;
583 if (toplevel->override_color != -1 ) {
584 /* override : use the override_color instead */
585 color = x_color_lookup (toplevel->override_color);
586 } else {
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) {
603 case PATH_CURVETO:
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);
613 /* Fall through */
614 case PATH_MOVETO:
615 case PATH_MOVETO_OPEN:
616 case PATH_LINETO:
617 last_x = next_x;
618 last_y = next_y;
619 break;
620 case PATH_END:
621 break;
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;
639 int i;
641 if (w_current->draw_grips == FALSE)
642 return;
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) {
650 case PATH_CURVETO:
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);
654 /* Fall through */
655 case PATH_MOVETO:
656 case PATH_MOVETO_OPEN:
657 case PATH_LINETO:
658 /* Destination point grip */
659 o_grips_draw (w_current, section->x3, section->y3);
660 break;
661 case PATH_END:
662 break;