49 _points_to_snap_to->clear();
55 SPDesktop const *dt = _snapmanager->getDesktop();
57 return _snapmanager->snapprefs.getObjectTolerance() / zoom;
66 bool const &first_point)
const
72 _points_to_snap_to->clear();
82 if (((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_other) || (p_is_a_node && p_is_other))) {
83 g_warning(
"Snap warning: node type is ambiguous");
88 bool prefs_bbox = prefs->
getBool(
"/tools/bounding_box");
89 bbox_type = !prefs_bbox ?
94 if (
auto document = _snapmanager->getDocument()) {
95 auto ignore_page = _snapmanager->getPageToIgnore();
96 for (
auto page : document->getPageManager().getPages()) {
97 if (ignore_page ==
page)
118 getBBoxPoints(document->preferredBounds(), _points_to_snap_to.get(),
false,
125 for (
const auto & _candidate : *_snapmanager->_obj_snapper_candidates) {
126 SPItem *root_item = _candidate.item;
127 g_return_if_fail(root_item);
130 if (p_is_a_node || p_is_other || (p_is_a_bbox && !_snapmanager->snapprefs.getStrictSnapping())) {
162 std::vector<SPItem*> rotationSource=_snapmanager->getRotationCenterSource();
163 for (
auto itemlist : rotationSource) {
164 if (_candidate.item == itemlist) {
172 root_item->
getSnappoints(*_points_to_snap_to, &_snapmanager->snapprefs);
180 if (p_is_a_bbox || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node) || p_is_other) {
183 if (!_candidate.clip_or_mask) {
197 std::vector<SnapCandidatePoint> *unselected_nodes,
205 if (unselected_nodes !=
nullptr && unselected_nodes->size() > 0) {
206 g_assert(_points_to_snap_to !=
nullptr);
207 _points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end());
211 bool success =
false;
212 bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping();
214 for (
const auto & k : *_points_to_snap_to) {
215 if (_allowSourceToSnapToTarget(p.
getSourceType(), k.getTargetType(), strict_snapping)) {
218 if (!
c.isUndefined()) {
221 if (
Geom::L2(target_pt -
c.projection(target_pt)) > 1e-9) {
226 dist =
Geom::L2(target_pt - p_proj_on_constraint);
259 for (
const auto & k : *_points_to_snap_to) {
265 if ((dist < tol && dist2 < tol) ||
always) {
275 bool const &first_point)
const
292 int prefs_bbox = prefs->
getBool(
"/tools/bounding_box",
false);
293 bbox_type = !prefs_bbox ?
297 auto document = _snapmanager->getDocument();
298 auto &pm = document->getPageManager();
299 for (
auto page : document->getPageManager().getPages()) {
301 auto pathv = _getPathvFromRect(
page->getDesktopRect());
305 auto margin = _getPathvFromRect(
page->getDesktopMargin());
307 auto bleed = _getPathvFromRect(
page->getDesktopBleed());
312 if (!pm.hasPages()) {
315 auto pathv = _getPathvFromRect(*(_snapmanager->getDocument()->preferredBounds()));
320 for (
const auto & _candidate : *_snapmanager->_obj_snapper_candidates) {
323 SPItem *root_item =
nullptr;
325 auto use = cast<SPUse>(_candidate.item);
327 i2doc = use->get_root_transform();
328 root_item = use->root();
329 g_return_if_fail(root_item);
331 i2doc = _candidate.item->i2doc_affine();
332 root_item = _candidate.item;
339 if (p_is_other || p_is_a_node || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_bbox)) {
340 if (is<SPText>(root_item) || is<SPFlowtext>(root_item)) {
345 Geom::Affine transform = root_item->
i2dt_affine() * _candidate.additional_affine * _snapmanager->getDesktop()->doc2dt();
348 std::array<Geom::LineSegment, 1>
const segments{baseline};
359 bool very_complex_path =
false;
360 auto path = cast<SPPath>(root_item);
362 very_complex_path = path->nodesInPath() > 500;
366 if (
auto const shape = cast<SPShape>(root_item)) {
367 if (
auto const curve = shape->curve()) {
370 transform *= _candidate.additional_affine;
371 transform *= _snapmanager->getDesktop()->doc2dt();
372 auto pv =
curve->get_pathvector();
384 if (p_is_other || p_is_a_bbox || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) {
387 if (!_candidate.clip_or_mask) {
388 if (
auto rect = root_item->
bounds(bbox_type, i2doc)) {
389 auto path = _getPathvFromRect(*rect);
402 std::vector<SnapCandidatePoint> *unselected_nodes,
403 SPPath const *selected_path)
const
408 SPDesktop const *dt = _snapmanager->getDesktop();
409 g_assert(dt !=
nullptr);
421 if (node_tool_active) {
424 _paths_to_snap_to->emplace_back(
curve->get_pathvector() * selected_path->
i2doc_affine(),
434 bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping();
439 for (
const auto & it_p : *_paths_to_snap_to) {
440 if (_allowSourceToSnapToTarget(p.
getSourceType(), it_p.target_type, strict_snapping)) {
441 bool const being_edited = node_tool_active && it_p.currently_being_edited;
444 for (
auto &it_pv : it_p.path_vector) {
447 std::vector<double> anp = it_pv.nearestTimePerCurve(p_doc);
451 std::vector<double>::const_iterator np = anp.begin();
452 unsigned int index = 0;
453 for (; np != anp.end(); ++np,
index++) {
465 g_assert(unselected_nodes !=
nullptr);
468 c1 = isUnselectedNode(start_pt, unselected_nodes);
469 c2 = isUnselectedNode(end_pt, unselected_nodes);
480 if (!being_edited || (c1 && c2)) {
483 if (dist < getSnapperTolerance()) {
493 isr.
curves.emplace_back(sp_dt, sp_tangent_dt, num_path,
index, dist, getSnapperTolerance(),
always,
false,
curve, p.
getSourceType(), p.
getSourceNum(), it_p.target_type, it_p.target_bbox);
494 if (snap_tang || snap_perp) {
496 _snapPathsTangPerp(snap_tang, snap_perp, isr, p,
curve, dt);
510 if (unselected_nodes ==
nullptr) {
514 if (unselected_nodes->size() == 0) {
518 for (
const auto & unselected_node : *unselected_nodes) {
519 if (
Geom::L2(point - unselected_node.getPoint()) < 1e-4) {
531 std::vector<SnapCandidatePoint> *unselected_nodes,
532 SPPath const *selected_path)
const
539 SPDesktop const *dt = _snapmanager->getDesktop();
540 g_assert(dt !=
nullptr);
543 if (!is_zero(direction_vector)) {
549 Geom::Point const p_min_on_cl = dt->
dt2doc(p_proj_on_constraint - getSnapperTolerance() * direction_vector);
550 Geom::Point const p_max_on_cl = dt->
dt2doc(p_proj_on_constraint + getSnapperTolerance() * direction_vector);
557 if (
c.isCircular()) {
560 pb.
feed(constraint_circle);
562 constraint_path = pb.
peek();
565 constraint_line.
start(p_min_on_cl);
567 constraint_path.
push_back(constraint_line);
580 if (node_tool_active) {
588 bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping();
591 for (
const auto & k : *_paths_to_snap_to) {
592 if (_allowSourceToSnapToTarget(p.
getSourceType(), k.target_type, strict_snapping)) {
594 std::vector<Geom::PVIntersection> inters = constraint_path.
intersect(k.path_vector);
596 bool const being_edited = node_tool_active && k.currently_being_edited;
599 for (
const auto & inter : inters) {
600 int index = inter.second.path_index;
607 g_assert(unselected_nodes !=
nullptr);
610 c1 = isUnselectedNode(start_pt, unselected_nodes);
611 c2 = isUnselectedNode(end_pt, unselected_nodes);
614 if (!being_edited || (c1 && c2)) {
622 if (dist <= tolerance) {
635 std::vector<SPObject const *>
const *it,
636 std::vector<SnapCandidatePoint> *unselected_nodes)
const
638 if (_snap_enabled ==
false || _snapmanager->snapprefs.isSourceSnappable(p.
getSourceType()) ==
false || ThisSnapperMightSnap() ==
false) {
647 _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap,
false,
Geom::identity());
650 _snapNodes(isr, p, unselected_nodes);
653 unsigned n = (unselected_nodes ==
nullptr) ? 0 : unselected_nodes->size();
660 SPPath const *path =
nullptr;
662 SPPath const *tmpPath = cast<SPPath>(*it->begin());
663 if ((it->size() == 1) && tmpPath) {
668 _snapPaths(isr, p, unselected_nodes, path);
670 _snapPaths(isr, p,
nullptr,
nullptr);
679 std::vector<SPObject const *>
const *it,
680 std::vector<SnapCandidatePoint> *unselected_nodes)
const
682 if (_snap_enabled ==
false || _snapmanager->snapprefs.isSourceSnappable(p.
getSourceType()) ==
false || ThisSnapperMightSnap() ==
false) {
694 _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap,
false,
Geom::identity());
701 _snapNodes(isr, p, unselected_nodes,
c, pp);
705 unsigned n = (unselected_nodes ==
nullptr) ? 0 : unselected_nodes->size();
712 SPPath const *path =
nullptr;
714 SPPath const *tmpPath = cast<SPPath>(*it->begin());
715 if ((it->size() == 1) && tmpPath) {
720 _snapPathsConstrained(isr, p,
c, pp, unselected_nodes, path);
722 _snapPathsConstrained(isr, p,
c, pp,
nullptr,
nullptr);
734 _paths_to_snap_to->clear();
746 std::vector<SnapCandidatePoint> *points,
762 std::vector<SnapCandidatePoint> *points,
773 for (
unsigned k = 0; k < 4; k++) {
774 if (corner_src || corner_tgt) {
775 points->emplace_back(bbox->corner(k), corner_src, -1, corner_tgt, *bbox);
778 if (edge_src || edge_tgt) {
779 points->emplace_back((bbox->corner(k) + bbox->corner((k + 1) % 4)) / 2, edge_src, -1, edge_tgt, *bbox);
782 if (mid_src || mid_tgt) {
783 points->emplace_back(bbox->midpoint(), mid_src, -1, mid_tgt, *bbox);
790 bool allow_this_pair_to_snap =
true;
792 if (strict_snapping) {
795 allow_this_pair_to_snap =
false;
799 return allow_this_pair_to_snap;
807 std::vector<std::pair<Geom::Point, bool> >
const origins_and_vectors = p.
getOriginsAndVectors();
809 for (
const auto & origins_and_vector : origins_and_vectors) {
811 if (origins_and_vector.second) {
813 if (
curve->degreesOfFreedom() <= 2) {
826 std::vector<double> ts;
829 if (origins_and_vector.second) {
830 ts = find_tangents_by_vector(origin_or_vector_doc,
curve->toSBasis());
832 ts = find_tangents(origin_or_vector_doc,
curve->toSBasis());
834 for (
double t : ts) {
842 if (origins_and_vector.second) {
843 ts = find_normals_by_vector(origin_or_vector_doc,
curve->toSBasis());
845 ts = find_normals(origin_or_vector_doc,
curve->toSBasis());
847 for (
double t : ts) {
3x3 matrix representing an affine transformation.
Set of all points at a fixed distance from the center.
Abstract continuous curve on a plane defined on [0,1].
Infinite line on a plane.
Axis-aligned rectangle that can be empty.
Store paths to a PathVector.
PathVector const & peek() const
Retrieve the path.
void feed(Path const &other) override
Output a subpath.
void flush() override
Flush any internal state of the generator.
void push_back(Path const &path)
Append a path at the end.
std::vector< PVIntersection > intersect(PathVector const &other, Coord precision=EPSILON) const
Sequence of contiguous curves, aka spline.
const_iterator begin() const
void appendNew(Args &&... args)
Append a new curve to the path.
void start(Point const &p)
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
void constrainedSnap(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, SnapConstraint const &c, std::vector< SPObject const * > const *it, std::vector< SnapCandidatePoint > *unselected_nodes) const override
void _collectNodes(Inkscape::SnapSourceType const &t, bool const &first_point) const
std::unique_ptr< std::vector< SnapCandidatePath > > _paths_to_snap_to
Geom::PathVector _getPathvFromRect(Geom::Rect const rect) const
ObjectSnapper(SnapManager *sm, Geom::Coord const d)
bool ThisSnapperMightSnap() const override
~ObjectSnapper() override
void freeSnap(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector< SPObject const * > const *it, std::vector< SnapCandidatePoint > *unselected_nodes) const override
bool isUnselectedNode(Geom::Point const &point, std::vector< Inkscape::SnapCandidatePoint > const *unselected_nodes) const
bool _allowSourceToSnapToTarget(SnapSourceType source, SnapTargetType target, bool strict_snapping) const
void _snapPathsConstrained(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, SnapConstraint const &c, Geom::Point const &p_proj_on_constraint, std::vector< SnapCandidatePoint > *unselected_nodes, SPPath const *selected_path) const
void _collectPaths(Geom::Point p, Inkscape::SnapSourceType const source_type, bool const &first_point) const
Returns index of first NR_END bpath in array.
void _snapTranslatingGuide(IntermSnapResults &isr, Geom::Point const &p, Geom::Point const &guide_normal) const
bool getSnapperAlwaysSnap(SnapSourceType const &source) const override
void _snapPathsTangPerp(bool snap_tang, bool snap_perp, IntermSnapResults &isr, SnapCandidatePoint const &p, Geom::Curve const *curve, SPDesktop const *dt) const
Geom::Coord getSnapperTolerance() const override
void _snapNodes(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, std::vector< SnapCandidatePoint > *unselected_nodes, SnapConstraint const &c=SnapConstraint(), Geom::Point const &p_proj_on_constraint=Geom::Point()) const
void _snapPaths(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes, SPPath const *selected_path) const
std::unique_ptr< std::vector< SnapCandidatePoint > > _points_to_snap_to
void _clear_paths() const
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
Class to store data for points which are snap candidates, either as a source or as a target.
std::vector< std::pair< Geom::Point, bool > > const & getOriginsAndVectors() const
Inkscape::SnapSourceType getSourceType() const
Geom::Point const & getPoint() const
long getSourceNum() const
Class describing the result of an attempt to snap.
Geom::Coord getSnapDistance() const
Parent for classes that can snap points to something.
Generates the layout for either wrapped or non-wrapped text and stores the result.
std::vector< Geom::LineSegment > getBaselines() const
Returns all the baselines of a text element.
bool outputExists() const
Returns true if there are some glyphs in this object, ie whether computeFlow() has been called on a n...
Wrapper around a Geom::PathVector object.
Geom::PathVector & get_pathvector()
To do: update description of desktop.
double current_zoom() const
Geom::Affine const & dt2doc() const
Geom::Affine const & doc2dt() const
Base class for visual SVG elements.
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Geom::OptRect bounds(BBoxType type, Geom::Affine const &transform=Geom::identity()) const
Geom::OptRect desktopBounds(BBoxType type) const
void getSnappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
SVG <path> implementation.
Class to coordinate snapping operations.
Editable view implementation.
double Coord
Floating point type used to store coordinates.
Point projection(Point const &p, Line const &line)
Angle distance(Angle const &a, Angle const &b)
Affine identity()
Create an identity matrix.
SBasis L2(D2< SBasis > const &a, unsigned k)
Point unit_vector(Point const &a)
D2< T > rot90(D2< T > const &a)
SnapSourceType
enumerations of snap source types and snap target types.
@ SNAPSOURCE_OTHERS_CATEGORY
@ SNAPSOURCE_BBOX_MIDPOINT
@ SNAPSOURCE_DATUMS_CATEGORY
@ SNAPSOURCE_GUIDE_ORIGIN
@ SNAPSOURCE_NODE_CATEGORY
@ SNAPSOURCE_BBOX_EDGE_MIDPOINT
@ SNAPSOURCE_BBOX_CATEGORY
@ SNAPTARGET_BBOX_EDGE_MIDPOINT
@ SNAPTARGET_PAGE_MARGIN_BORDER
@ SNAPTARGET_PAGE_MARGIN_CENTER
@ SNAPTARGET_ROTATION_CENTER
@ SNAPTARGET_PAGE_BLEED_CORNER
@ SNAPTARGET_BBOX_MIDPOINT
@ SNAPTARGET_PAGE_EDGE_CENTER
@ SNAPTARGET_PAGE_EDGE_CORNER
@ SNAPTARGET_NODE_CATEGORY
@ SNAPTARGET_PAGE_EDGE_BORDER
@ SNAPTARGET_TEXT_BASELINE
@ SNAPTARGET_PAGE_MARGIN_CORNER
@ SNAPTARGET_PATH_TANGENTIAL
@ SNAPTARGET_PATH_PERPENDICULAR
@ SNAPTARGET_PATH_INTERSECTION
@ SNAPTARGET_PAGE_BLEED_BORDER
@ SNAPTARGET_BBOX_CATEGORY
void getBBoxPoints(Geom::OptRect const bbox, std::vector< SnapCandidatePoint > *points, bool const isTarget, bool const corners, bool const edges, bool const midpoint)
Default version of the getBBoxPoints with default corner source types.
callback interface for SVG path data
std::optional< SPCurve > curve_for_item(SPItem *item)
Gets an SPCurve from the SPItem.
Singleton class to access the preferences file in a convenient way.
Edges edges(Path const &p, Crossings const &cr, unsigned ix)
TODO: insert short description here.
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SPRoot: SVG <svg> implementation.
std::list< Inkscape::SnappedCurve > curves
std::list< Inkscape::SnappedPoint > points
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)