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

Inkscape::SnappedPoint SnapManager::_snapTransformed ( std::vector< Inkscape::SnapCandidatePoint > const &  points,
Geom::Point const &  pointer,
bool  constrained,
Inkscape::Snapper::ConstraintLine const &  constraint,
Transformation  transformation_type,
Geom::Point const &  transformation,
Geom::Point const &  origin,
Geom::Dim2  dim,
bool  uniform 
) const [private]

Method for snapping sets of points while they are being transformed.

Method for snapping sets of points while they are being transformed, when using for example the selector tool. This method is for internal use only, and should not have to be called directly. Use freeSnapTransalation(), constrainedSnapScale(), etc. instead.

This is what is being done in this method: transform each point, find out whether a free snap or constrained snap is more appropriate, do the snapping, calculate some metrics to quantify the snap "distance", and see if it's better than the previous snap. Finally, the best ("nearest") snap from all these points is returned.

pointsCollection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
pointerLocation of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
constrainedtrue if the snap is constrained, e.g. for stretching or for purely horizontal translation.
constraintThe direction or line along which snapping must occur, if 'constrained' is true; otherwise undefined.
transformation_typeType of transformation to apply to points before trying to snap them.
transformationDescription of the transformation; details depend on the type.
originOrigin of the transformation, if applicable.
dimDimension to which the transformation applies, if applicable.
uniformtrue if the transformation should be uniform; only applicable for stretching and scaling.
An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.

Definition at line 507 of file snap.cpp.

References _transformPoint(), constrainedSnap(), freeSnap(), and someSnapperMightSnap().

Referenced by constrainedSnapScale(), constrainedSnapSkew(), constrainedSnapStretch(), constrainedSnapTranslation(), freeSnapScale(), and freeSnapTranslation().

    /* We have a list of points, which we are proposing to transform in some way.  We need to see
    ** if any of these points, when transformed, snap to anything.  If they do, we return the
    ** appropriate transformation with `true'; otherwise we return the original scale with `false'.

    /* Quick check to see if we have any snappers that are enabled
    ** Also used to globally disable all snapping
    if (someSnapperMightSnap() == false || points.size() == 0) {
        return Inkscape::SnappedPoint(pointer);

    std::vector<Inkscape::SnapCandidatePoint> transformed_points;
    Geom::Rect bbox;

    long source_num = 0;
    for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {

        /* Work out the transformed version of this point */
        Geom::Point transformed = _transformPoint(*i, transformation_type, transformation, origin, dim, uniform);

        // add the current transformed point to the box hulling all transformed points
        if (i == points.begin()) {
            bbox = Geom::Rect(transformed, transformed);
        } else {

        transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num));

    /* The current best transformation */
    Geom::Point best_transformation = transformation;

    /* The current best metric for the best transformation; lower is better, NR_HUGE
    ** means that we haven't snapped anything.
    Geom::Point best_scale_metric(NR_HUGE, NR_HUGE);
    Inkscape::SnappedPoint best_snapped_point;
    g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
    g_assert(best_snapped_point.getAtIntersection() == false);

    std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();

    // std::cout << std::endl;
    bool first_free_snap = true;
    for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {

        /* Snap it */
        Inkscape::SnappedPoint snapped_point;
        Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
        Geom::Point const b = ((*i).getPoint() - origin); // vector to original point

        if (constrained) {
            if ((transformation_type == SCALE || transformation_type == STRETCH) && uniform) {
                // When uniformly scaling, each point will have its own unique constraint line,
                // running from the scaling origin to the original untransformed point. We will
                // calculate that line here
                dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, b);
            } else if (transformation_type == STRETCH) { // when non-uniform stretching {
                dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).getPoint(), component_vectors[dim]);
            } else if (transformation_type == TRANSLATION) {
                // When doing a constrained translation, all points will move in the same direction, i.e.
                // either horizontally or vertically. The lines along which they move are therefore all
                // parallel, but might not be colinear. Therefore we will have to set the point through
                // which the constraint-line runs here, for each point individually.
            } // else: leave the original constraint, e.g. for skewing
            if (transformation_type == SCALE && !uniform) {
                g_warning("Non-uniform constrained scaling is not supported!");
            snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
        } else {
            bool const c1 = fabs(b[Geom::X]) < 1e-6;
            bool const c2 = fabs(b[Geom::Y]) < 1e-6;
            if (transformation_type == SCALE && (c1 || c2) && !(c1 && c2)) {
                // When scaling, a point aligned either horizontally or vertically with the origin can only
                // move in that specific direction; therefore it should only snap in that direction, otherwise
                // we will get snapped points with an invalid transformation
                dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]);
                snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
            } else {
                // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
                // requirements, then freeSnap might never see the SnapCandidatePoint with source_num == 0. The freeSnap()
                // method in the object snapper depends on this, because only for source-num == 0 the target nodes will
                // be collected. Therefore we enforce that the first SnapCandidatePoint that is to be freeSnapped always
                // has source_num == 0;
                // TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
                // of it and explicitely communicate to the object snapper that this is a first point
                if (first_free_snap) {
                    first_free_snap = false;
                snapped_point = freeSnap(*j, bbox);
        // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl;
        snapped_point.setPointerDistance(Geom::L2(pointer - (*i).getPoint()));

        Geom::Point result;

        if (snapped_point.getSnapped()) {
            /* We snapped.  Find the transformation that describes where the snapped point has
            ** ended up, and also the metric for this transformation.
            Geom::Point const a = (snapped_point.getPoint() - origin); // vector to snapped point
            //Geom::Point const b = (*i - origin); // vector to original point

            switch (transformation_type) {
                case TRANSLATION:
                    result = snapped_point.getPoint() - (*i).getPoint();
                    /* Consider the case in which a box is almost aligned with a grid in both
                     * horizontal and vertical directions. The distance to the intersection of
                     * the grid lines will always be larger then the distance to a single grid
                     * line. If we prefer snapping to an intersection instead of to a single
                     * grid line, then we cannot use "metric = Geom::L2(result)". Therefore the
                     * snapped distance will be used as a metric. Please note that the snapped
                     * distance is defined as the distance to the nearest line of the intersection,
                     * and not to the intersection itself!
                    // Only for translations, the relevant metric will be the real snapped distance,
                    // so we don't have to do anything special here
                case SCALE:
                    result = Geom::Point(NR_HUGE, NR_HUGE);
                    // If this point *i is horizontally or vertically aligned with
                    // the origin of the scaling, then it will scale purely in X or Y
                    // We can therefore only calculate the scaling in this direction
                    // and the scaling factor for the other direction should remain
                    // untouched (unless scaling is uniform of course)
                    for (int index = 0; index < 2; index++) {
                        if (fabs(b[index]) > 1e-6) { // if SCALING CAN occur in this direction
                            if (fabs(fabs(a[index]/b[index]) - fabs(transformation[index])) > 1e-12) { // if SNAPPING DID occur in this direction
                                result[index] = a[index] / b[index]; // then calculate it!
                            // we might leave result[1-index] = NR_HUGE
                            // if scaling didn't occur in the other direction
                    if (uniform) {
                        if (fabs(result[0]) < fabs(result[1])) {
                            result[1] = result[0];
                        } else {
                            result[0] = result[1];
                    // Compare the resulting scaling with the desired scaling
                    Geom::Point scale_metric = Geom::abs(result - transformation); // One or both of its components might be NR_HUGE
                    snapped_point.setSnapDistance(std::min(scale_metric[0], scale_metric[1]));
                    snapped_point.setSecondSnapDistance(std::max(scale_metric[0], scale_metric[1]));
                case STRETCH:
                    result = Geom::Point(NR_HUGE, NR_HUGE);
                    if (fabs(b[dim]) > 1e-6) { // if STRETCHING will occur for this point
                        result[dim] = a[dim] / b[dim];
                        result[1-dim] = uniform ? result[dim] : 1;
                    } else { // STRETCHING might occur for this point, but only when the stretching is uniform
                        if (uniform && fabs(b[1-dim]) > 1e-6) {
                           result[1-dim] = a[1-dim] / b[1-dim];
                           result[dim] = result[1-dim];
                    // Store the metric for this transformation as a virtual distance
                    snapped_point.setSnapDistance(std::abs(result[dim] - transformation[dim]));
                case SKEW:
                    result[0] = (snapped_point.getPoint()[dim] - ((*i).getPoint())[dim]) / (((*i).getPoint())[1 - dim] - origin[1 - dim]); // skew factor
                    result[1] = transformation[1]; // scale factor
                    // Store the metric for this transformation as a virtual distance
                    snapped_point.setSnapDistance(std::abs(result[0] - transformation[0]));

            if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) {
                best_transformation = result;
                best_snapped_point = snapped_point;


    Geom::Coord best_metric;
    if (transformation_type == SCALE) {
        // When scaling, don't ever exit with one of scaling components set to NR_HUGE
        for (int index = 0; index < 2; index++) {
            if (best_transformation[index] == NR_HUGE) {
                if (uniform && best_transformation[1-index] < NR_HUGE) {
                    best_transformation[index] = best_transformation[1-index];
                } else {
                    best_transformation[index] = transformation[index];

    best_metric = best_snapped_point.getSnapDistance();
    // Using " < 1e6" instead of " < NR_HUGE" for catching some rounding errors
    // These rounding errors might be caused by NRRects, see bug #1584301
    best_snapped_point.setSnapDistance(best_metric < 1e6 ? best_metric : NR_HUGE);
    return best_snapped_point;

Here is the call graph for this function:

Here is the caller graph for this function:

Generated by  Doxygen 1.6.0   Back to index