doc: update copyright line for 2021
[adg.git] / src / adg / adg-ruled-fill.c
blob73ad2657a850e4d4bc8d37f67d2724d3c784ce3b
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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_WITH_PRIVATE(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 gobject_class->get_property = _adg_get_property;
102 gobject_class->set_property = _adg_set_property;
104 style_class->apply = _adg_apply;
106 fill_style_class->set_extents = _adg_set_extents;
108 param = adg_param_spec_dress("line-dress",
109 P_("Line Dress"),
110 P_("Dress to be used for rendering the lines"),
111 ADG_DRESS_LINE_FILL,
112 G_PARAM_READWRITE);
113 g_object_class_install_property(gobject_class, PROP_LINE_DRESS, param);
115 param = g_param_spec_double("spacing",
116 P_("Spacing"),
117 P_("The spacing in global spaces between the lines"),
118 0, G_MAXDOUBLE, 16,
119 G_PARAM_READWRITE);
120 g_object_class_install_property(gobject_class, PROP_SPACING, param);
122 param = g_param_spec_double("angle",
123 P_("Angle"),
124 P_("The angle (in radians) of the lines"),
125 0, G_PI, G_PI_4,
126 G_PARAM_READWRITE);
127 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
130 static void
131 adg_ruled_fill_init(AdgRuledFill *ruled_fill)
133 AdgRuledFillPrivate *data = adg_ruled_fill_get_instance_private(ruled_fill);
134 data->line_dress = ADG_DRESS_LINE_FILL;
135 data->angle = G_PI_4;
136 data->spacing = 16;
139 static void
140 _adg_get_property(GObject *object, guint prop_id,
141 GValue *value, GParamSpec *pspec)
143 AdgRuledFillPrivate *data = adg_ruled_fill_get_instance_private((AdgRuledFill *) object);
145 switch (prop_id) {
146 case PROP_LINE_DRESS:
147 g_value_set_enum(value, data->line_dress);
148 break;
149 case PROP_SPACING:
150 g_value_set_double(value, data->spacing);
151 break;
152 case PROP_ANGLE:
153 g_value_set_double(value, data->angle);
154 break;
155 default:
156 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
157 break;
161 static void
162 _adg_set_property(GObject *object, guint prop_id,
163 const GValue *value, GParamSpec *pspec)
165 AdgRuledFillPrivate *data = adg_ruled_fill_get_instance_private((AdgRuledFill *) object);
167 switch (prop_id) {
168 case PROP_LINE_DRESS:
169 data->line_dress = g_value_get_enum(value);
170 break;
171 case PROP_SPACING:
172 data->spacing = g_value_get_double(value);
173 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
174 break;
175 case PROP_ANGLE:
176 data->angle = g_value_get_double(value);
177 adg_fill_style_set_pattern((AdgFillStyle *) object, NULL);
178 break;
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
181 break;
187 * adg_ruled_fill_new:
189 * Constructs a new empty ruled fill style initialized with default params.
191 * Returns: a newly created ruled fill style
193 * Since: 1.0
195 AdgRuledFill *
196 adg_ruled_fill_new(void)
198 return g_object_new(ADG_TYPE_RULED_FILL, NULL);
202 * adg_ruled_fill_set_line_dress:
203 * @ruled_fill: an #AdgRuledFill object
204 * @dress: the new line dress
206 * Sets a new line dress on @ruled_fill.
208 * Since: 1.0
210 void
211 adg_ruled_fill_set_line_dress(AdgRuledFill *ruled_fill, AdgDress dress)
213 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
214 g_object_set(ruled_fill, "line-dress", dress, NULL);
218 * adg_ruled_fill_get_line_dress:
219 * @ruled_fill: an #AdgRuledFill object
221 * Gets the @ruled_fill dress to be used for rendering the lines.
223 * Returns: (transfer none): the line dress.
225 * Since: 1.0
227 AdgDress
228 adg_ruled_fill_get_line_dress(AdgRuledFill *ruled_fill)
230 AdgRuledFillPrivate *data;
232 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), ADG_DRESS_UNDEFINED);
234 data = adg_ruled_fill_get_instance_private(ruled_fill);
235 return data->line_dress;
239 * adg_ruled_fill_set_spacing:
240 * @ruled_fill: an #AdgRuledFill
241 * @spacing: the new spacing
243 * Sets a new spacing on @ruled_fill.
245 * Since: 1.0
247 void
248 adg_ruled_fill_set_spacing(AdgRuledFill *ruled_fill, gdouble spacing)
250 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
251 g_object_set(ruled_fill, "spacing", spacing, NULL);
255 * adg_ruled_fill_get_spacing:
256 * @ruled_fill: an #AdgRuledFill
258 * Gets the current spacing of @ruled_fill.
260 * Returns: the spacing (in global space)
262 * Since: 1.0
264 gdouble
265 adg_ruled_fill_get_spacing(AdgRuledFill *ruled_fill)
267 AdgRuledFillPrivate *data;
269 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
271 data = adg_ruled_fill_get_instance_private(ruled_fill);
272 return data->spacing;
276 * adg_ruled_fill_set_angle:
277 * @ruled_fill: an #AdgRuledFill
278 * @angle: the new angle
280 * Sets a new angle on @ruled_fill.
282 * Since: 1.0
284 void
285 adg_ruled_fill_set_angle(AdgRuledFill *ruled_fill, gdouble angle)
287 g_return_if_fail(ADG_IS_RULED_FILL(ruled_fill));
288 g_object_set(ruled_fill, "angle", angle, NULL);
292 * adg_ruled_fill_get_angle:
293 * @ruled_fill: an #AdgRuledFill
295 * Gets the current angle of @ruled_fill.
297 * Returns: the angle (in radians)
299 * Since: 1.0
301 gdouble
302 adg_ruled_fill_get_angle(AdgRuledFill *ruled_fill)
304 AdgRuledFillPrivate *data;
306 g_return_val_if_fail(ADG_IS_RULED_FILL(ruled_fill), 0);
308 data = adg_ruled_fill_get_instance_private(ruled_fill);
309 return data->angle;
313 static void
314 _adg_apply(AdgStyle *style, AdgEntity *entity, cairo_t *cr)
316 AdgFillStyle *fill_style;
317 cairo_pattern_t *pattern;
318 const CpmlExtents *extents;
320 fill_style = (AdgFillStyle *) style;
321 pattern = adg_fill_style_get_pattern(fill_style);
322 extents = adg_fill_style_get_extents(fill_style);
324 if (pattern == NULL) {
325 pattern = _adg_create_pattern((AdgRuledFill *) style, entity, cr);
326 if (pattern == NULL)
327 return;
329 adg_fill_style_set_pattern(fill_style, pattern);
330 cairo_pattern_destroy(pattern);
333 cairo_translate(cr, extents->org.x, extents->org.y);
335 if (_ADG_OLD_STYLE_CLASS->apply)
336 _ADG_OLD_STYLE_CLASS->apply(style, entity, cr);
339 static void
340 _adg_set_extents(AdgFillStyle *fill_style, const CpmlExtents *extents)
342 CpmlExtents old, new;
344 cpml_extents_copy(&old, adg_fill_style_get_extents(fill_style));
346 /* The pattern is invalidated (and thus regenerated) only
347 * when the new extents are wider than the old ones */
348 if (old.size.x >= extents->size.x && old.size.y >= extents->size.y) {
349 new.org = extents->org;
350 new.size = old.size;
351 } else {
352 cpml_extents_copy(&new, extents);
353 adg_fill_style_set_pattern(fill_style, NULL);
356 if (_ADG_OLD_FILL_STYLE_CLASS->set_extents)
357 _ADG_OLD_FILL_STYLE_CLASS->set_extents(fill_style, &new);
360 static cairo_pattern_t *
361 _adg_create_pattern(AdgRuledFill *ruled_fill, AdgEntity *entity, cairo_t *cr)
363 AdgFillStyle *fill_style;
364 const CpmlExtents *extents;
365 AdgRuledFillPrivate *data;
366 AdgStyle *line_style;
367 cairo_pattern_t *pattern;
368 cairo_surface_t *surface;
369 CpmlPair spacing;
370 cairo_t *context;
372 fill_style = (AdgFillStyle *) ruled_fill;
373 extents = adg_fill_style_get_extents(fill_style);
375 /* Check for valid extents */
376 if (!extents->is_defined)
377 return NULL;
379 data = adg_ruled_fill_get_instance_private(ruled_fill);
380 line_style = adg_entity_style(entity, data->line_dress);
381 surface = cairo_surface_create_similar(cairo_get_target(cr),
382 CAIRO_CONTENT_COLOR_ALPHA,
383 extents->size.x, extents->size.y);
384 pattern = cairo_pattern_create_for_surface(surface);
386 /* The pattern holds a reference to the surface, so
387 * there is no need to hold another reference here */
388 cairo_surface_destroy(surface);
390 spacing.x = cos(data->angle) * data->spacing;
391 spacing.y = sin(data->angle) * data->spacing;
393 context = cairo_create(surface);
394 adg_style_apply(line_style, entity, context);
395 _adg_draw_lines(&spacing, &extents->size, context);
396 cairo_destroy(context);
398 return pattern;
401 static void
402 _adg_draw_lines(const CpmlPair *spacing, const CpmlPair *size, cairo_t *cr)
404 CpmlPair step, step1, step2;
405 CpmlPair p1, p2;
407 /* There should be some sort of spacing and a destination area */
408 if ((spacing->x == 0 && spacing->y == 0) ||
409 (size->x <= 0 && size->y <= 0))
410 return;
412 /* Revert spacings if needed to inspect only the x >= 0 cases */
413 cpml_pair_copy(&step, spacing);
414 if (spacing->x < 0 || (spacing->x == 0 && spacing->y < 0)) {
415 step.x = -step.x;
416 step.y = -step.y;
419 p1.x = step.x / 2;
420 p2.y = step.y / 2;
421 p1.y = step.y == 0 ? p2.y : 0;
422 p2.x = step.x == 0 ? p1.x : 0;
423 if (step.y < 0) {
424 p1.y += size->y;
425 p2.y += size->y;
428 step2.x = 0;
429 step2.y = step.y;
431 if (step.x != 0) {
432 step1.x = step.x;
433 step1.y = 0;
435 while (p1.x < size->x) {
436 if (p2.y <= 0 || p2.y >= size->y) {
437 step2.x = step.x;
438 step2.y = 0;
440 cairo_move_to(cr, p1.x, p1.y);
441 cairo_line_to(cr, p2.x, p2.y);
442 p1.x += step1.x;
443 p1.y += step1.y;
444 p2.x += step2.x;
445 p2.y += step2.y;
449 if (step.y != 0) {
450 step1.x = 0;
451 step1.y = step.y;
453 while (p1.y >= 0 && p1.y <= size->y) {
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 p1.x += step1.x;
461 p1.y += step1.y;
462 p2.x += step2.x;
463 p2.y += step2.y;
467 cairo_stroke(cr);