1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 * SECTION:adg-ruled-fill
23 * @short_description: A style composed of evenly spaced lines.
25 * The spacing between the lines could be changed using the
26 * adg_ruled_fill_set_spacing() method. The angle of the lines should
27 * be changed with adg_ruled_fill_set_angle().
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
38 #include "adg-ruled-fill.h"
39 #include "adg-ruled-fill-private.h"
43 #define PARENT_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
44 #define PARENT_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
54 static void get_property (GObject
*object
,
58 static void set_property (GObject
*object
,
62 static void apply (AdgStyle
*style
,
64 static void set_extents (AdgFillStyle
*fill_style
,
65 const CpmlExtents
*extents
);
66 static gboolean
set_spacing (AdgRuledFill
*ruled_fill
,
68 static gboolean
set_angle (AdgRuledFill
*ruled_fill
,
70 static cairo_pattern_t
*create_pattern (AdgRuledFill
*ruled_fill
,
72 static void draw_lines (const CpmlPair
*spacing
,
77 G_DEFINE_TYPE(AdgRuledFill
, adg_ruled_fill
, ADG_TYPE_FILL_STYLE
);
81 adg_ruled_fill_class_init(AdgRuledFillClass
*klass
)
83 GObjectClass
*gobject_class
;
84 AdgStyleClass
*style_class
;
85 AdgFillStyleClass
*fill_style_class
;
88 gobject_class
= (GObjectClass
*) klass
;
89 style_class
= (AdgStyleClass
*) klass
;
90 fill_style_class
= (AdgFillStyleClass
*) klass
;
92 g_type_class_add_private(klass
, sizeof(AdgRuledFillPrivate
));
94 gobject_class
->get_property
= get_property
;
95 gobject_class
->set_property
= set_property
;
97 style_class
->apply
= apply
;
99 fill_style_class
->set_extents
= set_extents
;
101 param
= g_param_spec_double("spacing",
103 P_("The spacing in global spaces between the lines"),
106 g_object_class_install_property(gobject_class
, PROP_SPACING
, param
);
108 param
= g_param_spec_double("angle",
110 P_("The angle (in radians) of the lines"),
113 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
117 adg_ruled_fill_init(AdgRuledFill
*ruled_fill
)
119 AdgRuledFillPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill
,
121 AdgRuledFillPrivate
);
123 data
->angle
= G_PI_4
;
126 ruled_fill
->data
= data
;
130 get_property(GObject
*object
,
131 guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
133 AdgRuledFillPrivate
*data
= ((AdgRuledFill
*) object
)->data
;
137 g_value_set_double(value
, data
->spacing
);
140 g_value_set_double(value
, data
->angle
);
143 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
149 set_property(GObject
*object
,
150 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
152 AdgRuledFill
*ruled_fill
= (AdgRuledFill
*) object
;
156 set_spacing(ruled_fill
, g_value_get_double(value
));
159 set_angle(ruled_fill
, g_value_get_double(value
));
162 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
169 * adg_ruled_fill_get_spacing:
170 * @ruled_fill: an #AdgRuledFill
172 * Gets the current spacing of @ruled_fill.
174 * Returns: the spacing (in global space)
177 adg_ruled_fill_get_spacing(AdgRuledFill
*ruled_fill
)
179 AdgRuledFillPrivate
*data
;
181 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
183 data
= ruled_fill
->data
;
185 return data
->spacing
;
189 * adg_ruled_fill_set_spacing:
190 * @ruled_fill: an #AdgRuledFill
191 * @spacing: the new spacing
193 * Sets a new spacing on @ruled_fill.
196 adg_ruled_fill_set_spacing(AdgRuledFill
*ruled_fill
, gdouble spacing
)
198 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
200 if (set_spacing(ruled_fill
, spacing
))
201 g_object_notify((GObject
*) ruled_fill
, "spacing");
205 * adg_ruled_fill_get_angle:
206 * @ruled_fill: an #AdgRuledFill
208 * Gets the current angle of @ruled_fill.
210 * Returns: the angle (in radians)
213 adg_ruled_fill_get_angle(AdgRuledFill
*ruled_fill
)
215 AdgRuledFillPrivate
*data
;
217 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
219 data
= ruled_fill
->data
;
225 * adg_ruled_fill_set_angle:
226 * @ruled_fill: an #AdgRuledFill
227 * @angle: the new angle
229 * Sets a new angle on @ruled_fill.
232 adg_ruled_fill_set_angle(AdgRuledFill
*ruled_fill
, gdouble angle
)
234 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
236 if (set_angle(ruled_fill
, angle
))
237 g_object_notify((GObject
*) ruled_fill
, "angle");
242 apply(AdgStyle
*style
, cairo_t
*cr
)
244 AdgRuledFill
*ruled_fill
;
245 AdgFillStyle
*fill_style
;
246 cairo_pattern_t
*pattern
;
248 cairo_matrix_t matrix
;
250 ruled_fill
= (AdgRuledFill
*) style
;
251 fill_style
= (AdgFillStyle
*) style
;
252 pattern
= adg_fill_style_get_pattern(fill_style
);
254 if (pattern
== NULL
) {
255 pattern
= create_pattern(ruled_fill
, cr
);
259 adg_fill_style_set_pattern(fill_style
, pattern
);
260 cairo_pattern_destroy(pattern
);
263 adg_fill_style_get_extents(fill_style
, &extents
);
264 cairo_matrix_init_translate(&matrix
, -extents
.org
.x
, -extents
.org
.y
);
265 cairo_pattern_set_matrix(pattern
, &matrix
);
267 if (PARENT_STYLE_CLASS
->apply
!= NULL
)
268 PARENT_STYLE_CLASS
->apply(style
, cr
);
272 set_extents(AdgFillStyle
*fill_style
, const CpmlExtents
*extents
)
274 CpmlExtents old
, new;
276 adg_fill_style_get_extents(fill_style
, &old
);
278 /* The pattern is invalidated (and thus regenerated) only
279 * when the new extents are wider than the old ones */
280 if (old
.size
.x
>= extents
->size
.x
&& old
.size
.y
>= extents
->size
.y
) {
281 new.org
= extents
->org
;
284 cpml_extents_copy(&new, extents
);
285 adg_fill_style_set_pattern(fill_style
, NULL
);
288 if (PARENT_FILL_STYLE_CLASS
->set_extents
!= NULL
)
289 PARENT_FILL_STYLE_CLASS
->set_extents(fill_style
, &new);
293 set_spacing(AdgRuledFill
*ruled_fill
, gdouble spacing
)
295 AdgRuledFillPrivate
*data
= ruled_fill
->data
;
297 if (spacing
== data
->spacing
)
300 data
->spacing
= spacing
;
301 adg_fill_style_set_pattern((AdgFillStyle
*) ruled_fill
, NULL
);
307 set_angle(AdgRuledFill
*ruled_fill
, gdouble angle
)
309 AdgRuledFillPrivate
*data
= ruled_fill
->data
;
311 if (angle
== data
->angle
)
315 adg_fill_style_set_pattern((AdgFillStyle
*) ruled_fill
, NULL
);
320 static cairo_pattern_t
*
321 create_pattern(AdgRuledFill
*ruled_fill
, cairo_t
*cr
)
323 AdgFillStyle
*fill_style
;
325 AdgRuledFillPrivate
*data
;
326 cairo_pattern_t
*pattern
;
327 cairo_surface_t
*surface
;
331 fill_style
= (AdgFillStyle
*) ruled_fill
;
332 adg_fill_style_get_extents(fill_style
, &extents
);
334 /* Check for valid extents */
335 if (!extents
.is_defined
)
338 data
= ruled_fill
->data
;
340 surface
= cairo_surface_create_similar(cairo_get_target(cr
),
341 CAIRO_CONTENT_COLOR_ALPHA
,
342 extents
.size
.x
, extents
.size
.y
);
343 pattern
= cairo_pattern_create_for_surface(surface
);
344 /* The pattern holds a reference to the surface */
345 cairo_surface_destroy(surface
);
347 spacing
.x
= cos(data
->angle
) * data
->spacing
;
348 spacing
.y
= sin(data
->angle
) * data
->spacing
;
350 context
= cairo_create(surface
);
351 draw_lines(&spacing
, &extents
.size
, context
);
352 cairo_destroy(context
);
358 draw_lines(const CpmlPair
*spacing
, const CpmlPair
*size
, cairo_t
*cr
)
360 CpmlPair step
, step1
, step2
;
363 /* There should be some sort of spacing and a destination area */
364 if ((spacing
->x
== 0 && spacing
->y
== 0) ||
365 (size
->x
<= 0 && size
->y
<= 0))
368 /* Revert spacings if needed to inspect only the x >= 0 cases */
369 cpml_pair_copy(&step
, spacing
);
370 if (spacing
->x
< 0 || (spacing
->x
== 0 && spacing
->y
< 0))
371 cpml_pair_negate(&step
);
375 p1
.y
= step
.y
== 0 ? p2
.y
: 0;
376 p2
.x
= step
.x
== 0 ? p1
.x
: 0;
389 while (p1
.x
< size
->x
) {
390 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
394 cairo_move_to(cr
, p1
.x
, p1
.y
);
395 cairo_line_to(cr
, p2
.x
, p2
.y
);
396 cpml_pair_add(&p1
, &step1
);
397 cpml_pair_add(&p2
, &step2
);
405 while (p1
.y
>= 0 && p1
.y
<= size
->y
) {
406 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
410 cairo_move_to(cr
, p1
.x
, p1
.y
);
411 cairo_line_to(cr
, p2
.x
, p2
.y
);
412 cpml_pair_add(&p1
, &step1
);
413 cpml_pair_add(&p2
, &step2
);