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 #include <math.h> 00022 #include <string.h> 00023 #include <libgeda_priv.h> 00024 00025 typedef struct st_sweep_event SWEEP_EVENT; 00026 typedef struct st_sweep_status SWEEP_STATUS; 00027 00028 struct st_sweep_status { 00029 gint x; /* current x coordinate */ 00030 gint y1; /* ending y coordinate */ 00031 gdouble m1; /* inverse slope: y/x */ 00032 gdouble b1; /* x intercept */ 00033 }; 00034 00035 struct st_sweep_event { 00036 gint y0; /* starting y coordinate */ 00037 SWEEP_STATUS status; 00038 }; 00039 00040 static gint calculate_initial_sweep(gint pitch, gint min_y, gint max_y); 00041 static gint compare_events(gconstpointer a, gconstpointer b); 00042 static gint compare_status(gconstpointer a, gconstpointer b); 00043 00056 static gint calculate_initial_sweep(gint pitch, gint min_y, gint max_y) 00057 { 00058 gint delta = max_y - min_y; 00059 00060 return min_y + ((delta - ((delta - pitch) / pitch * pitch)) / 2); 00061 } 00062 00074 static gint compare_events(gconstpointer a, gconstpointer b) 00075 { 00076 SWEEP_EVENT *event_a = (SWEEP_EVENT*) a; 00077 SWEEP_EVENT *event_b = (SWEEP_EVENT*) b; 00078 00079 return (event_a->y0 - event_b->y0); 00080 } 00081 00093 static gint compare_status(gconstpointer a, gconstpointer b) 00094 { 00095 SWEEP_STATUS *status_a = (SWEEP_STATUS*) a; 00096 SWEEP_STATUS *status_b = (SWEEP_STATUS*) b; 00097 00098 return (status_b->x - status_a->x); 00099 } 00100 00115 void m_hatch_box(BOX *box, gint angle, gint pitch, GArray *lines) 00116 { 00117 GArray *corners; 00118 sPOINT point; 00119 00120 g_return_if_fail(box!=NULL); 00121 g_return_if_fail(lines!=NULL); 00122 00123 corners = g_array_sized_new(FALSE, FALSE, sizeof(sPOINT), 4); 00124 00125 point.x = box->upper_x; 00126 point.y = box->upper_y; 00127 g_array_append_val(corners, point); 00128 00129 point.x = box->lower_x; 00130 point.y = box->upper_y; 00131 g_array_append_val(corners, point); 00132 00133 point.x = box->lower_x; 00134 point.y = box->lower_y; 00135 g_array_append_val(corners, point); 00136 00137 point.x = box->upper_x; 00138 point.y = box->lower_y; 00139 g_array_append_val(corners, point); 00140 00141 m_hatch_polygon(corners, angle, pitch, lines); 00142 00143 g_array_free(corners, TRUE); 00144 } 00145 00160 void m_hatch_circle(CIRCLE *circle, gint angle, gint pitch, GArray *lines) 00161 { 00162 gint radius; 00163 gint sweep_y; 00164 TRANSFORM transform; 00165 00166 g_return_if_fail(circle!=NULL); 00167 g_return_if_fail(lines!=NULL); 00168 00169 m_transform_init(&transform); 00170 m_transform_rotate(&transform, angle); 00171 m_transform_scale(&transform, 0.01); 00172 m_transform_translate(&transform, circle->center_x, circle->center_y ); 00173 00174 radius = 100 * circle->radius; 00175 sweep_y = calculate_initial_sweep(100 * pitch, -radius, radius); 00176 00177 while ( sweep_y < radius ) { 00178 LINE line; 00179 gint x = round(sqrt(pow(radius,2) - pow(sweep_y,2))); 00180 00181 line.x[0] = -x; 00182 line.y[0] = sweep_y; 00183 line.x[1] = x; 00184 line.y[1] = sweep_y; 00185 00186 m_transform_line(&transform, &line); 00187 g_array_append_val(lines, line); 00188 00189 sweep_y += 100 * pitch; 00190 } 00191 } 00192 00207 void m_hatch_path (PATH *path, gint angle, gint pitch, GArray *lines) 00208 { 00209 GArray *points; 00210 00211 g_return_if_fail (path != NULL); 00212 g_return_if_fail (lines != NULL); 00213 00214 points = g_array_new (FALSE, FALSE, sizeof (sPOINT)); 00215 00216 s_path_to_polygon (path, points); 00217 m_hatch_polygon (points, angle, pitch, lines); 00218 00219 g_array_free (points, TRUE); 00220 } 00221 00237 void m_hatch_polygon(GArray *points, gint angle, gint pitch, GArray *lines) 00238 { 00239 BOUNDS bounds; 00240 GArray *events; 00241 TRANSFORM inverse; 00242 GArray *points2; 00243 GArray *status; 00244 gint sweep_y; 00245 TRANSFORM transform; 00246 00247 g_return_if_fail(points!=NULL); 00248 g_return_if_fail(pitch>0); 00249 g_return_if_fail(lines!=NULL); 00250 00251 events = g_array_new(FALSE, FALSE, sizeof(SWEEP_EVENT)); 00252 points2 = g_array_sized_new(FALSE, FALSE, sizeof(sPOINT), points->len); 00253 status = g_array_new(FALSE, FALSE, sizeof(SWEEP_STATUS)); 00254 00255 m_transform_init(&transform); 00256 m_transform_scale(&transform, 10); 00257 m_transform_rotate(&transform, -angle); 00258 m_transform_invert(&transform, &inverse); 00259 00260 g_array_append_vals(points2, points->data, points->len); 00261 m_transform_points(&transform, points2); 00262 00263 /* build list of sweep events */ 00264 if ( points2->len > 1 ) { 00265 gint index; 00266 sPOINT *p0 = &g_array_index(points2, sPOINT, points2->len-1); 00267 for (index=0; index<points2->len; index++) { 00268 sPOINT *p1 = &g_array_index(points2, sPOINT, index); 00269 if ( p0->y != p1->y ) { 00270 SWEEP_EVENT event; 00271 event.y0 = min(p0->y, p1->y); 00272 event.status.y1 = max(p0->y, p1->y); 00273 event.status.m1 = (gdouble)( p1->x - p0->x ) / (gdouble)( p1->y - p0->y ); 00274 event.status.b1 = p0->x - event.status.m1 * p0->y; 00275 g_array_append_val(events, event); 00276 } 00277 p0 = p1; 00278 } 00279 } 00280 00281 /* sort sweep events in ascending order by starting y coordinate */ 00282 g_array_sort(events, compare_events); 00283 00284 m_bounds_of_points(&bounds, (sPOINT*)points2->data, points2->len); 00285 sweep_y = calculate_initial_sweep(10 * pitch, bounds.min_y, bounds.max_y); 00286 00287 while ( events->len > 0 || status->len > 0 ) { 00288 gint index; 00289 00290 /* add new segments that intersect the sweep line */ 00291 index = 0; 00292 while ( index < events->len ) { 00293 SWEEP_EVENT *event = &g_array_index(events, SWEEP_EVENT, index); 00294 if ( sweep_y >= event->y0 ) { 00295 SWEEP_STATUS st = event->status; 00296 g_array_append_val(status, st); 00297 g_array_remove_index(events, index); 00298 } else { 00299 index++; 00300 } 00301 } 00302 00303 /* remove status no longer intersecting sweep line */ 00304 index = status->len; 00305 while ( index-- > 0 ) { 00306 if ( sweep_y >= g_array_index(status, SWEEP_STATUS, index).y1 ) { 00307 g_array_remove_index_fast(status, index); 00308 } 00309 } 00310 00311 /* (re)calculate x coordinates at sweep line */ 00312 for (index=0; index<status->len; index++) { 00313 SWEEP_STATUS *st = &g_array_index(status, SWEEP_STATUS, index); 00314 st->x = st->m1 * sweep_y + st->b1; 00315 } 00316 00317 /* sort the sweep status in ascending order by x coordinate */ 00318 g_array_sort(status, compare_status); 00319 00320 /* draw hatch segments */ 00321 index = 0; 00322 while ( index+1 < status->len ) { 00323 LINE line; 00324 line.x[0] = g_array_index(status, SWEEP_STATUS, index ).x; 00325 line.y[0] = sweep_y; 00326 line.x[1] = g_array_index(status, SWEEP_STATUS, index+1 ).x; 00327 line.y[1] = sweep_y; 00328 m_transform_line(&inverse, &line); 00329 g_array_append_val(lines, line); 00330 index += 2; 00331 } 00332 00333 sweep_y += 10 * pitch; 00334 } 00335 00336 g_array_free(events, TRUE); 00337 g_array_free(points2, TRUE); 00338 g_array_free(status, TRUE); 00339 } 00340