1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012 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().
35 * All fields are private and should not be used directly.
36 * Use its public methods instead.
42 #include "adg-internal.h"
43 #include "adg-pattern.h"
44 #include "adg-dress.h"
45 #include "adg-dress-builtins.h"
46 #include "adg-style.h"
47 #include "adg-fill-style.h"
50 #include "adg-ruled-fill.h"
51 #include "adg-ruled-fill-private.h"
54 #define _ADG_OLD_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
55 #define _ADG_OLD_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
58 G_DEFINE_TYPE(AdgRuledFill
, adg_ruled_fill
, ADG_TYPE_FILL_STYLE
)
68 static void _adg_get_property (GObject
*object
,
72 static void _adg_set_property (GObject
*object
,
76 static void _adg_apply (AdgStyle
*style
,
79 static void _adg_set_extents (AdgFillStyle
*fill_style
,
80 const CpmlExtents
*extents
);
81 static cairo_pattern_t
*_adg_create_pattern (AdgRuledFill
*ruled_fill
,
84 static void _adg_draw_lines (const CpmlPair
*spacing
,
90 adg_ruled_fill_class_init(AdgRuledFillClass
*klass
)
92 GObjectClass
*gobject_class
;
93 AdgStyleClass
*style_class
;
94 AdgFillStyleClass
*fill_style_class
;
97 gobject_class
= (GObjectClass
*) klass
;
98 style_class
= (AdgStyleClass
*) klass
;
99 fill_style_class
= (AdgFillStyleClass
*) klass
;
101 g_type_class_add_private(klass
, sizeof(AdgRuledFillPrivate
));
103 gobject_class
->get_property
= _adg_get_property
;
104 gobject_class
->set_property
= _adg_set_property
;
106 style_class
->apply
= _adg_apply
;
108 fill_style_class
->set_extents
= _adg_set_extents
;
110 param
= adg_param_spec_dress("line-dress",
112 P_("Dress to be used for rendering the lines"),
115 g_object_class_install_property(gobject_class
, PROP_LINE_DRESS
, param
);
117 param
= g_param_spec_double("spacing",
119 P_("The spacing in global spaces between the lines"),
122 g_object_class_install_property(gobject_class
, PROP_SPACING
, param
);
124 param
= g_param_spec_double("angle",
126 P_("The angle (in radians) of the lines"),
129 g_object_class_install_property(gobject_class
, PROP_ANGLE
, param
);
133 adg_ruled_fill_init(AdgRuledFill
*ruled_fill
)
135 AdgRuledFillPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill
,
137 AdgRuledFillPrivate
);
139 data
->line_dress
= ADG_DRESS_LINE_FILL
;
140 data
->angle
= G_PI_4
;
143 ruled_fill
->data
= data
;
147 _adg_get_property(GObject
*object
, guint prop_id
,
148 GValue
*value
, GParamSpec
*pspec
)
150 AdgRuledFillPrivate
*data
= ((AdgRuledFill
*) object
)->data
;
153 case PROP_LINE_DRESS
:
154 g_value_set_int(value
, data
->line_dress
);
157 g_value_set_double(value
, data
->spacing
);
160 g_value_set_double(value
, data
->angle
);
163 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
169 _adg_set_property(GObject
*object
, guint prop_id
,
170 const GValue
*value
, GParamSpec
*pspec
)
172 AdgRuledFill
*ruled_fill
;
173 AdgRuledFillPrivate
*data
;
175 ruled_fill
= (AdgRuledFill
*) object
;
176 data
= ruled_fill
->data
;
179 case PROP_LINE_DRESS
:
180 data
->line_dress
= g_value_get_int(value
);
183 data
->spacing
= g_value_get_double(value
);
184 adg_fill_style_set_pattern((AdgFillStyle
*) object
, NULL
);
187 data
->angle
= g_value_get_double(value
);
188 adg_fill_style_set_pattern((AdgFillStyle
*) object
, NULL
);
191 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
198 * adg_ruled_fill_new:
200 * Constructs a new empty ruled fill style initialized with default params.
202 * Returns: a newly created ruled fill style
207 adg_ruled_fill_new(void)
209 return g_object_new(ADG_TYPE_RULED_FILL
, NULL
);
213 * adg_ruled_fill_set_line_dress:
214 * @ruled_fill: an #AdgRuledFill object
215 * @dress: the new line dress
217 * Sets a new line dress on @ruled_fill.
222 adg_ruled_fill_set_line_dress(AdgRuledFill
*ruled_fill
, AdgDress dress
)
224 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
225 g_object_set(ruled_fill
, "line-dress", dress
, NULL
);
229 * adg_ruled_fill_get_line_dress:
230 * @ruled_fill: an #AdgRuledFill object
232 * Gets the @ruled_fill dress to be used for rendering the lines.
234 * Returns: (transfer none): the line dress.
239 adg_ruled_fill_get_line_dress(AdgRuledFill
*ruled_fill
)
241 AdgRuledFillPrivate
*data
;
243 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), ADG_DRESS_UNDEFINED
);
245 data
= ruled_fill
->data
;
247 return data
->line_dress
;
251 * adg_ruled_fill_set_spacing:
252 * @ruled_fill: an #AdgRuledFill
253 * @spacing: the new spacing
255 * Sets a new spacing on @ruled_fill.
260 adg_ruled_fill_set_spacing(AdgRuledFill
*ruled_fill
, gdouble spacing
)
262 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
263 g_object_set(ruled_fill
, "spacing", spacing
, NULL
);
267 * adg_ruled_fill_get_spacing:
268 * @ruled_fill: an #AdgRuledFill
270 * Gets the current spacing of @ruled_fill.
272 * Returns: the spacing (in global space)
277 adg_ruled_fill_get_spacing(AdgRuledFill
*ruled_fill
)
279 AdgRuledFillPrivate
*data
;
281 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
283 data
= ruled_fill
->data
;
285 return data
->spacing
;
289 * adg_ruled_fill_set_angle:
290 * @ruled_fill: an #AdgRuledFill
291 * @angle: the new angle
293 * Sets a new angle on @ruled_fill.
298 adg_ruled_fill_set_angle(AdgRuledFill
*ruled_fill
, gdouble angle
)
300 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill
));
301 g_object_set(ruled_fill
, "angle", angle
, NULL
);
305 * adg_ruled_fill_get_angle:
306 * @ruled_fill: an #AdgRuledFill
308 * Gets the current angle of @ruled_fill.
310 * Returns: the angle (in radians)
315 adg_ruled_fill_get_angle(AdgRuledFill
*ruled_fill
)
317 AdgRuledFillPrivate
*data
;
319 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill
), 0);
321 data
= ruled_fill
->data
;
328 _adg_apply(AdgStyle
*style
, AdgEntity
*entity
, cairo_t
*cr
)
330 AdgFillStyle
*fill_style
;
332 const CpmlExtents
*extents
;
334 fill_style
= (AdgFillStyle
*) style
;
335 pattern
= adg_fill_style_get_pattern(fill_style
);
336 extents
= adg_fill_style_get_extents(fill_style
);
338 if (pattern
== NULL
) {
339 pattern
= _adg_create_pattern((AdgRuledFill
*) style
, entity
, cr
);
343 adg_fill_style_set_pattern(fill_style
, pattern
);
344 cairo_pattern_destroy(pattern
);
347 cairo_translate(cr
, extents
->org
.x
, extents
->org
.y
);
349 if (_ADG_OLD_STYLE_CLASS
->apply
)
350 _ADG_OLD_STYLE_CLASS
->apply(style
, entity
, cr
);
354 _adg_set_extents(AdgFillStyle
*fill_style
, const CpmlExtents
*extents
)
356 CpmlExtents old
, new;
358 cpml_extents_copy(&old
, adg_fill_style_get_extents(fill_style
));
360 /* The pattern is invalidated (and thus regenerated) only
361 * when the new extents are wider than the old ones */
362 if (old
.size
.x
>= extents
->size
.x
&& old
.size
.y
>= extents
->size
.y
) {
363 new.org
= extents
->org
;
366 cpml_extents_copy(&new, extents
);
367 adg_fill_style_set_pattern(fill_style
, NULL
);
370 if (_ADG_OLD_FILL_STYLE_CLASS
->set_extents
)
371 _ADG_OLD_FILL_STYLE_CLASS
->set_extents(fill_style
, &new);
374 static cairo_pattern_t
*
375 _adg_create_pattern(AdgRuledFill
*ruled_fill
, AdgEntity
*entity
, cairo_t
*cr
)
377 AdgFillStyle
*fill_style
;
378 const CpmlExtents
*extents
;
379 AdgRuledFillPrivate
*data
;
380 AdgStyle
*line_style
;
381 cairo_pattern_t
*pattern
;
382 cairo_surface_t
*surface
;
386 fill_style
= (AdgFillStyle
*) ruled_fill
;
387 extents
= adg_fill_style_get_extents(fill_style
);
389 /* Check for valid extents */
390 if (!extents
->is_defined
)
393 data
= ruled_fill
->data
;
394 line_style
= adg_entity_style(entity
, data
->line_dress
);
395 surface
= cairo_surface_create_similar(cairo_get_target(cr
),
396 CAIRO_CONTENT_COLOR_ALPHA
,
397 extents
->size
.x
, extents
->size
.y
);
398 pattern
= cairo_pattern_create_for_surface(surface
);
400 /* The pattern holds a reference to the surface, so
401 * there is no need to hold another reference here */
402 cairo_surface_destroy(surface
);
404 spacing
.x
= cos(data
->angle
) * data
->spacing
;
405 spacing
.y
= sin(data
->angle
) * data
->spacing
;
407 context
= cairo_create(surface
);
408 adg_style_apply(line_style
, entity
, context
);
409 _adg_draw_lines(&spacing
, &extents
->size
, context
);
410 cairo_destroy(context
);
416 _adg_draw_lines(const CpmlPair
*spacing
, const CpmlPair
*size
, cairo_t
*cr
)
418 CpmlPair step
, step1
, step2
;
421 /* There should be some sort of spacing and a destination area */
422 if ((spacing
->x
== 0 && spacing
->y
== 0) ||
423 (size
->x
<= 0 && size
->y
<= 0))
426 /* Revert spacings if needed to inspect only the x >= 0 cases */
427 cpml_pair_copy(&step
, spacing
);
428 if (spacing
->x
< 0 || (spacing
->x
== 0 && spacing
->y
< 0)) {
435 p1
.y
= step
.y
== 0 ? p2
.y
: 0;
436 p2
.x
= step
.x
== 0 ? p1
.x
: 0;
449 while (p1
.x
< size
->x
) {
450 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
454 cairo_move_to(cr
, p1
.x
, p1
.y
);
455 cairo_line_to(cr
, p2
.x
, p2
.y
);
467 while (p1
.y
>= 0 && p1
.y
<= size
->y
) {
468 if (p2
.y
<= 0 || p2
.y
>= size
->y
) {
472 cairo_move_to(cr
, p1
.x
, p1
.y
);
473 cairo_line_to(cr
, p2
.x
, p2
.y
);