1 /*
2 * SVG <rect> implementation
3 *
4 * Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * bulia byak <buliabyak@users.sf.net>
7 *
8 * Copyright (C) 1999-2002 Lauris Kaplinski
9 * Copyright (C) 2000-2001 Ximian, Inc.
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
19 #include <display/curve.h>
20 #include <libnr/nr-matrix-ops.h>
21 #include <libnr/nr-matrix-fns.h>
22 #include <2geom/rect.h>
24 #include "inkscape.h"
25 #include "document.h"
26 #include "attributes.h"
27 #include "style.h"
28 #include "sp-rect.h"
29 #include <glibmm/i18n.h>
30 #include "xml/repr.h"
31 #include "sp-guide.h"
32 #include "preferences.h"
34 #define noRECT_VERBOSE
36 static void sp_rect_class_init(SPRectClass *klass);
37 static void sp_rect_init(SPRect *rect);
39 static void sp_rect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
40 static void sp_rect_set(SPObject *object, unsigned key, gchar const *value);
41 static void sp_rect_update(SPObject *object, SPCtx *ctx, guint flags);
42 static Inkscape::XML::Node *sp_rect_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
44 static gchar *sp_rect_description(SPItem *item);
45 static Geom::Matrix sp_rect_set_transform(SPItem *item, Geom::Matrix const &xform);
46 static void sp_rect_convert_to_guides(SPItem *item);
48 static void sp_rect_set_shape(SPShape *shape);
49 static void sp_rect_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
51 static SPShapeClass *parent_class;
53 GType
54 sp_rect_get_type(void)
55 {
56 static GType type = 0;
58 if (!type) {
59 GTypeInfo info = {
60 sizeof(SPRectClass),
61 NULL, /* base_init */
62 NULL, /* base_finalize */
63 (GClassInitFunc) sp_rect_class_init,
64 NULL, /* class_finalize */
65 NULL, /* class_data */
66 sizeof(SPRect),
67 16, /* n_preallocs */
68 (GInstanceInitFunc) sp_rect_init,
69 NULL, /* value_table */
70 };
71 type = g_type_register_static(SP_TYPE_SHAPE, "SPRect", &info, (GTypeFlags)0);
72 }
73 return type;
74 }
76 static void
77 sp_rect_class_init(SPRectClass *klass)
78 {
79 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
80 SPItemClass *item_class = (SPItemClass *) klass;
81 SPShapeClass *shape_class = (SPShapeClass *) klass;
83 parent_class = (SPShapeClass *)g_type_class_ref(SP_TYPE_SHAPE);
85 sp_object_class->build = sp_rect_build;
86 sp_object_class->write = sp_rect_write;
87 sp_object_class->set = sp_rect_set;
88 sp_object_class->update = sp_rect_update;
90 item_class->description = sp_rect_description;
91 item_class->set_transform = sp_rect_set_transform;
92 item_class->convert_to_guides = sp_rect_convert_to_guides;
93 item_class->snappoints = sp_rect_snappoints; //override the default sp_shape_snappoints; see sp_rect_snappoints for details
95 shape_class->set_shape = sp_rect_set_shape;
96 }
98 static void
99 sp_rect_init(SPRect */*rect*/)
100 {
101 /* Initializing to zero is automatic */
102 /* sp_svg_length_unset(&rect->x, SP_SVG_UNIT_NONE, 0.0, 0.0); */
103 /* sp_svg_length_unset(&rect->y, SP_SVG_UNIT_NONE, 0.0, 0.0); */
104 /* sp_svg_length_unset(&rect->width, SP_SVG_UNIT_NONE, 0.0, 0.0); */
105 /* sp_svg_length_unset(&rect->height, SP_SVG_UNIT_NONE, 0.0, 0.0); */
106 /* sp_svg_length_unset(&rect->rx, SP_SVG_UNIT_NONE, 0.0, 0.0); */
107 /* sp_svg_length_unset(&rect->ry, SP_SVG_UNIT_NONE, 0.0, 0.0); */
108 }
110 static void
111 sp_rect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
112 {
113 if (((SPObjectClass *) parent_class)->build)
114 ((SPObjectClass *) parent_class)->build(object, document, repr);
116 sp_object_read_attr(object, "x");
117 sp_object_read_attr(object, "y");
118 sp_object_read_attr(object, "width");
119 sp_object_read_attr(object, "height");
120 sp_object_read_attr(object, "rx");
121 sp_object_read_attr(object, "ry");
122 }
124 static void
125 sp_rect_set(SPObject *object, unsigned key, gchar const *value)
126 {
127 SPRect *rect = SP_RECT(object);
129 /* fixme: We need real error processing some time */
131 switch (key) {
132 case SP_ATTR_X:
133 rect->x.readOrUnset(value);
134 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
135 break;
136 case SP_ATTR_Y:
137 rect->y.readOrUnset(value);
138 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
139 break;
140 case SP_ATTR_WIDTH:
141 if (!rect->width.read(value) || rect->width.value < 0.0) {
142 rect->width.unset();
143 }
144 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
145 break;
146 case SP_ATTR_HEIGHT:
147 if (!rect->height.read(value) || rect->height.value < 0.0) {
148 rect->height.unset();
149 }
150 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
151 break;
152 case SP_ATTR_RX:
153 if (!rect->rx.read(value) || rect->rx.value < 0.0) {
154 rect->rx.unset();
155 }
156 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
157 break;
158 case SP_ATTR_RY:
159 if (!rect->ry.read(value) || rect->ry.value < 0.0) {
160 rect->ry.unset();
161 }
162 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
163 break;
164 default:
165 if (((SPObjectClass *) parent_class)->set)
166 ((SPObjectClass *) parent_class)->set(object, key, value);
167 break;
168 }
169 }
171 static void
172 sp_rect_update(SPObject *object, SPCtx *ctx, guint flags)
173 {
174 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
175 SPRect *rect = (SPRect *) object;
176 SPStyle *style = object->style;
177 SPItemCtx const *ictx = (SPItemCtx const *) ctx;
178 double const w = (ictx->vp.x1 - ictx->vp.x0);
179 double const h = (ictx->vp.y1 - ictx->vp.y0);
180 double const em = style->font_size.computed;
181 double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype.
182 rect->x.update(em, ex, w);
183 rect->y.update(em, ex, h);
184 rect->width.update(em, ex, w);
185 rect->height.update(em, ex, h);
186 rect->rx.update(em, ex, w);
187 rect->ry.update(em, ex, h);
188 sp_shape_set_shape((SPShape *) object);
189 flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // since we change the description, it's not a "just translation" anymore
190 }
192 if (((SPObjectClass *) parent_class)->update)
193 ((SPObjectClass *) parent_class)->update(object, ctx, flags);
194 }
196 static Inkscape::XML::Node *
197 sp_rect_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
198 {
199 SPRect *rect = SP_RECT(object);
201 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
202 repr = xml_doc->createElement("svg:rect");
203 }
205 sp_repr_set_svg_double(repr, "width", rect->width.computed);
206 sp_repr_set_svg_double(repr, "height", rect->height.computed);
207 if (rect->rx._set) sp_repr_set_svg_double(repr, "rx", rect->rx.computed);
208 if (rect->ry._set) sp_repr_set_svg_double(repr, "ry", rect->ry.computed);
209 sp_repr_set_svg_double(repr, "x", rect->x.computed);
210 sp_repr_set_svg_double(repr, "y", rect->y.computed);
212 if (((SPObjectClass *) parent_class)->write)
213 ((SPObjectClass *) parent_class)->write(object, xml_doc, repr, flags);
215 return repr;
216 }
218 static gchar *
219 sp_rect_description(SPItem *item)
220 {
221 g_return_val_if_fail(SP_IS_RECT(item), NULL);
223 return g_strdup(_("<b>Rectangle</b>"));
224 }
226 #define C1 0.554
228 static void
229 sp_rect_set_shape(SPShape *shape)
230 {
231 SPRect *rect = (SPRect *) shape;
233 if ((rect->height.computed < 1e-18) || (rect->width.computed < 1e-18)) {
234 sp_shape_set_curve_insync(SP_SHAPE(rect), NULL, TRUE);
235 return;
236 }
238 SPCurve *c = new SPCurve();
240 double const x = rect->x.computed;
241 double const y = rect->y.computed;
242 double const w = rect->width.computed;
243 double const h = rect->height.computed;
244 double const w2 = w / 2;
245 double const h2 = h / 2;
246 double const rx = std::min(( rect->rx._set
247 ? rect->rx.computed
248 : ( rect->ry._set
249 ? rect->ry.computed
250 : 0.0 ) ),
251 .5 * rect->width.computed);
252 double const ry = std::min(( rect->ry._set
253 ? rect->ry.computed
254 : ( rect->rx._set
255 ? rect->rx.computed
256 : 0.0 ) ),
257 .5 * rect->height.computed);
258 /* TODO: Handle negative rx or ry as per
259 * http://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute once Inkscape has proper error
260 * handling (see http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing).
261 */
263 /* We don't use proper circular/elliptical arcs, but bezier curves can approximate a 90-degree
264 * arc fairly well.
265 */
266 if ((rx > 1e-18) && (ry > 1e-18)) {
267 c->moveto(x + rx, y);
268 if (rx < w2) c->lineto(x + w - rx, y);
269 c->curveto(x + w - rx * (1 - C1), y, x + w, y + ry * (1 - C1), x + w, y + ry);
270 if (ry < h2) c->lineto(x + w, y + h - ry);
271 c->curveto(x + w, y + h - ry * (1 - C1), x + w - rx * (1 - C1), y + h, x + w - rx, y + h);
272 if (rx < w2) c->lineto(x + rx, y + h);
273 c->curveto(x + rx * (1 - C1), y + h, x, y + h - ry * (1 - C1), x, y + h - ry);
274 if (ry < h2) c->lineto(x, y + ry);
275 c->curveto(x, y + ry * (1 - C1), x + rx * (1 - C1), y, x + rx, y);
276 } else {
277 c->moveto(x + 0.0, y + 0.0);
278 c->lineto(x + w, y + 0.0);
279 c->lineto(x + w, y + h);
280 c->lineto(x + 0.0, y + h);
281 }
283 c->closepath();
284 sp_shape_set_curve_insync(SP_SHAPE(rect), c, TRUE);
285 c->unref();
286 }
288 /* fixme: Think (Lauris) */
290 void
291 sp_rect_position_set(SPRect *rect, gdouble x, gdouble y, gdouble width, gdouble height)
292 {
293 g_return_if_fail(rect != NULL);
294 g_return_if_fail(SP_IS_RECT(rect));
296 rect->x.computed = x;
297 rect->y.computed = y;
298 rect->width.computed = width;
299 rect->height.computed = height;
301 SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
302 }
304 void
305 sp_rect_set_rx(SPRect *rect, gboolean set, gdouble value)
306 {
307 g_return_if_fail(rect != NULL);
308 g_return_if_fail(SP_IS_RECT(rect));
310 rect->rx._set = set;
311 if (set) rect->rx.computed = value;
313 SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
314 }
316 void
317 sp_rect_set_ry(SPRect *rect, gboolean set, gdouble value)
318 {
319 g_return_if_fail(rect != NULL);
320 g_return_if_fail(SP_IS_RECT(rect));
322 rect->ry._set = set;
323 if (set) rect->ry.computed = value;
325 SP_OBJECT(rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
326 }
328 /*
329 * Initially we'll do:
330 * Transform x, y, set x, y, clear translation
331 */
333 /* fixme: Use preferred units somehow (Lauris) */
334 /* fixme: Alternately preserve whatever units there are (lauris) */
336 static Geom::Matrix
337 sp_rect_set_transform(SPItem *item, Geom::Matrix const &xform)
338 {
339 SPRect *rect = SP_RECT(item);
341 /* Calculate rect start in parent coords. */
342 Geom::Point pos( Geom::Point(rect->x.computed, rect->y.computed) * xform );
344 /* This function takes care of translation and scaling, we return whatever parts we can't
345 handle. */
346 Geom::Matrix ret(Geom::Matrix(xform).without_translation());
347 gdouble const sw = hypot(ret[0], ret[1]);
348 gdouble const sh = hypot(ret[2], ret[3]);
349 if (sw > 1e-9) {
350 ret[0] /= sw;
351 ret[1] /= sw;
352 } else {
353 ret[0] = 1.0;
354 ret[1] = 0.0;
355 }
356 if (sh > 1e-9) {
357 ret[2] /= sh;
358 ret[3] /= sh;
359 } else {
360 ret[2] = 0.0;
361 ret[3] = 1.0;
362 }
364 /* fixme: Would be nice to preserve units here */
365 rect->width = rect->width.computed * sw;
366 rect->height = rect->height.computed * sh;
367 if (rect->rx._set) {
368 rect->rx = rect->rx.computed * sw;
369 }
370 if (rect->ry._set) {
371 rect->ry = rect->ry.computed * sh;
372 }
374 /* Find start in item coords */
375 pos = pos * ret.inverse();
376 rect->x = pos[Geom::X];
377 rect->y = pos[Geom::Y];
379 sp_rect_set_shape(rect);
381 // Adjust stroke width
382 sp_item_adjust_stroke(item, sqrt(fabs(sw * sh)));
384 // Adjust pattern fill
385 sp_item_adjust_pattern(item, xform * ret.inverse());
387 // Adjust gradient fill
388 sp_item_adjust_gradient(item, xform * ret.inverse());
390 item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
392 return ret;
393 }
396 /**
397 Returns the ratio in which the vector from p0 to p1 is stretched by transform
398 */
399 static gdouble
400 vector_stretch(Geom::Point p0, Geom::Point p1, Geom::Matrix xform)
401 {
402 if (p0 == p1)
403 return 0;
404 return (Geom::distance(p0 * xform, p1 * xform) / Geom::distance(p0, p1));
405 }
407 void
408 sp_rect_set_visible_rx(SPRect *rect, gdouble rx)
409 {
410 if (rx == 0) {
411 rect->rx.computed = 0;
412 rect->rx._set = false;
413 } else {
414 rect->rx.computed = rx / vector_stretch(
415 Geom::Point(rect->x.computed + 1, rect->y.computed),
416 Geom::Point(rect->x.computed, rect->y.computed),
417 SP_ITEM(rect)->transform);
418 rect->rx._set = true;
419 }
420 SP_OBJECT(rect)->updateRepr();
421 }
423 void
424 sp_rect_set_visible_ry(SPRect *rect, gdouble ry)
425 {
426 if (ry == 0) {
427 rect->ry.computed = 0;
428 rect->ry._set = false;
429 } else {
430 rect->ry.computed = ry / vector_stretch(
431 Geom::Point(rect->x.computed, rect->y.computed + 1),
432 Geom::Point(rect->x.computed, rect->y.computed),
433 SP_ITEM(rect)->transform);
434 rect->ry._set = true;
435 }
436 SP_OBJECT(rect)->updateRepr();
437 }
439 gdouble
440 sp_rect_get_visible_rx(SPRect *rect)
441 {
442 if (!rect->rx._set)
443 return 0;
444 return rect->rx.computed * vector_stretch(
445 Geom::Point(rect->x.computed + 1, rect->y.computed),
446 Geom::Point(rect->x.computed, rect->y.computed),
447 SP_ITEM(rect)->transform);
448 }
450 gdouble
451 sp_rect_get_visible_ry(SPRect *rect)
452 {
453 if (!rect->ry._set)
454 return 0;
455 return rect->ry.computed * vector_stretch(
456 Geom::Point(rect->x.computed, rect->y.computed + 1),
457 Geom::Point(rect->x.computed, rect->y.computed),
458 SP_ITEM(rect)->transform);
459 }
461 Geom::Rect
462 sp_rect_get_rect (SPRect *rect)
463 {
464 Geom::Point p0 = Geom::Point(rect->x.computed, rect->y.computed);
465 Geom::Point p2 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed);
466 return Geom::Rect(p0, p2);
467 }
469 void
470 sp_rect_compensate_rxry(SPRect *rect, Geom::Matrix xform)
471 {
472 if (rect->rx.computed == 0 && rect->ry.computed == 0)
473 return; // nothing to compensate
475 // test unit vectors to find out compensation:
476 Geom::Point c(rect->x.computed, rect->y.computed);
477 Geom::Point cx = c + Geom::Point(1, 0);
478 Geom::Point cy = c + Geom::Point(0, 1);
480 // apply previous transform if any
481 c *= SP_ITEM(rect)->transform;
482 cx *= SP_ITEM(rect)->transform;
483 cy *= SP_ITEM(rect)->transform;
485 // find out stretches that we need to compensate
486 gdouble eX = vector_stretch(cx, c, xform);
487 gdouble eY = vector_stretch(cy, c, xform);
489 // If only one of the radii is set, set both radii so they have the same visible length
490 // This is needed because if we just set them the same length in SVG, they might end up unequal because of transform
491 if ((rect->rx._set && !rect->ry._set) || (rect->ry._set && !rect->rx._set)) {
492 gdouble r = MAX(rect->rx.computed, rect->ry.computed);
493 rect->rx.computed = r / eX;
494 rect->ry.computed = r / eY;
495 } else {
496 rect->rx.computed = rect->rx.computed / eX;
497 rect->ry.computed = rect->ry.computed / eY;
498 }
500 // Note that a radius may end up larger than half-side if the rect is scaled down;
501 // that's ok because this preserves the intended radii in case the rect is enlarged again,
502 // and set_shape will take care of trimming too large radii when generating d=
504 rect->rx._set = rect->ry._set = true;
505 }
507 void
508 sp_rect_set_visible_width(SPRect *rect, gdouble width)
509 {
510 rect->width.computed = width / vector_stretch(
511 Geom::Point(rect->x.computed + 1, rect->y.computed),
512 Geom::Point(rect->x.computed, rect->y.computed),
513 SP_ITEM(rect)->transform);
514 rect->width._set = true;
515 SP_OBJECT(rect)->updateRepr();
516 }
518 void
519 sp_rect_set_visible_height(SPRect *rect, gdouble height)
520 {
521 rect->height.computed = height / vector_stretch(
522 Geom::Point(rect->x.computed, rect->y.computed + 1),
523 Geom::Point(rect->x.computed, rect->y.computed),
524 SP_ITEM(rect)->transform);
525 rect->height._set = true;
526 SP_OBJECT(rect)->updateRepr();
527 }
529 gdouble
530 sp_rect_get_visible_width(SPRect *rect)
531 {
532 if (!rect->width._set)
533 return 0;
534 return rect->width.computed * vector_stretch(
535 Geom::Point(rect->x.computed + 1, rect->y.computed),
536 Geom::Point(rect->x.computed, rect->y.computed),
537 SP_ITEM(rect)->transform);
538 }
540 gdouble
541 sp_rect_get_visible_height(SPRect *rect)
542 {
543 if (!rect->height._set)
544 return 0;
545 return rect->height.computed * vector_stretch(
546 Geom::Point(rect->x.computed, rect->y.computed + 1),
547 Geom::Point(rect->x.computed, rect->y.computed),
548 SP_ITEM(rect)->transform);
549 }
551 /**
552 * Sets the snappoint p to the unrounded corners of the rectangle
553 */
554 static void sp_rect_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
555 {
556 /* This method overrides sp_shape_snappoints, which is the default for any shape. The default method
557 returns all eight points along the path of a rounded rectangle, but not the real corners. Snapping
558 the startpoint and endpoint of each rounded corner is not very useful and really confusing. Instead
559 we could snap either the real corners, or not snap at all. Bulia Byak opted to snap the real corners,
560 but it should be noted that this might be confusing in some cases with relatively large radii. With
561 small radii though the user will easily understand which point is snapping. */
563 g_assert(item != NULL);
564 g_assert(SP_IS_RECT(item));
566 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
567 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
568 return;
569 }
571 SPRect *rect = SP_RECT(item);
573 Geom::Matrix const i2d (sp_item_i2d_affine (item));
575 Geom::Point p0 = Geom::Point(rect->x.computed, rect->y.computed) * i2d;
576 Geom::Point p1 = Geom::Point(rect->x.computed, rect->y.computed + rect->height.computed) * i2d;
577 Geom::Point p2 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed) * i2d;
578 Geom::Point p3 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed) * i2d;
580 if (snapprefs->getSnapToItemNode()) {
581 p.push_back(Inkscape::SnapCandidatePoint(p0, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER));
582 p.push_back(Inkscape::SnapCandidatePoint(p1, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER));
583 p.push_back(Inkscape::SnapCandidatePoint(p2, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER));
584 p.push_back(Inkscape::SnapCandidatePoint(p3, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER));
585 }
587 if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
588 p.push_back(Inkscape::SnapCandidatePoint((p0 + p1)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
589 p.push_back(Inkscape::SnapCandidatePoint((p1 + p2)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
590 p.push_back(Inkscape::SnapCandidatePoint((p2 + p3)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
591 p.push_back(Inkscape::SnapCandidatePoint((p3 + p0)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
592 }
594 if (snapprefs->getSnapObjectMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
595 p.push_back(Inkscape::SnapCandidatePoint((p0 + p2)/2, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
596 }
598 }
600 void
601 sp_rect_convert_to_guides(SPItem *item) {
602 SPRect *rect = SP_RECT(item);
604 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
605 if (!prefs->getBool("/tools/shapes/rect/convertguides", true)) {
606 sp_item_convert_to_guides(SP_ITEM(rect));
607 return;
608 }
610 std::list<std::pair<Geom::Point, Geom::Point> > pts;
612 Geom::Matrix const i2d (sp_item_i2d_affine(SP_ITEM(rect)));
614 Geom::Point A1(Geom::Point(rect->x.computed, rect->y.computed) * i2d);
615 Geom::Point A2(Geom::Point(rect->x.computed, rect->y.computed + rect->height.computed) * i2d);
616 Geom::Point A3(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed) * i2d);
617 Geom::Point A4(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed) * i2d);
619 pts.push_back(std::make_pair(A1, A2));
620 pts.push_back(std::make_pair(A2, A3));
621 pts.push_back(std::make_pair(A3, A4));
622 pts.push_back(std::make_pair(A4, A1));
624 sp_guide_pt_pairs_to_guides(inkscape_active_desktop(), pts);
625 }
627 /*
628 Local Variables:
629 mode:c++
630 c-file-style:"stroustrup"
631 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
632 indent-tabs-mode:nil
633 fill-column:99
634 End:
635 */
636 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :