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; }
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; }