1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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
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 */
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
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
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
)
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
)
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
) {
179 gint x
= round(sqrt(pow(radius
,2) - pow(sweep_y
,2)));
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
)
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
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
)
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 ) {
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
) {
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
);
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 ) {
290 /* add new segments that intersect the sweep line */
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
);
303 /* remove status no longer intersecting sweep line */
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 */
322 while ( index
+1 < status
->len
) {
324 line
.x
[0] = g_array_index(status
, SWEEP_STATUS
, index
).x
;
326 line
.x
[1] = g_array_index(status
, SWEEP_STATUS
, index
+1 ).x
;
328 m_transform_line(&inverse
, &line
);
329 g_array_append_val(lines
, line
);
333 sweep_y
+= 10 * pitch
;
336 g_array_free(events
, TRUE
);
337 g_array_free(points2
, TRUE
);
338 g_array_free(status
, TRUE
);