Code

Fix sculpting of nodes with non-degenerate handles.
authorKrzysztof Kosiński <tweenk.pl@gmail.com>
Thu, 22 Jul 2010 00:09:48 +0000 (02:09 +0200)
committerKrzysztof Kosiński <tweenk.pl@gmail.com>
Thu, 22 Jul 2010 00:09:48 +0000 (02:09 +0200)
src/ui/tool/control-point-selection.cpp
src/ui/tool/control-point-selection.h

index f880d2ddfe2c9faf604f3add7c9588b7d1257951..615587eebb7aaa62bc38277c7978bae2b936ca48 100644 (file)
@@ -273,8 +273,11 @@ void ControlPointSelection::_pointGrabbed(SelectableControlPoint *point)
     _grabbed_point = point;
     _farthest_point = point;
     double maxdist = 0;
+    Geom::Matrix m;
+    m.setIdentity();
     for (iterator i = _points.begin(); i != _points.end(); ++i) {
         _original_positions.insert(std::make_pair(*i, (*i)->position()));
+        _last_trans.insert(std::make_pair(*i, m));
         double dist = Geom::distance(*_grabbed_point, **i);
         if (dist > maxdist) {
             maxdist = dist;
@@ -288,12 +291,52 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion *
     Geom::Point abs_delta = new_pos - _original_positions[_grabbed_point];
     double fdist = Geom::distance(_original_positions[_grabbed_point], _original_positions[_farthest_point]);
     if (held_alt(*event) && fdist > 0) {
-        // sculpting
+        // Sculpting
         for (iterator i = _points.begin(); i != _points.end(); ++i) {
             SelectableControlPoint *cur = (*i);
+            Geom::Matrix trans;
+            trans.setIdentity();
             double dist = Geom::distance(_original_positions[cur], _original_positions[_grabbed_point]);
             double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist);
-            cur->move(_original_positions[cur] + abs_delta * deltafrac);
+            if (dist != 0.0) {
+                // The sculpting transformation is not affine, but it can be
+                // locally approximated by one. Here we compute the local
+                // affine approximation of the sculpting transformation near
+                // the currently transformed point. We then transform the point
+                // by this approximation. This gives us sensible behavior for node handles.
+                // NOTE: probably it would be better to transform the node handles,
+                // but ControlPointSelection is supposed to work for any
+                // SelectableControlPoints, not only Nodes. We could create a specialized
+                // NodeSelection class that inherits from this one and move sculpting there.
+                Geom::Point origdx(Geom::EPSILON, 0);
+                Geom::Point origdy(0, Geom::EPSILON);
+                Geom::Point origp = _original_positions[cur];
+                Geom::Point origpx = _original_positions[cur] + origdx;
+                Geom::Point origpy = _original_positions[cur] + origdy;
+                double distdx = Geom::distance(origpx, _original_positions[_grabbed_point]);
+                double distdy = Geom::distance(origpy, _original_positions[_grabbed_point]);
+                double deltafracdx = 0.5 + 0.5 * cos(M_PI * distdx/fdist);
+                double deltafracdy = 0.5 + 0.5 * cos(M_PI * distdy/fdist);
+                Geom::Point newp = origp + abs_delta * deltafrac;
+                Geom::Point newpx = origpx + abs_delta * deltafracdx;
+                Geom::Point newpy = origpy + abs_delta * deltafracdy;
+                Geom::Point newdx = (newpx - newp) / Geom::EPSILON;
+                Geom::Point newdy = (newpy - newp) / Geom::EPSILON;
+
+                Geom::Matrix itrans(newdx[Geom::X], newdx[Geom::Y], newdy[Geom::X], newdy[Geom::Y], 0, 0);
+                if (itrans.isSingular())
+                    itrans.setIdentity();
+
+                trans *= Geom::Translate(-cur->position());
+                trans *= _last_trans[cur].inverse();
+                trans *= itrans;
+                trans *= Geom::Translate(_original_positions[cur] + abs_delta * deltafrac);
+                _last_trans[cur] = itrans;
+            } else {
+                trans *= Geom::Translate(-cur->position() + _original_positions[cur] + abs_delta * deltafrac);
+            }
+            cur->transform(trans);
+            //cur->move(_original_positions[cur] + abs_delta * deltafrac);
         }
     } else {
         Geom::Point delta = new_pos - _grabbed_point->position();
@@ -309,6 +352,7 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion *
 void ControlPointSelection::_pointUngrabbed()
 {
     _original_positions.clear();
+    _last_trans.clear();
     _dragging = false;
     _grabbed_point = _farthest_point = NULL;
     _updateBounds();
index 514ecb2e3df13d6eb1940f16ae39def34cde88e2..8023c3e2884825f8eb28942ce110b0fe89c22f31 100644 (file)
@@ -1,5 +1,5 @@
 /** @file
- * Node selection - stores a set of nodes and applies transformations
+ * Control point selection - stores a set of control points and applies transformations
  * to them
  */
 /* Authors:
@@ -9,8 +9,8 @@
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
-#ifndef SEEN_UI_TOOL_NODE_SELECTION_H
-#define SEEN_UI_TOOL_NODE_SELECTION_H
+#ifndef SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H
+#define SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H
 
 #include <memory>
 #include <boost/optional.hpp>
@@ -132,6 +132,7 @@ private:
     set_type _points;
     set_type _all_points;
     INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Point> _original_positions;
+    INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Matrix> _last_trans;
     boost::optional<double> _rot_radius;
     boost::optional<double> _mouseover_rot_radius;
     Geom::OptRect _bounds;