s/2010 N/2010,2011 N/
[adg.git] / src / adg / adg-ruled-fill.c
blob646abe8c6c093dc188d117c7943db6084d9d415d
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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.
21 /**
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().
28 **/
30 /**
31 * AdgRuledFill:
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
35 **/
38 #include "adg-internal.h"
39 #include "adg-ruled-fill.h"
40 #include "adg-ruled-fill-private.h"
41 #include "adg-dress-builtins.h"
42 #include <math.h>
44 #define _ADG_OLD_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
45 #define _ADG_OLD_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
48 G_DEFINE_TYPE(AdgRuledFill, adg_ruled_fill, ADG_TYPE_FILL_STYLE);
50 enum {
51 PROP_0,
52 PROP_LINE_DRESS,
53 PROP_SPACING,
54 PROP_ANGLE
58 static void _adg_get_property (GObject *object,
59 guint prop_id,
60 GValue *value,
61 GParamSpec *pspec);
62 static void _adg_set_property (GObject *object,
63 guint prop_id,
64 const GValue *value,
65 GParamSpec *pspec);
66 static void _adg_apply (AdgStyle *style,
67 AdgEntity *entity,
68 cairo_t *cr);
69 static void _adg_set_extents (AdgFillStyle *fill_style,
70 const CpmlExtents *extents);
71 static cairo_pattern_t *_adg_create_pattern (AdgRuledFill *ruled_fill,
72 AdgEntity *entity,
73 cairo_t *cr);
74 static void _adg_draw_lines (const CpmlPair *spacing,
75 const CpmlPair *size,
76 cairo_t *cr);
79 static void
80 adg_ruled_fill_class_init(AdgRuledFillClass *klass)
82 GObjectClass *gobject_class;
83 AdgStyleClass *style_class;
84 AdgFillStyleClass *fill_style_class;
85 GParamSpec *param;
87 gobject_class = (GObjectClass *) klass;
88 style_class = (AdgStyleClass *) klass;
89 fill_style_class = (AdgFillStyleClass *) klass;
91 g_type_class_add_private(klass, sizeof(AdgRuledFillPrivate));
93 gobject_class->get_property = _adg_get_property;
94 gobject_class->set_property = _adg_set_property;
96 style_class->apply = _adg_apply;
98 fill_style_class->set_extents = _adg_set_extents;
100 param = adg_param_spec_dress("line-dress",
101 P_("Line Dress"),
102 P_("Dress to be used for rendering the lines"),
103 ADG_DRESS_LINE_FILL,
104 G_PARAM_READWRITE);
105 g_object_class_install_property(gobject_class, PROP_LINE_DRESS, param);
107 param = g_param_spec_double("spacing",
108 P_("Spacing"),
109 P_("The spacing in global spaces between the lines"),
110 0, G_MAXDOUBLE, 16,
111 G_PARAM_READWRITE);
112 g_object_class_install_property(gobject_class, PROP_SPACING, param);
114 param = g_param_spec_double("angle",
115 P_("Angle"),
116 P_("The angle (in radians) of the lines"),
117 0, G_PI, G_PI_4,
118 G_PARAM_READWRITE);
119 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
122 static void
123 adg_ruled_fill_init(AdgRuledFill *ruled_fill)
125 AdgRuledFillPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill,
126 ADG_TYPE_RULED_FILL,
127 AdgRuledFillPrivate);
129 data->line_dress = ADG_DRESS_LINE_FILL;
130 data->angle = G_PI_4;
131 data->spacing = 16;
133 ruled_fill->data = data;
136 static void
137 _adg_get_property(GObject *object, guint prop_id,
138 GValue *value, GParamSpec *pspec)
140 AdgRuledFillPrivate *data = ((AdgRuledFill *) object)->data;
142 switch (prop_id) {
143 case PROP_LINE_DRESS:
144 g_value_set_int(value, data->line_dress);
145 break;
146 case PROP_SPACING:
147 g_value_set_double(value, data->spacing);
148 break;
149 case PROP_ANGLE:
150 g_value_set_double(value, data->angle);
151 break;
152 default:
153 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
154 break;
158 static void
159 _adg_set_property(GObject *object, guint prop_id,
160 const GValue *value, GParamSpec *pspec)
162 AdgRuledFill *ruled_fill;
163 AdgRuledFillPrivate *data;
165 ruled_fill = (AdgRuledFill *) object;
166 data = ruled_fill->data;
168 switch (prop_id) {
169 case PROP_LINE_DRESS:
170 data->line_dress = g_value_get_int(value);
171 break;
172 case PROP_SPACING:
173 data->spacing = g_value_get_double(value);
174 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
175 break;
176 case PROP_ANGLE:
177 data->angle = g_value_get_double(value);
178 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
179 break;
180 default:
181 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
182 break;
188 * adg_ruled_fill_new:
190 * Constructs a new empty ruled fill style initialized with default params.
192 * Returns: a newly created ruled fill style
194 AdgRuledFill *
195 adg_ruled_fill_new(void)
197 return g_object_new(ADG_TYPE_RULED_FILL, NULL);
201 * adg_ruled_fill_set_line_dress:
202 * @ruled_fill: an #AdgRuledFill object
203 * @dress: the new line dress
205 * Sets a new line dress on @ruled_fill.
207 void
208 adg_ruled_fill_set_line_dress(AdgRuledFill *ruled_fill, AdgDress dress)
210 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
211 g_object_set(ruled_fill, "line-dress", dress, NULL);
215 * adg_ruled_fill_get_line_dress:
216 * @ruled_fill: an #AdgRuledFill object
218 * Gets the @ruled_fill dress to be used for rendering the lines.
220 * Returns: the line dress
222 AdgDress
223 adg_ruled_fill_get_line_dress(AdgRuledFill *ruled_fill)
225 AdgRuledFillPrivate *data;
227 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), ADG_DRESS_UNDEFINED);
229 data = ruled_fill->data;
231 return data->line_dress;
235 * adg_ruled_fill_set_spacing:
236 * @ruled_fill: an #AdgRuledFill
237 * @spacing: the new spacing
239 * Sets a new spacing on @ruled_fill.
241 void
242 adg_ruled_fill_set_spacing(AdgRuledFill *ruled_fill, gdouble spacing)
244 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
245 g_object_set(ruled_fill, "spacing", spacing, NULL);
249 * adg_ruled_fill_get_spacing:
250 * @ruled_fill: an #AdgRuledFill
252 * Gets the current spacing of @ruled_fill.
254 * Returns: the spacing (in global space)
256 gdouble
257 adg_ruled_fill_get_spacing(AdgRuledFill *ruled_fill)
259 AdgRuledFillPrivate *data;
261 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
263 data = ruled_fill->data;
265 return data->spacing;
269 * adg_ruled_fill_set_angle:
270 * @ruled_fill: an #AdgRuledFill
271 * @angle: the new angle
273 * Sets a new angle on @ruled_fill.
275 void
276 adg_ruled_fill_set_angle(AdgRuledFill *ruled_fill, gdouble angle)
278 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
279 g_object_set(ruled_fill, "angle", angle, NULL);
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)
290 gdouble
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;
299 return data->angle;
303 static void
304 _adg_apply(AdgStyle *style, AdgEntity *entity, cairo_t *cr)
306 AdgFillStyle *fill_style;
307 AdgPattern *pattern;
308 const CpmlExtents *extents;
310 fill_style = (AdgFillStyle *) style;
311 pattern = adg_fill_style_get_pattern(fill_style);
312 extents = adg_fill_style_get_extents(fill_style);
314 if (pattern == NULL) {
315 pattern = _adg_create_pattern((AdgRuledFill *) style, entity, cr);
316 if (pattern == NULL)
317 return;
319 adg_fill_style_set_pattern(fill_style, pattern);
320 cairo_pattern_destroy(pattern);
323 cairo_translate(cr, extents->org.x, extents->org.y);
325 if (_ADG_OLD_STYLE_CLASS->apply)
326 _ADG_OLD_STYLE_CLASS->apply(style, entity, cr);
329 static void
330 _adg_set_extents(AdgFillStyle *fill_style, const CpmlExtents *extents)
332 CpmlExtents old, new;
334 cpml_extents_copy(&old, adg_fill_style_get_extents(fill_style));
336 /* The pattern is invalidated (and thus regenerated) only
337 * when the new extents are wider than the old ones */
338 if (old.size.x >= extents->size.x && old.size.y >= extents->size.y) {
339 new.org = extents->org;
340 new.size = old.size;
341 } else {
342 cpml_extents_copy(&new, extents);
343 adg_fill_style_set_pattern(fill_style, NULL);
346 if (_ADG_OLD_FILL_STYLE_CLASS->set_extents)
347 _ADG_OLD_FILL_STYLE_CLASS->set_extents(fill_style, &new);
350 static cairo_pattern_t *
351 _adg_create_pattern(AdgRuledFill *ruled_fill, AdgEntity *entity, cairo_t *cr)
353 AdgFillStyle *fill_style;
354 const CpmlExtents *extents;
355 AdgRuledFillPrivate *data;
356 AdgStyle *line_style;
357 cairo_pattern_t *pattern;
358 cairo_surface_t *surface;
359 CpmlPair spacing;
360 cairo_t *context;
362 fill_style = (AdgFillStyle *) ruled_fill;
363 extents = adg_fill_style_get_extents(fill_style);
365 /* Check for valid extents */
366 if (!extents->is_defined)
367 return NULL;
369 data = ruled_fill->data;
370 line_style = adg_entity_style(entity, data->line_dress);
371 surface = cairo_surface_create_similar(cairo_get_target(cr),
372 CAIRO_CONTENT_COLOR_ALPHA,
373 extents->size.x, extents->size.y);
374 pattern = cairo_pattern_create_for_surface(surface);
376 /* The pattern holds a reference to the surface, so
377 * there is no need to hold another reference here */
378 cairo_surface_destroy(surface);
380 spacing.x = cos(data->angle) * data->spacing;
381 spacing.y = sin(data->angle) * data->spacing;
383 context = cairo_create(surface);
384 adg_style_apply(line_style, entity, context);
385 _adg_draw_lines(&spacing, &extents->size, context);
386 cairo_destroy(context);
388 return pattern;
391 static void
392 _adg_draw_lines(const CpmlPair *spacing, const CpmlPair *size, cairo_t *cr)
394 CpmlPair step, step1, step2;
395 CpmlPair p1, p2;
397 /* There should be some sort of spacing and a destination area */
398 if ((spacing->x == 0 && spacing->y == 0) ||
399 (size->x <= 0 && size->y <= 0))
400 return;
402 /* Revert spacings if needed to inspect only the x >= 0 cases */
403 cpml_pair_copy(&step, spacing);
404 if (spacing->x < 0 || (spacing->x == 0 && spacing->y < 0)) {
405 step.x = -step.x;
406 step.y = -step.y;
409 p1.x = step.x / 2;
410 p2.y = step.y / 2;
411 p1.y = step.y == 0 ? p2.y : 0;
412 p2.x = step.x == 0 ? p1.x : 0;
413 if (step.y < 0) {
414 p1.y += size->y;
415 p2.y += size->y;
418 step2.x = 0;
419 step2.y = step.y;
421 if (step.x != 0) {
422 step1.x = step.x;
423 step1.y = 0;
425 while (p1.x < size->x) {
426 if (p2.y <= 0 || p2.y >= size->y) {
427 step2.x = step.x;
428 step2.y = 0;
430 cairo_move_to(cr, p1.x, p1.y);
431 cairo_line_to(cr, p2.x, p2.y);
432 p1.x += step1.x;
433 p1.y += step1.y;
434 p2.x += step2.x;
435 p2.y += step2.y;
439 if (step.y != 0) {
440 step1.x = 0;
441 step1.y = step.y;
443 while (p1.y >= 0 && p1.y <= size->y) {
444 if (p2.y <= 0 || p2.y >= size->y) {
445 step2.x = step.x;
446 step2.y = 0;
448 cairo_move_to(cr, p1.x, p1.y);
449 cairo_line_to(cr, p2.x, p2.y);
450 p1.x += step1.x;
451 p1.y += step1.y;
452 p2.x += step2.x;
453 p2.y += step2.y;
457 cairo_stroke(cr);