void SnapManager::freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Geom::OptRect const &bbox_to_snap) const { Inkscape::SnappedPoint const s = freeSnap(Inkscape::SnapCandidatePoint(p, source_type, Inkscape::SNAPTARGET_PATH), bbox_to_snap); s.getPointIfSnapped(p); }
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; }
void spdc_endpoint_snap_rotation(ToolBase const *const ec, Geom::Point &p, Geom::Point const &o, guint state) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); SnapManager &m = ec->desktop->namedview->snap_manager; m.setup(ec->desktop); bool snap_enabled = m.snapprefs.getSnapEnabledGlobally(); if (state & GDK_SHIFT_MASK) { // SHIFT disables all snapping, except the angular snapping. After all, the user explicitly asked for angular // snapping by pressing CTRL, otherwise we wouldn't have arrived here. But although we temporarily disable // the snapping here, we must still call for a constrained snap in order to apply the constraints (i.e. round // to the nearest angle increment) m.snapprefs.setSnapEnabledGlobally(false); } Inkscape::SnappedPoint dummy = m.constrainedAngularSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE), boost::optional<Geom::Point>(), o, snaps); p = dummy.getPoint(); if (state & GDK_SHIFT_MASK) { m.snapprefs.setSnapEnabledGlobally(snap_enabled); // restore the original setting } m.unSetup(); }
void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type), constraint, bbox_to_snap); p = s.getPoint(); // If we didn't snap, then we will return the point projected onto the constraint }
void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) { NodeList::iterator second = first.next(); // special cancel handling - retract handles when if the segment was degenerate if (_is_drag_cancelled(event) && _segment_was_degenerate) { first->front()->retract(); second->back()->retract(); _pm.update(); return; } if (_drag_initiated && !(event->state & GDK_SHIFT_MASK)) { SnapManager &m = _desktop->namedview->snap_manager; SPItem *path = static_cast<SPItem *>(_pm._path); m.setup(_desktop, true, path); // We will not try to snap to "path" itself Inkscape::SnapCandidatePoint scp(new_pos, Inkscape::SNAPSOURCE_OTHER_HANDLE); Inkscape::SnappedPoint sp = m.freeSnap(scp, Geom::OptRect(), false); new_pos = sp.getPoint(); m.unSetup(); } // Magic Bezier Drag Equations follow! // "weight" describes how the influence of the drag should be distributed // among the handles; 0 = front handle only, 1 = back handle only. double weight, t = _t; if (t <= 1.0 / 6.0) weight = 0; else if (t <= 0.5) weight = (pow((6 * t - 1) / 2.0, 3)) / 2; else if (t <= 5.0 / 6.0) weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5; else weight = 1; Geom::Point delta = new_pos - position(); Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta; Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta; //modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed if(!_pm._isBSpline()){ first->front()->move(first->front()->position() + offset0); second->back()->move(second->back()->position() + offset1); }else if(weight>=0.8){ if(held_shift(*event)){ second->back()->move(new_pos); } else { second->move(second->position() + delta); } }else if(weight<=0.2){ if(held_shift(*event)){ first->back()->move(new_pos); } else { first->move(first->position() + delta); } }else{ first->move(first->position() + delta); second->move(second->position() + delta); } _pm.update(); }
Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin) { if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) return t; if (_desktop && _desktop->gridsEnabled()) { bool success = false; Geom::Point nearest_multiple; Geom::Coord nearest_distance = Geom::infinity(); Inkscape::SnappedPoint bestSnappedPoint(t); // It will snap to the grid for which we find the closest snap. This might be a different // grid than to which the objects were initially aligned. I don't see an easy way to fix // this, so when using multiple grids one can get unexpected results // Cannot use getGridSnappers() because we need both the grids AND their snappers // Therefore we iterate through all grids manually for (std::vector<Inkscape::CanvasGrid *>::const_iterator it = _named_view->grids.begin(); it != _named_view->grids.end(); ++it) { Inkscape::CanvasGrid *grid = (*it); const Inkscape::Snapper* snapper = grid->snapper; if (snapper && snapper->ThisSnapperMightSnap()) { // To find the nearest multiple of the grid pitch for a given translation t, we // will use the grid snapper. Simply snapping the value t to the grid will do, but // only if the origin of the grid is at (0,0). If it's not then compensate for this // in the translation t Geom::Point const t_offset = t + grid->origin; IntermSnapResults isr; // Only the first three parameters are being used for grid snappers snapper->freeSnap(isr, Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH),Geom::OptRect(), NULL, NULL); // Find the best snap for this grid, including intersections of the grid-lines bool old_val = _snapindicator; _snapindicator = false; Inkscape::SnappedPoint s = findBestSnap(Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH), isr, false, true); _snapindicator = old_val; if (s.getSnapped() && (s.getSnapDistance() < nearest_distance)) { // use getSnapDistance() instead of getWeightedDistance() here because the pointer's position // doesn't tell us anything about which node to snap success = true; nearest_multiple = s.getPoint() - grid->origin; nearest_distance = s.getSnapDistance(); bestSnappedPoint = s; } } } if (success) { bestSnappedPoint.setPoint(origin + nearest_multiple); _desktop->snapindicator->set_new_snaptarget(bestSnappedPoint); return nearest_multiple; } } return t; }
Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p, Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { // First project the mouse pointer onto the constraint Geom::Point pp = constraint.projection(p.getPoint()); Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::infinity(), 0, false, true, false); if (!someSnapperMightSnap()) { // Always return point on constraint return no_snap; } Inkscape::SnappedPoint result = no_snap; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if ((prefs->getBool("/options/snapmousepointer/value", false)) && p.isSingleHandle()) { // Snapping the mouse pointer instead of the constrained position of the knot allows // to snap to things which don't intersect with the constraint line; this is basically // then just a freesnap with the constraint applied afterwards // We'll only do this if we're dragging a single handle, and for example not when transforming an object in the selector tool result = freeSnap(p, bbox_to_snap); if (result.getSnapped()) { // only change the snap indicator if we really snapped to something if (_snapindicator && _desktop) { _desktop->snapindicator->set_new_snaptarget(result); } // Apply the constraint result.setPoint(constraint.projection(result.getPoint())); return result; } return no_snap; } IntermSnapResults isr; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); ++i) { (*i)->constrainedSnap(isr, p, bbox_to_snap, constraint, &_items_to_ignore, _unselected_nodes); } result = findBestSnap(p, isr, true); if (result.getSnapped()) { // only change the snap indicator if we really snapped to something if (_snapindicator && _desktop) { _desktop->snapindicator->set_new_snaptarget(result); } return result; } return no_snap; }
void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p, bool to_paths_only) { // setup() must have been called before calling this method! if (_snapindicator) { _snapindicator = false; // prevent other methods from drawing a snap indicator; we want to control this here Inkscape::SnappedPoint s = freeSnap(p, Geom::OptRect(), to_paths_only); g_assert(_desktop != NULL); if (s.getSnapped()) { _desktop->snapindicator->set_new_snaptarget(s, true); } else { _desktop->snapindicator->remove_snaptarget(true); } _snapindicator = true; // restore the original value } }
void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const { if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally() || !snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GUIDE)) { return; } Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN, Inkscape::SNAPTARGET_UNDEFINED); IntermSnapResults isr; Inkscape::Snapper::SnapConstraint cl(guideline.getPoint(), Geom::rot90(guideline.getNormal())); SnapperList snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); ++i) { (*i)->constrainedSnap(isr, candidate, Geom::OptRect(), cl, NULL, NULL); } Inkscape::SnappedPoint const s = findBestSnap(candidate, isr, false); s.getPointIfSnapped(p); }
void spdc_endpoint_snap_free(ToolBase const * const ec, Geom::Point& p, boost::optional<Geom::Point> &start_of_line, guint const /*state*/) { SPDesktop *dt = ec->desktop; SnapManager &m = dt->namedview->snap_manager; Inkscape::Selection *selection = dt->getSelection(); // selection->singleItem() is the item that is currently being drawn. This item will not be snapped to (to avoid self-snapping) // TODO: Allow snapping to the stationary parts of the item, and only ignore the last segment m.setup(dt, true, selection->singleItem()); Inkscape::SnapCandidatePoint scp(p, Inkscape::SNAPSOURCE_NODE_HANDLE); if (start_of_line) { scp.addOrigin(*start_of_line); } Inkscape::SnappedPoint sp = m.freeSnap(scp); p = sp.getPoint(); m.unSetup(); }
void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point &origin_or_vector, bool origin, bool freeze_angle) const { if (freeze_angle && origin) { g_warning("Dear developer, when snapping guides you shouldn't ask me to freeze the guide's vector when you haven't specified one"); // You've supplied me with an origin instead of a vector } if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally() || !snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GUIDE)) { return; } Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE); if (origin) { candidate.addOrigin(origin_or_vector); } else { candidate = Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN); candidate.addVector(Geom::rot90(origin_or_vector)); } IntermSnapResults isr; SnapperList snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); ++i) { (*i)->freeSnap(isr, candidate, Geom::OptRect(), NULL, NULL); } Inkscape::SnappedPoint const s = findBestSnap(candidate, isr, false); s.getPointIfSnapped(p); if (!freeze_angle && s.getSnapped()) { if (!Geom::are_near(s.getTangent(), Geom::Point(0,0))) { // If the tangent has been set ... origin_or_vector = Geom::rot90(s.getTangent()); // then use it to update the normal of the guide // PS: The tangent might not have been set if we snapped for example to a node } } }
Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item, Geom::Point const &pt, Geom::Point const ¢er, int state) { Geom::Point p[2]; bool const shift = state & GDK_SHIFT_MASK; bool const control = state & GDK_CONTROL_MASK; SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, false, item); Inkscape::SnappedPoint snappoint; if (control) { /* Control is down: we are constrained to producing integer-ratio rectangles */ /* Vector from the centre of the box to the point we are dragging to */ Geom::Point delta = pt - center; /* Round it so that we have an integer-ratio (or golden ratio) box */ if (fabs(delta[Geom::X]) > fabs(delta[Geom::Y]) && (delta[Geom::Y] != 0.0)) { double ratio = delta[Geom::X] / delta[Geom::Y]; double ratioabs = fabs (ratio); double sign = (ratio < 0 ? -1 : 1); if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) { delta[Geom::X] = sign * goldenratio * delta[Geom::Y]; } else { delta[Geom::X] = floor(ratio + 0.5) * delta[Geom::Y]; } } else if (delta[Geom::X] != 0.0) { double ratio = delta[Geom::Y] / delta[Geom::X]; double ratioabs = fabs (ratio); double sign = (ratio < 0 ? -1 : 1); if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) { delta[Geom::Y] = sign * goldenratio * delta[Geom::X]; } else { delta[Geom::Y] = floor(delta[Geom::Y] / delta[Geom::X] + 0.5) * delta[Geom::X]; } } /* p[1] is the dragged point with the integer-ratio constraint */ p[1] = center + delta; if (shift) { /* Shift is down, so our origin is the centre point rather than the corner ** point; this means that corner-point movements are bound to each other. */ /* p[0] is the opposite corner of our box */ p[0] = center - delta; Inkscape::SnappedPoint s[2]; /* Try to snap p[0] (the opposite corner) along the constraint vector */ s[0] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE), Inkscape::Snapper::ConstraintLine(p[0] - p[1])); /* Try to snap p[1] (the dragged corner) along the constraint vector */ s[1] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), Inkscape::Snapper::ConstraintLine(p[1] - p[0])); /* Choose the best snap and update points accordingly */ if (s[0].getSnapDistance() < s[1].getSnapDistance()) { if (s[0].getSnapped()) { p[0] = s[0].getPoint(); p[1] = 2 * center - s[0].getPoint(); snappoint = s[0]; } } else { if (s[1].getSnapped()) { p[0] = 2 * center - s[1].getPoint(); p[1] = s[1].getPoint(); snappoint = s[1]; } } } else { /* Our origin is the opposite corner. Snap the drag point along the constraint vector */ p[0] = center; snappoint = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), Inkscape::Snapper::ConstraintLine(p[1] - p[0])); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } } } else if (shift) { /* Shift is down, so our origin is the centre point rather than the corner point; ** this means that corner-point movements are bound to each other. */ p[1] = pt; p[0] = 2 * center - p[1]; Inkscape::SnappedPoint s[2]; s[0] = m.freeSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE)); s[1] = m.freeSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE)); if (s[0].getSnapDistance() < s[1].getSnapDistance()) { if (s[0].getSnapped()) { p[0] = s[0].getPoint(); p[1] = 2 * center - s[0].getPoint(); snappoint = s[0]; } } else { if (s[1].getSnapped()) { p[0] = 2 * center - s[1].getPoint(); p[1] = s[1].getPoint(); snappoint = s[1]; } } } else { /* There's no constraint on the corner point, so just snap it to anything */ p[0] = center; p[1] = pt; snappoint = m.freeSnap(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_HANDLE)); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } } if (snappoint.getSnapped()) { desktop->snapindicator->set_new_snaptarget(snappoint); } p[0] *= desktop->dt2doc(); p[1] *= desktop->dt2doc(); return Geom::Rect(Geom::Point(MIN(p[0][Geom::X], p[1][Geom::X]), MIN(p[0][Geom::Y], p[1][Geom::Y])), Geom::Point(MAX(p[0][Geom::X], p[1][Geom::X]), MAX(p[0][Geom::Y], p[1][Geom::Y]))); }
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; }
Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandidatePoint const &p, std::vector<Inkscape::Snapper::SnapConstraint> const &constraints, bool dont_snap, Geom::OptRect const &bbox_to_snap) const { Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(p.getPoint(), p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::infinity(), 0, false, true, false); if (constraints.size() == 0) { return no_snap; } // We haven't tried to snap yet; we will first determine which constraint is closest to where we are now, // i.e. lets find out which of the constraints yields the closest projection of point p // Project the mouse pointer on each of the constraints std::vector<Geom::Point> projections; for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); ++c) { // Project the mouse pointer onto the constraint; In case we don't snap then we will // return the projection onto the constraint, such that the constraint is always enforced Geom::Point pp = (*c).projection(p.getPoint()); projections.push_back(pp); } // Select the closest constraint no_snap.setPoint(projections.front()); Inkscape::Snapper::SnapConstraint cc = constraints.front(); //closest constraint std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); std::vector<Geom::Point>::iterator pp = projections.begin(); for (; pp != projections.end(); ++pp) { if (Geom::L2(*pp - p.getPoint()) < Geom::L2(no_snap.getPoint() - p.getPoint())) { no_snap.setPoint(*pp); // Remember the projection onto the closest constraint cc = *c; // Remember the closest constraint itself } ++c; } if (!someSnapperMightSnap() || dont_snap) { return no_snap; } IntermSnapResults isr; SnapperList const snappers = getSnappers(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool snap_mouse = prefs->getBool("/options/snapmousepointer/value", false); Inkscape::SnappedPoint result = no_snap; if (snap_mouse && p.isSingleHandle()) { // Snapping the mouse pointer instead of the constrained position of the knot allows // to snap to things which don't intersect with the constraint line; this is basically // then just a freesnap with the constraint applied afterwards // We'll only to this if we're dragging a single handle, and for example not when transforming an object in the selector tool result = freeSnap(p, bbox_to_snap); // Now apply the constraint afterwards result.setPoint(cc.projection(result.getPoint())); } else { // Try to snap along the closest constraint for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); ++i) { (*i)->constrainedSnap(isr, p, bbox_to_snap, cc, &_items_to_ignore,_unselected_nodes); } result = findBestSnap(p, isr, true); } return result.getSnapped() ? result : no_snap; }
static gint pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mevent) { if ((mevent.state & GDK_CONTROL_MASK) && (mevent.state & GDK_BUTTON1_MASK)) { // mouse was accidentally moved during Ctrl+click; // ignore the motion and create a single point pc->is_drawing = false; return TRUE; } gint ret = FALSE; SPDesktop *const dt = pc->desktop; SPEventContext *event_context = SP_EVENT_CONTEXT(pc); if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) { // allow scrolling return FALSE; } if ( ( mevent.state & GDK_BUTTON1_MASK ) && !pc->grab && pc->is_drawing) { /* Grab mouse, so release will not pass unnoticed */ pc->grab = SP_CANVAS_ITEM(dt->acetate); sp_canvas_item_grab(pc->grab, ( GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK ), NULL, mevent.time); } /* Find desktop coordinates */ Geom::Point p = dt->w2d(Geom::Point(mevent.x, mevent.y)); /* Test whether we hit any anchor. */ SPDrawAnchor *anchor = spdc_test_inside(pc, Geom::Point(mevent.x, mevent.y)); if (pencil_within_tolerance) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); if ( Geom::LInfty( Geom::Point(mevent.x,mevent.y) - pencil_drag_origin_w ) < tolerance ) { return FALSE; // Do not drag if we're within tolerance from origin. } } // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process the // motion notify coordinates as given (no snapping back to origin) pencil_within_tolerance = false; switch (pc->state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Set red endpoint */ if (anchor) { p = anchor->dp; } else { Geom::Point ptnr(p); spdc_endpoint_snap(pc, ptnr, mevent.state); p = ptnr; } spdc_set_endpoint(pc, p); ret = TRUE; break; default: /* We may be idle or already freehand */ if ( mevent.state & GDK_BUTTON1_MASK && pc->is_drawing ) { pc->state = SP_PENCIL_CONTEXT_FREEHAND; if ( !pc->sa && !pc->green_anchor ) { /* Create green anchor */ pc->green_anchor = sp_draw_anchor_new(pc, pc->green_curve, TRUE, pc->p[0]); } /** \todo * fixme: I am not sure whether we want to snap to anchors * in middle of freehand (Lauris) */ SnapManager &m = dt->namedview->snap_manager; if (anchor) { p = anchor->dp; } else if ((mevent.state & GDK_SHIFT_MASK) == 0) { m.setup(dt); Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p); if (s.getSnapped()) { s.getPoint(p); pc->prev_snap_was_succesful = true; } else { pc->prev_snap_was_succesful = false; } } if ( pc->npoints != 0) { // buttonpress may have happened before we entered draw context! if (!(pc->prev_snap_was_succesful && m.snapprefs.getSnapPostponedGlobally())) { // When snapping is enabled but temporarily on hold because the mouse is moving // fast, then we don't want to add nodes off-grid spdc_add_freehand_point(pc, p, mevent.state); ret = TRUE; } } if (anchor && !pc->anchor_statusbar) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Release</b> here to close and finish the path.")); pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); pc->anchor_statusbar = false; } else if (!anchor) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Drawing a freehand path")); } } else { if (anchor && !pc->anchor_statusbar) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> to continue the path from this point.")); pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); pc->anchor_statusbar = false; } } break; } return ret; }
static gint pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &bevent) { gint ret = FALSE; SPEventContext *event_context = SP_EVENT_CONTEXT(pc); if ( bevent.button == 1 && !event_context->space_panning) { SPDrawContext *dc = SP_DRAW_CONTEXT (pc); SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(dc); Inkscape::Selection *selection = sp_desktop_selection(desktop); if (Inkscape::have_viable_layer(desktop, dc->_message_context) == false) { return TRUE; } Geom::Point const button_w(bevent.x, bevent.y); /* Find desktop coordinates */ Geom::Point p = pc->desktop->w2d(button_w); /* Test whether we hit any anchor. */ SPDrawAnchor *anchor = spdc_test_inside(pc, button_w); pencil_drag_origin_w = Geom::Point(bevent.x,bevent.y); pencil_within_tolerance = true; switch (pc->state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Current segment will be finished with release */ ret = TRUE; break; default: /* Set first point of sequence */ if (bevent.state & GDK_CONTROL_MASK) { spdc_create_single_dot(event_context, p, "/tools/freehand/pencil", bevent.state); ret = true; break; } if (anchor) { p = anchor->dp; desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); } else { if (!(bevent.state & GDK_SHIFT_MASK)) { // This is the first click of a new curve; deselect item so that // this curve is not combined with it (unless it is drawn from its // anchor, which is handled by the sibling branch above) selection->clear(); desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new path")); SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p); if (s.getSnapped()) { s.getPoint(p); pc->prev_snap_was_succesful = true; } else { pc->prev_snap_was_succesful = false; } } else if (selection->singleItem() && SP_IS_PATH(selection->singleItem())) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Appending to selected path")); } } pc->sa = anchor; spdc_set_startpoint(pc, p); ret = TRUE; break; } pc->is_drawing = true; } return ret; }