Code

added refcount logging to GC::Anchored and shared string printf in util
[inkscape.git] / src / sp-rect.cpp
1 #define __SP_RECT_C__
3 /*
4  * SVG <rect> implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
21 #include <display/curve.h>
22 #include <libnr/nr-matrix-div.h>
23 #include <libnr/nr-matrix-fns.h>
25 #include "attributes.h"
26 #include "style.h"
27 #include "sp-rect.h"
28 #include <glibmm/i18n.h>
29 #include "xml/repr.h"
31 #define noRECT_VERBOSE
33 static void sp_rect_class_init(SPRectClass *klass);
34 static void sp_rect_init(SPRect *rect);
36 static void sp_rect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
37 static void sp_rect_set(SPObject *object, unsigned key, gchar const *value);
38 static void sp_rect_update(SPObject *object, SPCtx *ctx, guint flags);
39 static Inkscape::XML::Node *sp_rect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
41 static gchar *sp_rect_description(SPItem *item);
42 static NR::Matrix sp_rect_set_transform(SPItem *item, NR::Matrix const &xform);
44 static void sp_rect_set_shape(SPShape *shape);
46 static SPShapeClass *parent_class;
48 GType
49 sp_rect_get_type(void)
50 {
51     static GType type = 0;
53     if (!type) {
54         GTypeInfo info = {
55             sizeof(SPRectClass),
56             NULL,   /* base_init */
57             NULL,   /* base_finalize */
58             (GClassInitFunc) sp_rect_class_init,
59             NULL,   /* class_finalize */
60             NULL,   /* class_data */
61             sizeof(SPRect),
62             16,     /* n_preallocs */
63             (GInstanceInitFunc) sp_rect_init,
64             NULL,   /* value_table */
65         };
66         type = g_type_register_static(SP_TYPE_SHAPE, "SPRect", &info, (GTypeFlags)0);
67     }
68     return type;
69 }
71 static void
72 sp_rect_class_init(SPRectClass *klass)
73 {
74     SPObjectClass *sp_object_class = (SPObjectClass *) klass;
75     SPItemClass *item_class = (SPItemClass *) klass;
76     SPShapeClass *shape_class = (SPShapeClass *) klass;
78     parent_class = (SPShapeClass *)g_type_class_ref(SP_TYPE_SHAPE);
80     sp_object_class->build = sp_rect_build;
81     sp_object_class->write = sp_rect_write;
82     sp_object_class->set = sp_rect_set;
83     sp_object_class->update = sp_rect_update;
85     item_class->description = sp_rect_description;
86     item_class->set_transform = sp_rect_set_transform;
88     shape_class->set_shape = sp_rect_set_shape;
89 }
91 static void
92 sp_rect_init(SPRect *rect)
93 {
94     /* Initializing to zero is automatic */
95     /* sp_svg_length_unset(&rect->x, SP_SVG_UNIT_NONE, 0.0, 0.0); */
96     /* sp_svg_length_unset(&rect->y, SP_SVG_UNIT_NONE, 0.0, 0.0); */
97     /* sp_svg_length_unset(&rect->width, SP_SVG_UNIT_NONE, 0.0, 0.0); */
98     /* sp_svg_length_unset(&rect->height, SP_SVG_UNIT_NONE, 0.0, 0.0); */
99     /* sp_svg_length_unset(&rect->rx, SP_SVG_UNIT_NONE, 0.0, 0.0); */
100     /* sp_svg_length_unset(&rect->ry, SP_SVG_UNIT_NONE, 0.0, 0.0); */
103 static void
104 sp_rect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
106     SPRect *rect = SP_RECT(object);
108     if (((SPObjectClass *) parent_class)->build)
109         ((SPObjectClass *) parent_class)->build(object, document, repr);
111     sp_object_read_attr(object, "x");
112     sp_object_read_attr(object, "y");
113     sp_object_read_attr(object, "width");
114     sp_object_read_attr(object, "height");
115     sp_object_read_attr(object, "rx");
116     sp_object_read_attr(object, "ry");
118     Inkscape::Version const version = sp_object_get_sodipodi_version(object);
120     if ( version.major == 0 && version.minor == 29 ) {
121         if (rect->rx._set && rect->ry._set) {
122             /* 0.29 treated 0.0 radius as missing value */
123             if ((rect->rx.value != 0.0) && (rect->ry.value == 0.0)) {
124                 repr->setAttribute("ry", NULL);
125                 sp_object_read_attr(object, "ry");
126             } else if ((rect->ry.value != 0.0) && (rect->rx.value == 0.0)) {
127                 repr->setAttribute("rx", NULL);
128                 sp_object_read_attr(object, "rx");
129             }
130         }
131     }
134 static void
135 sp_rect_set(SPObject *object, unsigned key, gchar const *value)
137     SPRect *rect = SP_RECT(object);
139     /* fixme: We need real error processing some time */
141     switch (key) {
142         case SP_ATTR_X:
143             rect->x.readOrUnset(value);
144             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
145             break;
146         case SP_ATTR_Y:
147             rect->y.readOrUnset(value);
148             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
149             break;
150         case SP_ATTR_WIDTH:
151             if (!rect->width.read(value) || rect->width.value < 0.0) {
152                 rect->width.unset();
153             }
154             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
155             break;
156         case SP_ATTR_HEIGHT:
157             if (!rect->height.read(value) || rect->height.value < 0.0) {
158                 rect->height.unset();
159             }
160             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
161             break;
162         case SP_ATTR_RX:
163             if (!rect->rx.read(value) || rect->rx.value < 0.0) {
164                 rect->rx.unset();
165             }
166             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
167             break;
168         case SP_ATTR_RY:
169             if (!rect->ry.read(value) || rect->ry.value < 0.0) {
170                 rect->ry.unset();
171             }
172             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
173             break;
174         default:
175             if (((SPObjectClass *) parent_class)->set)
176                 ((SPObjectClass *) parent_class)->set(object, key, value);
177             break;
178     }
181 static void
182 sp_rect_update(SPObject *object, SPCtx *ctx, guint flags)
184     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
185         SPRect *rect = (SPRect *) object;
186         SPStyle *style = object->style;
187         SPItemCtx const *ictx = (SPItemCtx const *) ctx;
188         double const d = NR::expansion(ictx->i2vp);
189         double const w = (ictx->vp.x1 - ictx->vp.x0) / d;
190         double const h = (ictx->vp.y1 - ictx->vp.y0) / d;
191         double const em = style->font_size.computed;
192         double const ex = 0.5 * em;  // fixme: get x height from pango or libnrtype.
193         rect->x.update(em, ex, w);
194         rect->y.update(em, ex, h);
195         rect->width.update(em, ex, w);
196         rect->height.update(em, ex, h);
197         rect->rx.update(em, ex, w);
198         rect->ry.update(em, ex, h);
199         sp_shape_set_shape((SPShape *) object);
200         flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // since we change the description, it's not a "just translation" anymore
201     }
203     if (((SPObjectClass *) parent_class)->update)
204         ((SPObjectClass *) parent_class)->update(object, ctx, flags);
207 static Inkscape::XML::Node *
208 sp_rect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
210     SPRect *rect = SP_RECT(object);
212     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
213         repr = sp_repr_new("svg:rect");
214     }
216     sp_repr_set_svg_double(repr, "width", rect->width.computed);
217     sp_repr_set_svg_double(repr, "height", rect->height.computed);
218     if (rect->rx._set) sp_repr_set_svg_double(repr, "rx", rect->rx.computed);
219     if (rect->ry._set) sp_repr_set_svg_double(repr, "ry", rect->ry.computed);
220     sp_repr_set_svg_double(repr, "x", rect->x.computed);
221     sp_repr_set_svg_double(repr, "y", rect->y.computed);
223     if (((SPObjectClass *) parent_class)->write)
224         ((SPObjectClass *) parent_class)->write(object, repr, flags);
226     return repr;
229 static gchar *
230 sp_rect_description(SPItem *item)
232     g_return_val_if_fail(SP_IS_RECT(item), NULL);
234     return g_strdup(_("<b>Rectangle</b>"));
237 #define C1 0.554
239 static void
240 sp_rect_set_shape(SPShape *shape)
242     SPRect *rect = (SPRect *) shape;
244     if ((rect->height.computed < 1e-18) || (rect->width.computed < 1e-18)) return;
246     SPCurve *c = sp_curve_new();
248     double const x = rect->x.computed;
249     double const y = rect->y.computed;
250     double const w = rect->width.computed;
251     double const h = rect->height.computed;
252     double const w2 = w / 2;
253     double const h2 = h / 2;
254     double const rx = std::min(( rect->rx._set
255                                  ? rect->rx.computed
256                                  : ( rect->ry._set
257                                      ? rect->ry.computed
258                                      : 0.0 ) ),
259                                .5 * rect->width.computed);
260     double const ry = std::min(( rect->ry._set
261                                  ? rect->ry.computed
262                                  : ( rect->rx._set
263                                      ? rect->rx.computed
264                                      : 0.0 ) ),
265                                .5 * rect->height.computed);
266     /* TODO: Handle negative rx or ry as per
267      * http://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute once Inkscape has proper error
268      * handling (see http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing).
269      */
271     /* We don't use proper circular/elliptical arcs, but bezier curves can approximate a 90-degree
272      * arc fairly well.
273      */
274     if ((rx > 1e-18) && (ry > 1e-18)) {
275         sp_curve_moveto(c, x + rx, y);
276         if (rx < w2) sp_curve_lineto(c, x + w - rx, y);
277         sp_curve_curveto(c, x + w - rx * (1 - C1), y,     x + w, y + ry * (1 - C1),       x + w, y + ry);
278         if (ry < h2) sp_curve_lineto(c, x + w, y + h - ry);
279         sp_curve_curveto(c, x + w, y + h - ry * (1 - C1),     x + w - rx * (1 - C1), y + h,       x + w - rx, y + h);
280         if (rx < w2) sp_curve_lineto(c, x + rx, y + h);
281         sp_curve_curveto(c, x + rx * (1 - C1), y + h,     x, y + h - ry * (1 - C1),       x, y + h - ry);
282         if (ry < h2) sp_curve_lineto(c, x, y + ry);
283         sp_curve_curveto(c, x, y + ry * (1 - C1),     x + rx * (1 - C1), y,       x + rx, y);
284     } else {
285         sp_curve_moveto(c, x + 0.0, y + 0.0);
286         sp_curve_lineto(c, x + w, y + 0.0);
287         sp_curve_lineto(c, x + w, y + h);
288         sp_curve_lineto(c, x + 0.0, y + h);
289         sp_curve_lineto(c, x + 0.0, y + 0.0);
290     }
292     sp_curve_closepath_current(c);
293     sp_shape_set_curve_insync(SP_SHAPE(rect), c, TRUE);
294     sp_curve_unref(c);
297 /* fixme: Think (Lauris) */
299 void
300 sp_rect_position_set(SPRect *rect, gdouble x, gdouble y, gdouble width, gdouble height)
302     g_return_if_fail(rect != NULL);
303     g_return_if_fail(SP_IS_RECT(rect));
305     rect->x.computed = x;
306     rect->y.computed = y;
307     rect->width.computed = width;
308     rect->height.computed = height;
310     SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
313 void
314 sp_rect_set_rx(SPRect *rect, gboolean set, gdouble value)
316     g_return_if_fail(rect != NULL);
317     g_return_if_fail(SP_IS_RECT(rect));
319     rect->rx._set = set;
320     if (set) rect->rx.computed = value;
322     SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
325 void
326 sp_rect_set_ry(SPRect *rect, gboolean set, gdouble value)
328     g_return_if_fail(rect != NULL);
329     g_return_if_fail(SP_IS_RECT(rect));
331     rect->ry._set = set;
332     if (set) rect->ry.computed = value;
334     SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
337 /*
338  * Initially we'll do:
339  * Transform x, y, set x, y, clear translation
340  */
342 /* fixme: Use preferred units somehow (Lauris) */
343 /* fixme: Alternately preserve whatever units there are (lauris) */
345 static NR::Matrix
346 sp_rect_set_transform(SPItem *item, NR::Matrix const &xform)
348     SPRect *rect = SP_RECT(item);
350     /* Calculate rect start in parent coords. */
351     NR::Point pos( NR::Point(rect->x.computed, rect->y.computed) * xform );
353     /* This function takes care of translation and scaling, we return whatever parts we can't
354        handle. */
355     NR::Matrix ret(NR::transform(xform));
356     gdouble const sw = hypot(ret[0], ret[1]);
357     gdouble const sh = hypot(ret[2], ret[3]);
358     if (sw > 1e-9) {
359         ret[0] /= sw;
360         ret[1] /= sw;
361     } else {
362         ret[0] = 1.0;
363         ret[1] = 0.0;
364     }
365     if (sh > 1e-9) {
366         ret[2] /= sh;
367         ret[3] /= sh;
368     } else {
369         ret[2] = 0.0;
370         ret[3] = 1.0;
371     }
373     /* fixme: Would be nice to preserve units here */
374     rect->width = rect->width.computed * sw;
375     rect->height = rect->height.computed * sh;
376     if (rect->rx._set) {
377         rect->rx = rect->rx.computed * sw;
378     }
379     if (rect->ry._set) {
380         rect->ry = rect->ry.computed * sh;
381     }
383     /* Find start in item coords */
384     pos = pos * ret.inverse();
385     rect->x = pos[NR::X];
386     rect->y = pos[NR::Y];
388     sp_rect_set_shape(rect);
390     // Adjust stroke width
391     sp_item_adjust_stroke(item, sqrt(fabs(sw * sh)));
393     // Adjust pattern fill
394     sp_item_adjust_pattern(item, xform / ret);
396     // Adjust gradient fill
397     sp_item_adjust_gradient(item, xform / ret);
399     item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
401     return ret;
405 /**
406 Returns the ratio in which the vector from p0 to p1 is stretched by transform
407  */
408 static gdouble
409 vector_stretch(NR::Point p0, NR::Point p1, NR::Matrix xform)
411     if (p0 == p1)
412         return 0;
413     return (NR::distance(p0 * xform, p1 * xform) / NR::distance(p0, p1));
416 void
417 sp_rect_set_visible_rx(SPRect *rect, gdouble rx)
419     if (rx == 0) {
420         rect->rx.computed = 0;
421         rect->rx._set = false;
422     } else {
423         rect->rx.computed = rx / vector_stretch(
424             NR::Point(rect->x.computed + 1, rect->y.computed),
425             NR::Point(rect->x.computed, rect->y.computed),
426             SP_ITEM(rect)->transform);
427         rect->rx._set = true;
428     }
429     SP_OBJECT(rect)->updateRepr();
432 void
433 sp_rect_set_visible_ry(SPRect *rect, gdouble ry)
435     if (ry == 0) {
436         rect->ry.computed = 0;
437         rect->ry._set = false;
438     } else {
439         rect->ry.computed = ry / vector_stretch(
440             NR::Point(rect->x.computed, rect->y.computed + 1),
441             NR::Point(rect->x.computed, rect->y.computed),
442             SP_ITEM(rect)->transform);
443         rect->ry._set = true;
444     }
445     SP_OBJECT(rect)->updateRepr();
448 gdouble
449 sp_rect_get_visible_rx(SPRect *rect)
451     if (!rect->rx._set)
452         return 0;
453     return rect->rx.computed * vector_stretch(
454         NR::Point(rect->x.computed + 1, rect->y.computed),
455         NR::Point(rect->x.computed, rect->y.computed),
456         SP_ITEM(rect)->transform);
459 gdouble
460 sp_rect_get_visible_ry(SPRect *rect)
462     if (!rect->ry._set)
463         return 0;
464     return rect->ry.computed * vector_stretch(
465         NR::Point(rect->x.computed, rect->y.computed + 1),
466         NR::Point(rect->x.computed, rect->y.computed),
467         SP_ITEM(rect)->transform);
470 void
471 sp_rect_compensate_rxry(SPRect *rect, NR::Matrix xform)
473     if (rect->rx.computed == 0 && rect->ry.computed == 0)
474         return; // nothing to compensate
476     // test unit vectors to find out compensation:
477     NR::Point c(rect->x.computed, rect->y.computed);
478     NR::Point cx = c + NR::Point(1, 0);
479     NR::Point cy = c + NR::Point(0, 1);
481     // apply previous transform if any
482     c *= SP_ITEM(rect)->transform;
483     cx *= SP_ITEM(rect)->transform;
484     cy *= SP_ITEM(rect)->transform;
486     // find out stretches that we need to compensate
487     gdouble eX = vector_stretch(cx, c, xform);
488     gdouble eY = vector_stretch(cy, c, xform);
490     // If only one of the radii is set, set both radii so they have the same visible length
491     // This is needed because if we just set them the same length in SVG, they might end up unequal because of transform
492     if ((rect->rx._set && !rect->ry._set) || (rect->ry._set && !rect->rx._set)) {
493         gdouble r = MAX(rect->rx.computed, rect->ry.computed);
494         rect->rx.computed = r / eX;
495         rect->ry.computed = r / eY;
496     } else {
497         rect->rx.computed = rect->rx.computed / eX;
498         rect->ry.computed = rect->ry.computed / eY;
499     }
501     // Note that a radius may end up larger than half-side if the rect is scaled down;
502     // that's ok because this preserves the intended radii in case the rect is enlarged again,
503     // and set_shape will take care of trimming too large radii when generating d=
505     rect->rx._set = rect->ry._set = true;
508 void
509 sp_rect_set_visible_width(SPRect *rect, gdouble width)
511     rect->width.computed = width / vector_stretch(
512         NR::Point(rect->x.computed + 1, rect->y.computed),
513         NR::Point(rect->x.computed, rect->y.computed),
514         SP_ITEM(rect)->transform);
515     rect->width._set = true;
516     SP_OBJECT(rect)->updateRepr();
519 void
520 sp_rect_set_visible_height(SPRect *rect, gdouble height)
522     rect->height.computed = height / vector_stretch(
523         NR::Point(rect->x.computed, rect->y.computed + 1),
524         NR::Point(rect->x.computed, rect->y.computed),
525         SP_ITEM(rect)->transform);
526     rect->height._set = true;
527     SP_OBJECT(rect)->updateRepr();
530 gdouble
531 sp_rect_get_visible_width(SPRect *rect)
533     if (!rect->width._set)
534         return 0;
535     return rect->width.computed * vector_stretch(
536         NR::Point(rect->x.computed + 1, rect->y.computed),
537         NR::Point(rect->x.computed, rect->y.computed),
538         SP_ITEM(rect)->transform);
541 gdouble
542 sp_rect_get_visible_height(SPRect *rect)
544     if (!rect->height._set)
545         return 0;
546     return rect->height.computed * vector_stretch(
547         NR::Point(rect->x.computed, rect->y.computed + 1),
548         NR::Point(rect->x.computed, rect->y.computed),
549         SP_ITEM(rect)->transform);
552 /*
553   Local Variables:
554   mode:c++
555   c-file-style:"stroustrup"
556   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
557   indent-tabs-mode:nil
558   fill-column:99
559   End:
560 */
561 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :