doc: update copyright line for 2021
[adg.git] / src / adg / adg-projection.c
blob3f5647f147cc877876deabe586949ab6b09f3d52
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-projection
23 * @short_description: The standard symbol for specifying the projection scheme
25 * The #AdgProjection is an entity representing the standard symbol
26 * of the projection scheme.
28 * Since: 1.0
29 **/
31 /**
32 * AdgProjection:
34 * All fields are private and should not be used directly.
35 * Use its public methods instead.
37 * Since: 1.0
38 **/
41 #include "adg-internal.h"
42 #include "adg-model.h"
43 #include "adg-trail.h"
44 #include "adg-path.h"
45 #include "adg-dress.h"
46 #include "adg-param-dress.h"
48 #include "adg-projection.h"
49 #include "adg-projection-private.h"
52 G_DEFINE_TYPE_WITH_PRIVATE(AdgProjection, adg_projection, ADG_TYPE_ENTITY)
54 enum {
55 PROP_0,
56 PROP_SYMBOL_DRESS,
57 PROP_AXIS_DRESS,
58 PROP_SCHEME
62 static void _adg_get_property (GObject *object,
63 guint param_id,
64 GValue *value,
65 GParamSpec *pspec);
66 static void _adg_set_property (GObject *object,
67 guint param_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static void _adg_arrange (AdgEntity *entity);
71 static void _adg_render (AdgEntity *entity,
72 cairo_t *cr);
73 static void _adg_arrange_class (AdgProjectionClass *projection_class,
74 AdgProjectionScheme scheme);
77 static void
78 adg_projection_class_init(AdgProjectionClass *klass)
80 GObjectClass *gobject_class;
81 AdgEntityClass *entity_class;
82 GParamSpec *param;
83 AdgProjectionClassPrivate *data_class;
85 gobject_class = (GObjectClass *) klass;
86 entity_class = (AdgEntityClass *) klass;
88 gobject_class->get_property = _adg_get_property;
89 gobject_class->set_property = _adg_set_property;
91 entity_class->arrange = _adg_arrange;
92 entity_class->render = _adg_render;
94 param = adg_param_spec_dress("symbol-dress",
95 P_("Symbol Dress"),
96 P_("The line dress to use for rendering the views of the projection"),
97 ADG_DRESS_LINE,
98 G_PARAM_READWRITE);
99 g_object_class_install_property(gobject_class, PROP_SYMBOL_DRESS, param);
101 param = adg_param_spec_dress("axis-dress",
102 P_("Axis Dress"),
103 P_("The line dress to use for rendering the axis of the projection scheme"),
104 ADG_DRESS_LINE,
105 G_PARAM_READWRITE);
106 g_object_class_install_property(gobject_class, PROP_AXIS_DRESS, param);
108 param = g_param_spec_enum("scheme",
109 P_("Projection Scheme"),
110 P_("The projection scheme to be represented"),
111 ADG_TYPE_PROJECTION_SCHEME,
112 ADG_PROJECTION_SCHEME_UNDEFINED,
113 G_PARAM_READWRITE);
114 g_object_class_install_property(gobject_class, PROP_SCHEME, param);
116 /* Initialize the private class data: the allocated struct is
117 * never freed as this type is registered statically, hence
118 * never destroyed. A better approach would be to use the old
119 * type initialization (no G_TYPE_DEFINE and friends) that
120 * allows to specify a custom class finalization method */
121 data_class = g_new(AdgProjectionClassPrivate, 1);
123 data_class->scheme = ADG_PROJECTION_SCHEME_UNDEFINED;
124 data_class->symbol = NULL;
125 data_class->axis = NULL;
126 data_class->extents.is_defined = FALSE;
128 klass->data_class = data_class;
131 static void
132 adg_projection_init(AdgProjection *projection)
134 AdgProjectionPrivate *data = adg_projection_get_instance_private(projection);
135 data->symbol_dress = ADG_DRESS_LINE;
136 data->axis_dress = ADG_DRESS_LINE;
137 data->scheme = ADG_PROJECTION_SCHEME_UNDEFINED;
140 static void
141 _adg_get_property(GObject *object, guint prop_id,
142 GValue *value, GParamSpec *pspec)
144 AdgProjectionPrivate *data = adg_projection_get_instance_private((AdgProjection *) object);
146 switch (prop_id) {
147 case PROP_SYMBOL_DRESS:
148 g_value_set_enum(value, data->symbol_dress);
149 break;
150 case PROP_AXIS_DRESS:
151 g_value_set_enum(value, data->axis_dress);
152 break;
153 case PROP_SCHEME:
154 g_value_set_enum(value, data->scheme);
155 break;
156 default:
157 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
158 break;
162 static void
163 _adg_set_property(GObject *object, guint prop_id,
164 const GValue *value, GParamSpec *pspec)
166 AdgProjectionPrivate *data = adg_projection_get_instance_private((AdgProjection *) object);
168 switch (prop_id) {
169 case PROP_SYMBOL_DRESS:
170 data->symbol_dress = g_value_get_enum(value);
171 break;
172 case PROP_AXIS_DRESS:
173 data->axis_dress = g_value_get_enum(value);
174 break;
175 case PROP_SCHEME:
176 data->scheme = g_value_get_enum(value);
177 adg_entity_invalidate((AdgEntity *) object);
178 break;
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
181 break;
187 * adg_projection_new:
188 * @scheme: the scheme represented by this projection
190 * Creates a new projection entity representing the selected @scheme.
191 * If @scheme is invalid, a projection symbol without a scheme is
192 * returned, that is #AdgProjection:scheme is set to
193 * #ADG_PROJECTION_SCHEME_UNDEFINED, and a warning is raised.
195 * Returns: (transfer full): the newly created projection entity.
197 * Since: 1.0
199 AdgProjection *
200 adg_projection_new(AdgProjectionScheme scheme)
202 return g_object_new(ADG_TYPE_PROJECTION, "scheme", scheme, NULL);
206 * adg_projection_set_symbol_dress:
207 * @projection: an #AdgProjection
208 * @dress: the new #AdgDress to use
210 * Sets a new line dress for rendering the symbol of @projection. The
211 * new dress must be a line dress: the check is done by calling
212 * adg_dress_are_related() with @dress and the old dress as
213 * arguments. Check out its documentation for further details.
215 * The default dress is a transparent line dress: the rendering
216 * callback will stroke the symbol using the default color with
217 * a predefined thickness.
219 * Since: 1.0
221 void
222 adg_projection_set_symbol_dress(AdgProjection *projection, AdgDress dress)
224 g_return_if_fail(ADG_IS_PROJECTION(projection));
225 g_object_set(projection, "symbol-dress", dress, NULL);
229 * adg_projection_get_symbol_dress:
230 * @projection: an #AdgProjection
232 * Gets the line dress to be used in stroking the symbol of @projection.
234 * Returns: (transfer none): the requested line dress.
236 * Since: 1.0
238 AdgDress
239 adg_projection_get_symbol_dress(AdgProjection *projection)
241 AdgProjectionPrivate *data;
243 g_return_val_if_fail(ADG_IS_PROJECTION(projection), ADG_DRESS_UNDEFINED);
245 data = adg_projection_get_instance_private(projection);
246 return data->symbol_dress;
250 * adg_projection_set_axis_dress:
251 * @projection: an #AdgProjection
252 * @dress: the new #AdgDress to use
254 * Sets a new line dress for rendering the axis of @projection.
255 * The new dress must be a line dress: the check is done by
256 * calling adg_dress_are_related() with @dress and the old
257 * dress as arguments. Check out its documentation for
258 * further details.
260 * The default dress is a transparent line dress: the rendering
261 * callback will stroke the axis using the default line style.
263 * Since: 1.0
265 void
266 adg_projection_set_axis_dress(AdgProjection *projection, AdgDress dress)
268 g_return_if_fail(ADG_IS_PROJECTION(projection));
269 g_object_set(projection, "axis-dress", dress, NULL);
273 * adg_projection_get_axis_dress:
274 * @projection: an #AdgProjection
276 * Gets the line dress to be used in stroking the axis of @projection.
278 * Returns: (transfer none): the requested line dress.
280 * Since: 1.0
282 AdgDress
283 adg_projection_get_axis_dress(AdgProjection *projection)
285 AdgProjectionPrivate *data;
287 g_return_val_if_fail(ADG_IS_PROJECTION(projection), ADG_DRESS_UNDEFINED);
289 data = adg_projection_get_instance_private(projection);
290 return data->axis_dress;
294 * adg_projection_set_scheme:
295 * @projection: an #AdgProjection
296 * @scheme: the new projection scheme
298 * Sets a new scheme on @projection. If @scheme is different
299 * from the old one, @projection is invalidated.
301 * Since: 1.0
303 void
304 adg_projection_set_scheme(AdgProjection *projection,
305 AdgProjectionScheme scheme)
307 g_return_if_fail(ADG_IS_PROJECTION(projection));
308 g_object_set(projection, "scheme", scheme, NULL);
312 * adg_projection_get_scheme:
313 * @projection: an #AdgProjection
315 * Gets the scheme represented by @projection.
317 * Returns: (transfer none): the scheme of @projection
319 * Since: 1.0
321 AdgProjectionScheme
322 adg_projection_get_scheme(AdgProjection *projection)
324 AdgProjectionPrivate *data;
326 g_return_val_if_fail(ADG_IS_PROJECTION(projection),
327 ADG_PROJECTION_SCHEME_UNDEFINED);
329 data = adg_projection_get_instance_private(projection);
330 return data->scheme;
334 static void
335 _adg_arrange(AdgEntity *entity)
337 AdgProjectionPrivate *data = adg_projection_get_instance_private((AdgProjection *) entity);
338 AdgProjectionClass *projection_class = ADG_PROJECTION_GET_CLASS(entity);
339 AdgProjectionClassPrivate *data_class = projection_class->data_class;
340 CpmlExtents extents;
342 _adg_arrange_class(projection_class, data->scheme);
343 cpml_extents_copy(&extents, &data_class->extents);
345 cpml_extents_transform(&extents, adg_entity_get_local_matrix(entity));
346 cpml_extents_transform(&extents, adg_entity_get_global_matrix(entity));
347 adg_entity_set_extents(entity, &extents);
350 static void
351 _adg_arrange_class(AdgProjectionClass *projection_class,
352 AdgProjectionScheme scheme)
354 AdgProjectionClassPrivate *data_class;
355 AdgPath *symbol, *axis;
357 data_class = projection_class->data_class;
359 if (data_class->scheme == scheme)
360 return;
362 if (data_class->symbol != NULL)
363 g_object_unref(data_class->symbol);
365 if (data_class->axis != NULL)
366 g_object_unref(data_class->axis);
368 data_class->scheme = scheme;
370 switch(scheme) {
371 case ADG_PROJECTION_SCHEME_UNDEFINED:
372 symbol = NULL;
373 axis = NULL;
374 break;
375 case ADG_PROJECTION_SCHEME_FIRST_ANGLE:
376 symbol = adg_path_new();
377 adg_path_move_to_explicit(symbol, 4, 19);
378 adg_path_line_to_explicit(symbol, 24, 24);
379 adg_path_line_to_explicit(symbol, 24, 4);
380 adg_path_line_to_explicit(symbol, 4, 9);
381 adg_path_close(symbol);
382 adg_path_move_to_explicit(symbol, 49, 14);
383 adg_path_arc_to_explicit(symbol, 29, 14, 49, 14);
384 adg_path_move_to_explicit(symbol, 44, 14);
385 adg_path_arc_to_explicit(symbol, 34, 14, 44, 14);
387 axis = adg_path_new();
388 adg_path_move_to_explicit(axis, 0, 14);
389 adg_path_line_to_explicit(axis, 53, 14);
390 adg_path_move_to_explicit(axis, 39, 0);
391 adg_path_line_to_explicit(axis, 39, 28);
392 break;
393 case ADG_PROJECTION_SCHEME_THIRD_ANGLE:
394 symbol = adg_path_new();
395 adg_path_move_to_explicit(symbol, 29, 19);
396 adg_path_line_to_explicit(symbol, 49, 24);
397 adg_path_line_to_explicit(symbol, 49, 4);
398 adg_path_line_to_explicit(symbol, 29, 9);
399 adg_path_close(symbol);
400 adg_path_move_to_explicit(symbol, 24, 14);
401 adg_path_arc_to_explicit(symbol, 4, 14, 24, 14);
402 adg_path_move_to_explicit(symbol, 19, 14);
403 adg_path_arc_to_explicit(symbol, 9, 14, 19, 14);
405 axis = adg_path_new();
406 adg_path_move_to_explicit(axis, 0, 14);
407 adg_path_line_to_explicit(axis, 53, 14);
408 adg_path_move_to_explicit(axis, 14, 0);
409 adg_path_line_to_explicit(axis, 14, 28);
410 break;
411 default:
412 g_return_if_reached();
413 break;
416 data_class->symbol = symbol;
417 data_class->axis = axis;
418 data_class->extents.is_defined = FALSE;
420 if (axis != NULL)
421 cpml_extents_add(&data_class->extents,
422 adg_trail_get_extents((AdgTrail *) axis));
424 if (symbol != NULL)
425 cpml_extents_add(&data_class->extents,
426 adg_trail_get_extents((AdgTrail *) symbol));
429 static void
430 _adg_render(AdgEntity *entity, cairo_t *cr)
432 AdgProjectionPrivate *data = adg_projection_get_instance_private((AdgProjection *) entity);
433 AdgProjectionClassPrivate *data_class = ADG_PROJECTION_GET_CLASS(entity)->data_class;
434 const cairo_path_t *cairo_path;
436 cairo_transform(cr, adg_entity_get_global_matrix(entity));
438 if (data_class->symbol != NULL) {
439 cairo_path = adg_trail_get_cairo_path((AdgTrail *) data_class->symbol);
441 cairo_save(cr);
442 cairo_transform(cr, adg_entity_get_local_matrix(entity));
443 cairo_append_path(cr, cairo_path);
444 cairo_restore(cr);
446 cairo_set_line_width(cr, 2);
447 adg_entity_apply_dress(entity, data->symbol_dress, cr);
449 cairo_stroke(cr);
452 if (data_class->axis != NULL) {
453 const gdouble dashes[] = { 5, 2, 1, 2 };
455 cairo_path = adg_trail_get_cairo_path((AdgTrail *) data_class->axis);
457 cairo_save(cr);
458 cairo_transform(cr, adg_entity_get_local_matrix(entity));
459 cairo_append_path(cr, cairo_path);
460 cairo_restore(cr);
462 cairo_set_line_width(cr, 1);
463 cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), -1.5);
464 adg_entity_apply_dress(entity, data->axis_dress, cr);
466 cairo_stroke(cr);