gschem

o_path.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * gschem - gEDA Schematic Capture
00003  * Copyright (C) 1998-2010 Ales Hvezda
00004  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 #include <config.h>
00021 
00022 #include <stdio.h>
00023 #include <math.h>
00024 #include <cairo.h>
00025 
00026 #include "gschem.h"
00027 
00028 #ifdef HAVE_LIBDMALLOC
00029 #include <dmalloc.h>
00030 #endif
00031 
00032 #define NUM_BEZIER_SEGMENTS 100
00033 
00034 
00035 typedef void (*FILL_FUNC) (GSCHEM_TOPLEVEL *w_current,
00036                            COLOR *color, PATH *path,
00037                            gint fill_width,
00038                            gint angle1, gint pitch1, gint angle2, gint pitch2);
00039 
00040 
00041 static void hint_coordinates (int x, int y, double *fx, double *fy, int width)
00042 {
00043   double offset = ((width % 2) == 0) ? 0 : 0.5;
00044   *fx = (double)x + offset;
00045   *fy = (double)y + offset;
00046 }
00047 
00048 
00049 static void path_path (GSCHEM_TOPLEVEL *w_current, OBJECT *object)
00050 {
00051   PATH *path = object->path;
00052   int line_width;
00053   int i;
00054   int x1, y1, x2, y2, x3, y3;
00055   double fx1 = 0.0, fy1 = 0.0;
00056   double fx2 = 0.0, fy2 = 0.0;
00057   double fx3 = 0.0, fy3 = 0.0;
00058 
00059   line_width = SCREENabs (w_current, object->line_width);
00060   if (line_width <= 0) {
00061     line_width = 1;
00062   }
00063 
00064   for (i = 0; i <  path->num_sections; i++) {
00065     PATH_SECTION *section = &path->sections[i];
00066 
00067     switch (section->code) {
00068       case PATH_CURVETO:
00069         /* Two control point grips */
00070         WORLDtoSCREEN (w_current, section->x1, section->y1, &x1, &y1);
00071         WORLDtoSCREEN (w_current, section->x2, section->y2, &x2, &y2);
00072         hint_coordinates (x1, y1, &fx1, &fy1, line_width);
00073         hint_coordinates (x2, y2, &fx2, &fy2, line_width);
00074         /* Fall through */
00075       case PATH_MOVETO:
00076       case PATH_MOVETO_OPEN:
00077       case PATH_LINETO:
00078         /* Destination point grip */
00079         WORLDtoSCREEN (w_current, section->x3, section->y3, &x3, &y3);
00080         hint_coordinates (x3, y3, &fx3, &fy3, line_width);
00081       case PATH_END:
00082         break;
00083     }
00084 
00085     switch (section->code) {
00086       case PATH_MOVETO:
00087         cairo_close_path (w_current->cr);
00088         /* fall-through */
00089       case PATH_MOVETO_OPEN:
00090         cairo_move_to (w_current->cr, fx3, fy3);
00091         break;
00092       case PATH_CURVETO:
00093         cairo_curve_to (w_current->cr, fx1, fy1, fx2, fy2, fx3, fy3);
00094         break;
00095       case PATH_LINETO:
00096         cairo_line_to (w_current->cr, fx3, fy3);
00097         break;
00098       case PATH_END:
00099         cairo_close_path (w_current->cr);
00100         break;
00101     }
00102   }
00103 }
00104 
00105 
00106 static PATH *path_copy_modify (PATH *path, int dx, int dy,
00107                                int new_x, int new_y, int whichone)
00108 {
00109   PATH *new_path;
00110   int x1, y1, x2, y2, x3, y3;
00111   int i;
00112   int grip_no = 0;
00113 
00114   new_path = g_malloc (sizeof (PATH));
00115   new_path->sections = g_malloc (path->num_sections * sizeof (PATH_SECTION));
00116   new_path->num_sections = path->num_sections;
00117   new_path->num_sections_max = path->num_sections;
00118 
00119   for (i = 0; i <  path->num_sections; i++) {
00120     PATH_SECTION *section     = &path->sections[i];
00121     PATH_SECTION *new_section = &new_path->sections[i];
00122 
00123     x1 = section->x1 + dx; y1 = section->y1 + dy;
00124     x2 = section->x2 + dx; y2 = section->y2 + dy;
00125     x3 = section->x3 + dx; y3 = section->y3 + dy;
00126 
00127     switch (section->code) {
00128       case PATH_CURVETO:
00129         /* Two control point grips */
00130         if (whichone == grip_no++) {
00131           x1 = new_x; y1 = new_y;
00132         }
00133         if (whichone == grip_no++) {
00134           x2 = new_x; y2 = new_y;
00135         }
00136         /* Fall through */
00137       case PATH_MOVETO:
00138       case PATH_MOVETO_OPEN:
00139       case PATH_LINETO:
00140         /* Destination point grip */
00141         if (whichone == grip_no++) {
00142           x3 = new_x; y3 = new_y;
00143         }
00144       case PATH_END:
00145         break;
00146     }
00147 
00148     new_section->code = section->code;
00149     new_section->x1 = x1;  new_section->y1 = y1;
00150     new_section->x2 = x2;  new_section->y2 = y2;
00151     new_section->x3 = x3;  new_section->y3 = y3;
00152   }
00153   return new_path;
00154 }
00155 
00156 
00172 static void o_path_fill_hollow (GSCHEM_TOPLEVEL *w_current,
00173                                 COLOR *color, PATH *path,
00174                                 gint fill_width,
00175                                 gint angle1, gint pitch1,
00176                                 gint angle2, gint pitch2)
00177 {
00178   /* NOP */
00179 }
00180 
00197 static void o_path_fill_fill (GSCHEM_TOPLEVEL *w_current,
00198                               COLOR *color, PATH *path,
00199                               gint fill_width,
00200                               gint angle1, gint pitch1,
00201                               gint angle2, gint pitch2)
00202 {
00203   /* NOP: We'll fill it when we do the stroking */
00204 }
00205 
00224 static void o_path_fill_hatch (GSCHEM_TOPLEVEL *w_current,
00225                                COLOR *color, PATH *path,
00226                                gint fill_width,
00227                                gint angle1, gint pitch1,
00228                                gint angle2, gint pitch2)
00229 {
00230   int i;
00231   GArray *lines;
00232 
00233   gschem_cairo_set_source_color (w_current, color);
00234 
00235   lines = g_array_new (FALSE, FALSE, sizeof (LINE));
00236   m_hatch_path (path, angle1, pitch1, lines);
00237 
00238   for (i=0; i < lines->len; i++) {
00239     LINE *line = &g_array_index (lines, LINE, i);
00240 
00241     gschem_cairo_line (w_current, END_NONE, fill_width, line->x[0], line->y[0],
00242                                                         line->x[1], line->y[1]);
00243   }
00244   gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, fill_width, -1, -1);
00245 
00246   g_array_free (lines, TRUE);
00247 }
00248 
00249 
00269 static void o_path_fill_mesh (GSCHEM_TOPLEVEL *w_current,
00270                               COLOR *color, PATH *path,
00271                               gint fill_width,
00272                               gint angle1, gint pitch1,
00273                               gint angle2, gint pitch2)
00274 {
00275   o_path_fill_hatch (w_current, color, path,
00276                      fill_width, angle1, pitch1, -1, -1);
00277   o_path_fill_hatch (w_current, color, path,
00278                      fill_width, angle2, pitch2, -1, -1);
00279 }
00280 
00281 
00292 void o_path_draw(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
00293 {
00294   PATH *path = o_current->path;
00295   int angle1, pitch1, angle2, pitch2;
00296   FILL_FUNC fill_func;
00297 
00298   if (path == NULL) {
00299     return;
00300   }
00301 
00302   angle1 = o_current->fill_angle1;
00303   pitch1 = o_current->fill_pitch1;
00304   angle2 = o_current->fill_angle2;
00305   pitch2 = o_current->fill_pitch2;
00306 
00307   switch(o_current->fill_type) {
00308     case FILLING_HOLLOW:
00309       angle1 = -1; angle2 = -1;
00310       pitch1 = 1; pitch2 = 1;
00311       /* this function is empty ! however if it do not use it we have to add
00312        * a test before the call. Simply putting a return here instead is not
00313        * possible as it would prevent any hollow path from having its grips
00314        * drawn
00315        */
00316       fill_func = o_path_fill_hollow;
00317       break;
00318 
00319     case FILLING_FILL:
00320       angle1 = -1; angle2 = -1;
00321       pitch1 = 1; pitch2 = 1;
00322       fill_func = o_path_fill_fill;
00323       break;
00324 
00325     case FILLING_MESH:
00326       fill_func = o_path_fill_mesh;
00327       break;
00328 
00329     case FILLING_HATCH:
00330       angle2 = -1;
00331       pitch2 = 1;
00332       fill_func = o_path_fill_hatch;
00333       break;
00334 
00335     case FILLING_VOID:
00336     default:
00337       angle1 = -1; angle2 = -1;
00338       pitch1 = 1; pitch2 = 1;
00339       fill_func = o_path_fill_hollow;
00340       fprintf(stderr, _("Unknown type for path (fill)!\n"));
00341   }
00342 
00343   if((pitch1 <= 0) || (pitch2 <= 0)) {
00344     fill_func = o_path_fill_fill;
00345   }
00346 
00347   (*fill_func) (w_current, o_drawing_color (w_current, o_current),
00348                 path, o_current->fill_width, angle1, pitch1, angle2, pitch2);
00349 
00350   path_path (w_current, o_current);
00351 
00352   gschem_cairo_set_source_color (w_current,
00353                                  o_drawing_color (w_current, o_current));
00354 
00355   if (o_current->fill_type == FILLING_FILL)
00356     cairo_fill_preserve (w_current->cr);
00357 
00358   gschem_cairo_stroke (w_current, o_current->line_type,
00359                                   o_current->line_end,
00360                                   o_current->line_width,
00361                                   o_current->line_length,
00362                                   o_current->line_space);
00363 
00364   if (o_current->selected && w_current->draw_grips) {
00365     o_path_draw_grips (w_current, o_current);
00366   }
00367 }
00368 
00369 
00374 void o_path_invalidate_rubber (GSCHEM_TOPLEVEL *w_current)
00375 {
00376   PATH *path = w_current->which_object->path;
00377   int min_x, min_y, max_x, max_y;
00378   int x1, y1, x2, y2, x3, y3;
00379   int new_x, new_y, whichone;
00380   int grip_no = 0;
00381   int i;
00382 
00383   min_x = G_MAXINT;  max_x = G_MININT;
00384   min_y = G_MAXINT;  max_y = G_MININT;
00385 
00386   new_x = w_current->second_wx;
00387   new_y = w_current->second_wy;
00388   whichone = w_current->which_grip;
00389 
00390   for (i = 0; i <  path->num_sections; i++) {
00391     PATH_SECTION *section = &path->sections[i];
00392 
00393     x1 = section->x1; y1 = section->y1;
00394     x2 = section->x2; y2 = section->y2;
00395     x3 = section->x3; y3 = section->y3;
00396 
00397     switch (section->code) {
00398       case PATH_CURVETO:
00399         /* Two control point grips */
00400         if (whichone == grip_no++) {
00401           x1 = new_x; y1 = new_y;
00402         }
00403         if (whichone == grip_no++) {
00404           x2 = new_x; y2 = new_y;
00405         }
00406         min_x = MIN (min_x, x1);  min_y = MIN (min_y, y1);
00407         max_x = MAX (max_x, x1);  max_y = MAX (max_y, y1);
00408         min_x = MIN (min_x, x2);  min_y = MIN (min_y, y2);
00409         max_x = MAX (max_x, x2);  max_y = MAX (max_y, y2);
00410         /* Fall through */
00411       case PATH_MOVETO:
00412       case PATH_MOVETO_OPEN:
00413       case PATH_LINETO:
00414         /* Destination point grip */
00415         if (whichone == grip_no++) {
00416           x3 = new_x; y3 = new_y;
00417         }
00418         min_x = MIN (min_x, x3);  min_y = MIN (min_y, y3);
00419         max_x = MAX (max_x, x3);  max_y = MAX (max_y, y3);
00420       case PATH_END:
00421         break;
00422     }
00423   }
00424 
00425   WORLDtoSCREEN (w_current, min_x, max_y, &x1, &y1);
00426   WORLDtoSCREEN (w_current, max_x, min_y, &x2, &y2);
00427   o_invalidate_rect (w_current, x1, y1, x2, y2);
00428 }
00429 
00430 
00442 void o_path_draw_place (GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *o_current)
00443 {
00444   OBJECT object;
00445 
00446   g_return_if_fail (o_current->path != NULL);
00447 
00448   /* Setup a fake object to pass the drawing routine */
00449   object.line_width = 0; /* clamped to 1 pixel in circle_path */
00450   object.path = path_copy_modify (o_current->path, dx, dy, 0, 0, -1);
00451 
00452   path_path (w_current, &object);
00453   g_free (object.path->sections);
00454   g_free (object.path);
00455 
00456   gschem_cairo_set_source_color (w_current,
00457                                  x_color_lookup_dark (o_current->color));
00458   gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00459 }
00460 
00474 void o_path_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00475 {
00476   /* TODO: Implement support for drawing paths from within gschem */
00477 }
00478 
00479 
00494 void o_path_end(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00495 {
00496   /* TODO: Implement support for drawing paths from within gschem */
00497 }
00498 
00499 
00513 void o_path_motion (GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
00514 {
00515   if (w_current->rubber_visible)
00516     o_path_invalidate_rubber (w_current);
00517 
00518   w_current->second_wx = w_x;
00519   w_current->second_wy = w_y;
00520 
00521   o_path_invalidate_rubber (w_current);
00522   w_current->rubber_visible = 1;
00523 }
00524 
00525 
00535 void o_path_draw_rubber (GSCHEM_TOPLEVEL *w_current)
00536 {
00537   OBJECT object;
00538 
00539   /* Setup a fake object to pass the drawing routine */
00540   object.line_width = 0; /* clamped to 1 pixel in circle_path */
00541   object.path = path_copy_modify (w_current->which_object->path, 0, 0,
00542                                   w_current->second_wx,
00543                                   w_current->second_wy, w_current->which_grip);
00544 
00545   path_path (w_current, &object);
00546   g_free (object.path->sections);
00547   g_free (object.path);
00548 
00549   gschem_cairo_set_source_color (w_current, x_color_lookup (SELECT_COLOR));
00550   gschem_cairo_stroke (w_current, TYPE_SOLID, END_SQUARE, 0, -1, -1);
00551 }
00552 
00553 
00563 static void draw_control_lines (GSCHEM_TOPLEVEL *w_current,
00564                                 OBJECT *o_current)
00565 {
00566   TOPLEVEL *toplevel = w_current->toplevel;
00567   int i;
00568   int next_x, next_y;
00569   int last_x = 0, last_y = 0;
00570   PATH_SECTION *section;
00571   COLOR *color;
00572 
00573   if (toplevel->override_color != -1 ) {
00574     /* override : use the override_color instead */
00575     color = x_color_lookup (toplevel->override_color);
00576   } else {
00577     /* use the normal selection color */
00578     color = x_color_lookup_dark (SELECT_COLOR);
00579   }
00580 
00581   /* set the color for the grip */
00582   gschem_cairo_set_source_color (w_current, color);
00583 
00584   for (i = 0; i <  o_current->path->num_sections; i++) {
00585     section = &o_current->path->sections[i];
00586 
00587     if (section->code != PATH_END) {
00588       next_x = section->x3;
00589       next_y = section->y3;
00590     }
00591 
00592     switch (section->code) {
00593     case PATH_CURVETO:
00594       /* Two control point grips */
00595       gschem_cairo_line (w_current, END_NONE, 0,
00596                          last_x, last_y, section->x1, section->y1);
00597       gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00598 
00599       gschem_cairo_line (w_current, END_NONE, 0,
00600                          next_x, next_y, section->x2, section->y2);
00601       gschem_cairo_stroke (w_current, TYPE_SOLID, END_NONE, 0, -1, -1);
00602 
00603       /* Fall through */
00604     case PATH_MOVETO:
00605     case PATH_MOVETO_OPEN:
00606     case PATH_LINETO:
00607       last_x = next_x;
00608       last_y = next_y;
00609       break;
00610     case PATH_END:
00611       break;
00612     }
00613   }
00614 }
00615 
00616 
00626 void o_path_draw_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
00627 {
00628   PATH_SECTION *section;
00629   int i;
00630 
00631   if (w_current->draw_grips == FALSE)
00632     return;
00633 
00634   draw_control_lines (w_current, o_current);
00635 
00636   for (i = 0; i <  o_current->path->num_sections; i++) {
00637     section = &o_current->path->sections[i];
00638 
00639     switch (section->code) {
00640     case PATH_CURVETO:
00641       /* Two control point grips */
00642       o_grips_draw (w_current, section->x1, section->y1);
00643       o_grips_draw (w_current, section->x2, section->y2);
00644       /* Fall through */
00645     case PATH_MOVETO:
00646     case PATH_MOVETO_OPEN:
00647     case PATH_LINETO:
00648       /* Destination point grip */
00649       o_grips_draw (w_current, section->x3, section->y3);
00650       break;
00651     case PATH_END:
00652       break;
00653     }
00654   }
00655 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines