gschem
|
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 }