Code

remember rotation centers, correctly this time (by johncliff and me)
authorbuliabyak <buliabyak@users.sourceforge.net>
Thu, 26 Jan 2006 05:16:27 +0000 (05:16 +0000)
committerbuliabyak <buliabyak@users.sourceforge.net>
Thu, 26 Jan 2006 05:16:27 +0000 (05:16 +0000)
src/attributes.cpp
src/attributes.h
src/seltrans.cpp
src/seltrans.h
src/sp-item.cpp
src/sp-item.h

index ce9c0e105230021bd845b4c194058f5008316b17..10b33e178c19f1780e96c9e7b9201522bda2d895 100644 (file)
@@ -36,6 +36,8 @@ static SPStyleProp const props[] = {
     {SP_ATTR_SODIPODI_NONPRINTABLE, "sodipodi:nonprintable"},
     {SP_ATTR_CONNECTOR_AVOID, "inkscape:connector-avoid"},
     {SP_ATTR_STYLE, "style"},
+    {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"},
+    {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"},
     /* SPAnchor */
     {SP_ATTR_XLINK_HREF, "xlink:href"},
     {SP_ATTR_XLINK_TYPE, "xlink:type"},
index 2b4a68f34d520c2a8820a7bd3c12d215cd4b1623..b36cf255717234adaab53aa030403f726f262512 100644 (file)
@@ -36,6 +36,8 @@ enum SPAttributeEnum {
     SP_ATTR_SODIPODI_NONPRINTABLE,
     SP_ATTR_CONNECTOR_AVOID,
     SP_ATTR_STYLE,
+    SP_ATTR_TRANSFORM_CENTER_X,
+       SP_ATTR_TRANSFORM_CENTER_Y,
     /* SPAnchor */
     SP_ATTR_XLINK_HREF,
     SP_ATTR_XLINK_TYPE,
index ca65b5d761a7cd28e223686f71f8cd71456fd7b5..f83c646b7bc6da5dd61614079524175cd06e62d8 100644 (file)
@@ -50,6 +50,7 @@ static void sp_remove_handles(SPKnot *knot[], gint num);
 
 static void sp_sel_trans_handle_grab(SPKnot *knot, guint state, gpointer data);
 static void sp_sel_trans_handle_ungrab(SPKnot *knot, guint state, gpointer data);
+static void sp_sel_trans_handle_click(SPKnot *knot, guint state, gpointer data);
 static void sp_sel_trans_handle_new_event(SPKnot *knot, NR::Point *position, guint32 state, gpointer data);
 static gboolean sp_sel_trans_handle_request(SPKnot *knot, NR::Point *p, guint state, gboolean *data);
 
@@ -100,8 +101,6 @@ Inkscape::SelTrans::SelTrans(SPDesktop *desktop) :
 
     _updateVolatileState();
 
-    _center = _box.midpoint();
-
     _updateHandles();
 
     _selection = SP_DT_SELECTION(desktop);
@@ -210,6 +209,15 @@ void Inkscape::SelTrans::increaseState()
 void Inkscape::SelTrans::setCenter(NR::Point const &p)
 {
     _center = p;
+
+    // Write the new center position into all selected items
+    for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) {
+        SPItem *it = (SPItem*)sp_object_ref(SP_OBJECT(l->data), NULL);
+        it->setCenter(p);
+        SP_OBJECT(it)->updateRepr();
+    }
+    sp_document_maybe_done (SP_DT_DOCUMENT(_desktop), "center::move");
+
     _updateHandles();
 }
 
@@ -301,44 +309,30 @@ void Inkscape::SelTrans::transform(NR::Matrix const &rel_affine, NR::Point const
     _updateHandles();
 }
 
-void Inkscape::SelTrans::_centreTrans(Inkscape::XML::Node *current) const
-{
-    for ( Inkscape::XML::Node *child = sp_repr_children(current) ; child ; child = sp_repr_next(child) ) {
-        _centreTrans(child);
-    }
-    double const cx = sp_repr_get_double_attribute(current, "inkscape:c_rx", 9999999);
-    double const cy = sp_repr_get_double_attribute(current, "inkscape:c_ry", 9999999);
-    if (cx != 9999999) {
-        NR::Point object_centre = NR::Point(cx, cy) * _current;
-        sp_repr_set_svg_double(current, "inkscape:c_rx", object_centre[NR::X]);
-        sp_repr_set_svg_double(current, "inkscape:c_ry", object_centre[NR::Y]);
-    }
- }
-
-
-
-
 void Inkscape::SelTrans::ungrab()
 {
     g_return_if_fail(_grabbed);
 
     Inkscape::Selection *selection = SP_DT_SELECTION(_desktop);
-
     bool updh = true;
     if (!_empty && _changed) {
         sp_selection_apply_affine(selection, _current, (_show == SHOW_OUTLINE)? true : false);
         _center *= _current;
+
+        // Transform may have changed the objects' bboxes, so we need to write the _center into them again
+        for (unsigned i = 0; i < _items.size(); i++) {
+            SPItem *currentItem = _items[i].first;
+            if (currentItem->isCenterSet() || _current[1] != 0 || _current[2] != 0) { // only if it's already set, or if it's a rotation/skew
+                currentItem->setCenter (_center);
+                SP_OBJECT(currentItem)->updateRepr();
+            }
+        }
+
         sp_document_done(SP_DT_DOCUMENT(_desktop));
         updh = false;
     }
 
-    for (unsigned i = 0; i < _items.size(); i++)
-    {
-        Inkscape::XML::Node *current = SP_OBJECT_REPR(_items[i].first);
-        if (current != NULL) {
-            _centreTrans(current);
-        }
-
+    for (unsigned i = 0; i < _items.size(); i++) {
         sp_object_unref(SP_OBJECT(_items[i].first), NULL);
     }
     _items.clear();
@@ -455,6 +449,8 @@ void Inkscape::SelTrans::_updateHandles()
                          G_CALLBACK(sp_sel_trans_handle_grab), (gpointer) &handle_center);
         g_signal_connect(G_OBJECT(_chandle), "ungrabbed",
                          G_CALLBACK(sp_sel_trans_handle_ungrab), (gpointer) &handle_center);
+        g_signal_connect(G_OBJECT(_chandle), "clicked",
+                         G_CALLBACK(sp_sel_trans_handle_click), (gpointer) &handle_center);
     }
 
     sp_remove_handles(&_chandle, 1);
@@ -469,16 +465,23 @@ void Inkscape::SelTrans::_updateHandles()
                     _("<b>Skew</b> selection; with <b>Ctrl</b> to snap angle; with <b>Shift</b> to skew around the opposite side"),
                     _("<b>Rotate</b> selection; with <b>Ctrl</b> to snap angle; with <b>Shift</b> to rotate around the opposite corner"));
     }
+
+    // Extract the position of the center from the first selected object
+    GSList *items = (GSList *) _desktop->selection->itemList();
+    if (items) {
+        SPItem *first = reinterpret_cast<SPItem*>(g_slist_last(items)->data); // from the first item in selection
+        if (first->isCenterSet()) { // only if set explicitly
+            _center =  first->getCenter(); 
+        } else {
+            _center = _box.midpoint();
+        }
+    } else {
+        _center = _box.midpoint();
+    }
+
     if ( _state == STATE_SCALE ) {
         sp_knot_hide(_chandle);
     } else {
-        Inkscape::Selection *selection = _desktop->selection;
-        Inkscape::XML::Node *current = selection->singleRepr();
-        if (current != NULL && sp_repr_get_double_attribute(current, "inkscape:c_rx", 99999999) != 99999999) {
-            double cx = sp_repr_get_double_attribute(current, "inkscape:c_rx", _center[NR::X]);
-            double cy = sp_repr_get_double_attribute(current, "inkscape:c_ry", _center[NR::Y]);
-            _center = NR::Point(cx, cy);
-        }
         sp_knot_show(_chandle);
         sp_knot_moveto(_chandle, &_center);
     }
@@ -581,6 +584,32 @@ static gboolean sp_sel_trans_handle_request(SPKnot *knot, NR::Point *position, g
         );
 }
 
+static void sp_sel_trans_handle_click(SPKnot *knot, guint state, gpointer data)
+{
+    SP_SELECT_CONTEXT(knot->desktop->event_context)->_seltrans->handleClick(
+        knot, state, *(SPSelTransHandle const *) data
+        );
+}
+
+void Inkscape::SelTrans::handleClick(SPKnot *knot, guint state, SPSelTransHandle const &handle)
+{
+    switch (handle.anchor) {
+        case GTK_ANCHOR_CENTER:
+            if (state & GDK_SHIFT_MASK) {
+                // Unset the  center position for all selected items
+                for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) {
+                    SPItem *it = (SPItem*)sp_object_ref(SP_OBJECT(l->data), NULL);
+                    it->unsetCenter();
+                    SP_OBJECT(it)->updateRepr();
+                }
+                sp_document_maybe_done (SP_DT_DOCUMENT(_desktop), "center::unset");
+            }
+            break;
+        default:
+            break;
+    }
+}
+
 void Inkscape::SelTrans::handleGrab(SPKnot *knot, guint state, SPSelTransHandle const &handle)
 {
     switch (handle.anchor) {
@@ -657,7 +686,6 @@ void Inkscape::SelTrans::_selChanged(Inkscape::Selection *selection)
 {
     if (!_grabbed) {
         _updateVolatileState();
-        _center = _box.midpoint();
         _updateHandles();
     }
 }
@@ -667,17 +695,6 @@ void Inkscape::SelTrans::_selModified(Inkscape::Selection *selection, guint flag
     if (!_grabbed) {
         _updateVolatileState();
 
-        if (
-             (flags != (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG)) &&
-             (flags != SP_OBJECT_PARENT_MODIFIED_FLAG) &&
-             (flags != SP_OBJECT_CHILD_MODIFIED_FLAG) &&
-             !_changed) {
-            // Only reset center if object itself is modified (not style, parent or child),
-            // and this is not a local change by seltrans
-            // (still annoyingly recenters on keyboard transforms, fixme)
-            _center = _box.midpoint();
-        }
-
         // reset internal flag
         _changed = false;
 
@@ -1032,14 +1049,6 @@ gboolean Inkscape::SelTrans::centerRequest(NR::Point &pt, guint state)
         }
     }
 
-    Inkscape::Selection *selection = _desktop->selection;
-    Inkscape::XML::Node *current = selection->singleRepr();
-    if (current != NULL){
-        sp_repr_set_svg_double(current, "inkscape:c_rx", pt[X]);
-        sp_repr_set_svg_double(current, "inkscape:c_ry", pt[Y]);
-
-    }
-
     if (!(state & GDK_SHIFT_MASK)) {
         // screen pixels to snap center to bbox
 #define SNAP_DIST 5
index 30be58eeb98ba4d16773328cb7b9845403672858..8ac56766e6ea6151e1d8c780f5f7f9e0bd549f48 100644 (file)
@@ -65,6 +65,7 @@ public:
 
     gboolean handleRequest(SPKnot *knot, NR::Point *position, guint state, SPSelTransHandle const &handle);
     void handleGrab(SPKnot *knot, guint state, SPSelTransHandle const &handle);
+    void handleClick(SPKnot *knot, guint state, SPSelTransHandle const &handle);
     void handleNewEvent(SPKnot *knot, NR::Point *position, guint state, SPSelTransHandle const &handle);
 
     enum Show
@@ -87,8 +88,7 @@ private:
     void _selModified(Inkscape::Selection *selection, guint flags);
     void _showHandles(SPKnot *knot[], SPSelTransHandle const handle[], gint num,
                       gchar const *even_tip, gchar const *odd_tip);
-    void _centreTrans(Inkscape::XML::Node *current) const;
-    
+
     enum State {
         STATE_SCALE,
        STATE_ROTATE
index 97aeb187720916a0403d1baf4377ef6a9a2d3d90..7339160c7cb0351cb4a3f12e1cce9240257fb556 100644 (file)
@@ -134,8 +134,8 @@ sp_item_init(SPItem *item)
 
     item->sensitive = TRUE;
 
-    item->r_cx = 0;
-    item->r_cx = 0;
+    item->transform_center_x = 0;
+    item->transform_center_y = 0;
 
     item->transform = NR::identity();
 
@@ -234,6 +234,42 @@ SPItem::setExplicitlyHidden(bool const val) {
     this->updateRepr();
 }
 
+/**
+ * Sets the transform_center_x and transform_center_y properties to retain the rotation centre
+ */
+void
+SPItem::setCenter(NR::Point object_centre) {
+    NR::Rect bbox = invokeBbox(sp_item_i2d_affine(this));
+    if (!bbox.isEmpty()) {
+        transform_center_x = object_centre[NR::X] - bbox.midpoint()[NR::X];
+        if (fabs(transform_center_x) < 1e-5) // rounding error
+            transform_center_x = 0;
+        transform_center_y = object_centre[NR::Y] - bbox.midpoint()[NR::Y];
+        if (fabs(transform_center_y) < 1e-5) // rounding error
+            transform_center_y = 0;
+    }
+}
+
+void
+SPItem::unsetCenter() {
+    transform_center_x = 0;
+    transform_center_y = 0;
+}
+
+bool SPItem::isCenterSet() {
+    return (transform_center_x != 0 || transform_center_y != 0);
+}
+
+NR::Point SPItem::getCenter() {
+    NR::Rect bbox = invokeBbox(sp_item_i2d_affine(this));
+    if (!bbox.isEmpty()) {
+        return bbox.midpoint() + NR::Point (this->transform_center_x, this->transform_center_y);
+    } else {
+        return NR::Point (0, 0); // something's wrong!
+    }
+}
+
+
 namespace {
 
 bool is_item(SPObject const &object) {
@@ -313,8 +349,8 @@ sp_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
     sp_object_read_attr(object, "mask");
     sp_object_read_attr(object, "sodipodi:insensitive");
     sp_object_read_attr(object, "sodipodi:nonprintable");
-    sp_object_read_attr(object, "inkscape:r_cx");
-    sp_object_read_attr(object, "inkscape:r_cy");
+    sp_object_read_attr(object, "inkscape:transform-center-x");
+    sp_object_read_attr(object, "inkscape:transform-center-y");
     sp_object_read_attr(object, "inkscape:connector-avoid");
 
     if (((SPObjectClass *) (parent_class))->build) {
@@ -416,6 +452,16 @@ sp_item_set(SPObject *object, unsigned key, gchar const *value)
         case SP_ATTR_CONNECTOR_AVOID:
             item->avoidRef->setAvoid(value);
             break;
+        case SP_ATTR_TRANSFORM_CENTER_X:
+            if (value) {
+                item->transform_center_x = g_strtod(value, NULL);
+            }
+            break;
+        case SP_ATTR_TRANSFORM_CENTER_Y:
+            if (value) {
+                item->transform_center_y = g_strtod(value, NULL);
+            }
+            break;
         default:
             if (SP_ATTRIBUTE_IS_CSS(key)) {
                 sp_style_read_from_object(object->style, object);
@@ -583,8 +629,14 @@ sp_item_write(SPObject *const object, Inkscape::XML::Node *repr, guint flags)
 
     if (flags & SP_OBJECT_WRITE_EXT) {
         repr->setAttribute("sodipodi:insensitive", ( item->sensitive ? NULL : "true" ));
-        repr->setAttribute("inkscape:r_cx", ( item->r_cx ? NULL : "true" ));
-        repr->setAttribute("inkscape:r_cy", ( item->r_cy ? NULL : "true" ));
+        if (item->transform_center_x != 0)
+            sp_repr_set_svg_double (repr, "inkscape:transform-center-x", item->transform_center_x);
+        else 
+            repr->setAttribute ("inkscape:transform-center-x", NULL);
+        if (item->transform_center_y != 0)
+            sp_repr_set_svg_double (repr, "inkscape:transform-center-y", item->transform_center_y);
+        else 
+            repr->setAttribute ("inkscape:transform-center-y", NULL);    
     }
 
     if (((SPObjectClass *) (parent_class))->write) {
index 252821f362b060ead5c1eebc2a4366dee9dcae5b..141d3f88119d0dd5e774243adf615e28036f5529 100644 (file)
@@ -91,9 +91,9 @@ struct SPItemCtx {
 struct SPItem : public SPObject {
     unsigned int sensitive : 1;
     unsigned int stop_paint: 1;
-    double r_cx;
-    double r_cy;
-    
+    double transform_center_x;
+    double transform_center_y;
+
     NR::Matrix transform;
     
     SPClipPathReference *clip_ref;
@@ -119,7 +119,12 @@ struct SPItem : public SPObject {
     bool isExplicitlyHidden() const;
     
     void setExplicitlyHidden(bool val);
-    
+
+    void setCenter(NR::Point object_centre);
+    void unsetCenter();
+    bool isCenterSet();
+    NR::Point getCenter();
+
     bool isVisibleAndUnlocked() const;
     
     bool isVisibleAndUnlocked(unsigned display_key) const;