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"
40 #include "adg-dress-builtins.h"
44 #define PARENT_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
45 #define PARENT_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
56 static void get_property (GObject
*object
,
60 static void set_property (GObject
*object
,
64 static void apply (AdgStyle
*style
,
67 static void set_extents (AdgFillStyle
*fill_style
,
68 const CpmlExtents
*extents
);
69 static gboolean
set_spacing (AdgRuledFill
*ruled_fill
,
71 static gboolean
set_angle (AdgRuledFill
*ruled_fill
,
73 static cairo_pattern_t
*create_pattern (AdgRuledFill
*ruled_fill
,
76 static void draw_lines (const CpmlPair
*spacing
,
81 G_DEFINE_TYPE(AdgRuledFill
, adg_ruled_fill
, ADG_TYPE_FILL_STYLE
);
85 adg_ruled_fill_class_init(AdgRuledFillClass
*klass
)
87 GObjectClass
*gobject_class
;
88 AdgStyleClass
*style_class
;
89 AdgFillStyleClass
*fill_style_class
;
92 gobject_class
= (GObjectClass
*) klass
;
93 style_class
= (AdgStyleClass
*) klass
;
94 fill_style_class
= (AdgFillStyleClass
*) klass
;
96 g_type_class_add_private(klass
, sizeof(AdgRuledFillPrivate
));
98 gobject_class
->get_property
= get_property
;
99 gobject_class
->set_property
= set_property
;
101 style_class
->apply
= apply
;
103 fill_style_class
->set_extents
= set_extents
;
105 param
= adg_param_spec_dress("line-dress",
107 P_("Dress to be used for rendering the lines"),
108 ADG_DRESS_LINE_HATCH
,
110 g_object_class_install_property(gobject_class
, PROP_LINE_DRESS
, param
);
112 param
= g_param_spec_double("spacing",
114 P_("The spacing in global spaces between the lines"),
117 g_object_class_install_property(gobject_class
, PROP_SPACING
, param
);
119 param
= g_param_spec_double("angle",
121 P_("The angle (in radians) of the lines"),
124 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
128 adg_ruled_fill_init(AdgRuledFill
*ruled_fill
)
130 AdgRuledFillPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill
,
132 AdgRuledFillPrivate
);
134 data
->line_dress
= ADG_DRESS_LINE_HATCH
;
135 data
->angle
= G_PI_4
;
138 ruled_fill
->data
= data
;
142 get_property(GObject
*object
,
143 guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
145 AdgRuledFillPrivate
*data
= ((AdgRuledFill
*) object
)->data
;
148 case PROP_LINE_DRESS
:
149 g_value_set_int(value
, data
->line_dress
);
152 g_value_set_double(value
, data
->spacing
);
155 g_value_set_double(value
, data
->angle
);
158 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
164 set_property(GObject
*object
,
165 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
167 AdgRuledFill
*ruled_fill
;
168 AdgRuledFillPrivate
*data
;
170 ruled_fill
= (AdgRuledFill
*) object
;
171 data
= ruled_fill
->data
;
174 case PROP_LINE_DRESS
:
175 adg_dress_set(&data
->line_dress
, g_value_get_int(value
));
178 set_spacing(ruled_fill
, g_value_get_double(value
));
181 set_angle(ruled_fill
, g_value_get_double(value
));
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
191 * adg_ruled_fill_set_line_dress:
192 * @ruled_fill: an #AdgRuledFill object
193 * @dress: the new line dress
195 * Sets a new line dress on @ruled_fill.
198 adg_ruled_fill_set_line_dress(AdgRuledFill
*ruled_fill
, AdgDress dress
)
200 AdgRuledFillPrivate
*data
;
202 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
204 data
= ruled_fill
->data
;
206 if (adg_dress_set(&data
->line_dress
, dress
))
207 g_object_notify((GObject
*) ruled_fill
, "line-dress");
211 * adg_ruled_fill_get_line_dress:
212 * @ruled_fill: an #AdgRuledFill object
214 * Gets the @ruled_fill dress to be used for rendering the lines.
216 * Returns: the line dress
219 adg_ruled_fill_get_line_dress(AdgRuledFill
*ruled_fill
)
221 AdgRuledFillPrivate
*data
;
223 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), ADG_DRESS_UNDEFINED
);
225 data
= ruled_fill
->data
;
227 return data
->line_dress
;
231 * adg_ruled_fill_set_spacing:
232 * @ruled_fill: an #AdgRuledFill
233 * @spacing: the new spacing
235 * Sets a new spacing on @ruled_fill.
238 adg_ruled_fill_set_spacing(AdgRuledFill
*ruled_fill
, gdouble spacing
)
240 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
242 if (set_spacing(ruled_fill
, spacing
))
243 g_object_notify((GObject
*) ruled_fill
, "spacing");
247 * adg_ruled_fill_get_spacing:
248 * @ruled_fill: an #AdgRuledFill
250 * Gets the current spacing of @ruled_fill.
252 * Returns: the spacing (in global space)
255 adg_ruled_fill_get_spacing(AdgRuledFill
*ruled_fill
)
257 AdgRuledFillPrivate
*data
;
259 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
261 data
= ruled_fill
->data
;
263 return data
->spacing
;
267 * adg_ruled_fill_set_angle:
268 * @ruled_fill: an #AdgRuledFill
269 * @angle: the new angle
271 * Sets a new angle on @ruled_fill.
274 adg_ruled_fill_set_angle(AdgRuledFill
*ruled_fill
, gdouble angle
)
276 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
278 if (set_angle(ruled_fill
, angle
))
279 g_object_notify((GObject
*) ruled_fill
, "angle");
283 * adg_ruled_fill_get_angle:
284 * @ruled_fill: an #AdgRuledFill
286 * Gets the current angle of @ruled_fill.
288 * Returns: the angle (in radians)
291 adg_ruled_fill_get_angle(AdgRuledFill
*ruled_fill
)
293 AdgRuledFillPrivate
*data
;
295 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
297 data
= ruled_fill
->data
;
304 apply(AdgStyle
*style
, AdgEntity
*entity
, cairo_t
*cr
)
306 AdgFillStyle
*fill_style
;
308 const CpmlExtents
*extents
;
309 cairo_matrix_t matrix
;
311 fill_style
= (AdgFillStyle
*) style
;
312 pattern
= adg_fill_style_get_pattern(fill_style
);
313 extents
= adg_fill_style_get_extents(fill_style
);
315 if (pattern
== NULL
) {
316 pattern
= create_pattern((AdgRuledFill
*) style
, entity
, cr
);
320 adg_fill_style_set_pattern(fill_style
, pattern
);
321 cairo_pattern_destroy(pattern
);
324 cairo_matrix_init_translate(&matrix
, -extents
->org
.x
, -extents
->org
.y
);
325 cairo_pattern_set_matrix(pattern
, &matrix
);
327 if (PARENT_STYLE_CLASS
->apply
)
328 PARENT_STYLE_CLASS
->apply(style
, entity
, cr
);
332 set_extents(AdgFillStyle
*fill_style
, const CpmlExtents
*extents
)
334 CpmlExtents old
, new;
336 cpml_extents_copy(&old
, adg_fill_style_get_extents(fill_style
));
338 /* The pattern is invalidated (and thus regenerated) only
339 * when the new extents are wider than the old ones */
340 if (old
.size
.x
>= extents
->size
.x
&& old
.size
.y
>= extents
->size
.y
) {
341 new.org
= extents
->org
;
344 cpml_extents_copy(&new, extents
);
345 adg_fill_style_set_pattern(fill_style
, NULL
);
348 if (PARENT_FILL_STYLE_CLASS
->set_extents
)
349 PARENT_FILL_STYLE_CLASS
->set_extents(fill_style
, &new);
353 set_spacing(AdgRuledFill
*ruled_fill
, gdouble spacing
)
355 AdgRuledFillPrivate
*data
= ruled_fill
->data
;
357 if (spacing
== data
->spacing
)
360 data
->spacing
= spacing
;
361 adg_fill_style_set_pattern((AdgFillStyle
*) ruled_fill
, NULL
);
367 set_angle(AdgRuledFill
*ruled_fill
, gdouble angle
)
369 AdgRuledFillPrivate
*data
= ruled_fill
->data
;
371 if (angle
== data
->angle
)
375 adg_fill_style_set_pattern((AdgFillStyle
*) ruled_fill
, NULL
);
380 static cairo_pattern_t
*
381 create_pattern(AdgRuledFill
*ruled_fill
, AdgEntity
*entity
, cairo_t
*cr
)
383 AdgFillStyle
*fill_style
;
384 const CpmlExtents
*extents
;
385 AdgRuledFillPrivate
*data
;
386 AdgStyle
*line_style
;
387 cairo_pattern_t
*pattern
;
388 cairo_surface_t
*surface
;
392 fill_style
= (AdgFillStyle
*) ruled_fill
;
393 extents
= adg_fill_style_get_extents(fill_style
);
395 /* Check for valid extents */
396 if (!extents
->is_defined
)
399 data
= ruled_fill
->data
;
400 line_style
= adg_entity_style(entity
, data
->line_dress
);
401 surface
= cairo_surface_create_similar(cairo_get_target(cr
),
402 CAIRO_CONTENT_COLOR_ALPHA
,
403 extents
->size
.x
, extents
->size
.y
);
404 pattern
= cairo_pattern_create_for_surface(surface
);
406 /* The pattern holds a reference to the surface, so the
407 * surface should be unreferenced once */
408 cairo_surface_destroy(surface
);
410 spacing
.x
= cos(data
->angle
) * data
->spacing
;
411 spacing
.y
= sin(data
->angle
) * data
->spacing
;
413 context
= cairo_create(surface
);
414 adg_style_apply(line_style
, entity
, context
);
415 draw_lines(&spacing
, &extents
->size
, context
);
416 cairo_destroy(context
);
422 draw_lines(const CpmlPair
*spacing
, const CpmlPair
*size
, cairo_t
*cr
)
424 CpmlPair step
, step1
, step2
;
427 /* There should be some sort of spacing and a destination area */
428 if ((spacing
->x
== 0 && spacing
->y
== 0) ||
429 (size
->x
<= 0 && size
->y
<= 0))
432 /* Revert spacings if needed to inspect only the x >= 0 cases */
433 cpml_pair_copy(&step
, spacing
);
434 if (spacing
->x
< 0 || (spacing
->x
== 0 && spacing
->y
< 0))
435 cpml_pair_negate(&step
);
439 p1
.y
= step
.y
== 0 ? p2
.y
: 0;
440 p2
.x
= step
.x
== 0 ? p1
.x
: 0;
453 while (p1
.x
< size
->x
) {
454 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
458 cairo_move_to(cr
, p1
.x
, p1
.y
);
459 cairo_line_to(cr
, p2
.x
, p2
.y
);
460 cpml_pair_add(&p1
, &step1
);
461 cpml_pair_add(&p2
, &step2
);
469 while (p1
.y
>= 0 && p1
.y
<= size
->y
) {
470 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
474 cairo_move_to(cr
, p1
.x
, p1
.y
);
475 cairo_line_to(cr
, p2
.x
, p2
.y
);
476 cpml_pair_add(&p1
, &step1
);
477 cpml_pair_add(&p2
, &step2
);