libgeda
|
00001 /* gEDA - GPL Electronic Design Automation 00002 * libgeda - gEDA's library 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 <string.h> 00025 00026 #include "libgeda_priv.h" 00027 00028 #ifdef HAVE_LIBDMALLOC 00029 #include <dmalloc.h> 00030 #endif 00031 00032 00033 typedef void (*DRAW_FUNC) (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00034 int line_width, int length, int space, 00035 int origin_x, int origin_y); 00036 00037 00038 typedef void (*FILL_FUNC) (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00039 int fill_width, 00040 int angle1, int pitch1, int angle2, int pitch2, 00041 int origin_x, int origin_y); 00042 00043 00074 OBJECT *o_path_new (TOPLEVEL *toplevel, 00075 char type, int color, const char *path_string) 00076 { 00077 OBJECT *new_node; 00078 00079 /* create the object */ 00080 new_node = s_basic_new_object (type, "path"); 00081 new_node->color = color; 00082 00083 new_node->path = s_path_parse (path_string); 00084 00085 /* path type and filling initialized to default */ 00086 o_set_line_options (toplevel, new_node, 00087 END_NONE, TYPE_SOLID, 0, -1, -1); 00088 o_set_fill_options (toplevel, new_node, 00089 FILLING_HOLLOW, -1, -1, -1, -1, -1); 00090 00091 /* compute bounding box */ 00092 o_path_recalc (toplevel, new_node); 00093 00094 return new_node; 00095 } 00096 00097 00109 OBJECT *o_path_copy (TOPLEVEL *toplevel, OBJECT *o_current) 00110 { 00111 OBJECT *new_obj; 00112 char *path_string; 00113 00114 path_string = s_path_string_from_path (o_current->path); 00115 new_obj = o_path_new (toplevel, OBJ_PATH, o_current->color, path_string); 00116 g_free (path_string); 00117 00118 /* copy the path type and filling options */ 00119 o_set_line_options (toplevel, new_obj, o_current->line_end, 00120 o_current->line_type, o_current->line_width, 00121 o_current->line_length, o_current->line_space); 00122 o_set_fill_options (toplevel, new_obj, 00123 o_current->fill_type, o_current->fill_width, 00124 o_current->fill_pitch1, o_current->fill_angle1, 00125 o_current->fill_pitch2, o_current->fill_angle2); 00126 00127 /* calc the bounding box */ 00128 o_path_recalc (toplevel, o_current); 00129 00130 /* return the new tail of the object list */ 00131 return new_obj; 00132 } 00133 00134 00155 OBJECT *o_path_read (TOPLEVEL *toplevel, 00156 const char *first_line, TextBuffer *tb, 00157 unsigned int release_ver, unsigned int fileformat_ver, GError **err) 00158 { 00159 OBJECT *new_obj; 00160 char type; 00161 int color; 00162 int line_width, line_space, line_length; 00163 int line_end; 00164 int line_type; 00165 int fill_type, fill_width, angle1, pitch1, angle2, pitch2; 00166 int num_lines = 0; 00167 int i; 00168 char *string; 00169 GString *pathstr; 00170 00171 /* 00172 * The current path format to describe a line is a space separated 00173 * list of characters and numbers in plain ASCII on a single path. 00174 * The meaning of each item is described in the file format documentation. 00175 */ 00176 /* Allocate enough space */ 00177 if (sscanf (first_line, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n", 00178 &type, &color, &line_width, &line_end, &line_type, 00179 &line_length, &line_space, &fill_type, &fill_width, &angle1, 00180 &pitch1, &angle2, &pitch2, &num_lines) != 14) { 00181 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse path object")); 00182 return NULL; 00183 } 00184 00185 /* 00186 * Checks if the required color is valid. 00187 */ 00188 if (color < 0 || color > MAX_COLORS) { 00189 s_log_message (_("Found an invalid color [ %s ]\n"), first_line); 00190 s_log_message (_("Setting color to default color\n")); 00191 color = DEFAULT_COLOR; 00192 } 00193 00194 /* 00195 * A path is internally described by its two ends. A new object is 00196 * allocated, initialized and added to the list of objects. Its path 00197 * type is set according to the values of the fields on the path. 00198 */ 00199 00200 pathstr = g_string_new (""); 00201 for (i = 0; i < num_lines; i++) { 00202 const gchar *line; 00203 00204 line = s_textbuffer_next_line (tb); 00205 00206 if (line == NULL) { 00207 g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE, _("Unexpected end-of-file when reading path")); 00208 return NULL; 00209 } 00210 00211 pathstr = g_string_append (pathstr, line); 00212 } 00213 00214 /* retrieve the character string from the GString */ 00215 string = g_string_free (pathstr, FALSE); 00216 string = remove_last_nl (string); 00217 00218 /* create a new path */ 00219 new_obj = o_path_new (toplevel, type, color, string); 00220 g_free (string); 00221 00222 /* set its line options */ 00223 o_set_line_options (toplevel, new_obj, 00224 line_end, line_type, line_width, line_length, line_space); 00225 /* set its fill options */ 00226 o_set_fill_options (toplevel, new_obj, 00227 fill_type, fill_width, pitch1, angle1, pitch2, angle2); 00228 00229 return new_obj; 00230 } 00231 00232 00246 char *o_path_save (TOPLEVEL *toplevel, OBJECT *object) 00247 { 00248 int line_width, line_space, line_length; 00249 char *buf; 00250 int num_lines; 00251 OBJECT_END line_end; 00252 OBJECT_TYPE line_type; 00253 OBJECT_FILLING fill_type; 00254 int fill_width, angle1, pitch1, angle2, pitch2; 00255 char *path_string; 00256 00257 /* description of the line type */ 00258 line_width = object->line_width; 00259 line_end = object->line_end; 00260 line_type = object->line_type; 00261 line_length = object->line_length; 00262 line_space = object->line_space; 00263 00264 /* filling parameters */ 00265 fill_type = object->fill_type; 00266 fill_width = object->fill_width; 00267 angle1 = object->fill_angle1; 00268 pitch1 = object->fill_pitch1; 00269 angle2 = object->fill_angle2; 00270 pitch2 = object->fill_pitch2; 00271 00272 path_string = s_path_string_from_path (object->path); 00273 num_lines = o_text_num_lines (path_string); 00274 buf = g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n%s", 00275 object->type, object->color, line_width, line_end, 00276 line_type, line_length, line_space, fill_type, 00277 fill_width, angle1, pitch1, angle2, pitch2, 00278 num_lines, path_string); 00279 g_free (path_string); 00280 00281 return buf; 00282 } 00283 00284 00300 void o_path_modify (TOPLEVEL *toplevel, OBJECT *object, 00301 int x, int y, int whichone) 00302 { 00303 int i; 00304 int grip_no = 0; 00305 PATH_SECTION *section; 00306 00307 o_emit_pre_change_notify (toplevel, object); 00308 00309 for (i = 0; i < object->path->num_sections; i++) { 00310 section = &object->path->sections[i]; 00311 00312 switch (section->code) { 00313 case PATH_CURVETO: 00314 /* Two control point grips */ 00315 if (whichone == grip_no++) { 00316 section->x1 = x; 00317 section->y1 = y; 00318 } 00319 if (whichone == grip_no++) { 00320 section->x2 = x; 00321 section->y2 = y; 00322 } 00323 /* Fall through */ 00324 case PATH_MOVETO: 00325 case PATH_MOVETO_OPEN: 00326 case PATH_LINETO: 00327 /* Destination point grip */ 00328 if (whichone == grip_no++) { 00329 section->x3 = x; 00330 section->y3 = y; 00331 } 00332 break; 00333 case PATH_END: 00334 break; 00335 } 00336 } 00337 00338 /* Update bounding box */ 00339 o_path_recalc (toplevel, object); 00340 o_emit_change_notify (toplevel, object); 00341 } 00342 00343 00354 void o_path_translate_world (TOPLEVEL *toplevel, 00355 int dx, int dy, OBJECT *object) 00356 { 00357 PATH_SECTION *section; 00358 int i; 00359 00360 for (i = 0; i < object->path->num_sections; i++) { 00361 section = &object->path->sections[i]; 00362 00363 switch (section->code) { 00364 case PATH_CURVETO: 00365 section->x1 += dx; 00366 section->y1 += dy; 00367 section->x2 += dx; 00368 section->y2 += dy; 00369 /* Fall through */ 00370 case PATH_MOVETO: 00371 case PATH_MOVETO_OPEN: 00372 case PATH_LINETO: 00373 section->x3 += dx; 00374 section->y3 += dy; 00375 break; 00376 case PATH_END: 00377 break; 00378 } 00379 } 00380 00381 /* Update bounding box */ 00382 o_path_recalc (toplevel, object); 00383 } 00384 00385 00399 void o_path_rotate_world (TOPLEVEL *toplevel, 00400 int world_centerx, int world_centery, int angle, 00401 OBJECT *object) 00402 { 00403 PATH_SECTION *section; 00404 int i; 00405 00406 for (i = 0; i < object->path->num_sections; i++) { 00407 section = &object->path->sections[i]; 00408 00409 switch (section->code) { 00410 case PATH_CURVETO: 00411 /* Two control point grips */ 00412 section->x1 -= world_centerx; section->y1 -= world_centery; 00413 section->x2 -= world_centerx; section->y2 -= world_centery; 00414 rotate_point_90 (section->x1, section->y1, angle, §ion->x1, §ion->y1); 00415 rotate_point_90 (section->x2, section->y2, angle, §ion->x2, §ion->y2); 00416 section->x1 += world_centerx; section->y1 += world_centery; 00417 section->x2 += world_centerx; section->y2 += world_centery; 00418 /* Fall through */ 00419 case PATH_MOVETO: 00420 case PATH_MOVETO_OPEN: 00421 case PATH_LINETO: 00422 /* Destination point grip */ 00423 section->x3 -= world_centerx; section->y3 -= world_centery; 00424 rotate_point_90 (section->x3, section->y3, angle, §ion->x3, §ion->y3); 00425 section->x3 += world_centerx; section->y3 += world_centery; 00426 break; 00427 case PATH_END: 00428 break; 00429 } 00430 } 00431 o_path_recalc (toplevel, object); 00432 } 00433 00434 00445 void o_path_mirror_world (TOPLEVEL *toplevel, int world_centerx, 00446 int world_centery, OBJECT *object) 00447 { 00448 PATH_SECTION *section; 00449 int i; 00450 00451 for (i = 0; i < object->path->num_sections; i++) { 00452 section = &object->path->sections[i]; 00453 00454 switch (section->code) { 00455 case PATH_CURVETO: 00456 /* Two control point grips */ 00457 section->x1 = 2 * world_centerx - section->x1; 00458 section->x2 = 2 * world_centerx - section->x2; 00459 /* Fall through */ 00460 case PATH_MOVETO: 00461 case PATH_MOVETO_OPEN: 00462 case PATH_LINETO: 00463 /* Destination point grip */ 00464 section->x3 = 2 * world_centerx - section->x3; 00465 break; 00466 case PATH_END: 00467 break; 00468 } 00469 } 00470 00471 o_path_recalc (toplevel, object); 00472 } 00473 00474 00482 void o_path_recalc (TOPLEVEL *toplevel, OBJECT *o_current) 00483 { 00484 int left = 0, right = 0, top = 0, bottom = 0; 00485 00486 g_return_if_fail (o_current->path != NULL); 00487 00488 /* Update the bounding box */ 00489 if (o_current->path->num_sections > 0) { 00490 world_get_path_bounds (toplevel, o_current, &left, &top, &right, &bottom); 00491 o_current->w_left = left; 00492 o_current->w_top = top; 00493 o_current->w_right = right; 00494 o_current->w_bottom = bottom; 00495 o_current->w_bounds_valid = TRUE; 00496 } else { 00497 o_current->w_bounds_valid = FALSE; 00498 } 00499 } 00500 00501 00518 void world_get_path_bounds (TOPLEVEL *toplevel, OBJECT *object, 00519 int *left, int *top, int *right, int *bottom) 00520 { 00521 PATH_SECTION *section; 00522 int halfwidth; 00523 int i; 00524 int found_bound = FALSE; 00525 00526 /* Find the bounds of the path region */ 00527 for (i = 0; i < object->path->num_sections; i++) { 00528 section = &object->path->sections[i]; 00529 switch (section->code) { 00530 case PATH_CURVETO: 00531 /* Bezier curves with this construction of control points will lie 00532 * within the convex hull of the control and curve end points */ 00533 *left = (found_bound) ? MIN (*left, section->x1) : section->x1; 00534 *top = (found_bound) ? MIN (*top, section->y1) : section->y1; 00535 *right = (found_bound) ? MAX (*right, section->x1) : section->x1; 00536 *bottom = (found_bound) ? MAX (*bottom, section->y1) : section->y1; 00537 found_bound = TRUE; 00538 *left = MIN (*left, section->x2); 00539 *top = MIN (*top, section->y2); 00540 *right = MAX (*right, section->x2); 00541 *bottom = MAX (*bottom, section->y2); 00542 /* Fall through */ 00543 case PATH_MOVETO: 00544 case PATH_MOVETO_OPEN: 00545 case PATH_LINETO: 00546 *left = (found_bound) ? MIN (*left, section->x3) : section->x3; 00547 *top = (found_bound) ? MIN (*top, section->y3) : section->y3; 00548 *right = (found_bound) ? MAX (*right, section->x3) : section->x3; 00549 *bottom = (found_bound) ? MAX (*bottom, section->y3) : section->y3; 00550 found_bound = TRUE; 00551 break; 00552 case PATH_END: 00553 break; 00554 } 00555 } 00556 00557 if (found_bound) { 00558 /* This isn't strictly correct, but a 1st order approximation */ 00559 halfwidth = object->line_width / 2; 00560 *left -= halfwidth; 00561 *top -= halfwidth; 00562 *right += halfwidth; 00563 *bottom += halfwidth; 00564 } 00565 } 00566 00577 gboolean o_path_get_position (TOPLEVEL *toplevel, gint *x, gint *y, 00578 OBJECT *object) 00579 { 00580 if (object->path->num_sections == 0) 00581 return FALSE; 00582 00583 *x = object->path->sections[0].x3; 00584 *y = object->path->sections[0].y3; 00585 return TRUE; 00586 } 00587 00605 static void o_path_print_solid (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00606 int line_width, int length, int space, 00607 int origin_x, int origin_y) 00608 { 00609 int i; 00610 00611 for (i = 0; i < path->num_sections; i++) { 00612 PATH_SECTION *section = &path->sections[i]; 00613 00614 if (i > 0) 00615 fprintf (fp, " "); 00616 00617 switch (section->code) { 00618 case PATH_MOVETO: 00619 fprintf (fp, "closepath"); 00620 /* Fall through */ 00621 case PATH_MOVETO_OPEN: 00622 fprintf (fp, "%i %i moveto", 00623 section->x3 - origin_x, section->y3 - origin_y); 00624 break; 00625 case PATH_CURVETO: 00626 fprintf (fp, "%i %i %i %i %i %i curveto", 00627 section->x1 - origin_x, section->y1 - origin_y, 00628 section->x2 - origin_x, section->y2 - origin_y, 00629 section->x3 - origin_x, section->y3 - origin_y); 00630 break; 00631 case PATH_LINETO: 00632 fprintf (fp, "%i %i lineto", 00633 section->x3 - origin_x, section->y3 - origin_y); 00634 break; 00635 case PATH_END: 00636 fprintf (fp, "closepath"); 00637 break; 00638 } 00639 } 00640 00641 fprintf (fp, " stroke\n"); 00642 } 00643 00644 00662 static void o_path_print_dotted (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00663 int line_width, int length, int space, 00664 int origin_x, int origin_y) 00665 { 00666 o_path_print_solid (toplevel, fp, path, line_width, 00667 length, space, origin_x, origin_y); 00668 } 00669 00670 00687 static void o_path_print_dashed (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00688 int line_width, int length, int space, 00689 int origin_x, int origin_y) 00690 { 00691 o_path_print_solid (toplevel, fp, path, line_width, 00692 length, space, origin_x, origin_y); 00693 } 00694 00695 00712 static void o_path_print_center (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00713 int line_width, int length, 00714 int space, int origin_x, int origin_y) 00715 { 00716 o_path_print_solid (toplevel, fp, path, line_width, 00717 length, space, origin_x, origin_y); 00718 } 00719 00720 00737 static void o_path_print_phantom (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00738 int line_width, int length, 00739 int space, int origin_x, int origin_y) 00740 { 00741 o_path_print_solid (toplevel, fp, path, line_width, 00742 length, space, origin_x, origin_y); 00743 } 00744 00745 00767 static void o_path_print_filled (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00768 int fill_width, 00769 int angle1, int pitch1, int angle2, int pitch2, 00770 int origin_x, int origin_y) 00771 { 00772 int i; 00773 00774 for (i = 0; i < path->num_sections; i++) { 00775 PATH_SECTION *section = &path->sections[i]; 00776 00777 if (i > 0) 00778 fprintf (fp, " "); 00779 00780 switch (section->code) { 00781 case PATH_MOVETO: 00782 fprintf (fp, "closepath"); 00783 /* Fall through */ 00784 case PATH_MOVETO_OPEN: 00785 fprintf (fp, "%i %i moveto", 00786 section->x3 - origin_x, section->y3 - origin_y); 00787 break; 00788 case PATH_CURVETO: 00789 fprintf (fp, "%i %i %i %i %i %i curveto", 00790 section->x1 - origin_x, section->y1 - origin_y, 00791 section->x2 - origin_x, section->y2 - origin_y, 00792 section->x3 - origin_x, section->y3 - origin_y); 00793 break; 00794 case PATH_LINETO: 00795 fprintf (fp, "%i %i lineto", 00796 section->x3 - origin_x, section->y3 - origin_y); 00797 break; 00798 case PATH_END: 00799 fprintf (fp, "closepath"); 00800 break; 00801 } 00802 } 00803 00804 fprintf (fp, " fill\n"); 00805 } 00806 00807 00832 static void o_path_print_hatch (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00833 int fill_width, 00834 int angle1, int pitch1, int angle2, int pitch2, 00835 int origin_x, int origin_y) 00836 { 00837 int i; 00838 GArray *lines; 00839 00840 g_return_if_fail (toplevel != NULL); 00841 g_return_if_fail (fp != NULL); 00842 00843 /* Avoid printing line widths too small */ 00844 if (fill_width <= 1) fill_width = 2; 00845 00846 lines = g_array_new (FALSE, FALSE, sizeof(LINE)); 00847 00848 m_hatch_path (path, angle1, pitch1, lines); 00849 00850 for (i=0; i < lines->len; i++) { 00851 LINE *line = &g_array_index (lines, LINE, i); 00852 00853 fprintf (fp,"%d %d %d %d %d line\n", line->x[0], line->y[0], 00854 line->x[1], line->y[1], fill_width); 00855 } 00856 00857 g_array_free (lines, TRUE); 00858 } 00859 00860 00882 static void o_path_print_mesh (TOPLEVEL *toplevel, FILE *fp, PATH *path, 00883 int fill_width, 00884 int angle1, int pitch1, int angle2, int pitch2, 00885 int origin_x, int origin_y) 00886 { 00887 o_path_print_hatch (toplevel, fp, path, fill_width, 00888 angle1, pitch1, -1, -1, origin_x, origin_y); 00889 00890 o_path_print_hatch (toplevel, fp, path, fill_width, 00891 angle2, pitch2, -1, -1, origin_x, origin_y); 00892 } 00893 00894 00907 void o_path_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current, 00908 int origin_x, int origin_y) 00909 { 00910 int line_width, length, space; 00911 int fill_width, angle1, pitch1, angle2, pitch2; 00912 DRAW_FUNC outl_func = NULL; 00913 FILL_FUNC fill_func = NULL; 00914 00929 line_width = o_current->line_width; 00930 00931 if (line_width <= 2) { 00932 if (toplevel->line_style == THICK) { 00933 line_width = LINE_WIDTH; 00934 } else { 00935 line_width=2; 00936 } 00937 } 00938 length = o_current->line_length; 00939 space = o_current->line_space; 00940 00941 switch(o_current->line_type) { 00942 case TYPE_SOLID: 00943 length = -1; space = -1; 00944 outl_func = o_path_print_solid; 00945 break; 00946 00947 case TYPE_DOTTED: 00948 length = -1; 00949 outl_func = o_path_print_dotted; 00950 break; 00951 00952 case TYPE_DASHED: 00953 outl_func = o_path_print_dashed; 00954 break; 00955 00956 case TYPE_CENTER: 00957 outl_func = o_path_print_center; 00958 break; 00959 00960 case TYPE_PHANTOM: 00961 outl_func = o_path_print_phantom; 00962 break; 00963 00964 case TYPE_ERASE: 00965 /* Unused for now, print it solid */ 00966 length = -1; space = -1; 00967 outl_func = o_path_print_solid; 00968 break; 00969 } 00970 00971 if((length == 0) || (space == 0)) { 00972 length = -1; space = -1; 00973 outl_func = o_path_print_solid; 00974 } 00975 00976 f_print_set_color (toplevel, fp, o_current->color); 00977 00978 f_print_set_line_width (fp, line_width); 00979 00980 (*outl_func) (toplevel, fp, o_current->path, line_width, 00981 length, space, origin_x, origin_y); 00982 00994 if(o_current->fill_type != FILLING_HOLLOW) { 00995 fill_width = o_current->fill_width; 00996 angle1 = o_current->fill_angle1; 00997 pitch1 = o_current->fill_pitch1; 00998 angle2 = o_current->fill_angle2; 00999 pitch2 = o_current->fill_pitch2; 01000 01001 switch(o_current->fill_type) { 01002 case FILLING_FILL: 01003 angle1 = -1; pitch1 = 1; 01004 angle2 = -1; pitch2 = 1; 01005 fill_width = -1; 01006 fill_func = o_path_print_filled; 01007 break; 01008 01009 case FILLING_MESH: 01010 fill_func = o_path_print_mesh; 01011 break; 01012 01013 case FILLING_HATCH: 01014 angle2 = -1; pitch2 = 1; 01015 fill_func = o_path_print_hatch; 01016 break; 01017 01018 case FILLING_VOID: 01019 /* Unused for now, print it filled */ 01020 angle1 = -1; pitch1 = 1; 01021 angle2 = -1; pitch2 = 1; 01022 fill_width = -1; 01023 fill_func = o_path_print_filled; 01024 break; 01025 01026 case FILLING_HOLLOW: 01027 /* nop */ 01028 break; 01029 01030 } 01031 01032 if((pitch1 <= 0) || (pitch2 <= 0)) { 01033 angle1 = -1; pitch1 = 1; 01034 angle2 = -1; pitch2 = 1; 01035 fill_func = o_path_print_filled; 01036 } 01037 01038 (*fill_func) (toplevel, fp, 01039 o_current->path, fill_width, 01040 angle1, pitch1, angle2, pitch2, origin_x, origin_y); 01041 } 01042 } 01043 01044 01055 double o_path_shortest_distance (OBJECT *object, int x, int y, int force_solid) 01056 { 01057 int solid; 01058 01059 solid = force_solid || object->fill_type != FILLING_HOLLOW; 01060 01061 return s_path_shortest_distance (object->path, x, y, solid); 01062 } 01063