Ejemplo n.º 1
0
Inkscape::SnappedPoint SnapManager::constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p,
                                                            boost::optional<Geom::Point> const &p_ref,
                                                            Geom::Point const &o,
                                                            unsigned const snaps) const
{
    Inkscape::SnappedPoint sp;
    if (snaps > 0) { // 0 means no angular snapping
        // p is at an arbitrary angle. Now we should snap this angle to specific increments.
        // For this we'll calculate the closest two angles, one at each side of the current angle
        Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1));
        Geom::Line p_line(o, p.getPoint());
        double angle = Geom::angle_between(y_axis, p_line);
        double angle_incr = M_PI / snaps;
        double angle_offset = 0;
        if (p_ref) {
            Geom::Line p_line_ref(o, *p_ref);
            angle_offset = Geom::angle_between(y_axis, p_line_ref);
        }
        double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr, angle_offset);
        double angle_floor = round_to_lower_multiple_plus(angle, angle_incr, angle_offset);
        // We have two angles now. The constrained snapper will try each of them and return the closest

        // Now do the snapping...
        std::vector<Inkscape::Snapper::SnapConstraint> constraints;
        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2)));
        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2)));
        sp = multipleConstrainedSnaps(p, constraints); // Constraints will always be applied, even if we didn't snap
        if (!sp.getSnapped()) { // If we haven't snapped then we only had the constraint applied;
            sp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE);
        }
    } else {
        sp = freeSnap(p);
    }
    return sp;
}
Ejemplo n.º 2
0
Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p,
                                                 IntermSnapResults const &isr,
                                                 bool constrained,
                                                 bool allowOffScreen,
                                                 bool to_path_only) const
{
    g_assert(_desktop != NULL);

    /*
    std::cout << "Type and number of snapped constraints: " << std::endl;
    std::cout << "  Points      : " << isr.points.size() << std::endl;
    std::cout << "  Grid lines  : " << isr.grid_lines.size()<< std::endl;
    std::cout << "  Guide lines : " << isr.guide_lines.size()<< std::endl;
    std::cout << "  Curves      : " << isr.curves.size()<< std::endl;
    */

    /*
    // Display all snap candidates on the canvas
    _desktop->snapindicator->remove_debugging_points();
    for (std::list<Inkscape::SnappedPoint>::const_iterator i = isr.points.begin(); i != isr.points.end(); i++) {
        _desktop->snapindicator->set_new_debugging_point((*i).getPoint());
    }
    for (std::list<Inkscape::SnappedCurve>::const_iterator i = isr.curves.begin(); i != isr.curves.end(); i++) {
        _desktop->snapindicator->set_new_debugging_point((*i).getPoint());
    }
    for (std::list<Inkscape::SnappedLine>::const_iterator i = isr.grid_lines.begin(); i != isr.grid_lines.end(); i++) {
        _desktop->snapindicator->set_new_debugging_point((*i).getPoint());
    }
    for (std::list<Inkscape::SnappedLine>::const_iterator i = isr.guide_lines.begin(); i != isr.guide_lines.end(); i++) {
        _desktop->snapindicator->set_new_debugging_point((*i).getPoint());
    }
    */

    // Store all snappoints
    std::list<Inkscape::SnappedPoint> sp_list;

    // search for the closest snapped point
    Inkscape::SnappedPoint closestPoint;
    if (getClosestSP(isr.points, closestPoint)) {
        sp_list.push_back(closestPoint);
    }

    // search for the closest snapped curve
    Inkscape::SnappedCurve closestCurve;
    // We might have collected the paths only to snap to their intersection, without the intention to snap to the paths themselves
    // Therefore we explicitly check whether the paths should be considered as snap targets themselves
    bool exclude_paths = !snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_PATH);
    if (getClosestCurve(isr.curves, closestCurve, exclude_paths)) {
        sp_list.push_back(Inkscape::SnappedPoint(closestCurve));
    }

    // search for the closest snapped grid line
    Inkscape::SnappedLine closestGridLine;
    if (getClosestSL(isr.grid_lines, closestGridLine)) {
        sp_list.push_back(Inkscape::SnappedPoint(closestGridLine));
    }

    // search for the closest snapped guide line
    Inkscape::SnappedLine closestGuideLine;
    if (getClosestSL(isr.guide_lines, closestGuideLine)) {
        sp_list.push_back(Inkscape::SnappedPoint(closestGuideLine));
    }

    // When freely snapping to a grid/guide/path, only one degree of freedom is eliminated
    // Therefore we will try get fully constrained by finding an intersection with another grid/guide/path

    // When doing a constrained snap however, we're already at an intersection of the constrained line and
    // the grid/guide/path we're snapping to. This snappoint is therefore fully constrained, so there's
    // no need to look for additional intersections
    if (!constrained) {
        if (snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_PATH_INTERSECTION)) {
            // search for the closest snapped intersection of curves
            Inkscape::SnappedPoint closestCurvesIntersection;
            if (getClosestIntersectionCS(isr.curves, p.getPoint(), closestCurvesIntersection, _desktop->dt2doc())) {
                closestCurvesIntersection.setSource(p.getSourceType());
                sp_list.push_back(closestCurvesIntersection);
            }
        }

        if (snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION)) {
            // search for the closest snapped intersection of a guide with a curve
            Inkscape::SnappedPoint closestCurveGuideIntersection;
            if (getClosestIntersectionCL(isr.curves, isr.guide_lines, p.getPoint(), closestCurveGuideIntersection, _desktop->dt2doc())) {
                closestCurveGuideIntersection.setSource(p.getSourceType());
                sp_list.push_back(closestCurveGuideIntersection);
            }
        }

        // search for the closest snapped intersection of grid lines
        Inkscape::SnappedPoint closestGridPoint;
        if (getClosestIntersectionSL(isr.grid_lines, closestGridPoint)) {
            closestGridPoint.setSource(p.getSourceType());
            closestGridPoint.setTarget(Inkscape::SNAPTARGET_GRID_INTERSECTION);
            sp_list.push_back(closestGridPoint);
        }

        // search for the closest snapped intersection of guide lines
        Inkscape::SnappedPoint closestGuidePoint;
        if (getClosestIntersectionSL(isr.guide_lines, closestGuidePoint)) {
            closestGuidePoint.setSource(p.getSourceType());
            closestGuidePoint.setTarget(Inkscape::SNAPTARGET_GUIDE_INTERSECTION);
            sp_list.push_back(closestGuidePoint);
        }

        // search for the closest snapped intersection of grid with guide lines
        if (snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GRID_GUIDE_INTERSECTION)) {
            Inkscape::SnappedPoint closestGridGuidePoint;
            if (getClosestIntersectionSL(isr.grid_lines, isr.guide_lines, closestGridGuidePoint)) {
                closestGridGuidePoint.setSource(p.getSourceType());
                closestGridGuidePoint.setTarget(Inkscape::SNAPTARGET_GRID_GUIDE_INTERSECTION);
                sp_list.push_back(closestGridGuidePoint);
            }
        }
    }

    // Filter out all snap targets that do NOT include a path; this is useful when we try to insert
    // a node in a path (on doubleclick in the node tool). We don't want to change the shape of the
    // path, so the snapped point must be on a path, and not e.g. on a grid intersection
    if (to_path_only) {
        std::list<Inkscape::SnappedPoint>::iterator i = sp_list.begin();

        while (i != sp_list.end()) {
            Inkscape::SnapTargetType t = (*i).getTarget();
            if (t == Inkscape::SNAPTARGET_LINE_MIDPOINT ||
                t == Inkscape::SNAPTARGET_PATH ||
                t == Inkscape::SNAPTARGET_PATH_PERPENDICULAR ||
                t == Inkscape::SNAPTARGET_PATH_TANGENTIAL ||
                t == Inkscape::SNAPTARGET_PATH_INTERSECTION ||
                t == Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION ||
                t == Inkscape::SNAPTARGET_PATH_CLIP ||
                t == Inkscape::SNAPTARGET_PATH_MASK ||
                t == Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) {
                ++i;
            } else {
                i = sp_list.erase(i);
            }
        }
    }

    // now let's see which snapped point gets a thumbs up
    Inkscape::SnappedPoint bestSnappedPoint(p.getPoint());
    // std::cout << "Finding the best snap..." << std::endl;
    for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); ++i) {
        // std::cout << "sp = " << (*i).getPoint() << " | source = " << (*i).getSource() << " | target = " << (*i).getTarget();
        bool onScreen = _desktop->get_display_area().contains((*i).getPoint());
        if (onScreen || allowOffScreen) { // Only snap to points which are not off the screen
            if ((*i).getSnapDistance() <= (*i).getTolerance()) { // Only snap to points within snapping range
                // if it's the first point, or if it is closer than the best snapped point so far
                if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) {
                    // then prefer this point over the previous one
                    bestSnappedPoint = *i;
                }
            }
        }
        // std::cout << std::endl;
    }

    // Update the snap indicator, if requested
    if (_snapindicator) {
        if (bestSnappedPoint.getSnapped()) {
            _desktop->snapindicator->set_new_snaptarget(bestSnappedPoint);
        } else {
            _desktop->snapindicator->remove_snaptarget();
        }
    }

    // std::cout << "findBestSnap = " << bestSnappedPoint.getPoint() << " | dist = " << bestSnappedPoint.getSnapDistance() << std::endl;
    return bestSnappedPoint;
}