Geom::OptRect CGroup::bounds(SPItem::BBoxType type, Geom::Affine const &transform) { Geom::OptRect bbox; GSList *l = _group->childList(false, SPObject::ActionBBox); while (l) { SPObject *o = SP_OBJECT (l->data); if (SP_IS_ITEM(o) && !SP_ITEM(o)->isHidden()) { SPItem *child = SP_ITEM(o); Geom::Affine const ct(child->transform * transform); bbox |= child->bounds(type, ct); } l = g_slist_remove (l, o); } return bbox; }
void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, std::vector<SPItem const *> const *it, bool const &first_point, Geom::Rect const &bbox_to_snap, bool const clip_or_mask, Geom::Affine const additional_affine) const // transformation of the item being clipped / masked { SPDesktop const *dt = _snapmanager->getDesktop(); if (dt == NULL) { g_warning("desktop == NULL, so we cannot snap; please inform the developers of this bug"); // Apparently the setup() method from the SnapManager class hasn't been called before trying to snap. } if (first_point) { _candidates->clear(); } Geom::Rect bbox_to_snap_incl = bbox_to_snap; // _incl means: will include the snapper tolerance bbox_to_snap_incl.expandBy(getSnapperTolerance()); // see? for ( SPObject *o = parent->firstChild(); o; o = o->getNext() ) { g_assert(dt != NULL); if (SP_IS_ITEM(o) && !(dt->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) { // Snapping to items in a locked layer is allowed // Don't snap to hidden objects, unless they're a clipped path or a mask /* See if this item is on the ignore list */ std::vector<SPItem const *>::const_iterator i; if (it != NULL) { i = it->begin(); while (i != it->end() && *i != o) { ++i; } } if (it == NULL || i == it->end()) { SPItem *item = SP_ITEM(o); if (item) { if (!clip_or_mask) { // cannot clip or mask more than once // The current item is not a clipping path or a mask, but might // still be the subject of clipping or masking itself ; if so, then // we should also consider that path or mask for snapping to SPObject *obj = SP_OBJECT(item->clip_ref ? item->clip_ref->getObject() : NULL); if (obj && _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH_CLIP)) { _findCandidates(obj, it, false, bbox_to_snap, true, item->i2doc_affine()); } obj = SP_OBJECT(item->mask_ref ? item->mask_ref->getObject() : NULL); if (obj && _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH_MASK)) { _findCandidates(obj, it, false, bbox_to_snap, true, item->i2doc_affine()); } } } if (SP_IS_GROUP(o)) { _findCandidates(o, it, false, bbox_to_snap, clip_or_mask, additional_affine); } else { Geom::OptRect bbox_of_item; Preferences *prefs = Preferences::get(); int prefs_bbox = prefs->getBool("/tools/bounding_box", 0); // We'll only need to obtain the visual bounding box if the user preferences tell // us to, AND if we are snapping to the bounding box itself. If we're snapping to // paths only, then we can just as well use the geometric bounding box (which is faster) SPItem::BBoxType bbox_type = (!prefs_bbox && _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_CATEGORY)) ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX; if (clip_or_mask) { // Oh oh, this will get ugly. We cannot use sp_item_i2d_affine directly because we need to // insert an additional transformation in document coordinates (code copied from sp_item_i2d_affine) bbox_of_item = item->bounds(bbox_type, item->i2doc_affine() * additional_affine * dt->doc2dt()); } else { bbox_of_item = item->desktopBounds(bbox_type); } if (bbox_of_item) { // See if the item is within range if (bbox_to_snap_incl.intersects(*bbox_of_item) || (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_ROTATION_CENTER) && bbox_to_snap_incl.contains(item->getCenter()))) { // rotation center might be outside of the bounding box // This item is within snapping range, so record it as a candidate _candidates->push_back(SnapCandidateItem(item, clip_or_mask, additional_affine)); // For debugging: print the id of the candidate to the console // SPObject *obj = (SPObject*)item; // std::cout << "Snap candidate added: " << obj->getId() << std::endl; } } } } } } }