build: depends on cairo-gobject if introspection is enabled
[adg.git] / src / adg / adg-ruled-fill.c
bloba235228fdd013b66c62820194ec59bf8e36ae618
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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().
29 * Since: 1.0
30 **/
32 /**
33 * AdgRuledFill:
35 * All fields are private and should not be used directly.
36 * Use its public methods instead.
38 * Since: 1.0
39 **/
42 #include "adg-internal.h"
43 #include "adg-style.h"
44 #include "adg-fill-style.h"
45 #include "adg-dress.h"
46 #include "adg-param-dress.h"
48 #include "adg-ruled-fill.h"
49 #include "adg-ruled-fill-private.h"
51 #include <math.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)
60 enum {
61 PROP_0,
62 PROP_LINE_DRESS,
63 PROP_SPACING,
64 PROP_ANGLE
68 static void _adg_get_property (GObject *object,
69 guint prop_id,
70 GValue *value,
71 GParamSpec *pspec);
72 static void _adg_set_property (GObject *object,
73 guint prop_id,
74 const GValue *value,
75 GParamSpec *pspec);
76 static void _adg_apply (AdgStyle *style,
77 AdgEntity *entity,
78 cairo_t *cr);
79 static void _adg_set_extents (AdgFillStyle *fill_style,
80 const CpmlExtents *extents);
81 static cairo_pattern_t *_adg_create_pattern (AdgRuledFill *ruled_fill,
82 AdgEntity *entity,
83 cairo_t *cr);
84 static void _adg_draw_lines (const CpmlPair *spacing,
85 const CpmlPair *size,
86 cairo_t *cr);
89 static void
90 adg_ruled_fill_class_init(AdgRuledFillClass *klass)
92 GObjectClass *gobject_class;
93 AdgStyleClass *style_class;
94 AdgFillStyleClass *fill_style_class;
95 GParamSpec *param;
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",
111 P_("Line Dress"),
112 P_("Dress to be used for rendering the lines"),
113 ADG_DRESS_LINE_FILL,
114 G_PARAM_READWRITE);
115 g_object_class_install_property(gobject_class, PROP_LINE_DRESS, param);
117 param = g_param_spec_double("spacing",
118 P_("Spacing"),
119 P_("The spacing in global spaces between the lines"),
120 0, G_MAXDOUBLE, 16,
121 G_PARAM_READWRITE);
122 g_object_class_install_property(gobject_class, PROP_SPACING, param);
124 param = g_param_spec_double("angle",
125 P_("Angle"),
126 P_("The angle (in radians) of the lines"),
127 0, G_PI, G_PI_4,
128 G_PARAM_READWRITE);
129 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
132 static void
133 adg_ruled_fill_init(AdgRuledFill *ruled_fill)
135 AdgRuledFillPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill,
136 ADG_TYPE_RULED_FILL,
137 AdgRuledFillPrivate);
139 data->line_dress = ADG_DRESS_LINE_FILL;
140 data->angle = G_PI_4;
141 data->spacing = 16;
143 ruled_fill->data = data;
146 static void
147 _adg_get_property(GObject *object, guint prop_id,
148 GValue *value, GParamSpec *pspec)
150 AdgRuledFillPrivate *data = ((AdgRuledFill *) object)->data;
152 switch (prop_id) {
153 case PROP_LINE_DRESS:
154 g_value_set_enum(value, data->line_dress);
155 break;
156 case PROP_SPACING:
157 g_value_set_double(value, data->spacing);
158 break;
159 case PROP_ANGLE:
160 g_value_set_double(value, data->angle);
161 break;
162 default:
163 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
164 break;
168 static void
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;
178 switch (prop_id) {
179 case PROP_LINE_DRESS:
180 data->line_dress = g_value_get_enum(value);
181 break;
182 case PROP_SPACING:
183 data->spacing = g_value_get_double(value);
184 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
185 break;
186 case PROP_ANGLE:
187 data->angle = g_value_get_double(value);
188 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
189 break;
190 default:
191 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
192 break;
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
204 * Since: 1.0
206 AdgRuledFill *
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.
219 * Since: 1.0
221 void
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.
236 * Since: 1.0
238 AdgDress
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.
257 * Since: 1.0
259 void
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)
274 * Since: 1.0
276 gdouble
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.
295 * Since: 1.0
297 void
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)
312 * Since: 1.0
314 gdouble
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;
323 return data->angle;
327 static void
328 _adg_apply(AdgStyle *style, AdgEntity *entity, cairo_t *cr)
330 AdgFillStyle *fill_style;
331 cairo_pattern_t *pattern;
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);
340 if (pattern == NULL)
341 return;
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);
353 static void
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;
364 new.size = old.size;
365 } else {
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;
383 CpmlPair spacing;
384 cairo_t *context;
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)
391 return NULL;
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);
412 return pattern;
415 static void
416 _adg_draw_lines(const CpmlPair *spacing, const CpmlPair *size, cairo_t *cr)
418 CpmlPair step, step1, step2;
419 CpmlPair p1, p2;
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))
424 return;
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)) {
429 step.x = -step.x;
430 step.y = -step.y;
433 p1.x = step.x / 2;
434 p2.y = step.y / 2;
435 p1.y = step.y == 0 ? p2.y : 0;
436 p2.x = step.x == 0 ? p1.x : 0;
437 if (step.y < 0) {
438 p1.y += size->y;
439 p2.y += size->y;
442 step2.x = 0;
443 step2.y = step.y;
445 if (step.x != 0) {
446 step1.x = step.x;
447 step1.y = 0;
449 while (p1.x < size->x) {
450 if (p2.y <= 0 || p2.y >= size->y) {
451 step2.x = step.x;
452 step2.y = 0;
454 cairo_move_to(cr, p1.x, p1.y);
455 cairo_line_to(cr, p2.x, p2.y);
456 p1.x += step1.x;
457 p1.y += step1.y;
458 p2.x += step2.x;
459 p2.y += step2.y;
463 if (step.y != 0) {
464 step1.x = 0;
465 step1.y = step.y;
467 while (p1.y >= 0 && p1.y <= size->y) {
468 if (p2.y <= 0 || p2.y >= size->y) {
469 step2.x = step.x;
470 step2.y = 0;
472 cairo_move_to(cr, p1.x, p1.y);
473 cairo_line_to(cr, p2.x, p2.y);
474 p1.x += step1.x;
475 p1.y += step1.y;
476 p2.x += step2.x;
477 p2.y += step2.y;
481 cairo_stroke(cr);