Logo Search packages:      
Sourcecode: inkscape version File versions  Download package

void sp_selection_apply_affine ( Inkscape::Selection selection,
Geom::Matrix const &  affine,
bool  set_i2d,
bool  compensate 
)

Apply matrix to the selection. set_i2d is normally true, which means objects are in the original transform, synced with their reprs, and need to jump to the new transform in one go. A value of set_i2d==false is only used by seltrans when it's dragging objects live (not outlines); in that case, items are already in the new position, but the repr is in the old, and this function then simply updates the repr from item->transform.

Definition at line 1299 of file selection-chemistry.cpp.

References Inkscape::Selection::box3DList(), Inkscape::Selection::desktop(), SPObject::firstChild(), Inkscape::Preferences::get(), SPItem::getCenter(), Inkscape::Preferences::getInt(), Inkscape::Selection::includes(), Geom::Matrix::inverse(), SPItem::isCenterSet(), Inkscape::Selection::isEmpty(), Geom::Matrix::isIdentity(), Geom::Matrix::isTranslation(), Inkscape::Selection::itemList(), Inkscape::Selection::perspList(), SPItem::setCenter(), sp_item_i2d_affine(), sp_item_write_transform(), sp_object_read_attr(), SPItem::transform, and SPObject::updateRepr().

Referenced by Inkscape::UI::ClipboardManagerImpl::_pasteDocument(), and file_import().

{
    if (selection->isEmpty())
        return;

    // For each perspective with a box in selection, check whether all boxes are selected and
    // unlink all non-selected boxes.
    Persp3D *persp;
    Persp3D *transf_persp;
    std::list<Persp3D *> plist = selection->perspList();
    for (std::list<Persp3D *>::iterator i = plist.begin(); i != plist.end(); ++i) {
        persp = (Persp3D *) (*i);

        if (!persp3d_has_all_boxes_in_selection (persp, selection)) {
            std::list<SPBox3D *> selboxes = selection->box3DList(persp);

            // create a new perspective as a copy of the current one and link the selected boxes to it
            transf_persp = persp3d_create_xml_element (SP_OBJECT_DOCUMENT(persp), persp->perspective_impl);

            for (std::list<SPBox3D *>::iterator b = selboxes.begin(); b != selboxes.end(); ++b)
                box3d_switch_perspectives(*b, persp, transf_persp);
        } else {
            transf_persp = persp;
        }

        persp3d_apply_affine_transformation(transf_persp, affine);
    }

    for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
        SPItem *item = SP_ITEM(l->data);

        Geom::Point old_center(0,0);
        if (set_i2d && item->isCenterSet())
            old_center = item->getCenter();

#if 0 /* Re-enable this once persistent guides have a graphical indication.
         At the time of writing, this is the only place to re-enable. */
        sp_item_update_cns(*item, selection->desktop());
#endif

        // we're moving both a clone and its original or any ancestor in clone chain?
        bool transform_clone_with_original = selection_contains_original(item, selection);
        // ...both a text-on-path and its path?
        bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(item))) ));
        // ...both a flowtext and its frame?
        bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame(NULL))); // (only the first frame is checked so far)
        // ...both an offset and its source?
        bool transform_offset_with_source = (SP_IS_OFFSET(item) && SP_OFFSET(item)->sourceHref) && selection->includes( sp_offset_get_source(SP_OFFSET(item)) );

        // If we're moving a connector, we want to detach it
        // from shapes that aren't part of the selection, but
        // leave it attached if they are
        if (cc_item_is_connector(item)) {
            SPItem *attItem[2];
            SP_PATH(item)->connEndPair.getAttachedItems(attItem);

            for (int n = 0; n < 2; ++n) {
                if (!selection->includes(attItem[n])) {
                    sp_conn_end_detach(item, n);
                }
            }
        }

        // "clones are unmoved when original is moved" preference
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
        int compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
        bool prefs_unmoved = (compensation == SP_CLONE_COMPENSATION_UNMOVED);
        bool prefs_parallel = (compensation == SP_CLONE_COMPENSATION_PARALLEL);

        /* If this is a clone and it's selected along with its original, do not move it;
         * it will feel the transform of its original and respond to it itself.
         * Without this, a clone is doubly transformed, very unintuitive.
         *
         * Same for textpath if we are also doing ANY transform to its path: do not touch textpath,
         * letters cannot be squeezed or rotated anyway, they only refill the changed path.
         * Same for linked offset if we are also moving its source: do not move it. */
        if (transform_textpath_with_path || transform_offset_with_source) {
            // Restore item->transform field from the repr, in case it was changed by seltrans.
            sp_object_read_attr(item, "transform");
        } else if (transform_flowtext_with_frame) {
            // apply the inverse of the region's transform to the <use> so that the flow remains
            // the same (even though the output itself gets transformed)
            for (SPObject *region = item->firstChild() ; region ; region = SP_OBJECT_NEXT(region)) {
                if (!SP_IS_FLOWREGION(region) && !SP_IS_FLOWREGIONEXCLUDE(region))
                    continue;
                for (SPObject *use = region->firstChild() ; use ; use = SP_OBJECT_NEXT(use)) {
                    if (!SP_IS_USE(use)) continue;
                    sp_item_write_transform(SP_USE(use), SP_OBJECT_REPR(use), item->transform.inverse(), NULL, compensate);
                }
            }
        } else if (transform_clone_with_original) {
            // We are transforming a clone along with its original. The below matrix juggling is
            // necessary to ensure that they transform as a whole, i.e. the clone's induced
            // transform and its move compensation are both cancelled out.

            // restore item->transform field from the repr, in case it was changed by seltrans
            sp_object_read_attr(item, "transform");

            // calculate the matrix we need to apply to the clone to cancel its induced transform from its original
            Geom::Matrix parent2dt = sp_item_i2d_affine(SP_ITEM(SP_OBJECT_PARENT(item)));
            Geom::Matrix t = parent2dt * affine * parent2dt.inverse();
            Geom::Matrix t_inv = t.inverse();
            Geom::Matrix result = t_inv * item->transform * t;

            if ((prefs_parallel || prefs_unmoved) && affine.isTranslation()) {
                // we need to cancel out the move compensation, too

                // find out the clone move, same as in sp_use_move_compensate
                Geom::Matrix parent = sp_use_get_parent_transform(SP_USE(item));
                Geom::Matrix clone_move = parent.inverse() * t * parent;

                if (prefs_parallel) {
                    Geom::Matrix move = result * clone_move * t_inv;
                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &move, compensate);

                } else if (prefs_unmoved) {
                    //if (SP_IS_USE(sp_use_get_original(SP_USE(item))))
                    //    clone_move = Geom::identity();
                    Geom::Matrix move = result * clone_move;
                    sp_item_write_transform(item, SP_OBJECT_REPR(item), move, &t, compensate);
                }

            } else {
                // just apply the result
                sp_item_write_transform(item, SP_OBJECT_REPR(item), result, &t, compensate);
            }

        } else {
            if (set_i2d) {
                sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
            }
            sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform, NULL, compensate);
        }

        // if we're moving the actual object, not just updating the repr, we can transform the
        // center by the same matrix (only necessary for non-translations)
        if (set_i2d && item->isCenterSet() && !(affine.isTranslation() || affine.isIdentity())) {
            item->setCenter(old_center * affine);
            item->updateRepr();
        }
    }
}


Generated by  Doxygen 1.6.0   Back to index