Bump gEDA version
[geda-gaf.git] / libgeda / src / m_hatch.c
blob1a041419139ee425f7d4966f25b31854a6759b32
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
21 #include <math.h>
22 #include <string.h>
23 #include <libgeda_priv.h>
25 typedef struct st_sweep_event SWEEP_EVENT;
26 typedef struct st_sweep_status SWEEP_STATUS;
28 struct st_sweep_status {
29 gint x; /* current x coordinate */
30 gint y1; /* ending y coordinate */
31 gdouble m1; /* inverse slope: y/x */
32 gdouble b1; /* x intercept */
35 struct st_sweep_event {
36 gint y0; /* starting y coordinate */
37 SWEEP_STATUS status;
40 static gint calculate_initial_sweep(gint pitch, gint min_y, gint max_y);
41 static gint compare_events(gconstpointer a, gconstpointer b);
42 static gint compare_status(gconstpointer a, gconstpointer b);
44 /*! \brief Calculate the initial y cooridinate of the hatch sweep line
46 * This function centers the hatch lines across the extents of the shape being
47 * hatched. This caclulation provides symmetrical hatch lines inside
48 * symmetrical shapes, such as circles and squares. This mechanism may not
49 * provide as nice of an appearance in asymmetrical shapes.
51 * \param pitch [in] The perpendicular distance between hatch lines.
52 * \param min_y [in] The minimum y coordinate of the object being hatched.
53 * \param max_y [in] The maximum y coordinate of the object being hatched.
54 * \return The initital y coordinate of the sweep line.
56 static gint calculate_initial_sweep(gint pitch, gint min_y, gint max_y)
58 gint delta = max_y - min_y;
60 return min_y + ((delta - ((delta - pitch) / pitch * pitch)) / 2);
63 /*! \brief Compares two sweep events
65 * Compares two sweep events for ordering the event queue. The prototype
66 * and behavior are consistant with GCompareFunc.
68 * \param a [in] The first sweep event.
69 * \param b [in] The second sweep event.
70 * \return A negative value if the first is less than the second, zero if the
71 * first equals the second, and a positive value if the first is greater than
72 * the second.
74 static gint compare_events(gconstpointer a, gconstpointer b)
76 SWEEP_EVENT *event_a = (SWEEP_EVENT*) a;
77 SWEEP_EVENT *event_b = (SWEEP_EVENT*) b;
79 return (event_a->y0 - event_b->y0);
82 /*! \brief Compares two sweep status structs
84 * Compares two sweep status for ordering the sweep status. The prototype
85 * and behavior are consistant with GCompareFunc.
87 * \param a [in] The first sweep status.
88 * \param b [in] The second sweep status.
89 * \return A negative value if the first is less than the second, zero if the
90 * first equals the second, and a positive value if the first is greater than
91 * the second.
93 static gint compare_status(gconstpointer a, gconstpointer b)
95 SWEEP_STATUS *status_a = (SWEEP_STATUS*) a;
96 SWEEP_STATUS *status_b = (SWEEP_STATUS*) b;
98 return (status_b->x - status_a->x);
101 /*! \brief Calculates line segments to hatch a box shape
103 * This function appends new line segments to the lines GArray. For creating
104 * a hatch pattern, the GArray must be cleared before calling this function.
105 * For creating cross hatch patterns, this function can be called multiple
106 * times with a different angle or pitch while passing the same lines GArray.
108 * \param box [in] The box shape to hatch.
109 * \param angle [in] The angle of the hatch lines with respect to the x axis.
110 * \param pitch [in] The distance between hatch lines
111 * \param lines [inout] A GArray of LINE to contain the new hatch line
112 * segments. This function appends new line segments to the GArray and leaves
113 * existing GArray contents unchanged.
115 void m_hatch_box(BOX *box, gint angle, gint pitch, GArray *lines)
117 GArray *corners;
118 sPOINT point;
120 g_return_if_fail(box!=NULL);
121 g_return_if_fail(lines!=NULL);
123 corners = g_array_sized_new(FALSE, FALSE, sizeof(sPOINT), 4);
125 point.x = box->upper_x;
126 point.y = box->upper_y;
127 g_array_append_val(corners, point);
129 point.x = box->lower_x;
130 point.y = box->upper_y;
131 g_array_append_val(corners, point);
133 point.x = box->lower_x;
134 point.y = box->lower_y;
135 g_array_append_val(corners, point);
137 point.x = box->upper_x;
138 point.y = box->lower_y;
139 g_array_append_val(corners, point);
141 m_hatch_polygon(corners, angle, pitch, lines);
143 g_array_free(corners, TRUE);
146 /*! \brief Calculates line segments to hatch a circle.
148 * This function appends new line segments to the lines GArray. For creating
149 * a hatch pattern, the GArray must be cleared before calling this function.
150 * For creating cross hatch patterns, this function can be called multiple
151 * times with a different angle or pitch while passing the same lines GArray.
153 * \param circle [in] The circle shape to hatch.
154 * \param angle [in] The angle of the hatch lines with respect to the x axis.
155 * \param pitch [in] The distance between hatch lines
156 * \param lines [inout] A GArray of LINE to contain the new hatch line
157 * segments. This function appends new line segments to the GArray and leaves
158 * existing GArray contents unchanged.
160 void m_hatch_circle(CIRCLE *circle, gint angle, gint pitch, GArray *lines)
162 gint radius;
163 gint sweep_y;
164 TRANSFORM transform;
166 g_return_if_fail(circle!=NULL);
167 g_return_if_fail(lines!=NULL);
169 m_transform_init(&transform);
170 m_transform_rotate(&transform, angle);
171 m_transform_scale(&transform, 0.01);
172 m_transform_translate(&transform, circle->center_x, circle->center_y );
174 radius = 100 * circle->radius;
175 sweep_y = calculate_initial_sweep(100 * pitch, -radius, radius);
177 while ( sweep_y < radius ) {
178 LINE line;
179 gint x = round(sqrt(pow(radius,2) - pow(sweep_y,2)));
181 line.x[0] = -x;
182 line.y[0] = sweep_y;
183 line.x[1] = x;
184 line.y[1] = sweep_y;
186 m_transform_line(&transform, &line);
187 g_array_append_val(lines, line);
189 sweep_y += 100 * pitch;
193 /*! \brief Calculates line segments to hatch a path.
195 * This function appends new line segments to the lines GArray. For creating
196 * a hatch pattern, the GArray must be cleared before calling this function.
197 * For creating cross hatch patterns, this function can be called multiple
198 * times with a different angle or pitch while passing the same lines GArray.
200 * \param path [in] The path shape to hatch.
201 * \param angle [in] The angle of the hatch lines with respect to the x axis.
202 * \param pitch [in] The distance between hatch lines
203 * \param lines [inout] A GArray of LINE to contain the new hatch line
204 * segments. This function appends new line segments to the GArray and leaves
205 * existing GArray contents unchanged.
207 void m_hatch_path (PATH *path, gint angle, gint pitch, GArray *lines)
209 GArray *points;
211 g_return_if_fail (path != NULL);
212 g_return_if_fail (lines != NULL);
214 points = g_array_new (FALSE, FALSE, sizeof (sPOINT));
216 s_path_to_polygon (path, points);
217 m_hatch_polygon (points, angle, pitch, lines);
219 g_array_free (points, TRUE);
222 /*! \brief Calculates line segments to hatch an arbitrary polygon.
224 * This function appends new line segments to the lines GArray. For creating
225 * a hatch pattern, the GArray must be cleared before calling this function.
226 * For creating cross hatch patterns, this function can be called multiple
227 * times with a different angle or pitch while passing the same lines GArray.
229 * \param points [in] The endpoints of the arbitrary closed polygon to hatch.
230 * \param angle [in] The angle of the hatch lines with respect to the x axis.
231 * \param pitch [in] The distance between hatch lines. This value must be
232 * greater than zero.
233 * \param lines [inout] A GArray of LINE to contain the new hatch line
234 * segments. This function appends new line segments to the GArray and leaves
235 * existing GArray contents unchanged.
237 void m_hatch_polygon(GArray *points, gint angle, gint pitch, GArray *lines)
239 BOUNDS bounds;
240 GArray *events;
241 TRANSFORM inverse;
242 GArray *points2;
243 GArray *status;
244 gint sweep_y;
245 TRANSFORM transform;
247 g_return_if_fail(points!=NULL);
248 g_return_if_fail(pitch>0);
249 g_return_if_fail(lines!=NULL);
251 events = g_array_new(FALSE, FALSE, sizeof(SWEEP_EVENT));
252 points2 = g_array_sized_new(FALSE, FALSE, sizeof(sPOINT), points->len);
253 status = g_array_new(FALSE, FALSE, sizeof(SWEEP_STATUS));
255 m_transform_init(&transform);
256 m_transform_scale(&transform, 10);
257 m_transform_rotate(&transform, -angle);
258 m_transform_invert(&transform, &inverse);
260 g_array_append_vals(points2, points->data, points->len);
261 m_transform_points(&transform, points2);
263 /* build list of sweep events */
264 if ( points2->len > 1 ) {
265 gint index;
266 sPOINT *p0 = &g_array_index(points2, sPOINT, points2->len-1);
267 for (index=0; index<points2->len; index++) {
268 sPOINT *p1 = &g_array_index(points2, sPOINT, index);
269 if ( p0->y != p1->y ) {
270 SWEEP_EVENT event;
271 event.y0 = min(p0->y, p1->y);
272 event.status.y1 = max(p0->y, p1->y);
273 event.status.m1 = (gdouble)( p1->x - p0->x ) / (gdouble)( p1->y - p0->y );
274 event.status.b1 = p0->x - event.status.m1 * p0->y;
275 g_array_append_val(events, event);
277 p0 = p1;
281 /* sort sweep events in ascending order by starting y coordinate */
282 g_array_sort(events, compare_events);
284 m_bounds_of_points(&bounds, (sPOINT*)points2->data, points2->len);
285 sweep_y = calculate_initial_sweep(10 * pitch, bounds.min_y, bounds.max_y);
287 while ( events->len > 0 || status->len > 0 ) {
288 gint index;
290 /* add new segments that intersect the sweep line */
291 index = 0;
292 while ( index < events->len ) {
293 SWEEP_EVENT *event = &g_array_index(events, SWEEP_EVENT, index);
294 if ( sweep_y >= event->y0 ) {
295 SWEEP_STATUS st = event->status;
296 g_array_append_val(status, st);
297 g_array_remove_index(events, index);
298 } else {
299 index++;
303 /* remove status no longer intersecting sweep line */
304 index = status->len;
305 while ( index-- > 0 ) {
306 if ( sweep_y >= g_array_index(status, SWEEP_STATUS, index).y1 ) {
307 g_array_remove_index_fast(status, index);
311 /* (re)calculate x coordinates at sweep line */
312 for (index=0; index<status->len; index++) {
313 SWEEP_STATUS *st = &g_array_index(status, SWEEP_STATUS, index);
314 st->x = st->m1 * sweep_y + st->b1;
317 /* sort the sweep status in ascending order by x coordinate */
318 g_array_sort(status, compare_status);
320 /* draw hatch segments */
321 index = 0;
322 while ( index+1 < status->len ) {
323 LINE line;
324 line.x[0] = g_array_index(status, SWEEP_STATUS, index ).x;
325 line.y[0] = sweep_y;
326 line.x[1] = g_array_index(status, SWEEP_STATUS, index+1 ).x;
327 line.y[1] = sweep_y;
328 m_transform_line(&inverse, &line);
329 g_array_append_val(lines, line);
330 index += 2;
333 sweep_y += 10 * pitch;
336 g_array_free(events, TRUE);
337 g_array_free(points2, TRUE);
338 g_array_free(status, TRUE);