示例#1
0
void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr,
                                         SnapCandidatePoint const &p,
                                         std::vector<SnapCandidatePoint> *unselected_nodes,
                                         SnapConstraint const &c,
                                         Geom::Point const &p_proj_on_constraint) const
{
    // Iterate through all nodes, find out which one is the closest to p, and snap to it!

    _collectNodes(p.getSourceType(), p.getSourceNum() <= 0);

    if (unselected_nodes != NULL && unselected_nodes->size() > 0) {
        g_assert(_points_to_snap_to != NULL);
        _points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end());
    }

    SnappedPoint s;
    bool success = false;
    bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping();

    for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); ++k) {
        if (_allowSourceToSnapToTarget(p.getSourceType(), (*k).getTargetType(), strict_snapping)) {
            Geom::Point target_pt = (*k).getPoint();
            Geom::Coord dist = Geom::infinity();
            if (!c.isUndefined()) {
                // We're snapping to nodes along a constraint only, so find out if this node
                // is at the constraint, while allowing for a small margin
                if (Geom::L2(target_pt - c.projection(target_pt)) > 1e-9) {
                    // The distance from the target point to its projection on the constraint
                    // is too large, so this point is not on the constraint. Skip it!
                    continue;
                }
                dist = Geom::L2(target_pt - p_proj_on_constraint);
            } else {
                // Free (unconstrained) snapping
                dist = Geom::L2(target_pt - p.getPoint());
            }

            if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) {
                s = SnappedPoint(target_pt, p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox());
                success = true;
            }
        }
    }

    if (success) {
        isr.points.push_back(s);
    }
}
示例#2
0
void Inkscape::ObjectSnapper::constrainedSnap( IntermSnapResults &isr,
                                                  SnapCandidatePoint const &p,
                                                  Geom::OptRect const &bbox_to_snap,
                                                  SnapConstraint const &c,
                                                  std::vector<SPItem const *> const *it,
                                                  std::vector<SnapCandidatePoint> *unselected_nodes) const
{
    if (_snap_enabled == false || _snapmanager->snapprefs.isSourceSnappable(p.getSourceType()) == false || ThisSnapperMightSnap() == false) {
        return;
    }

    // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
    Geom::Point pp = c.projection(p.getPoint());

    /* Get a list of all the SPItems that we will try to snap to */
    if (p.getSourceNum() <= 0) {
        Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(pp, pp);
        _findCandidates(_snapmanager->getDocument()->getRoot(), it, p.getSourceNum() <= 0, local_bbox_to_snap, false, Geom::identity());
    }

    // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line).
    // This is useful for example when scaling an object while maintaining a fixed aspect ratio. It's
    // nodes are only allowed to move in one direction (i.e. in one degree of freedom).

    _snapNodes(isr, p, unselected_nodes, c, pp);

    if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH, SNAPTARGET_PATH_INTERSECTION, SNAPTARGET_BBOX_EDGE, SNAPTARGET_PAGE_BORDER, SNAPTARGET_TEXT_BASELINE)) {
        _snapPathsConstrained(isr, p, c, pp);
    }
}
示例#3
0
void Inkscape::LineSnapper::constrainedSnap(IntermSnapResults &isr,
                                               Inkscape::SnapCandidatePoint const &p,
                                               Geom::OptRect const &/*bbox_to_snap*/,
                                               SnapConstraint const &c,
                                               std::vector<SPItem const *> const */*it*/,
                                               std::vector<SnapCandidatePoint> */*unselected_nodes*/) const

{
    if (_snap_enabled == false || _snapmanager->snapprefs.isSourceSnappable(p.getSourceType()) == false) {
        return;
    }

    // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
    Geom::Point pp = c.projection(p.getPoint());

    /* Get the lines that we will try to snap to */
    const LineList lines = _getSnapLines(pp);

    for (LineList::const_iterator i = lines.begin(); i != lines.end(); ++i) {
        Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : pp;
        Geom::Line gridguide_line(i->second, i->second + Geom::rot90(i->first));

        if (c.isCircular()) {
            // Find the intersections between the line and the circular constraint
            // First, project the origin of the circle onto the line
            Geom::Point const origin = c.getPoint();
            Geom::Point const p_proj = Geom::projection(origin, gridguide_line);
            Geom::Coord dist = Geom::L2(p_proj - origin); // distance from circle origin to constraint line
            Geom::Coord radius = c.getRadius();
            if (dist == radius) {
                // Only one point of intersection;
                _addSnappedPoint(isr, p_proj, Geom::L2(pp - p_proj), p.getSourceType(), p.getSourceNum(), true);
            } else if (dist < radius) {
                // Two points of intersection, symmetrical with respect to the projected point
                // Calculate half the length of the linesegment between the two points of intersection
                Geom::Coord l = sqrt(radius*radius - dist*dist);
                Geom::Coord d = Geom::L2(gridguide_line.versor()); // length of versor, needed to normalize the versor
                if (d > 0) {
                    Geom::Point v = l*gridguide_line.versor()/d;
                    _addSnappedPoint(isr, p_proj + v, Geom::L2(p.getPoint() - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true);
                    _addSnappedPoint(isr, p_proj - v, Geom::L2(p.getPoint() - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true);
                }
            }
        } else {
            // Find the intersections between the line and the linear constraint
            Geom::Line constraint_line(point_on_line, point_on_line + c.getDirection());
            Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default
            try
            {
                inters = Geom::intersection(constraint_line, gridguide_line);
            }
            catch (Geom::InfiniteSolutions &e)
            {
                // We're probably dealing with parallel lines, so snapping doesn't make any sense here
                continue; // jump to the next iterator in the for-loop
            }

            if (inters) {
                Geom::Point t = constraint_line.pointAt((*inters).ta);
                const Geom::Coord dist = Geom::L2(t - p.getPoint());
                if (dist < getSnapperTolerance()) {
                    // When doing a constrained snap, we're already at an intersection.
                    // This snappoint is therefore fully constrained, so there's no need
                    // to look for additional intersections; just return the snapped point
                    // and forget about the line
                    _addSnappedPoint(isr, t, dist, p.getSourceType(), p.getSourceNum(), true);
                }
            }
        }
    }
}
示例#4
0
void Inkscape::ObjectSnapper::_snapPathsConstrained(IntermSnapResults &isr,
                                     SnapCandidatePoint const &p,
                                     SnapConstraint const &c,
                                     Geom::Point const &p_proj_on_constraint) const
{

    _collectPaths(p_proj_on_constraint, p.getSourceType(), p.getSourceNum() <= 0);

    // Now we can finally do the real snapping, using the paths collected above

    SPDesktop const *dt = _snapmanager->getDesktop();
    g_assert(dt != NULL);

    Geom::Point direction_vector = c.getDirection();
    if (!is_zero(direction_vector)) {
        direction_vector = Geom::unit_vector(direction_vector);
    }

    // The intersection point of the constraint line with any path, must lie within two points on the
    // SnapConstraint: p_min_on_cl and p_max_on_cl. The distance between those points is twice the snapping tolerance
    Geom::Point const p_min_on_cl = dt->dt2doc(p_proj_on_constraint - getSnapperTolerance() * direction_vector);
    Geom::Point const p_max_on_cl = dt->dt2doc(p_proj_on_constraint + getSnapperTolerance() * direction_vector);
    Geom::Coord tolerance = getSnapperTolerance();

    // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have
    // to convert the snapper coordinates from the desktop coordinates to document coordinates

    std::vector<Geom::Path> constraint_path;
    if (c.isCircular()) {
        Geom::Circle constraint_circle(dt->dt2doc(c.getPoint()), c.getRadius());
        constraint_circle.getPath(constraint_path);
    } else {
        Geom::Path constraint_line;
        constraint_line.start(p_min_on_cl);
        constraint_line.appendNew<Geom::LineSegment>(p_max_on_cl);
        constraint_path.push_back(constraint_line);
    }
    // Length of constraint_path will always be one

    bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping();

    // Find all intersections of the constrained path with the snap target candidates
    std::vector<Geom::Point> intersections;
    for (std::vector<SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); ++k) {
        if (k->path_vector && _allowSourceToSnapToTarget(p.getSourceType(), (*k).target_type, strict_snapping)) {
            // Do the intersection math
            Geom::CrossingSet cs = Geom::crossings(constraint_path, *(k->path_vector));
            // Store the results as intersection points
            unsigned int index = 0;
            for (Geom::CrossingSet::const_iterator i = cs.begin(); i != cs.end(); ++i) {
                if (index >= constraint_path.size()) {
                    break;
                }
                // Reconstruct and store the points of intersection
                for (Geom::Crossings::const_iterator m = (*i).begin(); m != (*i).end(); ++m) {
                    intersections.push_back(constraint_path[index].pointAt((*m).ta));
                }
                index++;
            }

            //Geom::crossings will not consider the closing segment apparently, so we'll handle that separately here
            //TODO: This should have been fixed in rev. #9859, which makes this workaround obsolete
            for(Geom::PathVector::iterator it_pv = k->path_vector->begin(); it_pv != k->path_vector->end(); ++it_pv) {
                if (it_pv->closed()) {
                    // Get the closing linesegment and convert it to a path
                    Geom::Path cls;
                    cls.close(false);
                    cls.append(it_pv->back_closed());
                    // Intersect that closing path with the constrained path
                    Geom::Crossings cs = Geom::crossings(constraint_path.front(), cls);
                    // Reconstruct and store the points of intersection
                    index = 0; // assuming the constraint path vector has only one path
                    for (Geom::Crossings::const_iterator m = cs.begin(); m != cs.end(); ++m) {
                        intersections.push_back(constraint_path[index].pointAt((*m).ta));
                    }
                }
            }

            // Convert the collected points of intersection to snapped points
            for (std::vector<Geom::Point>::iterator p_inters = intersections.begin(); p_inters != intersections.end(); ++p_inters) {
                // Convert to desktop coordinates
                (*p_inters) = dt->doc2dt(*p_inters);
                // Construct a snapped point
                Geom::Coord dist = Geom::L2(p.getPoint() - *p_inters);
                SnappedPoint s = SnappedPoint(*p_inters, p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox);;
                // Store the snapped point
                if (dist <= tolerance) { // If the intersection is within snapping range, then we might snap to it
                    isr.points.push_back(s);
                }
            }
        }
    }
}