[build] Bumped version to 0.5.3
[adg.git] / adg / adg-ruled-fill.c
blob0ce455ebc5040088246a616374bbde812dd79981
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.
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-ruled-fill.h"
39 #include "adg-ruled-fill-private.h"
40 #include "adg-dress-builtins.h"
41 #include "adg-intl.h"
42 #include <math.h>
44 #define PARENT_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
45 #define PARENT_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
48 enum {
49 PROP_0,
50 PROP_LINE_DRESS,
51 PROP_SPACING,
52 PROP_ANGLE
56 static void get_property (GObject *object,
57 guint prop_id,
58 GValue *value,
59 GParamSpec *pspec);
60 static void set_property (GObject *object,
61 guint prop_id,
62 const GValue *value,
63 GParamSpec *pspec);
64 static void apply (AdgStyle *style,
65 AdgEntity *entity,
66 cairo_t *cr);
67 static void set_extents (AdgFillStyle *fill_style,
68 const CpmlExtents *extents);
69 static gboolean set_spacing (AdgRuledFill *ruled_fill,
70 gdouble spacing);
71 static gboolean set_angle (AdgRuledFill *ruled_fill,
72 gdouble angle);
73 static cairo_pattern_t *create_pattern (AdgRuledFill *ruled_fill,
74 AdgEntity *entity,
75 cairo_t *cr);
76 static void draw_lines (const CpmlPair *spacing,
77 const CpmlPair *size,
78 cairo_t *cr);
81 G_DEFINE_TYPE(AdgRuledFill, adg_ruled_fill, ADG_TYPE_FILL_STYLE);
84 static void
85 adg_ruled_fill_class_init(AdgRuledFillClass *klass)
87 GObjectClass *gobject_class;
88 AdgStyleClass *style_class;
89 AdgFillStyleClass *fill_style_class;
90 GParamSpec *param;
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",
106 P_("Line Dress"),
107 P_("Dress to be used for rendering the lines"),
108 ADG_DRESS_LINE_HATCH,
109 G_PARAM_READWRITE);
110 g_object_class_install_property(gobject_class, PROP_LINE_DRESS, param);
112 param = g_param_spec_double("spacing",
113 P_("Spacing"),
114 P_("The spacing in global spaces between the lines"),
115 0, G_MAXDOUBLE, 16,
116 G_PARAM_READWRITE);
117 g_object_class_install_property(gobject_class, PROP_SPACING, param);
119 param = g_param_spec_double("angle",
120 P_("Angle"),
121 P_("The angle (in radians) of the lines"),
122 0, G_PI, G_PI_4,
123 G_PARAM_READWRITE);
124 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
127 static void
128 adg_ruled_fill_init(AdgRuledFill *ruled_fill)
130 AdgRuledFillPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill,
131 ADG_TYPE_RULED_FILL,
132 AdgRuledFillPrivate);
134 data->line_dress = ADG_DRESS_LINE_HATCH;
135 data->angle = G_PI_4;
136 data->spacing = 16;
138 ruled_fill->data = data;
141 static void
142 get_property(GObject *object,
143 guint prop_id, GValue *value, GParamSpec *pspec)
145 AdgRuledFillPrivate *data = ((AdgRuledFill *) object)->data;
147 switch (prop_id) {
148 case PROP_LINE_DRESS:
149 g_value_set_int(value, data->line_dress);
150 break;
151 case PROP_SPACING:
152 g_value_set_double(value, data->spacing);
153 break;
154 case PROP_ANGLE:
155 g_value_set_double(value, data->angle);
156 break;
157 default:
158 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
159 break;
163 static void
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;
173 switch (prop_id) {
174 case PROP_LINE_DRESS:
175 adg_dress_set(&data->line_dress, g_value_get_int(value));
176 break;
177 case PROP_SPACING:
178 set_spacing(ruled_fill, g_value_get_double(value));
179 break;
180 case PROP_ANGLE:
181 set_angle(ruled_fill, g_value_get_double(value));
182 break;
183 default:
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
185 break;
191 * adg_ruled_fill_get_line_dress:
192 * @ruled_fill: an #AdgRuledFill object
194 * Gets the @ruled_fill dress to be used for rendering the lines.
196 * Returns: the line dress
198 AdgDress
199 adg_ruled_fill_get_line_dress(AdgRuledFill *ruled_fill)
201 AdgRuledFillPrivate *data;
203 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), ADG_DRESS_UNDEFINED);
205 data = ruled_fill->data;
207 return data->line_dress;
211 * adg_ruled_fill_set_line_dress:
212 * @ruled_fill: an #AdgRuledFill object
213 * @dress: the new line dress
215 * Sets a new line dress on @ruled_fill.
217 void
218 adg_ruled_fill_set_line_dress(AdgRuledFill *ruled_fill, AdgDress dress)
220 AdgRuledFillPrivate *data;
222 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
224 data = ruled_fill->data;
226 if (adg_dress_set(&data->line_dress, dress))
227 g_object_notify((GObject *) ruled_fill, "line-dress");
231 * adg_ruled_fill_get_spacing:
232 * @ruled_fill: an #AdgRuledFill
234 * Gets the current spacing of @ruled_fill.
236 * Returns: the spacing (in global space)
238 gdouble
239 adg_ruled_fill_get_spacing(AdgRuledFill *ruled_fill)
241 AdgRuledFillPrivate *data;
243 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
245 data = ruled_fill->data;
247 return data->spacing;
251 * adg_ruled_fill_set_spacing:
252 * @ruled_fill: an #AdgRuledFill
253 * @spacing: the new spacing
255 * Sets a new spacing on @ruled_fill.
257 void
258 adg_ruled_fill_set_spacing(AdgRuledFill *ruled_fill, gdouble spacing)
260 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
262 if (set_spacing(ruled_fill, spacing))
263 g_object_notify((GObject *) ruled_fill, "spacing");
267 * adg_ruled_fill_get_angle:
268 * @ruled_fill: an #AdgRuledFill
270 * Gets the current angle of @ruled_fill.
272 * Returns: the angle (in radians)
274 gdouble
275 adg_ruled_fill_get_angle(AdgRuledFill *ruled_fill)
277 AdgRuledFillPrivate *data;
279 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
281 data = ruled_fill->data;
283 return data->angle;
287 * adg_ruled_fill_set_angle:
288 * @ruled_fill: an #AdgRuledFill
289 * @angle: the new angle
291 * Sets a new angle on @ruled_fill.
293 void
294 adg_ruled_fill_set_angle(AdgRuledFill *ruled_fill, gdouble angle)
296 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
298 if (set_angle(ruled_fill, angle))
299 g_object_notify((GObject *) ruled_fill, "angle");
303 static void
304 apply(AdgStyle *style, AdgEntity *entity, cairo_t *cr)
306 AdgFillStyle *fill_style;
307 cairo_pattern_t *pattern;
308 CpmlExtents extents;
309 cairo_matrix_t matrix;
311 fill_style = (AdgFillStyle *) style;
312 pattern = adg_fill_style_get_pattern(fill_style);
314 if (pattern == NULL) {
315 pattern = 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 adg_fill_style_get_extents(fill_style, &extents);
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 != NULL)
328 PARENT_STYLE_CLASS->apply(style, entity, cr);
331 static void
332 set_extents(AdgFillStyle *fill_style, const CpmlExtents *extents)
334 CpmlExtents old, new;
336 adg_fill_style_get_extents(fill_style, &old);
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;
342 new.size = old.size;
343 } else {
344 cpml_extents_copy(&new, extents);
345 adg_fill_style_set_pattern(fill_style, NULL);
348 if (PARENT_FILL_STYLE_CLASS->set_extents != NULL)
349 PARENT_FILL_STYLE_CLASS->set_extents(fill_style, &new);
352 static gboolean
353 set_spacing(AdgRuledFill *ruled_fill, gdouble spacing)
355 AdgRuledFillPrivate *data = ruled_fill->data;
357 if (spacing == data->spacing)
358 return FALSE;
360 data->spacing = spacing;
361 adg_fill_style_set_pattern((AdgFillStyle *) ruled_fill, NULL);
363 return TRUE;
366 static gboolean
367 set_angle(AdgRuledFill *ruled_fill, gdouble angle)
369 AdgRuledFillPrivate *data = ruled_fill->data;
371 if (angle == data->angle)
372 return FALSE;
374 data->angle = angle;
375 adg_fill_style_set_pattern((AdgFillStyle *) ruled_fill, NULL);
377 return TRUE;
380 static cairo_pattern_t *
381 create_pattern(AdgRuledFill *ruled_fill, AdgEntity *entity, cairo_t *cr)
383 AdgFillStyle *fill_style;
384 CpmlExtents extents;
385 AdgRuledFillPrivate *data;
386 AdgStyle *line_style;
387 cairo_pattern_t *pattern;
388 cairo_surface_t *surface;
389 CpmlPair spacing;
390 cairo_t *context;
392 fill_style = (AdgFillStyle *) ruled_fill;
393 adg_fill_style_get_extents(fill_style, &extents);
395 /* Check for valid extents */
396 if (!extents.is_defined)
397 return NULL;
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);
418 return pattern;
421 static void
422 draw_lines(const CpmlPair *spacing, const CpmlPair *size, cairo_t *cr)
424 CpmlPair step, step1, step2;
425 CpmlPair p1, p2;
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))
430 return;
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);
437 p1.x = step.x / 2;
438 p2.y = step.y / 2;
439 p1.y = step.y == 0 ? p2.y : 0;
440 p2.x = step.x == 0 ? p1.x : 0;
441 if (step.y < 0) {
442 p1.y += size->y;
443 p2.y += size->y;
446 step2.x = 0;
447 step2.y = step.y;
449 if (step.x != 0) {
450 step1.x = step.x;
451 step1.y = 0;
453 while (p1.x < size->x) {
454 if (p2.y <= 0 || p2.y >= size->y) {
455 step2.x = step.x;
456 step2.y = 0;
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);
465 if (step.y != 0) {
466 step1.x = 0;
467 step1.y = step.y;
469 while (p1.y >= 0 && p1.y <= size->y) {
470 if (p2.y <= 0 || p2.y >= size->y) {
471 step2.x = step.x;
472 step2.y = 0;
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);
481 cairo_stroke(cr);