adg: inherit AdgDress from GEnum
[adg.git] / src / adg / adg-ruled-fill.c
blobcc5636771e9f888d0baae5bd2895608fe3b52c2d
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-dress.h"
44 #include "adg-style.h"
45 #include "adg-fill-style.h"
47 #include "adg-ruled-fill.h"
48 #include "adg-ruled-fill-private.h"
50 #include <math.h>
53 #define _ADG_OLD_STYLE_CLASS ((AdgStyleClass *) adg_ruled_fill_parent_class)
54 #define _ADG_OLD_FILL_STYLE_CLASS ((AdgFillStyleClass *) adg_ruled_fill_parent_class)
57 G_DEFINE_TYPE(AdgRuledFill, adg_ruled_fill, ADG_TYPE_FILL_STYLE)
59 enum {
60 PROP_0,
61 PROP_LINE_DRESS,
62 PROP_SPACING,
63 PROP_ANGLE
67 static void _adg_get_property (GObject *object,
68 guint prop_id,
69 GValue *value,
70 GParamSpec *pspec);
71 static void _adg_set_property (GObject *object,
72 guint prop_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void _adg_apply (AdgStyle *style,
76 AdgEntity *entity,
77 cairo_t *cr);
78 static void _adg_set_extents (AdgFillStyle *fill_style,
79 const CpmlExtents *extents);
80 static cairo_pattern_t *_adg_create_pattern (AdgRuledFill *ruled_fill,
81 AdgEntity *entity,
82 cairo_t *cr);
83 static void _adg_draw_lines (const CpmlPair *spacing,
84 const CpmlPair *size,
85 cairo_t *cr);
88 static void
89 adg_ruled_fill_class_init(AdgRuledFillClass *klass)
91 GObjectClass *gobject_class;
92 AdgStyleClass *style_class;
93 AdgFillStyleClass *fill_style_class;
94 GParamSpec *param;
96 gobject_class = (GObjectClass *) klass;
97 style_class = (AdgStyleClass *) klass;
98 fill_style_class = (AdgFillStyleClass *) klass;
100 g_type_class_add_private(klass, sizeof(AdgRuledFillPrivate));
102 gobject_class->get_property = _adg_get_property;
103 gobject_class->set_property = _adg_set_property;
105 style_class->apply = _adg_apply;
107 fill_style_class->set_extents = _adg_set_extents;
109 param = adg_param_spec_dress("line-dress",
110 P_("Line Dress"),
111 P_("Dress to be used for rendering the lines"),
112 ADG_DRESS_LINE_FILL,
113 G_PARAM_READWRITE);
114 g_object_class_install_property(gobject_class, PROP_LINE_DRESS, param);
116 param = g_param_spec_double("spacing",
117 P_("Spacing"),
118 P_("The spacing in global spaces between the lines"),
119 0, G_MAXDOUBLE, 16,
120 G_PARAM_READWRITE);
121 g_object_class_install_property(gobject_class, PROP_SPACING, param);
123 param = g_param_spec_double("angle",
124 P_("Angle"),
125 P_("The angle (in radians) of the lines"),
126 0, G_PI, G_PI_4,
127 G_PARAM_READWRITE);
128 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
131 static void
132 adg_ruled_fill_init(AdgRuledFill *ruled_fill)
134 AdgRuledFillPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(ruled_fill,
135 ADG_TYPE_RULED_FILL,
136 AdgRuledFillPrivate);
138 data->line_dress = ADG_DRESS_LINE_FILL;
139 data->angle = G_PI_4;
140 data->spacing = 16;
142 ruled_fill->data = data;
145 static void
146 _adg_get_property(GObject *object, guint prop_id,
147 GValue *value, GParamSpec *pspec)
149 AdgRuledFillPrivate *data = ((AdgRuledFill *) object)->data;
151 switch (prop_id) {
152 case PROP_LINE_DRESS:
153 g_value_set_enum(value, data->line_dress);
154 break;
155 case PROP_SPACING:
156 g_value_set_double(value, data->spacing);
157 break;
158 case PROP_ANGLE:
159 g_value_set_double(value, data->angle);
160 break;
161 default:
162 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
163 break;
167 static void
168 _adg_set_property(GObject *object, guint prop_id,
169 const GValue *value, GParamSpec *pspec)
171 AdgRuledFill *ruled_fill;
172 AdgRuledFillPrivate *data;
174 ruled_fill = (AdgRuledFill *) object;
175 data = ruled_fill->data;
177 switch (prop_id) {
178 case PROP_LINE_DRESS:
179 data->line_dress = g_value_get_enum(value);
180 break;
181 case PROP_SPACING:
182 data->spacing = g_value_get_double(value);
183 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
184 break;
185 case PROP_ANGLE:
186 data->angle = g_value_get_double(value);
187 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
188 break;
189 default:
190 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
191 break;
197 * adg_ruled_fill_new:
199 * Constructs a new empty ruled fill style initialized with default params.
201 * Returns: a newly created ruled fill style
203 * Since: 1.0
205 AdgRuledFill *
206 adg_ruled_fill_new(void)
208 return g_object_new(ADG_TYPE_RULED_FILL, NULL);
212 * adg_ruled_fill_set_line_dress:
213 * @ruled_fill: an #AdgRuledFill object
214 * @dress: the new line dress
216 * Sets a new line dress on @ruled_fill.
218 * Since: 1.0
220 void
221 adg_ruled_fill_set_line_dress(AdgRuledFill *ruled_fill, AdgDress dress)
223 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
224 g_object_set(ruled_fill, "line-dress", dress, NULL);
228 * adg_ruled_fill_get_line_dress:
229 * @ruled_fill: an #AdgRuledFill object
231 * Gets the @ruled_fill dress to be used for rendering the lines.
233 * Returns: (transfer none): the line dress.
235 * Since: 1.0
237 AdgDress
238 adg_ruled_fill_get_line_dress(AdgRuledFill *ruled_fill)
240 AdgRuledFillPrivate *data;
242 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), ADG_DRESS_UNDEFINED);
244 data = ruled_fill->data;
246 return data->line_dress;
250 * adg_ruled_fill_set_spacing:
251 * @ruled_fill: an #AdgRuledFill
252 * @spacing: the new spacing
254 * Sets a new spacing on @ruled_fill.
256 * Since: 1.0
258 void
259 adg_ruled_fill_set_spacing(AdgRuledFill *ruled_fill, gdouble spacing)
261 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
262 g_object_set(ruled_fill, "spacing", spacing, NULL);
266 * adg_ruled_fill_get_spacing:
267 * @ruled_fill: an #AdgRuledFill
269 * Gets the current spacing of @ruled_fill.
271 * Returns: the spacing (in global space)
273 * Since: 1.0
275 gdouble
276 adg_ruled_fill_get_spacing(AdgRuledFill *ruled_fill)
278 AdgRuledFillPrivate *data;
280 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
282 data = ruled_fill->data;
284 return data->spacing;
288 * adg_ruled_fill_set_angle:
289 * @ruled_fill: an #AdgRuledFill
290 * @angle: the new angle
292 * Sets a new angle on @ruled_fill.
294 * Since: 1.0
296 void
297 adg_ruled_fill_set_angle(AdgRuledFill *ruled_fill, gdouble angle)
299 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
300 g_object_set(ruled_fill, "angle", angle, NULL);
304 * adg_ruled_fill_get_angle:
305 * @ruled_fill: an #AdgRuledFill
307 * Gets the current angle of @ruled_fill.
309 * Returns: the angle (in radians)
311 * Since: 1.0
313 gdouble
314 adg_ruled_fill_get_angle(AdgRuledFill *ruled_fill)
316 AdgRuledFillPrivate *data;
318 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
320 data = ruled_fill->data;
322 return data->angle;
326 static void
327 _adg_apply(AdgStyle *style, AdgEntity *entity, cairo_t *cr)
329 AdgFillStyle *fill_style;
330 cairo_pattern_t *pattern;
331 const CpmlExtents *extents;
333 fill_style = (AdgFillStyle *) style;
334 pattern = adg_fill_style_get_pattern(fill_style);
335 extents = adg_fill_style_get_extents(fill_style);
337 if (pattern == NULL) {
338 pattern = _adg_create_pattern((AdgRuledFill *) style, entity, cr);
339 if (pattern == NULL)
340 return;
342 adg_fill_style_set_pattern(fill_style, pattern);
343 cairo_pattern_destroy(pattern);
346 cairo_translate(cr, extents->org.x, extents->org.y);
348 if (_ADG_OLD_STYLE_CLASS->apply)
349 _ADG_OLD_STYLE_CLASS->apply(style, entity, cr);
352 static void
353 _adg_set_extents(AdgFillStyle *fill_style, const CpmlExtents *extents)
355 CpmlExtents old, new;
357 cpml_extents_copy(&old, adg_fill_style_get_extents(fill_style));
359 /* The pattern is invalidated (and thus regenerated) only
360 * when the new extents are wider than the old ones */
361 if (old.size.x >= extents->size.x && old.size.y >= extents->size.y) {
362 new.org = extents->org;
363 new.size = old.size;
364 } else {
365 cpml_extents_copy(&new, extents);
366 adg_fill_style_set_pattern(fill_style, NULL);
369 if (_ADG_OLD_FILL_STYLE_CLASS->set_extents)
370 _ADG_OLD_FILL_STYLE_CLASS->set_extents(fill_style, &new);
373 static cairo_pattern_t *
374 _adg_create_pattern(AdgRuledFill *ruled_fill, AdgEntity *entity, cairo_t *cr)
376 AdgFillStyle *fill_style;
377 const CpmlExtents *extents;
378 AdgRuledFillPrivate *data;
379 AdgStyle *line_style;
380 cairo_pattern_t *pattern;
381 cairo_surface_t *surface;
382 CpmlPair spacing;
383 cairo_t *context;
385 fill_style = (AdgFillStyle *) ruled_fill;
386 extents = adg_fill_style_get_extents(fill_style);
388 /* Check for valid extents */
389 if (!extents->is_defined)
390 return NULL;
392 data = ruled_fill->data;
393 line_style = adg_entity_style(entity, data->line_dress);
394 surface = cairo_surface_create_similar(cairo_get_target(cr),
395 CAIRO_CONTENT_COLOR_ALPHA,
396 extents->size.x, extents->size.y);
397 pattern = cairo_pattern_create_for_surface(surface);
399 /* The pattern holds a reference to the surface, so
400 * there is no need to hold another reference here */
401 cairo_surface_destroy(surface);
403 spacing.x = cos(data->angle) * data->spacing;
404 spacing.y = sin(data->angle) * data->spacing;
406 context = cairo_create(surface);
407 adg_style_apply(line_style, entity, context);
408 _adg_draw_lines(&spacing, &extents->size, context);
409 cairo_destroy(context);
411 return pattern;
414 static void
415 _adg_draw_lines(const CpmlPair *spacing, const CpmlPair *size, cairo_t *cr)
417 CpmlPair step, step1, step2;
418 CpmlPair p1, p2;
420 /* There should be some sort of spacing and a destination area */
421 if ((spacing->x == 0 && spacing->y == 0) ||
422 (size->x <= 0 && size->y <= 0))
423 return;
425 /* Revert spacings if needed to inspect only the x >= 0 cases */
426 cpml_pair_copy(&step, spacing);
427 if (spacing->x < 0 || (spacing->x == 0 && spacing->y < 0)) {
428 step.x = -step.x;
429 step.y = -step.y;
432 p1.x = step.x / 2;
433 p2.y = step.y / 2;
434 p1.y = step.y == 0 ? p2.y : 0;
435 p2.x = step.x == 0 ? p1.x : 0;
436 if (step.y < 0) {
437 p1.y += size->y;
438 p2.y += size->y;
441 step2.x = 0;
442 step2.y = step.y;
444 if (step.x != 0) {
445 step1.x = step.x;
446 step1.y = 0;
448 while (p1.x < size->x) {
449 if (p2.y <= 0 || p2.y >= size->y) {
450 step2.x = step.x;
451 step2.y = 0;
453 cairo_move_to(cr, p1.x, p1.y);
454 cairo_line_to(cr, p2.x, p2.y);
455 p1.x += step1.x;
456 p1.y += step1.y;
457 p2.x += step2.x;
458 p2.y += step2.y;
462 if (step.y != 0) {
463 step1.x = 0;
464 step1.y = step.y;
466 while (p1.y >= 0 && p1.y <= size->y) {
467 if (p2.y <= 0 || p2.y >= size->y) {
468 step2.x = step.x;
469 step2.y = 0;
471 cairo_move_to(cr, p1.x, p1.y);
472 cairo_line_to(cr, p2.x, p2.y);
473 p1.x += step1.x;
474 p1.y += step1.y;
475 p2.x += step2.x;
476 p2.y += step2.y;
480 cairo_stroke(cr);