summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDiederik van Lierop <mail@diedenrezi.nl>2015-09-13 17:50:28 +0000
committerDiederik van Lierop <mail@diedenrezi.nl>2015-09-13 17:50:28 +0000
commit25900540fbb26b44933ce1ead2240893769cd60f (patch)
tree731a41270b51019617bf708b2ad6da26cb3cfe81 /src
parentSecond round of extension executable bit cleanup. (diff)
downloadinkscape-25900540fbb26b44933ce1ead2240893769cd60f.tar.gz
inkscape-25900540fbb26b44933ce1ead2240893769cd60f.zip
Refactoring of the code that handles transformations and snapping in the selector tool and the node tool. Splitting large chunks of code into some small classes, and eliminating some wrapper methods which were all just too similar
(bzr r14363)
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/Makefile_insert1
-rw-r--r--src/pure-transform.cpp359
-rw-r--r--src/pure-transform.h230
-rw-r--r--src/seltrans.cpp203
-rw-r--r--src/snap.cpp486
-rw-r--r--src/snap.h168
-rw-r--r--src/snapped-point.cpp5
-rw-r--r--src/snapped-point.h4
-rw-r--r--src/snapper.h2
-rw-r--r--src/ui/tool/transform-handle-set.cpp48
11 files changed, 804 insertions, 704 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ed93d5f14..ec7713464 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -240,6 +240,7 @@ set(inkscape_SRC
print.cpp
profile-manager.cpp
proj_pt.cpp
+ pure-transform.cpp
rdf.cpp
removeoverlap.cpp
resource-manager.cpp
@@ -367,6 +368,7 @@ set(inkscape_SRC
print.h
profile-manager.h
proj_pt.h
+ pure-transform.h
rdf.h
remove-last.h
removeoverlap.h
diff --git a/src/Makefile_insert b/src/Makefile_insert
index 800752df4..b9723ac42 100644
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
@@ -88,6 +88,7 @@ ink_common_sources += \
print.cpp print.h \
profile-manager.cpp profile-manager.h \
proj_pt.cpp proj_pt.h \
+ pure-transform.cpp pure-transform.h \
removeoverlap.cpp removeoverlap.h \
rdf.cpp rdf.h \
resource-manager.cpp resource-manager.h \
diff --git a/src/pure-transform.cpp b/src/pure-transform.cpp
new file mode 100644
index 000000000..aa89c8a8e
--- /dev/null
+++ b/src/pure-transform.cpp
@@ -0,0 +1,359 @@
+/*
+ * Class for pure transformations, such as translating, scaling, stretching, skewing, and rotating
+ *
+ * Authors:
+ * Diederik van Lierop <mail@diedenrezi.nl>
+ *
+ * Copyright (C) 2015 Diederik van Lierop
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "pure-transform.h"
+#include "snap.h"
+
+namespace Inkscape
+
+{
+
+void PureTransform::snap(::SnapManager *sm, std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer) {
+ std::vector<Inkscape::SnapCandidatePoint> transformed_points;
+ Geom::Rect bbox;
+
+ long source_num = 0;
+ for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
+
+ /* Work out the transformed version of this point */
+ Geom::Point transformed = getTransformedPoint(*i); // _transformPoint(*i, transformation_type, transformation, origin, dim, uniform);
+
+ // add the current transformed point to the box hulling all transformed points
+ if (i == points.begin()) {
+ bbox = Geom::Rect(transformed, transformed);
+ } else {
+ bbox.expandTo(transformed);
+ }
+
+ transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num, Inkscape::SNAPTARGET_UNDEFINED, Geom::OptRect()));
+ source_num++;
+ }
+
+ /* The current best metric for the best transformation; lower is better, whereas Geom::infinity()
+ ** means that we haven't snapped anything.
+ */
+ Inkscape::SnapCandidatePoint best_original_point;
+ g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
+ g_assert(best_snapped_point.getAtIntersection() == false);
+ g_assert(best_snapped_point.getSnapped() == false); // Check initialization to catch any regression
+
+ std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();
+
+ // std::cout << std::endl;
+ bool first_free_snap = true;
+
+ for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
+
+ // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
+ // requirements (this can happen when scaling, see PureScale::snap()), then freeSnap might never see the
+ // SnapCandidatePoint with source_num == 0. The freeSnap() method in the object snapper depends on this,
+ // because only for source-num == 0 the target nodes will be collected. Therefore we enforce that the first
+ // SnapCandidatePoint that is to be freeSnapped always has source_num == 0;
+ // TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
+ // of it and explicitly communicate to the object snapper that this is a first point
+ if (first_free_snap) {
+ (*j).setSourceNum(0);
+ first_free_snap = false;
+ }
+ Inkscape::SnappedPoint snapped_point = snap(sm, *j, (*i).getPoint(), bbox);
+
+ // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl;
+ snapped_point.setPointerDistance(Geom::L2(pointer - (*i).getPoint()));
+
+ /*Find the transformation that describes where the snapped point has
+ ** ended up, and also the metric for this transformation.
+ */
+
+ bool store_best_snap = false;
+ if (snapped_point.getSnapped()) {
+ // We snapped; keep track of the best snap
+ if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) {
+ store_best_snap = true;
+ }
+ } else {
+ // So we didn't snap for this point
+ if (!best_snapped_point.getSnapped()) {
+ // ... and none of the points before snapped either
+ // We might still need to apply a constraint though, if we tried a constrained snap. And
+ // in case of a free snap we might have use for the transformed point, so let's return that
+ // point, whether it's constrained or not
+ if (best_snapped_point.isOtherSnapBetter(snapped_point, true) || points.size() == 1) {
+ // .. so we must keep track of the best non-snapped constrained point
+ store_best_snap = true;
+ }
+ }
+ }
+
+ if (store_best_snap) {
+ best_original_point = (*i);
+ best_snapped_point = snapped_point;
+ }
+
+ ++j;
+ }
+
+ /* The current best transformation */
+ //Geom::Point best_transformation = getResult(best_original_point, best_snapped_point);
+ storeTransform(best_original_point, best_snapped_point);
+
+ Geom::Coord best_metric = best_snapped_point.getSnapDistance();
+
+ // Using " < 1e6" instead of " < Geom::infinity()" for catching some rounding errors
+ // These rounding errors might be caused by NRRects, see bug #1584301
+ best_snapped_point.setSnapDistance(best_metric < 1e6 ? best_metric : Geom::infinity());
+}
+
+
+
+
+
+Geom::Point PureTranslate::getTransformedPoint(SnapCandidatePoint const &p) const {
+ return p.getPoint() + _vector;
+}
+
+void PureTranslate::storeTransform(SnapCandidatePoint const original_point, SnappedPoint const snapped_point) {
+ /* Consider the case in which a box is almost aligned with a grid in both
+ * horizontal and vertical directions. The distance to the intersection of
+ * the grid lines will always be larger then the distance to a single grid
+ * line. If we prefer snapping to an intersection over to a single
+ * grid line, then we cannot use "metric = Geom::L2(result)". Therefore the
+ * snapped distance will be used as a metric. Please note that the snapped
+ * distance to an intersection is defined as the distance to the nearest line
+ * of the intersection, and not to the intersection itself!
+ */
+ // Only for translations, the relevant metric will be the real snapped distance,
+ // so we don't have to do anything special here
+ _vector_snapped = snapped_point.getPoint() - original_point.getPoint();
+}
+
+SnappedPoint PureTranslate::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point /*pt_orig*/, Geom::OptRect const &bbox_to_snap) const {
+ return sm->freeSnap(p, bbox_to_snap);
+}
+
+SnappedPoint PureTranslateConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ // Calculate a constraint dedicated for this specific point
+ // When doing a constrained translation, all points will move in the same direction, i.e.
+ // either horizontally or vertically. The lines along which they move are therefore all
+ // parallel, but might not be co-linear. Therefore we will have to specify the point through
+ // which the constraint-line runs here, for each point individually.
+ Snapper::SnapConstraint dedicated_constraint = Snapper::SnapConstraint(pt_orig, _direction);
+ return sm->constrainedSnap(p, dedicated_constraint, bbox_to_snap);
+}
+
+
+
+
+
+Geom::Point PureScale::getTransformedPoint(SnapCandidatePoint const &p) const {
+ return (p.getPoint() - _origin) * _scale + _origin;
+}
+
+void PureScale::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
+ _scale_snapped = Geom::Scale(Geom::infinity(), Geom::infinity());
+ // If this point *i is horizontally or vertically aligned with
+ // the origin of the scaling, then it will scale purely in X or Y
+ // We can therefore only calculate the scaling in this direction
+ // and the scaling factor for the other direction should remain
+ // untouched (unless scaling is uniform of course)
+ Geom::Point const a = snapped_point.getPoint() - _origin; // vector to snapped point
+ Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
+ for (int index = 0; index < 2; index++) {
+ if (fabs(b[index]) > 1e-4) { // if SCALING CAN occur in this direction
+ if (fabs(fabs(a[index]/b[index]) - fabs(_scale[index])) > 1e-7) { // if SNAPPING DID occur in this direction
+ _scale_snapped[index] = a[index] / b[index]; // then calculate it!
+ // _scale_snapped will be (1,1) if we haven't snapped, because the snapped point equals the original point
+ }
+ // we might have left result[1-index] = Geom::infinity() if scaling didn't occur in the other direction
+ }
+ }
+ if (_uniform) {
+ // Lock the scaling the be uniform, but keep the sign such that we don't change which quadrant we have dragged into
+ if (fabs(_scale_snapped[0]) < fabs(_scale_snapped[1])) {
+ _scale_snapped[1] = fabs(_scale_snapped[0]) * Geom::sgn(_scale[1]);
+ } else {
+ _scale_snapped[0] = fabs(_scale_snapped[1]) * Geom::sgn(_scale[0]);
+ }
+ }
+
+ // Don't ever exit with one of scaling components uninitialized
+ for (int index = 0; index < 2; index++) {
+ if (_scale_snapped[index] == Geom::infinity()) {
+ _scale_snapped[index] = _scale[index];
+ }
+ }
+
+ // Compare the resulting scaling with the desired scaling
+ Geom::Point scale_metric = _scale_snapped.vector() - _scale.vector();
+ snapped_point.setSnapDistance(Geom::L2(scale_metric));
+ snapped_point.setSecondSnapDistance(Geom::infinity());
+}
+
+// When scaling, a point aligned either horizontally or vertically with the origin can only
+// move in that specific direction; therefore it should only snap in that direction, so this
+// then becomes a constrained snap; otherwise we can use a free snap;
+SnappedPoint PureScale::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ Geom::Point const b = (pt_orig - _origin); // vector to original point (not the transformed point!)
+ bool const c1 = fabs(b[Geom::X]) < 1e-6;
+ bool const c2 = fabs(b[Geom::Y]) < 1e-6;
+ if ((c1 || c2) && !(c1 && c2)) {
+ Geom::Point cvec; cvec[c1] = 1.;
+ Snapper::SnapConstraint dedicated_constraint = Inkscape::Snapper::SnapConstraint(_origin, cvec);
+ return sm->constrainedSnap(p, dedicated_constraint, bbox_to_snap);
+ } else {
+ return sm->freeSnap(p, bbox_to_snap);
+ }
+}
+
+SnappedPoint PureScaleConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ // When constrained scaling, only uniform scaling is supported.
+ // When uniformly scaling, each point will have its own unique constraint line,
+ // running from the scaling origin to the original untransformed point. We will
+ // calculate that line here as a dedicated constraint
+ Geom::Point b = pt_orig - _origin;
+ Snapper::SnapConstraint dedicated_constraint = Inkscape::Snapper::SnapConstraint(_origin, b);
+ return sm->constrainedSnap(p, dedicated_constraint, bbox_to_snap);
+}
+
+
+
+
+
+Geom::Point PureStretchConstrained::getTransformedPoint(SnapCandidatePoint const &p) const {
+ Geom::Scale s(1, 1);
+ if (_uniform)
+ s[Geom::X] = s[Geom::Y] = _magnitude;
+ else {
+ s[_direction] = _magnitude;
+ s[1 - _direction] = 1;
+ }
+ return ((p.getPoint() - _origin) * s) + _origin;
+}
+
+SnappedPoint PureStretchConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ Snapper::SnapConstraint dedicated_constraint;
+ if (_uniform) {
+ // When uniformly stretching, each point will have its own unique constraint line,
+ // running from the scaling origin to the original untransformed point. We will
+ // calculate that line here
+ Geom::Point b = pt_orig - _origin;
+ dedicated_constraint = Inkscape::Snapper::SnapConstraint(_origin, b); // dedicated constraint
+ } else {
+ Geom::Point cvec; cvec[_direction] = 1.;
+ dedicated_constraint = Inkscape::Snapper::SnapConstraint(pt_orig, cvec);
+ }
+
+ return sm->constrainedSnap(p, dedicated_constraint, bbox_to_snap);
+}
+
+void PureStretchConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
+ Geom::Point const a = snapped_point.getPoint() - _origin; // vector to snapped point
+ Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
+
+ _stretch_snapped = Geom::Scale(Geom::infinity(), Geom::infinity());
+ if (fabs(b[_direction]) > 1e-6) { // if STRETCHING will occur for this point
+ _stretch_snapped[_direction] = a[_direction] / b[_direction];
+ _stretch_snapped[1-_direction] = _uniform ? _stretch_snapped[_direction] : 1;
+ } else { // STRETCHING might occur for this point, but only when the stretching is uniform
+ if (_uniform && fabs(b[1-_direction]) > 1e-6) {
+ _stretch_snapped[1-_direction] = a[1-_direction] / b[1-_direction];
+ _stretch_snapped[_direction] = _stretch_snapped[1-_direction];
+ }
+ }
+
+ // _stretch_snapped might have one or both components at infinity!
+
+ // Store the metric for this transformation as a virtual distance
+ snapped_point.setSnapDistance(std::abs(_stretch_snapped[_direction] - _magnitude));
+ snapped_point.setSecondSnapDistance(Geom::infinity());
+}
+
+
+
+
+
+Geom::Point PureSkewConstrained::getTransformedPoint(SnapCandidatePoint const &p) const {
+ Geom::Point transformed;
+ // Apply the skew factor
+ transformed[_direction] = (p.getPoint())[_direction] + _skew * ((p.getPoint())[1 - _direction] - _origin[1 - _direction]);
+ // While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed.
+ // Apply that scale factor here
+ transformed[1-_direction] = (p.getPoint() - _origin)[1 - _direction] * _scale + _origin[1 - _direction];
+ return transformed;
+}
+
+SnappedPoint PureSkewConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
+ // the transformation of the bounding box is equal to the transformation of the individual nodes. This is
+ // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
+ // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
+ // of bounding boxes is not allowed here.
+ g_assert(!(p.getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY));
+
+ Geom::Point constraint_vector;
+ constraint_vector[1-_direction] = 0.0;
+ constraint_vector[_direction] = 1.0;
+
+ return sm->constrainedSnap(p, Inkscape::Snapper::SnapConstraint(constraint_vector), bbox_to_snap);
+}
+
+void PureSkewConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
+ Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
+ _skew_snapped = (snapped_point.getPoint()[_direction] - (original_point.getPoint())[_direction]) / b[1 - _direction]; // skew factor
+
+ // Store the metric for this transformation as a virtual distance
+ snapped_point.setSnapDistance(std::abs(_skew_snapped - _skew));
+ snapped_point.setSecondSnapDistance(Geom::infinity());
+}
+
+
+
+
+Geom::Point PureRotateConstrained::getTransformedPoint(SnapCandidatePoint const &p) const {
+ return (p.getPoint() - _origin) * Geom::Rotate(_angle) + _origin;
+}
+
+SnappedPoint PureRotateConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
+ // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
+ // the transformation of the bounding box is equal to the transformation of the individual nodes. This is
+ // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
+ // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
+ // of bounding boxes is not allowed here.
+ g_assert(!(p.getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY));
+
+ // Calculate a constraint dedicated for this specific point
+ Geom::Point b = pt_orig - _origin;
+ Geom::Coord r = Geom::L2(b); // the radius of the circular constraint
+ Snapper::SnapConstraint dedicated_constraint = Inkscape::Snapper::SnapConstraint(_origin, b, r);
+ return sm->constrainedSnap(p, dedicated_constraint, bbox_to_snap);
+}
+
+void PureRotateConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
+ Geom::Point const a = snapped_point.getPoint() - _origin; // vector to snapped point
+ Geom::Point const b = (original_point.getPoint() - _origin); // vector to original point (not the transformed point!)
+ // a is vector to snapped point; b is vector to original point; now lets calculate angle between a and b
+ _angle_snapped = atan2(Geom::dot(Geom::rot90(b), a), Geom::dot(b, a));
+ if (Geom::L2(b) < 1e-9) { // points too close to the rotation center will not move. Don't try to snap these
+ // as they will always yield a perfect snap result if they're already snapped beforehand (e.g.
+ // when the transformation center has been snapped to a grid intersection in the selector tool)
+ snapped_point.setSnapDistance(Geom::infinity());
+ // PS1: Apparently we don't have to do this for skewing, but why?
+ // PS2: We cannot easily filter these points upstream, e.g. in the grab() method (seltrans.cpp)
+ // because the rotation center will change when pressing shift, and grab() won't be recalled.
+ // Filtering could be done in handleRequest() (again in seltrans.cpp), by iterating through
+ // the snap candidates. But hey, we're iterating here anyway.
+ } else {
+ snapped_point.setSnapDistance(fabs(_angle_snapped - _angle));
+ }
+ snapped_point.setSecondSnapDistance(Geom::infinity());
+
+}
+
+}
diff --git a/src/pure-transform.h b/src/pure-transform.h
new file mode 100644
index 000000000..8ea74ce76
--- /dev/null
+++ b/src/pure-transform.h
@@ -0,0 +1,230 @@
+/*
+ * Class for pure transformations, such as translating, scaling, stretching, skewing, and rotating. Pure means that they cannot
+ * be combined. This is what makes them different from affine transformations. Pure transformations are being used in the selector
+ * tool and node tool
+ *
+ * Authors:
+ * Diederik van Lierop <mail@diedenrezi.nl>
+ *
+ * Copyright (C) 2015 Diederik van Lierop
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_PURE_TRANSFORM_H
+#define SEEN_PURE_TRANSFORM_H
+
+#include <glib.h> // for g_warning
+#include "snapper.h" // for SnapConstraint
+
+class SnapManager;
+
+namespace Inkscape {
+
+class PureTransform {
+
+protected:
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const = 0;
+ virtual Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const = 0;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) = 0;
+
+public:
+ //PureTransform();
+ virtual ~PureTransform() {};
+// virtual PureTransform * clone () const = 0; // https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Virtual_Constructor
+
+ // Snap a group of points
+ SnappedPoint best_snapped_point;
+ void snap(::SnapManager *sm, std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer);
+};
+
+// **************************************************************************************************************
+
+class PureTranslate: public PureTransform {
+
+protected:
+ Geom::Point _vector;
+ Geom::Point _vector_snapped;
+
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+ virtual Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point);
+
+public:
+// PureTranslate(); // Default constructor
+// PureTranslate(PureTranslate const &); // Copy constructor
+ virtual ~PureTranslate() {};
+ PureTranslate(Geom::Point vector = Geom::Point()) {
+ _vector = vector;
+ _vector_snapped = vector;
+ }
+
+ Geom::Point getTranslationSnapped() {return _vector_snapped;}
+// PureTranslate * clone () const {return new PureTranslate(*this);}
+};
+
+
+class PureTranslateConstrained: public PureTranslate {
+
+protected:
+ Geom::Dim2 _direction;
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+
+public:
+ virtual ~PureTranslateConstrained() {};
+ PureTranslateConstrained(Geom::Coord displacement, Geom::Dim2 direction):
+ PureTranslate() {
+ _vector[direction] = displacement;
+ _vector[1-direction] = 0.0;
+ _direction = direction;
+ }
+ // PureTranslateConstrained * clone () const {return new PureTranslateConstrained(*this);}
+};
+
+// **************************************************************************************************************
+
+class PureScale: public PureTransform {
+
+protected:
+ Geom::Scale _scale;
+ Geom::Scale _scale_snapped;
+ Geom::Point _origin;
+ bool _uniform;
+
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+ virtual Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point);
+
+public:
+// PureScale(); // Default constructor
+// PureScale(PureScale const &); // Copy constructor
+ virtual ~PureScale() {};
+
+ PureScale(Geom::Scale scale, Geom::Point origin, bool uniform) {
+ _scale = scale;
+ _scale_snapped = scale;
+ _origin = origin;
+ _uniform = uniform;
+ }
+
+ Geom::Scale getScaleSnapped() {return _scale_snapped;}
+// PureScale * clone () const {return new PureScale (*this);}
+};
+
+class PureScaleConstrained: public PureScale {
+//Magnitude of the scale components will be the same, but the sign could still be different ()
+protected:
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+
+public:
+ virtual ~PureScaleConstrained() {};
+ PureScaleConstrained(Geom::Scale scale, Geom::Point origin):
+ PureScale(scale, origin, true) {}; // Non-uniform constrained scaling is not supported
+
+// PureScaleConstrained * clone () const {return new PureScaleConstrained(*this);}
+};
+
+// **************************************************************************************************************
+
+class PureStretchConstrained: public PureTransform {
+// A stretch is always implicitly constrained
+
+protected:
+ Geom::Coord _magnitude;
+ Geom::Scale _stretch_snapped;
+ Geom::Point _origin;
+ Geom::Dim2 _direction;
+ bool _uniform;
+
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+ virtual Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point);
+
+public:
+ virtual ~PureStretchConstrained() {};
+ PureStretchConstrained(Geom::Coord magnitude, Geom::Point origin, Geom::Dim2 direction, bool uniform) {
+ _magnitude = magnitude;
+ _origin = origin;
+ _direction = direction;
+ _uniform = uniform;
+ _stretch_snapped = Geom::Scale(magnitude, magnitude);
+ if (not uniform) {
+ _stretch_snapped[1-direction] = 1.0;
+ }
+ }
+
+ Geom::Scale getStretchSnapped() {return _stretch_snapped;}
+ Geom::Coord getMagnitude() {return _magnitude;}
+ Geom::Coord getMagnitudeSnapped() {return _stretch_snapped[_direction];}
+
+// PureStretchConstrained * clone () const {return new PureStretchConstrained(*this);}
+};
+
+// **************************************************************************************************************
+
+class PureSkewConstrained: public PureTransform {
+// A skew is always implicitly constrained
+
+protected:
+ Geom::Coord _skew;
+ Geom::Coord _skew_snapped;
+ Geom::Coord _scale;
+ Geom::Point _origin;
+ Geom::Dim2 _direction;
+
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+ Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point);
+
+public:
+ virtual ~PureSkewConstrained() {};
+ PureSkewConstrained(Geom::Coord skew, Geom::Coord scale, Geom::Point origin, Geom::Dim2 direction) {
+ _skew = skew;
+ _skew_snapped = skew;
+ _scale = scale;
+ _origin = origin;
+ _direction = direction;
+ };
+
+ Geom::Coord getSkewSnapped() {return _skew_snapped;}
+
+// PureSkewConstrained * clone () const {return new PureSkewConstrained(*this);}
+};
+
+// **************************************************************************************************************
+
+class PureRotateConstrained: public PureTransform {
+// A rotation is always implicitly constrained, so we will hide the constructor by making it protected; devs should use PureRotateConstrained instead
+// It's _constraint member variable though will be empty
+
+protected:
+ double _angle; // in radians
+ double _angle_snapped;
+ Geom::Point _origin;
+ bool _uniform;
+
+ virtual SnappedPoint snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const;
+ virtual Geom::Point getTransformedPoint(SnapCandidatePoint const &p) const;
+ virtual void storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point);
+
+public:
+// PureRotate(); // Default constructor
+// PureRotate(PureRotate const &); // Copy constructor
+ virtual ~PureRotateConstrained() {};
+
+ PureRotateConstrained(double angle, Geom::Point origin) {
+ _origin = origin;
+ _angle = angle; // in radians!
+ _angle_snapped = angle;
+ _uniform = true; // We do not yet allow for simultaneous rotation and scaling
+ }
+
+ double getAngleSnapped() {return _angle_snapped;}
+
+// PureRotate * clone () const {return new PureRotate(*this);}
+};
+
+}
+
+#endif // !SEEN_PURE_TRANSFORM_H
+
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index f7562923f..17d704975 100644
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -31,6 +31,7 @@
#include "knot.h"
#include "message-stack.h"
#include "snap.h"
+#include "pure-transform.h"
#include "selection.h"
#include "ui/tools/select-tool.h"
#include "sp-item.h"
@@ -897,10 +898,7 @@ gboolean Inkscape::SelTrans::scaleRequest(Geom::Point &pt, guint state)
// When scaling by an integer, snapping is not needed
} else {
// In all other cases we should try to snap now
- SnapManager &m = _desktop->namedview->snap_manager;
- m.setup(_desktop, false, _items_const);
-
- Inkscape::SnappedPoint bb, sn;
+ Inkscape::PureScale *bb, *sn;
if ((state & GDK_CONTROL_MASK) || _desktop->isToolboxButtonActive ("lock")) {
// Scale is locked to a 1:1 aspect ratio, so that s[X] must be made to equal s[Y].
@@ -915,30 +913,37 @@ gboolean Inkscape::SelTrans::scaleRequest(Geom::Point &pt, guint state)
}
// Snap along a suitable constraint vector from the origin.
- bb = m.constrainedSnapScale(_bbox_points, _point, default_scale, _origin_for_bboxpoints);
- sn = m.constrainedSnapScale(_snap_points, _point, geom_scale, _origin_for_specpoints);
+
+ bb = new Inkscape::PureScaleConstrained(default_scale, _origin_for_bboxpoints);
+ sn = new Inkscape::PureScaleConstrained(geom_scale, _origin_for_specpoints);
} else {
/* Scale aspect ratio is unlocked */
- bb = m.freeSnapScale(_bbox_points, _point, default_scale, _origin_for_bboxpoints);
- sn = m.freeSnapScale(_snap_points, _point, geom_scale, _origin_for_specpoints);
+ bb = new Inkscape::PureScale(default_scale, _origin_for_bboxpoints, false);
+ sn = new Inkscape::PureScale(geom_scale, _origin_for_specpoints, false);
}
+ SnapManager &m = _desktop->namedview->snap_manager;
+ m.setup(_desktop, false, _items_const);
+ m.snapTransformed(_bbox_points, _point, (*bb));
+ m.snapTransformed(_snap_points, _point, (*sn));
+ m.unSetup();
// These lines below are duplicated in stretchRequest
- if (bb.getSnapped() || sn.getSnapped()) {
- if (bb.getSnapped()) {
- if (!bb.isOtherSnapBetter(sn, false)) {
+ //TODO: Eliminate this code duplication
+ if (bb->best_snapped_point.getSnapped() || sn->best_snapped_point.getSnapped()) {
+ if (bb->best_snapped_point.getSnapped()) {
+ if (!bb->best_snapped_point.isOtherSnapBetter(sn->best_snapped_point, false)) {
// We snapped the bbox (which is either visual or geometric)
- _desktop->snapindicator->set_new_snaptarget(bb);
- default_scale = Geom::Scale(bb.getTransformation());
+ _desktop->snapindicator->set_new_snaptarget(bb->best_snapped_point);
+ default_scale = bb->getScaleSnapped();
// Calculate the new transformation and update the handle position
pt = _calcAbsAffineDefault(default_scale);
}
- } else if (sn.getSnapped()) {
- _desktop->snapindicator->set_new_snaptarget(sn);
+ } else if (sn->best_snapped_point.getSnapped()) {
+ _desktop->snapindicator->set_new_snaptarget(sn->best_snapped_point);
// We snapped the special points (e.g. nodes), which are not at the visual bbox
// The handle location however (pt) might however be at the visual bbox, so we
// will have to calculate pt taking the stroke width into account
- geom_scale = Geom::Scale(sn.getTransformation());
+ geom_scale = sn->getScaleSnapped();
pt = _calcAbsAffineGeom(geom_scale);
}
} else {
@@ -946,7 +951,9 @@ gboolean Inkscape::SelTrans::scaleRequest(Geom::Point &pt, guint state)
_calcAbsAffineDefault(default_scale);
_desktop->snapindicator->remove_snaptarget();
}
- m.unSetup();
+
+ delete bb;
+ delete sn;
}
/* Status text */
@@ -1003,21 +1010,22 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom
SnapManager &m = _desktop->namedview->snap_manager;
m.setup(_desktop, false, _items_const);
- Inkscape::SnappedPoint bb, sn;
- g_assert(bb.getSnapped() == false); // Check initialization to catch any regression
-
bool symmetrical = state & GDK_CONTROL_MASK;
- bb = m.constrainedSnapStretch(_bbox_points, _point, Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical);
- sn = m.constrainedSnapStretch(_snap_points, _point, Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical);
+ Inkscape::PureStretchConstrained bb = Inkscape::PureStretchConstrained(Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical);
+ Inkscape::PureStretchConstrained sn = Inkscape::PureStretchConstrained(Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical);
- if (bb.getSnapped()) {
+ m.snapTransformed(_bbox_points, _point, bb);
+ m.snapTransformed(_snap_points, _point, sn);
+ m.unSetup();
+
+ if (bb.best_snapped_point.getSnapped()) {
// We snapped the bbox (which is either visual or geometric)
- default_scale[axis] = bb.getTransformation()[axis];
+ default_scale[axis] = bb.getMagnitude();
}
- if (sn.getSnapped()) {
- geom_scale[axis] = sn.getTransformation()[axis];
+ if (sn.best_snapped_point.getSnapped()) {
+ geom_scale[axis] = sn.getMagnitude();
}
if (symmetrical) {
@@ -1028,21 +1036,21 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom
}
// These lines below are duplicated in scaleRequest
- if (bb.getSnapped() || sn.getSnapped()) {
- if (bb.getSnapped()) {
- if (!bb.isOtherSnapBetter(sn, false)) {
+ if (bb.best_snapped_point.getSnapped() || sn.best_snapped_point.getSnapped()) {
+ if (bb.best_snapped_point.getSnapped()) {
+ if (!bb.best_snapped_point.isOtherSnapBetter(sn.best_snapped_point, false)) {
// We snapped the bbox (which is either visual or geometric)
- _desktop->snapindicator->set_new_snaptarget(bb);
- default_scale = Geom::Scale(bb.getTransformation());
+ _desktop->snapindicator->set_new_snaptarget(bb.best_snapped_point);
+ default_scale = bb.getStretchSnapped();
// Calculate the new transformation and update the handle position
pt = _calcAbsAffineDefault(default_scale);
}
- } else if (sn.getSnapped()) {
- _desktop->snapindicator->set_new_snaptarget(sn);
+ } else if (sn.best_snapped_point.getSnapped()) {
+ _desktop->snapindicator->set_new_snaptarget(sn.best_snapped_point);
// We snapped the special points (e.g. nodes), which are not at the visual bbox
// The handle location however (pt) might however be at the visual bbox, so we
// will have to calculate pt taking the stroke width into account
- geom_scale = Geom::Scale(sn.getTransformation());
+ geom_scale = sn.getStretchSnapped();
pt = _calcAbsAffineGeom(geom_scale);
}
} else {
@@ -1050,8 +1058,6 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom
_calcAbsAffineDefault(default_scale);
_desktop->snapindicator->remove_snaptarget();
}
-
- m.unSetup();
}
// status text
@@ -1156,16 +1162,14 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P
SnapManager &m = _desktop->namedview->snap_manager;
m.setup(_desktop, false, _items_const);
- Geom::Point cvec; cvec[dim_b] = 1.;
- Inkscape::Snapper::SnapConstraint const constraint(cvec);
- // When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details
- Geom::Point const s(skew[dim_a], scale[dim_a]);
- Inkscape::SnappedPoint sn = m.constrainedSnapSkew(_snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b));
+ // When skewing, we cannot snap the corners of the bounding box, see the comment in PureSkewConstrained for details
+ Inkscape::PureSkewConstrained sn = Inkscape::PureSkewConstrained(skew[dim_a], scale[dim_a], _origin, Geom::Dim2(dim_b));
+ m.snapTransformed(_snap_points, _point, sn);
- if (sn.getSnapped()) {
+ if (sn.best_snapped_point.getSnapped()) {
// We snapped something, so change the skew to reflect it
- skew[dim_a] = sn.getTransformation()[0];
- _desktop->snapindicator->set_new_snaptarget(sn);
+ skew[dim_a] = sn.getSkewSnapped();
+ _desktop->snapindicator->set_new_snaptarget(sn.best_snapped_point);
} else {
_desktop->snapindicator->remove_snaptarget();
}
@@ -1243,13 +1247,14 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state)
SnapManager &m = _desktop->namedview->snap_manager;
m.setup(_desktop, false, _items_const);
// When rotating, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapRotate" for details
- Inkscape::SnappedPoint sn = m.constrainedSnapRotate(_snap_points, _point, radians, _origin);
+ Inkscape::PureRotateConstrained sn = Inkscape::PureRotateConstrained(radians, _origin);
+ m.snapTransformed(_snap_points, _point, sn);
m.unSetup();
- if (sn.getSnapped()) {
- _desktop->snapindicator->set_new_snaptarget(sn);
+ if (sn.best_snapped_point.getSnapped()) {
+ _desktop->snapindicator->set_new_snaptarget(sn.best_snapped_point);
// We snapped something, so change the rotation to reflect it
- radians = sn.getTransformation()[0];
+ radians = sn.getAngleSnapped();
r1 = Geom::Rotate(0);
r2 = Geom::Rotate(radians);
} else {
@@ -1352,30 +1357,19 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
bool const control = (state & GDK_CONTROL_MASK);
bool const shift = (state & GDK_SHIFT_MASK);
- if (alt) {
-
- // Alt pressed means: move only by integer multiples of the grid spacing
-
- if (control) { // ... if also constrained to the orthogonal axes
- if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) {
- dxy[Geom::Y] = 0;
- } else {
- dxy[Geom::X] = 0;
- }
+ if (control) { // constrained to the orthogonal axes
+ if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) {
+ dxy[Geom::Y] = 0;
+ } else {
+ dxy[Geom::X] = 0;
}
+ }
+
+ if (alt) {// Alt pressed means: move only by integer multiples of the grid spacing
m.setup(_desktop, true, _items_const);
dxy = m.multipleOfGridPitch(dxy, _point);
m.unSetup();
- } else if (shift) {
- if (control) { // shift & control: constrained movement without snapping
- if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) {
- dxy[Geom::Y] = 0;
- } else {
- dxy[Geom::X] = 0;
- }
- }
- } else { //!shift: with snapping
-
+ } else if (!shift) { //!shift: with snapping
/* We're snapping to things, possibly with a constraint to horizontal or
** vertical movement. Obtain a list of possible translations and then
** pick the smallest.
@@ -1386,52 +1380,55 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
/* This will be our list of possible translations */
std::list<Inkscape::SnappedPoint> s;
+ Inkscape::PureTranslate *bb, *sn;
+
if (control) { // constrained movement with snapping
/* Snap to things, and also constrain to horizontal or vertical movement */
- unsigned int dim = fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y]) ? Geom::X : Geom::Y;
+ Geom::Dim2 dim = fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y]) ? Geom::X : Geom::Y;
// When doing a constrained translation, all points will move in the same direction, i.e.
// either horizontally or vertically. Therefore we only have to specify the direction of
// the constraint-line once. The constraint lines are parallel, but might not be colinear.
// Therefore we will have to set the point through which the constraint-line runs
- // individually for each point to be snapped; this will be handled however by _snapTransformed()
- Geom::Point cvec; cvec[dim] = 1.;
- s.push_back(m.constrainedSnapTranslate(_bbox_points,
- _point,
- Inkscape::Snapper::SnapConstraint(cvec),
- dxy));
-
- s.push_back(m.constrainedSnapTranslate(_snap_points,
- _point,
- Inkscape::Snapper::SnapConstraint(cvec),
- dxy));
+ // individually for each point to be snapped; this will be handled however by snapTransformed()
+ bb = new Inkscape::PureTranslateConstrained(dxy[dim], dim);
+ sn = new Inkscape::PureTranslateConstrained(dxy[dim], dim);
} else { // !control
-
- // Let's leave this timer code here for a while. I'll probably need it in the near future (Diederik van Lierop)
- /* GTimeVal starttime;
- GTimeVal endtime;
- g_get_current_time(&starttime); */
-
/* Snap to things with no constraint */
- s.push_back(m.freeSnapTranslate(_bbox_points, _point, dxy));
- s.push_back(m.freeSnapTranslate(_snap_points, _point, dxy));
-
- /*g_get_current_time(&endtime);
- double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0;
- std::cout << "Time spent snapping: " << elapsed << std::endl; */
+ bb = new Inkscape::PureTranslate(dxy);
+ sn = new Inkscape::PureTranslate(dxy);
}
+ // Let's leave this timer code here for a while. I'll probably need it in the near future (Diederik van Lierop)
+ /* GTimeVal starttime;
+ GTimeVal endtime;
+ g_get_current_time(&starttime); */
+
+ m.snapTransformed(_bbox_points, _point, (*bb));
+ m.snapTransformed(_snap_points, _point, (*sn));
m.unSetup();
+ /*g_get_current_time(&endtime);
+ double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0;
+ std::cout << "Time spent snapping: " << elapsed << std::endl; */
+
/* Pick one */
Inkscape::SnappedPoint best_snapped_point;
- for (std::list<Inkscape::SnappedPoint>::const_iterator i = s.begin(); i != s.end(); ++i) {
- if (i->getSnapped()) {
- if (best_snapped_point.isOtherSnapBetter(*i, true)) {
- best_snapped_point = *i;
- dxy = i->getTransformation();
- }
- }
+
+ bool sn_is_best = sn->best_snapped_point.getSnapped();
+ bool bb_is_best = bb->best_snapped_point.getSnapped();
+
+ if (bb_is_best && sn_is_best) {
+ sn_is_best = bb->best_snapped_point.isOtherSnapBetter(sn->best_snapped_point, true);
+ bb_is_best = !sn_is_best;
+ }
+
+ if (sn_is_best) {
+ best_snapped_point = sn->best_snapped_point;
+ dxy = sn->getTranslationSnapped();
+ } else if (bb_is_best) {
+ best_snapped_point = bb->best_snapped_point;
+ dxy = bb->getTranslationSnapped();
}
if (best_snapped_point.getSnapped()) {
@@ -1609,7 +1606,7 @@ void Inkscape::SelTrans::_keepClosestPointOnly(Geom::Point const &p)
}
}
-
+// TODO: This code is duplicated in transform-handle-set.cpp; fix this!
void Inkscape::SelTrans::getNextClosestPoint(bool reverse)
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -1635,6 +1632,12 @@ void Inkscape::SelTrans::getNextClosestPoint(bool reverse)
} else {
_snap_points.push_back(*_all_snap_sources_iter);
}
+
+ // Show the updated snap source now; otherwise it won't be shown until the selection is being moved again
+ SnapManager &m = _desktop->namedview->snap_manager;
+ m.setup(_desktop);
+ m.displaySnapsource(*_all_snap_sources_iter);
+ m.unSetup();
}
}
}
diff --git a/src/snap.cpp b/src/snap.cpp
index 5a308777c..5a4a047b2 100644
--- a/src/snap.cpp
+++ b/src/snap.cpp
@@ -23,6 +23,7 @@
#include "snap-enums.h"
#include "snapped-line.h"
#include "snapped-curve.h"
+#include "pure-transform.h"
#include "display/canvas-grid.h"
#include "display/snap-indicator.h"
@@ -442,16 +443,11 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline)
s.getPointIfSnapped(p);
}
-Inkscape::SnappedPoint SnapManager::_snapTransformed(
+void SnapManager::snapTransformed(
std::vector<Inkscape::SnapCandidatePoint> const &points,
Geom::Point const &pointer,
- bool constrained,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Transformation transformation_type,
- Geom::Point const &transformation,
- Geom::Point const &origin,
- Geom::Dim2 dim,
- bool uniform)
+ Inkscape::PureTransform &transform
+ )
{
/* We have a list of points, which we are proposing to transform in some way. We need to see
** if any of these points, when transformed, snap to anything. If they do, we return the
@@ -459,47 +455,8 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
*/
if (points.size() == 0) {
- return Inkscape::SnappedPoint(pointer);
- }
-
- std::vector<Inkscape::SnapCandidatePoint> transformed_points;
- Geom::Rect bbox;
-
- long source_num = 0;
- for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
-
- /* Work out the transformed version of this point */
- Geom::Point transformed = _transformPoint(*i, transformation_type, transformation, origin, dim, uniform);
-
- // add the current transformed point to the box hulling all transformed points
- if (i == points.begin()) {
- bbox = Geom::Rect(transformed, transformed);
- } else {
- bbox.expandTo(transformed);
- }
-
- transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num, Inkscape::SNAPTARGET_UNDEFINED, Geom::OptRect()));
- source_num++;
- }
-
- /* The current best transformation */
- Geom::Point best_transformation = transformation;
-
- /* The current best metric for the best transformation; lower is better, Geom::infinity()
- ** means that we haven't snapped anything.
- */
- Inkscape::SnappedPoint best_snapped_point;
- g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
- g_assert(best_snapped_point.getAtIntersection() == false);
-
- // Warnings for the devs
- if (constrained && transformation_type == SCALE && !uniform) {
- g_warning("Non-uniform constrained scaling is not supported!");
- }
-
- if (!constrained && transformation_type == ROTATE) {
- // We do not yet allow for simultaneous rotation and scaling
- g_warning("Unconstrained rotation is not supported!");
+ transform.best_snapped_point = Inkscape::SnappedPoint(pointer);
+ return;
}
// We will try to snap a set of points, but we don't want to have a snap indicator displayed
@@ -508,349 +465,22 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
bool _orig_snapindicator_status = _snapindicator;
_snapindicator = false;
- std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();
-
- // std::cout << std::endl;
- bool first_free_snap = true;
-
- for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
-
- /* Snap it */
- Inkscape::SnappedPoint snapped_point;
- Inkscape::Snapper::SnapConstraint dedicated_constraint = constraint;
- Geom::Point const b = ((*i).getPoint() - origin); // vector to original point (not the transformed point! required for rotations!)
-
- if (constrained) {
- if (((transformation_type == SCALE || transformation_type == STRETCH) && uniform)) {
- // When uniformly scaling, each point will have its own unique constraint line,
- // running from the scaling origin to the original untransformed point. We will
- // calculate that line here
- dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b);
- } else if (transformation_type == ROTATE) {
- Geom::Coord r = Geom::L2(b); // the radius of the circular constraint
- dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b, r);
- } else if (transformation_type == STRETCH) { // when non-uniform stretching {
- Geom::Point cvec; cvec[dim] = 1.;
- dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), cvec);
- } else if (transformation_type == TRANSLATE) {
- // When doing a constrained translation, all points will move in the same direction, i.e.
- // either horizontally or vertically. The lines along which they move are therefore all
- // parallel, but might not be colinear. Therefore we will have to specify the point through
- // which the constraint-line runs here, for each point individually. (we could also have done this
- // earlier on, e.g. in seltrans.cpp but we're being lazy there and don't want to add an iteration loop)
- dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), constraint.getDirection());
- } // else: leave the original constraint, e.g. for skewing
- snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
- } else {
- bool const c1 = fabs(b[Geom::X]) < 1e-6;
- bool const c2 = fabs(b[Geom::Y]) < 1e-6;
- if (transformation_type == SCALE && (c1 || c2) && !(c1 && c2)) {
- // When scaling, a point aligned either horizontally or vertically with the origin can only
- // move in that specific direction; therefore it should only snap in that direction, otherwise
- // we will get snapped points with an invalid transformation
- Geom::Point cvec; cvec[c1] = 1.;
- dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, cvec);
- snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
- } else {
- // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
- // requirements, then freeSnap might never see the SnapCandidatePoint with source_num == 0. The freeSnap()
- // method in the object snapper depends on this, because only for source-num == 0 the target nodes will
- // be collected. Therefore we enforce that the first SnapCandidatePoint that is to be freeSnapped always
- // has source_num == 0;
- // TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
- // of it and explicitly communicate to the object snapper that this is a first point
- if (first_free_snap) {
- (*j).setSourceNum(0);
- first_free_snap = false;
- }
- snapped_point = freeSnap(*j, bbox);
- }
- }
- // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl;
- snapped_point.setPointerDistance(Geom::L2(pointer - (*i).getPoint()));
-
- // Allow the snapindicator to be displayed again
- _snapindicator = _orig_snapindicator_status;
-
- Geom::Point result;
-
- /*Find the transformation that describes where the snapped point has
- ** ended up, and also the metric for this transformation.
- */
- Geom::Point const a = snapped_point.getPoint() - origin; // vector to snapped point
- //Geom::Point const b = (*i - origin); // vector to original point
-
- switch (transformation_type) {
- case TRANSLATE:
- result = snapped_point.getPoint() - (*i).getPoint();
- /* Consider the case in which a box is almost aligned with a grid in both
- * horizontal and vertical directions. The distance to the intersection of
- * the grid lines will always be larger then the distance to a single grid
- * line. If we prefer snapping to an intersection over to a single
- * grid line, then we cannot use "metric = Geom::L2(result)". Therefore the
- * snapped distance will be used as a metric. Please note that the snapped
- * distance to an intersection is defined as the distance to the nearest line
- * of the intersection, and not to the intersection itself!
- */
- // Only for translations, the relevant metric will be the real snapped distance,
- // so we don't have to do anything special here
- break;
- case SCALE:
- {
- result = Geom::Point(Geom::infinity(), Geom::infinity());
- // If this point *i is horizontally or vertically aligned with
- // the origin of the scaling, then it will scale purely in X or Y
- // We can therefore only calculate the scaling in this direction
- // and the scaling factor for the other direction should remain
- // untouched (unless scaling is uniform of course)
- for (int index = 0; index < 2; index++) {
- if (fabs(b[index]) > 1e-4) { // if SCALING CAN occur in this direction
- if (fabs(fabs(a[index]/b[index]) - fabs(transformation[index])) > 1e-7) { // if SNAPPING DID occur in this direction
- result[index] = a[index] / b[index]; // then calculate it!
- }
- // we might have left result[1-index] = Geom::infinity()
- // if scaling didn't occur in the other direction
- }
- }
- if (uniform) {
- if (fabs(result[0]) < fabs(result[1])) {
- result[1] = result[0];
- } else {
- result[0] = result[1];
- }
- }
-
- // Compare the resulting scaling with the desired scaling
- Geom::Point scale_metric = result - transformation; // One or both of its components might be Geom::infinity()
- scale_metric[0] = fabs(scale_metric[0]);
- scale_metric[1] = fabs(scale_metric[1]);
- if (scale_metric[0] == Geom::infinity() || scale_metric[1] == Geom::infinity()) {
- snapped_point.setSnapDistance(std::min(scale_metric[0], scale_metric[1]));
- } else {
- snapped_point.setSnapDistance(Geom::L2(scale_metric));
- }
- snapped_point.setSecondSnapDistance(Geom::infinity());
- break;
- }
- case STRETCH:
- result = Geom::Point(Geom::infinity(), Geom::infinity());
- if (fabs(b[dim]) > 1e-6) { // if STRETCHING will occur for this point
- result[dim] = a[dim] / b[dim];
- result[1-dim] = uniform ? result[dim] : 1;
- } else { // STRETCHING might occur for this point, but only when the stretching is uniform
- if (uniform && fabs(b[1-dim]) > 1e-6) {
- result[1-dim] = a[1-dim] / b[1-dim];
- result[dim] = result[1-dim];
- }
- }
- // Store the metric for this transformation as a virtual distance
- snapped_point.setSnapDistance(std::abs(result[dim] - transformation[dim]));
- snapped_point.setSecondSnapDistance(Geom::infinity());
- break;
- case SKEW:
- result[0] = (snapped_point.getPoint()[dim] - ((*i).getPoint())[dim]) / b[1 - dim]; // skew factor
- result[1] = transformation[1]; // scale factor
- // Store the metric for this transformation as a virtual distance
- snapped_point.setSnapDistance(std::abs(result[0] - transformation[0]));
- snapped_point.setSecondSnapDistance(Geom::infinity());
- break;
- case ROTATE:
- // a is vector to snapped point; b is vector to original point; now lets calculate angle between a and b
- result[0] = atan2(Geom::dot(Geom::rot90(b), a), Geom::dot(b, a));
- result[1] = result[0]; // dummy value; how else should we store an angle in a point ;-)
- if (Geom::L2(b) < 1e-9) { // points too close to the rotation center will not move. Don't try to snap these
- // as they will always yield a perfect snap result if they're already snapped beforehand (e.g.
- // when the transformation center has been snapped to a grid intersection in the selector tool)
- snapped_point.setSnapDistance(Geom::infinity());
- // PS1: Apparently we don't have to do this for skewing, but why?
- // PS2: We cannot easily filter these points upstream, e.g. in the grab() method (seltrans.cpp)
- // because the rotation center will change when pressing shift, and grab() won't be recalled.
- // Filtering could be done in handleRequest() (again in seltrans.cpp), by iterating through
- // the snap candidates. But hey, we're iterating here anyway.
- } else {
- snapped_point.setSnapDistance(std::abs(result[0] - transformation[0]));
- }
- snapped_point.setSecondSnapDistance(Geom::infinity());
- break;
- default:
- g_assert_not_reached();
- }
-
- if (snapped_point.getSnapped()) {
- // We snapped; keep track of the best snap
- if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) {
- best_transformation = result;
- best_snapped_point = snapped_point;
- }
- } else {
- // So we didn't snap for this point
- if (!best_snapped_point.getSnapped()) {
- // ... and none of the points before snapped either
- // We might still need to apply a constraint though, if we tried a constrained snap. And
- // in case of a free snap we might have use for the transformed point, so let's return that
- // point, whether it's constrained or not
- if (best_snapped_point.isOtherSnapBetter(snapped_point, true) || points.size() == 1) {
- // .. so we must keep track of the best non-snapped constrained point
- best_transformation = result;
- best_snapped_point = snapped_point;
- }
- }
- }
-
- ++j;
- }
-
- Geom::Coord best_metric;
- if (transformation_type == SCALE) {
- // When scaling, don't ever exit with one of scaling components uninitialized
- for (int index = 0; index < 2; index++) {
- if (fabs(best_transformation[index]) == Geom::infinity()) {
- if (uniform && fabs(best_transformation[1-index]) < Geom::infinity()) {
- best_transformation[index] = best_transformation[1-index];
- } else {
- best_transformation[index] = transformation[index];
- }
- }
- }
- }
+ transform.snap(this, points, pointer);
- best_metric = best_snapped_point.getSnapDistance();
- best_snapped_point.setTransformation(best_transformation);
- // Using " < 1e6" instead of " < Geom::infinity()" for catching some rounding errors
- // These rounding errors might be caused by NRRects, see bug #1584301
- best_snapped_point.setSnapDistance(best_metric < 1e6 ? best_metric : Geom::infinity());
+ // Allow the snapindicator to be displayed again
+ _snapindicator = _orig_snapindicator_status;
if (_snapindicator) {
- if (best_snapped_point.getSnapped()) {
- _desktop->snapindicator->set_new_snaptarget(best_snapped_point);
+ if (transform.best_snapped_point.getSnapped()) {
+ _desktop->snapindicator->set_new_snaptarget(transform.best_snapped_point);
} else {
_desktop->snapindicator->remove_snaptarget();
}
}
- return best_snapped_point;
-}
-
-
-Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Point const &tr)
-{
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
- return result;
-}
-
-Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Geom::Point const &tr)
-{
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
-
- return result;
-}
-
-
-Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Scale const &s,
- Geom::Point const &o)
-{
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
-
- return result;
-}
-
-
-Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Scale const &s,
- Geom::Point const &o)
-{
- // When constrained scaling, only uniform scaling is supported.
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
-
- return result;
-}
-
-Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Coord const &s,
- Geom::Point const &o,
- Geom::Dim2 d,
- bool u)
-{
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), STRETCH, Geom::Point(s, s), o, d, u);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
-
- return result;
-}
-
-Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Geom::Point const &s,
- Geom::Point const &o,
- Geom::Dim2 d)
-{
- // "s" contains skew factor in s[0], and scale factor in s[1]
-
- // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
- // the transformation of the bounding box is equal to the transformation of the individual nodes. This is
- // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
- // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
- // of bounding boxes is not allowed here.
- if (!p.empty()) {
- g_assert(!(p.at(0).getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY));
- }
-
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, constraint, SKEW, s, o, d, false);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
- }
-
- return result;
-}
-
-Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Coord const &angle,
- Geom::Point const &o)
-{
- // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
- // the transformation of the bounding box is equal to the transformation of the individual nodes. This is
- // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
- // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
- // of bounding boxes is not allowed here.
-
- Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false);
-
- if (p.size() == 1) {
- displaySnapsource(Inkscape::SnapCandidatePoint(result.getPoint(), p.at(0).getSourceType()));
+ if (points.size() == 1) {
+ displaySnapsource(Inkscape::SnapCandidatePoint(transform.best_snapped_point.getPoint(), points.at(0).getSourceType()));
}
-
- return result;
-
}
Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p,
@@ -1090,51 +720,51 @@ SPDocument *SnapManager::getDocument() const
return _named_view->document;
}
-Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p,
- Transformation const transformation_type,
- Geom::Point const &transformation,
- Geom::Point const &origin,
- Geom::Dim2 const dim,
- bool const uniform) const
-{
- /* Work out the transformed version of this point */
- Geom::Point transformed;
- switch (transformation_type) {
- case TRANSLATE:
- transformed = p.getPoint() + transformation;
- break;
- case SCALE:
- transformed = (p.getPoint() - origin) * Geom::Scale(transformation[Geom::X], transformation[Geom::Y]) + origin;
- break;
- case STRETCH:
- {
- Geom::Scale s(1, 1);
- if (uniform)
- s[Geom::X] = s[Geom::Y] = transformation[dim];
- else {
- s[dim] = transformation[dim];
- s[1 - dim] = 1;
- }
- transformed = ((p.getPoint() - origin) * s) + origin;
- break;
- }
- case SKEW:
- // Apply the skew factor
- transformed[dim] = (p.getPoint())[dim] + transformation[0] * ((p.getPoint())[1 - dim] - origin[1 - dim]);
- // While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed.
- // Apply that scale factor here
- transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim];
- break;
- case ROTATE:
- // for rotations: transformation[0] stores the angle in radians
- transformed = (p.getPoint() - origin) * Geom::Rotate(transformation[0]) + origin;
- break;
- default:
- g_assert_not_reached();
- }
-
- return transformed;
-}
+//Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p,
+// Transformation const transformation_type,
+// Geom::Point const &transformation,
+// Geom::Point const &origin,
+// Geom::Dim2 const dim,
+// bool const uniform) const
+//{
+// /* Work out the transformed version of this point */
+// Geom::Point transformed;
+// switch (transformation_type) {
+// case TRANSLATE:
+// transformed = p.getPoint() + transformation;
+// break;
+// case SCALE:
+// transformed = (p.getPoint() - origin) * Geom::Scale(transformation[Geom::X], transformation[Geom::Y]) + origin;
+// break;
+// case STRETCH:
+// {
+// Geom::Scale s(1, 1);
+// if (uniform)
+// s[Geom::X] = s[Geom::Y] = transformation[dim];
+// else {
+// s[dim] = transformation[dim];
+// s[1 - dim] = 1;
+// }
+// transformed = ((p.getPoint() - origin) * s) + origin;
+// break;
+// }
+// case SKEW:
+// // Apply the skew factor
+// transformed[dim] = (p.getPoint())[dim] + transformation[0] * ((p.getPoint())[1 - dim] - origin[1 - dim]);
+// // While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed.
+// // Apply that scale factor here
+// transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim];
+// break;
+// case ROTATE:
+// // for rotations: transformation[0] stores the angle in radians
+// transformed = (p.getPoint() - origin) * Geom::Rotate(transformation[0]) + origin;
+// break;
+// default:
+// g_assert_not_reached();
+// }
+//
+// return transformed;
+//}
/**
* Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
diff --git a/src/snap.h b/src/snap.h
index 944c49b3e..c4dd67b4c 100644
--- a/src/snap.h
+++ b/src/snap.h
@@ -21,6 +21,8 @@
#include "guide-snapper.h"
#include "object-snapper.h"
#include "snap-preferences.h"
+//#include "pure-transform.h"
+
// Guides
enum SPGuideDragType { // used both here and in desktop-events.cpp
@@ -32,6 +34,11 @@ enum SPGuideDragType { // used both here and in desktop-events.cpp
class SPGuide;
class SPNamedView;
+
+namespace Inkscape {
+ class PureTransform;
+}
+
typedef struct _GSList GSList;
/**
@@ -327,110 +334,6 @@ public:
*/
void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const;
- /**
- * Apply a translation to a set of points and try to snap freely in 2 degrees-of-freedom.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param tr Proposed translation; the final translation can only be calculated after snapping has occurred.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Point const &tr);
-
- /**
- * Apply a translation to a set of points and try to snap along a constraint.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param constraint The direction or line along which snapping must occur.
- * @param tr Proposed translation; the final translation can only be calculated after snapping has occurred.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Geom::Point const &tr);
-
- /**
- * Apply a scaling to a set of points and try to snap freely in 2 degrees-of-freedom.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param s Proposed scaling; the final scaling can only be calculated after snapping has occurred.
- * @param o Origin of the scaling.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Scale const &s,
- Geom::Point const &o);
-
- /**
- * Apply a scaling to a set of points and snap such that the aspect ratio of the selection is preserved.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param s Proposed scaling; the final scaling can only be calculated after snapping has occurred.
- * @param o Origin of the scaling.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint constrainedSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Scale const &s,
- Geom::Point const &o);
-
- /**
- * Apply a stretch to a set of points and snap such that the direction of the stretch is preserved.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param s Proposed stretch; the final stretch can only be calculated after snapping has occurred.
- * @param o Origin of the stretching.
- * @param d Dimension in which to apply proposed stretch.
- * @param u true if the stretch should be uniform (i.e. to be applied equally in both dimensions).
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint constrainedSnapStretch(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Coord const &s,
- Geom::Point const &o,
- Geom::Dim2 d,
- bool uniform);
-
- /**
- * Apply a skew to a set of points and snap such that the direction of the skew is preserved.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param constraint The direction or line along which snapping must occur.
- * @param s Proposed skew; the final skew can only be calculated after snapping has occurred.
- * @param o Origin of the proposed skew.
- * @param d Dimension in which to apply proposed skew.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor
- Geom::Point const &o,
- Geom::Dim2 d);
-
- /**
- * Apply a rotation to a set of points and snap, without scaling.
- *
- * @param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
- * @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param angle Proposed rotation (in radians); the final rotation can only be calculated after snapping has occurred.
- * @param o Origin of the rotation.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
- */
- Inkscape::SnappedPoint constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p,
- Geom::Point const &pointer,
- Geom::Coord const &angle,
- Geom::Point const &o);
-
Inkscape::GuideSnapper guide; ///< guide snapper
Inkscape::ObjectSnapper object; ///< snapper to other objects
Inkscape::SnapPreferences snapprefs;
@@ -490,17 +393,6 @@ public:
*/
void displaySnapsource(Inkscape::SnapCandidatePoint const &p) const;
-protected:
- SPNamedView const *_named_view;
-
-private:
- std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
- std::vector<SPItem*> _rotation_center_source_items; // to avoid snapping a rotation center to itself
- SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged
- SPDesktop const *_desktop;
- bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to
- std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
-
/**
* Method for snapping sets of points while they are being transformed.
*
@@ -518,42 +410,22 @@ private:
*
* @param points Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
- * @param constrained true if the snap is constrained, e.g. for stretching or for purely horizontal translation.
- * @param constraint The direction or line along which snapping must occur, if 'constrained' is true; otherwise undefined.
- * @param transformation_type Type of transformation to apply to points before trying to snap them.
- * @param transformation Description of the transformation; details depend on the type.
- * @param origin Origin of the transformation, if applicable.
- * @param dim Dimension to which the transformation applies, if applicable.
- * @param uniform true if the transformation should be uniform; only applicable for stretching and scaling.
- * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
+ * @param transform Describes the type of transformation, it's parameters, and any additional constraints
*/
- Inkscape::SnappedPoint _snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points,
+ void snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points,
Geom::Point const &pointer,
- bool constrained,
- Inkscape::Snapper::SnapConstraint const &constraint,
- Transformation transformation_type,
- Geom::Point const &transformation,
- Geom::Point const &origin,
- Geom::Dim2 dim,
- bool uniform);
+ Inkscape::PureTransform &transform);
- /**
- * Takes an untransformed point, applies the given transformation, and returns the transformed point. Eliminates lots of duplicated code.
- *
- * @param p The untransformed position of the point, paired with an identifier of the type of the snap source.
- * @param transformation_type Type of transformation to apply.
- * @param transformation Mathematical description of the transformation; details depend on the type.
- * @param origin Origin of the transformation, if applicable.
- * @param dim Dimension to which the transformation applies, if applicable.
- * @param uniform true if the transformation should be uniform; only applicable for stretching and scaling.
- * @return The position of the point after transformation.
- */
- Geom::Point _transformPoint(Inkscape::SnapCandidatePoint const &p,
- Transformation const transformation_type,
- Geom::Point const &transformation,
- Geom::Point const &origin,
- Geom::Dim2 const dim,
- bool const uniform) const;
+protected:
+ SPNamedView const *_named_view;
+
+private:
+ std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
+ std::vector<SPItem*> _rotation_center_source_items; // to avoid snapping a rotation center to itself
+ SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged
+ SPDesktop const *_desktop;
+ bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to
+ std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
};
diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp
index ab545bd5f..f826211fa 100644
--- a/src/snapped-point.cpp
+++ b/src/snapped-point.cpp
@@ -29,7 +29,6 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const
_second_distance (Geom::infinity()),
_second_tolerance (1),
_second_always_snap (false),
- _transformation (Geom::Point(1,1)),
_target_bbox(target_bbox),
_pointer_distance (Geom::infinity())
{
@@ -50,7 +49,6 @@ Inkscape::SnappedPoint::SnappedPoint(Inkscape::SnapCandidatePoint const &p, Snap
_second_distance (Geom::infinity()),
_second_tolerance (1),
_second_always_snap (false),
- _transformation (Geom::Point(1,1)),
_target_bbox (p.getTargetBBox()),
_pointer_distance (Geom::infinity())
{
@@ -73,7 +71,6 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const
_second_always_snap(a2),
// tolerance should never be smaller than 1 px, as it is used for normalization in
// isOtherSnapBetter. We don't want a division by zero.
- _transformation (Geom::Point(1,1)),
_target_bbox (Geom::OptRect()),
_pointer_distance (Geom::infinity())
{
@@ -94,7 +91,6 @@ Inkscape::SnappedPoint::SnappedPoint():
_second_distance (Geom::infinity()),
_second_tolerance (1),
_second_always_snap (false),
- _transformation (Geom::Point(1,1)),
_target_bbox (Geom::OptRect()),
_pointer_distance (Geom::infinity())
{
@@ -115,7 +111,6 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p):
_second_distance (Geom::infinity()),
_second_tolerance (1),
_second_always_snap (false),
- _transformation (Geom::Point(1,1)),
_target_bbox (Geom::OptRect()),
_pointer_distance (Geom::infinity())
{
diff --git a/src/snapped-point.h b/src/snapped-point.h
index 9d77ab162..cf3a15c3b 100644
--- a/src/snapped-point.h
+++ b/src/snapped-point.h
@@ -66,8 +66,6 @@ public:
bool getFullyConstrained() const {return _fully_constrained;}
bool getConstrainedSnap() const {return _constrained_snap;}
bool getSnapped() const {return _distance < Geom::infinity();}
- Geom::Point getTransformation() const {return _transformation;}
- void setTransformation(Geom::Point const &t) {_transformation = t;}
void setTarget(SnapTargetType const target) {_target = target;}
SnapTargetType getTarget() const {return _target;}
void setTargetBBox(Geom::OptRect const target) {_target_bbox = target;}
@@ -122,8 +120,6 @@ protected:
Geom::Coord _second_tolerance;
/* If true then "Always snap" is on */
bool _second_always_snap;
- /* The transformation (translation, scale, skew, or stretch) from the original point to the snapped point */
- Geom::Point _transformation;
/* The bounding box we've snapped to (when applicable); will be used by the snapindicator */
Geom::OptRect _target_bbox;
/* Distance from the un-transformed point to the mouse pointer, measured at the point in time when dragging started */
diff --git a/src/snapper.h b/src/snapper.h
index 9616d0954..24f9b9442 100644
--- a/src/snapper.h
+++ b/src/snapper.h
@@ -76,6 +76,8 @@ public:
SnapConstraint(Geom::Point const &d) : _point(), _direction(d), _radius(0), _type(DIRECTION) {}
// Constructs a linear constraint
SnapConstraint(Geom::Point const &p, Geom::Point const &d) : _point(p), _direction(d), _radius(0), _type(LINE) {}
+ // Orthogonal version
+ SnapConstraint(Geom::Point const &p, Geom::Dim2 const &d) : _point(p), _radius(0), _type(LINE) {_direction = Geom::Point(); _direction[d] = 1.;}
SnapConstraint(Geom::Line const &l) : _point(l.origin()), _direction(l.versor()), _radius(0), _type(LINE) {}
// Constructs a circular constraint
SnapConstraint(Geom::Point const &p, Geom::Point const &d, Geom::Coord const &r) : _point(p), _direction(d), _radius(r), _type(CIRCLE) {}
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index da2a54989..748b9d4cc 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -15,9 +15,11 @@
#include <glib/gi18n.h>
#include <2geom/transforms.h>
#include "desktop.h"
+#include "sp-namedview.h"
#include "display/sodipodi-ctrlrect.h"
#include "preferences.h"
+#include "pure-transform.h"
#include "snap.h"
#include "snap-candidate.h"
#include "sp-namedview.h"
@@ -93,6 +95,7 @@ TransformHandle::TransformHandle(TransformHandleSet &th, SPAnchorType anchor, Gl
setVisible(false);
}
+// TODO: This code is duplicated in seltrans.cpp; fix this!
void TransformHandle::getNextClosestPoint(bool reverse)
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -113,6 +116,11 @@ void TransformHandle::getNextClosestPoint(bool reverse)
_snap_points.clear();
_snap_points.push_back(*_all_snap_sources_iter);
+ // Show the updated snap source now; otherwise it won't be shown until the selection is being moved again
+ SnapManager &m = _desktop->namedview->snap_manager;
+ m.setup(_desktop);
+ m.displaySnapsource(*_all_snap_sources_iter);
+ m.unSetup();
}
}
}
@@ -247,7 +255,7 @@ protected:
if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0))
return Geom::identity();
- double scale[2] = { vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y] };
+ Geom::Scale scale = Geom::Scale(vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y]);
if (held_alt(*event)) {
for (unsigned i = 0; i < 2; ++i) {
@@ -261,20 +269,21 @@ protected:
SnapManager &m = _th._desktop->namedview->snap_manager;
m.setupIgnoreSelection(_th._desktop, true, &_unselected_points);
- Inkscape::SnappedPoint sp;
+ Inkscape::PureScale *ptr;
if (held_control(*event)) {
scale[0] = scale[1] = std::min(scale[0], scale[1]);
- sp = m.constrainedSnapScale(_snap_points, _origin, Geom::Scale(scale[0], scale[1]), scc);
+ ptr = new Inkscape::PureScaleConstrained(Geom::Scale(scale[0], scale[1]), scc);
} else {
- sp = m.freeSnapScale(_snap_points, _origin, Geom::Scale(scale[0], scale[1]), scc);
+ ptr = new Inkscape::PureScale(Geom::Scale(scale[0], scale[1]), scc, false);
}
+ m.snapTransformed(_snap_points, _origin, (*ptr));
m.unSetup();
- if (sp.getSnapped()) {
- Geom::Point result = sp.getTransformation();
- scale[0] = result[0];
- scale[1] = result[1];
+ if (ptr->best_snapped_point.getSnapped()) {
+ scale = ptr->getScaleSnapped();
}
+
+ delete ptr;
}
_last_scale_x = scale[0];
@@ -349,11 +358,12 @@ protected:
m.setupIgnoreSelection(_th._desktop, true, &_unselected_points);
bool uniform = held_control(*event);
- Inkscape::SnappedPoint sp = m.constrainedSnapStretch(_snap_points, _origin, vs[d1], scc, d1, uniform);
+ Inkscape::PureStretchConstrained psc = Inkscape::PureStretchConstrained(vs[d1], scc, d1, uniform);
+ m.snapTransformed(_snap_points, _origin, psc);
m.unSetup();
- if (sp.getSnapped()) {
- Geom::Point result = sp.getTransformation();
+ if (psc.best_snapped_point.getSnapped()) {
+ Geom::Point result = psc.getStretchSnapped().vector(); //best_snapped_point.getTransformation();
vs[d1] = result[d1];
vs[d2] = result[d2];
} else {
@@ -414,11 +424,12 @@ protected:
} else {
SnapManager &m = _th._desktop->namedview->snap_manager;
m.setupIgnoreSelection(_th._desktop, true, &_unselected_points);
- Inkscape::SnappedPoint sp = m.constrainedSnapRotate(_snap_points, _origin, angle, rotc);
+ Inkscape::PureRotateConstrained prc = Inkscape::PureRotateConstrained(angle, rotc);
+ m.snapTransformed(_snap_points, _origin, prc);
m.unSetup();
- if (sp.getSnapped()) {
- angle = sp.getTransformation()[0];
+ if (prc.best_snapped_point.getSnapped()) {
+ angle = prc.getAngleSnapped(); //best_snapped_point.getTransformation()[0];
}
}
@@ -528,13 +539,12 @@ protected:
SnapManager &m = _th._desktop->namedview->snap_manager;
m.setupIgnoreSelection(_th._desktop, true, &_unselected_points);
- Geom::Point cvec; cvec[d2] = 1.0;
- Inkscape::Snapper::SnapConstraint const constraint(cvec);
- Inkscape::SnappedPoint sp = m.constrainedSnapSkew(_snap_points, _origin, constraint, Geom::Point(skew[d1], scale[d1]), scc, d2);
+ Inkscape::PureSkewConstrained psc = Inkscape::PureSkewConstrained(skew[d1], scale[d1], scc, d2);
+ m.snapTransformed(_snap_points, _origin, psc);
m.unSetup();
- if (sp.getSnapped()) {
- skew[d1] = sp.getTransformation()[0];
+ if (psc.best_snapped_point.getSnapped()) {
+ skew[d1] = psc.getSkewSnapped(); //best_snapped_point.getTransformation()[0];
}
}