1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * SVG <rect> implementation
4  *
5  * Authors:
6  *   Lauris Kaplinski <lauris@kaplinski.com>
7  *   bulia byak <buliabyak@users.sf.net>
8  *
9  * Copyright (C) 1999-2002 Lauris Kaplinski
10  * Copyright (C) 2000-2001 Ximian, Inc.
11  *
12  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13  */
14 
15 #include "display/curve.h"
16 
17 #include "inkscape.h"
18 #include "document.h"
19 #include "attributes.h"
20 #include "style.h"
21 #include "sp-rect.h"
22 #include "sp-guide.h"
23 #include "preferences.h"
24 #include "svg/svg.h"
25 #include <glibmm/i18n.h>
26 
27 #define noRECT_VERBOSE
28 
29 //#define OBJECT_TRACE
30 
SPRect()31 SPRect::SPRect() : SPShape() {
32 }
33 
34 SPRect::~SPRect() = default;
35 
build(SPDocument * doc,Inkscape::XML::Node * repr)36 void SPRect::build(SPDocument* doc, Inkscape::XML::Node* repr) {
37 #ifdef OBJECT_TRACE
38     objectTrace( "SPRect::build" );
39 #endif
40 
41     SPShape::build(doc, repr);
42 
43     this->readAttr(SPAttr::X);
44     this->readAttr(SPAttr::Y);
45     this->readAttr(SPAttr::WIDTH);
46     this->readAttr(SPAttr::HEIGHT);
47     this->readAttr(SPAttr::RX);
48     this->readAttr(SPAttr::RY);
49 
50 #ifdef OBJECT_TRACE
51     objectTrace( "SPRect::build", false );
52 #endif
53 }
54 
set(SPAttr key,gchar const * value)55 void SPRect::set(SPAttr key, gchar const *value) {
56 
57 #ifdef OBJECT_TRACE
58     std::stringstream temp;
59     temp << "SPRect::set: " << key  << " " << (value?value:"null");
60     objectTrace( temp.str() );
61 #endif
62 
63     /* fixme: We need real error processing some time */
64 
65     // We must update the SVGLengths immediately or nodes may be misplaced after they are moved.
66     double const w = viewport.width();
67     double const h = viewport.height();
68     double const em = style->font_size.computed;
69     double const ex = em * 0.5;
70 
71     switch (key) {
72         case SPAttr::X:
73             this->x.readOrUnset(value);
74             this->x.update( em, ex, w );
75             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
76             break;
77 
78         case SPAttr::Y:
79             this->y.readOrUnset(value);
80             this->y.update( em, ex, h );
81             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
82             break;
83 
84         case SPAttr::WIDTH:
85             if (!this->width.read(value) || this->width.value < 0.0) {
86             	this->width.unset();
87             }
88             this->width.update( em, ex, w );
89             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
90             break;
91 
92         case SPAttr::HEIGHT:
93             if (!this->height.read(value) || this->height.value < 0.0) {
94             	this->height.unset();
95             }
96             this->height.update( em, ex, h );
97             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
98             break;
99 
100         case SPAttr::RX:
101             if (!this->rx.read(value) || this->rx.value <= 0.0) {
102             	this->rx.unset();
103             }
104             this->rx.update( em, ex, w );
105             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
106             break;
107 
108         case SPAttr::RY:
109             if (!this->ry.read(value) || this->ry.value <= 0.0) {
110             	this->ry.unset();
111             }
112             this->ry.update( em, ex, h );
113             this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
114             break;
115 
116         default:
117             SPShape::set(key, value);
118             break;
119     }
120 #ifdef OBJECT_TRACE
121     objectTrace( "SPRect::set", false );
122 #endif
123 }
124 
update(SPCtx * ctx,unsigned int flags)125 void SPRect::update(SPCtx* ctx, unsigned int flags) {
126 
127 #ifdef OBJECT_TRACE
128     objectTrace( "SPRect::update", true, flags );
129 #endif
130 
131     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
132         SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx);
133 
134         double const w = ictx->viewport.width();
135         double const h = ictx->viewport.height();
136         double const em = style->font_size.computed;
137         double const ex = 0.5 * em;  // fixme: get x height from pango or libnrtype.
138 
139         this->x.update(em, ex, w);
140         this->y.update(em, ex, h);
141         this->width.update(em, ex, w);
142         this->height.update(em, ex, h);
143         this->rx.update(em, ex, w);
144         this->ry.update(em, ex, h);
145         this->set_shape();
146 
147         flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // since we change the description, it's not a "just translation" anymore
148     }
149 
150     SPShape::update(ctx, flags);
151 #ifdef OBJECT_TRACE
152     objectTrace( "SPRect::update", false, flags );
153 #endif
154 }
155 
write(Inkscape::XML::Document * xml_doc,Inkscape::XML::Node * repr,guint flags)156 Inkscape::XML::Node * SPRect::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) {
157 
158 #ifdef OBJECT_TRACE
159     objectTrace( "SPRect::write", true, flags );
160 #endif
161 
162     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
163         repr = xml_doc->createElement("svg:rect");
164     }
165     if (this->hasPathEffectOnClipOrMaskRecursive(this) && repr && strcmp(repr->name(), "svg:rect") == 0) {
166         repr->setCodeUnsafe(g_quark_from_string("svg:path"));
167         repr->setAttribute("sodipodi:type", "rect");
168     }
169     sp_repr_set_svg_length(repr, "width",  this->width);
170     sp_repr_set_svg_length(repr, "height", this->height);
171 
172     if (this->rx._set) {
173     	sp_repr_set_svg_length(repr, "rx", this->rx);
174     }
175 
176     if (this->ry._set) {
177     	sp_repr_set_svg_length(repr, "ry", this->ry);
178     }
179 
180     sp_repr_set_svg_length(repr, "x", this->x);
181     sp_repr_set_svg_length(repr, "y", this->y);
182     // write d=
183     if (strcmp(repr->name(), "svg:rect") != 0) {
184         set_rect_path_attribute(repr); // include set_shape()
185     } else {
186         this->set_shape(); // evaluate SPCurve
187     }
188     SPShape::write(xml_doc, repr, flags);
189 
190 #ifdef OBJECT_TRACE
191     objectTrace( "SPRect::write", false, flags );
192 #endif
193 
194     return repr;
195 }
196 
displayName() const197 const char* SPRect::displayName() const {
198     return _("Rectangle");
199 }
200 
201 #define C1 0.554
202 
set_shape()203 void SPRect::set_shape() {
204     if (hasBrokenPathEffect()) {
205         g_warning("The rect shape has unknown LPE on it!");
206 
207         if (this->getRepr()->attribute("d")) {
208             // unconditionally read the curve from d, if any, to preserve appearance
209             Geom::PathVector pv = sp_svg_read_pathv(this->getRepr()->attribute("d"));
210             setCurveInsync(std::make_unique<SPCurve>(pv));
211             setCurveBeforeLPE(curve());
212         }
213 
214         return;
215     }
216     if ((this->height.computed < 1e-18) || (this->width.computed < 1e-18)) {
217     	this->setCurveInsync(nullptr);
218     	this->setCurveBeforeLPE(nullptr);
219         return;
220     }
221 
222     auto c = std::make_unique<SPCurve>();
223 
224     double const x = this->x.computed;
225     double const y = this->y.computed;
226     double const w = this->width.computed;
227     double const h = this->height.computed;
228     double const w2 = w / 2;
229     double const h2 = h / 2;
230     double const rx = std::min(( this->rx._set
231                                  ? this->rx.computed
232                                  : ( this->ry._set
233                                      ? this->ry.computed
234                                      : 0.0 ) ),
235                                .5 * this->width.computed);
236     double const ry = std::min(( this->ry._set
237                                  ? this->ry.computed
238                                  : ( this->rx._set
239                                      ? this->rx.computed
240                                      : 0.0 ) ),
241                                .5 * this->height.computed);
242     /* TODO: Handle negative rx or ry as per
243      * http://www.w3.org/TR/SVG11/shapes.html#RectElementRXAttribute once Inkscape has proper error
244      * handling (see http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing).
245      */
246 
247     /* We don't use proper circular/elliptical arcs, but bezier curves can approximate a 90-degree
248      * arc fairly well.
249      */
250     if ((rx > 1e-18) && (ry > 1e-18)) {
251         c->moveto(x + rx, y);
252 
253         if (rx < w2) {
254         	c->lineto(x + w - rx, y);
255         }
256 
257         c->curveto(x + w - rx * (1 - C1), y, x + w, y + ry * (1 - C1), x + w, y + ry);
258 
259         if (ry < h2) {
260         	c->lineto(x + w, y + h - ry);
261         }
262 
263         c->curveto(x + w, y + h - ry * (1 - C1), x + w - rx * (1 - C1), y + h, x + w - rx, y + h);
264 
265         if (rx < w2) {
266         	c->lineto(x + rx, y + h);
267         }
268 
269         c->curveto(x + rx * (1 - C1), y + h, x, y + h - ry * (1 - C1), x, y + h - ry);
270 
271         if (ry < h2) {
272         	c->lineto(x, y + ry);
273         }
274 
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     }
282 
283     c->closepath();
284 
285 
286     /* Reset the shape's curve to the "original_curve"
287     * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
288 
289     auto const before = this->curveBeforeLPE();
290     if (before && before->get_pathvector() != c->get_pathvector()) {
291         setCurveBeforeLPE(std::move(c));
292         sp_lpe_item_update_patheffect(this, true, false);
293         return;
294     }
295     if (this->hasPathEffectOnClipOrMaskRecursive(this)) {
296         setCurveBeforeLPE(std::move(c));
297 
298         Inkscape::XML::Node *rectrepr = this->getRepr();
299         if (strcmp(rectrepr->name(), "svg:rect") == 0) {
300             sp_lpe_item_update_patheffect(this, true, false);
301             this->write(rectrepr->document(), rectrepr, SP_OBJECT_MODIFIED_FLAG);
302         }
303 
304         return;
305     }
306 
307     // This happends on undo, fix bug:#1791784
308     setCurveInsync(std::move(c));
309 }
310 
set_rect_path_attribute(Inkscape::XML::Node * repr)311 bool SPRect::set_rect_path_attribute(Inkscape::XML::Node *repr)
312 {
313     // Make sure our pathvector is up to date.
314     this->set_shape();
315 
316     if (_curve) {
317         repr->setAttribute("d", sp_svg_write_path(_curve->get_pathvector()));
318     } else {
319         repr->removeAttribute("d");
320     }
321 
322     return true;
323 }
324 
modified(guint flags)325 void SPRect::modified(guint flags)
326 {
327     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
328         this->set_shape();
329     }
330 
331     SPShape::modified(flags);
332 }
333 
334 /* fixme: Think (Lauris) */
335 
setPosition(gdouble x,gdouble y,gdouble width,gdouble height)336 void SPRect::setPosition(gdouble x, gdouble y, gdouble width, gdouble height) {
337     this->x = x;
338     this->y = y;
339     this->width = width;
340     this->height = height;
341 
342     this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
343 }
344 
setRx(bool set,gdouble value)345 void SPRect::setRx(bool set, gdouble value) {
346     this->rx._set = set;
347 
348     if (set) {
349     	this->rx = value;
350     }
351 
352     this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
353 }
354 
setRy(bool set,gdouble value)355 void SPRect::setRy(bool set, gdouble value) {
356     this->ry._set = set;
357 
358     if (set) {
359     	this->ry = value;
360     }
361 
362     this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
363 }
364 
update_patheffect(bool write)365 void SPRect::update_patheffect(bool write) {
366     SPShape::update_patheffect(write);
367 }
368 
set_transform(Geom::Affine const & xform)369 Geom::Affine SPRect::set_transform(Geom::Affine const& xform) {
370     if (pathEffectsEnabled() && !optimizeTransforms()) {
371         return xform;
372     }
373     /* Calculate rect start in parent coords. */
374     Geom::Point pos(Geom::Point(this->x.computed, this->y.computed) * xform);
375 
376     /* This function takes care of translation and scaling, we return whatever parts we can't
377        handle. */
378     Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
379     gdouble const sw = hypot(ret[0], ret[1]);
380     gdouble const sh = hypot(ret[2], ret[3]);
381 
382     if (sw > 1e-9) {
383         ret[0] /= sw;
384         ret[1] /= sw;
385     } else {
386         ret[0] = 1.0;
387         ret[1] = 0.0;
388     }
389 
390     if (sh > 1e-9) {
391         ret[2] /= sh;
392         ret[3] /= sh;
393     } else {
394         ret[2] = 0.0;
395         ret[3] = 1.0;
396     }
397 
398     /* Preserve units */
399     this->width.scale( sw );
400     this->height.scale( sh );
401 
402     if (this->rx._set) {
403     	this->rx.scale( sw );
404     }
405 
406     if (this->ry._set) {
407     	this->ry.scale( sh );
408     }
409 
410     /* Find start in item coords */
411     pos = pos * ret.inverse();
412     this->x = pos[Geom::X];
413     this->y = pos[Geom::Y];
414 
415     this->set_shape();
416 
417     // Adjust stroke width
418     this->adjust_stroke(sqrt(fabs(sw * sh)));
419 
420     // Adjust pattern fill
421     this->adjust_pattern(xform * ret.inverse());
422 
423     // Adjust gradient fill
424     this->adjust_gradient(xform * ret.inverse());
425 
426     return ret;
427 }
428 
429 
430 /**
431 Returns the ratio in which the vector from p0 to p1 is stretched by transform
432  */
vectorStretch(Geom::Point p0,Geom::Point p1,Geom::Affine xform)433 gdouble SPRect::vectorStretch(Geom::Point p0, Geom::Point p1, Geom::Affine xform) {
434     if (p0 == p1) {
435         return 0;
436     }
437 
438     return (Geom::distance(p0 * xform, p1 * xform) / Geom::distance(p0, p1));
439 }
440 
setVisibleRx(gdouble rx)441 void SPRect::setVisibleRx(gdouble rx) {
442     if (rx == 0) {
443         this->rx.unset();
444     } else {
445     	this->rx = rx / SPRect::vectorStretch(
446             Geom::Point(this->x.computed + 1, this->y.computed),
447             Geom::Point(this->x.computed, this->y.computed),
448             this->i2doc_affine());
449     }
450 
451     this->updateRepr();
452 }
453 
setVisibleRy(gdouble ry)454 void SPRect::setVisibleRy(gdouble ry) {
455     if (ry == 0) {
456     	this->ry.unset();
457     } else {
458     	this->ry = ry / SPRect::vectorStretch(
459             Geom::Point(this->x.computed, this->y.computed + 1),
460             Geom::Point(this->x.computed, this->y.computed),
461             this->i2doc_affine());
462     }
463 
464     this->updateRepr();
465 }
466 
getVisibleRx() const467 gdouble SPRect::getVisibleRx() const {
468     if (!this->rx._set) {
469         return 0;
470     }
471 
472     return this->rx.computed * SPRect::vectorStretch(
473         Geom::Point(this->x.computed + 1, this->y.computed),
474         Geom::Point(this->x.computed, this->y.computed),
475         this->i2doc_affine());
476 }
477 
getVisibleRy() const478 gdouble SPRect::getVisibleRy() const {
479     if (!this->ry._set) {
480         return 0;
481     }
482 
483     return this->ry.computed * SPRect::vectorStretch(
484         Geom::Point(this->x.computed, this->y.computed + 1),
485         Geom::Point(this->x.computed, this->y.computed),
486         this->i2doc_affine());
487 }
488 
getRect() const489 Geom::Rect SPRect::getRect() const {
490     Geom::Point p0 = Geom::Point(this->x.computed, this->y.computed);
491     Geom::Point p2 = Geom::Point(this->x.computed + this->width.computed, this->y.computed + this->height.computed);
492 
493     return Geom::Rect(p0, p2);
494 }
495 
compensateRxRy(Geom::Affine xform)496 void SPRect::compensateRxRy(Geom::Affine xform) {
497     if (this->rx.computed == 0 && this->ry.computed == 0) {
498         return; // nothing to compensate
499     }
500 
501     // test unit vectors to find out compensation:
502     Geom::Point c(this->x.computed, this->y.computed);
503     Geom::Point cx = c + Geom::Point(1, 0);
504     Geom::Point cy = c + Geom::Point(0, 1);
505 
506     // apply previous transform if any
507     c *= this->transform;
508     cx *= this->transform;
509     cy *= this->transform;
510 
511     // find out stretches that we need to compensate
512     gdouble eX = SPRect::vectorStretch(cx, c, xform);
513     gdouble eY = SPRect::vectorStretch(cy, c, xform);
514 
515     // If only one of the radii is set, set both radii so they have the same visible length
516     // This is needed because if we just set them the same length in SVG, they might end up unequal because of transform
517     if ((this->rx._set && !this->ry._set) || (this->ry._set && !this->rx._set)) {
518         gdouble r = MAX(this->rx.computed, this->ry.computed);
519         this->rx = r / eX;
520         this->ry = r / eY;
521     } else {
522     	this->rx = this->rx.computed / eX;
523     	this->ry = this->ry.computed / eY;
524     }
525 
526     // Note that a radius may end up larger than half-side if the rect is scaled down;
527     // that's ok because this preserves the intended radii in case the rect is enlarged again,
528     // and set_shape will take care of trimming too large radii when generating d=
529 }
530 
setVisibleWidth(gdouble width)531 void SPRect::setVisibleWidth(gdouble width) {
532     this->width = width / SPRect::vectorStretch(
533         Geom::Point(this->x.computed + 1, this->y.computed),
534         Geom::Point(this->x.computed, this->y.computed),
535         this->i2doc_affine());
536 
537     this->updateRepr();
538 }
539 
setVisibleHeight(gdouble height)540 void SPRect::setVisibleHeight(gdouble height) {
541     this->height = height / SPRect::vectorStretch(
542         Geom::Point(this->x.computed, this->y.computed + 1),
543         Geom::Point(this->x.computed, this->y.computed),
544         this->i2doc_affine());
545 
546 	this->updateRepr();
547 }
548 
getVisibleWidth() const549 gdouble SPRect::getVisibleWidth() const {
550     if (!this->width._set) {
551         return 0;
552     }
553 
554     return this->width.computed * SPRect::vectorStretch(
555         Geom::Point(this->x.computed + 1, this->y.computed),
556         Geom::Point(this->x.computed, this->y.computed),
557         this->i2doc_affine());
558 }
559 
getVisibleHeight() const560 gdouble SPRect::getVisibleHeight() const {
561     if (!this->height._set) {
562         return 0;
563     }
564 
565     return this->height.computed * SPRect::vectorStretch(
566         Geom::Point(this->x.computed, this->y.computed + 1),
567         Geom::Point(this->x.computed, this->y.computed),
568         this->i2doc_affine());
569 }
570 
snappoints(std::vector<Inkscape::SnapCandidatePoint> & p,Inkscape::SnapPreferences const * snapprefs) const571 void SPRect::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const {
572     /* This method overrides sp_shape_snappoints, which is the default for any shape. The default method
573     returns all eight points along the path of a rounded rectangle, but not the real corners. Snapping
574     the startpoint and endpoint of each rounded corner is not very useful and really confusing. Instead
575     we could snap either the real corners, or not snap at all. Bulia Byak opted to snap the real corners,
576     but it should be noted that this might be confusing in some cases with relatively large radii. With
577     small radii though the user will easily understand which point is snapping. */
578 
579     Geom::Affine const i2dt (this->i2dt_affine ());
580 
581     Geom::Point p0 = Geom::Point(this->x.computed, this->y.computed) * i2dt;
582     Geom::Point p1 = Geom::Point(this->x.computed, this->y.computed + this->height.computed) * i2dt;
583     Geom::Point p2 = Geom::Point(this->x.computed + this->width.computed, this->y.computed + this->height.computed) * i2dt;
584     Geom::Point p3 = Geom::Point(this->x.computed + this->width.computed, this->y.computed) * i2dt;
585 
586     if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_RECT_CORNER)) {
587         p.emplace_back(p0, Inkscape::SNAPSOURCE_RECT_CORNER, Inkscape::SNAPTARGET_RECT_CORNER);
588         p.emplace_back(p1, Inkscape::SNAPSOURCE_RECT_CORNER, Inkscape::SNAPTARGET_RECT_CORNER);
589         p.emplace_back(p2, Inkscape::SNAPSOURCE_RECT_CORNER, Inkscape::SNAPTARGET_RECT_CORNER);
590         p.emplace_back(p3, Inkscape::SNAPSOURCE_RECT_CORNER, Inkscape::SNAPTARGET_RECT_CORNER);
591     }
592 
593     if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_LINE_MIDPOINT)) {
594         p.emplace_back((p0 + p1)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT);
595         p.emplace_back((p1 + p2)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT);
596         p.emplace_back((p2 + p3)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT);
597         p.emplace_back((p3 + p0)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT);
598     }
599 
600     if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT)) {
601         p.emplace_back((p0 + p2)/2, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT);
602     }
603 }
604 
convert_to_guides() const605 void SPRect::convert_to_guides() const {
606     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
607 
608     if (!prefs->getBool("/tools/shapes/rect/convertguides", true)) {
609     	// Use bounding box instead of edges
610     	SPShape::convert_to_guides();
611         return;
612     }
613 
614     std::list<std::pair<Geom::Point, Geom::Point> > pts;
615 
616     Geom::Affine const i2dt(this->i2dt_affine());
617 
618     Geom::Point A1(Geom::Point(this->x.computed, this->y.computed) * i2dt);
619     Geom::Point A2(Geom::Point(this->x.computed, this->y.computed + this->height.computed) * i2dt);
620     Geom::Point A3(Geom::Point(this->x.computed + this->width.computed, this->y.computed + this->height.computed) * i2dt);
621     Geom::Point A4(Geom::Point(this->x.computed + this->width.computed, this->y.computed) * i2dt);
622 
623     pts.emplace_back(A1, A2);
624     pts.emplace_back(A2, A3);
625     pts.emplace_back(A3, A4);
626     pts.emplace_back(A4, A1);
627 
628     sp_guide_pt_pairs_to_guides(this->document, pts);
629 }
630 
631 /*
632   Local Variables:
633   mode:c++
634   c-file-style:"stroustrup"
635   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
636   indent-tabs-mode:nil
637   fill-column:99
638   End:
639 */
640 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
641