summaryrefslogtreecommitdiffstats
path: root/src/ui/tool/transform-handle-set.cpp
diff options
context:
space:
mode:
authorDiederik van Lierop <mail@diedenrezi.nl>2011-09-16 23:00:05 +0000
committerDiederik van Lierop <mail@diedenrezi.nl>2011-09-16 23:00:05 +0000
commitee17bac8a5d9b6bf840272885c1eda57c45fb4ad (patch)
tree2df5e6001c742e2f7a73007977c75ecd86fb7947 /src/ui/tool/transform-handle-set.cpp
parentCorrect the formula of the displacement map so that zero alpha value (diff)
downloadinkscape-ee17bac8a5d9b6bf840272885c1eda57c45fb4ad.tar.gz
inkscape-ee17bac8a5d9b6bf840272885c1eda57c45fb4ad.zip
Node tool, transforming a set of nodes: Fix crashes, and finish implementation of snapping
Fixed bugs: - https://launchpad.net/bugs/590261 (bzr r10633)
Diffstat (limited to 'src/ui/tool/transform-handle-set.cpp')
-rw-r--r--src/ui/tool/transform-handle-set.cpp170
1 files changed, 109 insertions, 61 deletions
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index 26263c26b..58f064b9a 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -28,6 +28,8 @@
#include "ui/tool/event-utils.h"
#include "ui/tool/transform-handle-set.h"
#include "ui/tool/node-tool.h"
+#include "ui/tool/node.h"
+#include "seltrans.h"
// FIXME BRAIN DAMAGE WARNING: this is a global variable in select-context.cpp
// It should be moved to a header
@@ -101,6 +103,7 @@ protected:
Geom::Point _origin;
TransformHandleSet &_th;
std::vector<Inkscape::SnapCandidatePoint> _snap_points;
+ std::vector<Inkscape::SnapCandidatePoint> _unselected_points;
private:
virtual bool grabbed(GdkEventMotion *) {
@@ -113,12 +116,13 @@ private:
_setState(_state);
// Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector.
- SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- SnapManager &m = desktop->namedview->snap_manager;
- InkNodeTool *nt = INK_NODE_TOOL(_desktop->event_context);
+ SnapManager &m = _th._desktop->namedview->snap_manager;
+ InkNodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
ControlPointSelection *selection = nt->_selected_nodes.get();
- _snap_points = selection->getOriginalPoints();
+ selection->setOriginalPoints();
+ selection->getOriginalPoints(_snap_points);
+ selection->getUnselectedPoints(_unselected_points);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (prefs->getBool("/options/snapclosestonly/value", false)) {
@@ -198,9 +202,6 @@ protected:
_sc_center = _th.rotationCenter();
_sc_opposite = _th.bounds().corner(_corner + 2);
_last_scale_x = _last_scale_y = 1.0;
- InkNodeTool *nt = INK_NODE_TOOL(_desktop->event_context);
- ControlPointSelection *selection = nt->_selected_nodes.get();
- selection->setOriginalPoints();
}
virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
@@ -214,30 +215,15 @@ protected:
if (held_alt(*event)) {
for (unsigned i = 0; i < 2; ++i) {
- if (scale[i] >= 1.0) scale[i] = round(scale[i]);
- else scale[i] = 1.0 / round(1.0 / scale[i]);
+ if (fabs(scale[i]) >= 1.0) {
+ scale[i] = round(scale[i]);
+ } else {
+ scale[i] = 1.0 / round(1.0 / MIN(scale[i],10));
+ }
}
} else {
- //SPDesktop *desktop = _th._desktop; // Won't work as _desktop is protected
- SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- SnapManager &m = desktop->namedview->snap_manager;
-
- // The lines below have been copied from Handle::dragged() in node.cpp, and need to be
- // activated if we want to snap to unselected (i.e. stationary) nodes and stationary pieces of paths of the
- // path that's currently being edited
- /*
- std::vector<Inkscape::SnapCandidatePoint> unselected;
- typedef ControlPointSelection::Set Set;
- Set &nodes = _parent->_selection.allPoints();
- for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) {
- Node *n = static_cast<Node*>(*i);
- Inkscape::SnapCandidatePoint p(n->position(), n->_snapSourceType(), n->_snapTargetType());
- unselected.push_back(p);
- }
- m.setupIgnoreSelection(_desktop, true, &unselected);
- */
-
- m.setupIgnoreSelection(_desktop);
+ SnapManager &m = _th._desktop->namedview->snap_manager;
+ m.setupIgnoreSelection(_th._desktop, true, &_unselected_points);
Inkscape::SnappedPoint sp;
if (held_control(*event)) {
@@ -306,10 +292,30 @@ protected:
vs[d1] = (new_pos - scc)[d1] / (_origin - scc)[d1];
if (held_alt(*event)) {
- if (vs[d1] >= 1.0) vs[d1] = round(vs[d1]);
- else vs[d1] = 1.0 / round(1.0 / vs[d1]);
+ if (fabs(vs[d1]) >= 1.0) {
+ vs[d1] = round(vs[d1]);
+ } else {
+ vs[d1] = 1.0 / round(1.0 / MIN(vs[d1],10));
+ }
+ vs[d2] = 1.0;
+ } else {
+ SnapManager &m = _th._desktop->namedview->snap_manager;
+ 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);
+ m.unSetup();
+
+ if (sp.getSnapped()) {
+ Geom::Point result = sp.getTransformation();
+ vs[d1] = result[d1];
+ vs[d2] = result[d2];
+ } else {
+ // on ctrl, apply uniform scaling instead of stretching
+ // Preserve aspect ratio, but never flip in the dimension not being edited (by using fabs())
+ vs[d2] = uniform ? fabs(vs[d1]) : 1.0;
+ }
}
- vs[d2] = held_control(*event) ? vs[d1] : 1.0;
_last_scale_x = vs[Geom::X];
_last_scale_y = vs[Geom::Y];
@@ -357,7 +363,17 @@ protected:
double angle = Geom::angle_between(_origin - rotc, new_pos - rotc);
if (held_control(*event)) {
angle = snap_angle(angle);
+ } 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);
+ m.unSetup();
+
+ if (sp.getSnapped()) {
+ angle = sp.getTransformation()[0];
+ }
}
+
_last_angle = angle;
Geom::Affine t = Geom::Translate(-rotc)
* Geom::Rotate(angle)
@@ -428,44 +444,76 @@ protected:
virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event)
{
Geom::Point scc = held_shift(*event) ? _skew_center : _skew_opposite;
- // d1 and d2 are reversed with respect to ScaleSideHandle
- Geom::Dim2 d1 = static_cast<Geom::Dim2>(_side % 2);
- Geom::Dim2 d2 = static_cast<Geom::Dim2>((_side + 1) % 2);
- Geom::Point proj, scale(1.0, 1.0);
+ Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2);
+ Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2);
+
+ Geom::Point const initial_delta = _origin - scc;
+
+ if (fabs(initial_delta[d1]) < 1e-15) {
+ return Geom::Affine();
+ }
+
+ // Calculate the scale factors, which can be either visual or geometric
+ // depending on which type of bbox is currently being used (see preferences -> selector tool)
+ Geom::Scale scale = calcScaleFactors(_origin, new_pos, scc, false);
+ Geom::Scale skew = calcScaleFactors(_origin, new_pos, scc, true);
+ scale[d2] = 1;
+ skew[d2] = 1;
// Skew handles allow scaling up to integer multiples of the original size
// in the second direction; prevent explosions
- // TODO should the scaling part be only active with Alt?
- if (!Geom::are_near(_origin[d2], scc[d2])) {
- scale[d2] = (new_pos - scc)[d2] / (_origin - scc)[d2];
- }
- if (scale[d2] < 1.0) {
- scale[d2] = copysign(1.0, scale[d2]);
+ if (fabs(scale[d1]) < 1) {
+ // Prevent shrinking of the selected object, while allowing mirroring
+ scale[d1] = copysign(1.0, scale[d1]);
} else {
- scale[d2] = floor(scale[d2]);
+ // Allow expanding of the selected object by integer multiples
+ scale[d1] = floor(scale[d1] + 0.5);
}
- // Calculate skew angle. The angle is calculated with regards to the point obtained
- // by projecting the handle position on the relevant side of the bounding box.
- // This avoids degeneracies when moving the skew angle over the rotation center
- proj[d1] = new_pos[d1];
- proj[d2] = scc[d2] + (_origin[d2] - scc[d2]) * scale[d2];
- double angle = 0;
- if (!Geom::are_near(proj[d2], scc[d2]))
- angle = Geom::angle_between(_origin - scc, proj - scc);
- if (held_control(*event)) angle = snap_angle(angle);
-
- // skew matrix has the from [[1, k],[0, 1]] for horizontal skew
- // and [[1,0],[k,1]] for vertical skew.
- Geom::Affine skew = Geom::identity();
- // correct the sign of the tangent
- skew[d2 + 1] = (d1 == Geom::X ? -1.0 : 1.0) * tan(angle);
+ double angle = atan(skew[d1] / scale[d1]);
+
+ if (held_control(*event)) {
+ angle = snap_angle(angle);
+ skew[d1] = tan(angle) * scale[d1];
+ } else {
+ 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);
+ m.unSetup();
+
+ if (sp.getSnapped()) {
+ skew[d1] = sp.getTransformation()[0];
+ }
+ }
_last_angle = angle;
+
+ // Update the handle position
+ Geom::Point new_new_pos;
+ new_new_pos[d2] = initial_delta[d1] * skew[d1] + _origin[d2];
+ new_new_pos[d1] = initial_delta[d1] * scale[d1] + scc[d1];
+
+ // Calculate the relative affine
+ Geom::Affine relative_affine = Geom::identity();
+ relative_affine[2*d1 + d1] = (new_new_pos[d1] - scc[d1]) / initial_delta[d1];
+ relative_affine[2*d1 + (d2)] = (new_new_pos[d2] - _origin[d2]) / initial_delta[d1];
+ relative_affine[2*(d2) + (d1)] = 0;
+ relative_affine[2*(d2) + (d2)] = 1;
+
+ for (int i = 0; i < 2; i++) {
+ if (fabs(relative_affine[3*i]) < 1e-15) {
+ relative_affine[3*i] = 1e-15;
+ }
+ }
+
Geom::Affine t = Geom::Translate(-scc)
- * Geom::Scale(scale) * skew
+ * relative_affine
* Geom::Translate(scc);
+
return t;
}
@@ -537,8 +585,8 @@ public:
protected:
virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event) {
- SnapManager &sm = _desktop->namedview->snap_manager;
- sm.setup(_desktop);
+ SnapManager &sm = _th._desktop->namedview->snap_manager;
+ sm.setup(_th._desktop);
bool snap = !held_shift(*event) && sm.someSnapperMightSnap();
if (held_control(*event)) {
// constrain to axes