Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
snapped-curve.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
12#include "snapped-curve.h"
14
15Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Point const &tangent, int num_path, int num_segm, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox)
16{
17 _num_path = num_path;
18 _num_segm = num_segm;
19 _distance = snapped_distance;
20 _tolerance = std::max(snapped_tolerance, 1.0);
21 _always_snap = always_snap;
22 _curve = curve;
25 _second_always_snap = false;
26 _point = snapped_point;
27 _tangent = tangent;
28 _at_intersection = false;
29 _fully_constrained = fully_constrained;
30 _source = source;
31 _source_num = source_num;
32 _target = target;
33 _target_bbox = target_bbox;
34}
35
37{
38 _num_path = 0;
39 _num_segm = 0;
40 _distance = Geom::infinity();
41 _tolerance = 1;
42 _always_snap = false;
43 _curve = nullptr;
44 _second_distance = Geom::infinity();
45 _second_tolerance = 1;
46 _second_always_snap = false;
47 _point = Geom::Point(0,0);
48 _tangent = Geom::Point(0,0);
49 _at_intersection = false;
50 _fully_constrained = false;
51 _source = SNAPSOURCE_UNDEFINED;
52 _source_num = -1;
53 _target = SNAPTARGET_UNDEFINED;
54 _target_bbox = Geom::OptRect();
55}
56
58= default;
59
61{
62 // Calculate the intersections of two curves, which are both within snapping range, and
63 // return only the closest intersection
64 // The point of intersection should be considered for snapping, but might be outside the snapping range
65 // PS: We need p (the location of the mouse pointer) to find out which intersection is the
66 // closest, as there might be multiple intersections of two curves
67 Geom::Crossings cs = crossings(*(this->_curve), *(curve._curve));
68
69 if (cs.size() > 0) {
70 // There might be multiple intersections: find the closest
71 Geom::Coord best_dist = Geom::infinity();
73 for (const auto & c : cs) {
74 Geom::Point p_ix = this->_curve->pointAt(c.ta);
75 Geom::Coord dist = Geom::distance(p_ix, p);
76
77 // Test if we have two segments (curves) from the same path..
78 if (this->_num_path == curve._num_path) {
79 // Never try to intersect a segment with itself
80 if (this->_num_segm == curve._num_segm) continue;
81 // Two subsequent segments (curves) in a path will have a common node; this node is not considered to be an intersection
82 if (this->_num_segm == curve._num_segm + 1 && c.ta == 0 && c.tb == 1) continue;
83 if (this->_num_segm + 1 == curve._num_segm && c.ta == 1 && c.tb == 0) continue;
84 }
85
86 if (dist < best_dist) {
87 best_dist = dist;
88 best_p = p_ix;
89 }
90 }
91
92 // Now we've found the closest intersection, return it as a SnappedPoint
93 bool const use_this_as_primary = _distance < curve.getSnapDistance();
94 Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;
95 Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;
96
97 // The intersection should in fact be returned in desktop coordinates
98 best_p = best_p * dt2doc;
99
100 Geom::Coord primaryDist = use_this_as_primary ? Geom::L2(best_p - this->getPoint()) : Geom::L2(best_p - curve.getPoint());
101 Geom::Coord secondaryDist = use_this_as_primary ? Geom::L2(best_p - curve.getPoint()) : Geom::L2(best_p - this->getPoint());
102 // TODO: Investigate whether it is possible to use document coordinates everywhere
103 // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.
104 // All paths are already in document coords and we are certainly not going to change THAT.
105 return SnappedPoint(best_p, Inkscape::SNAPSOURCE_UNDEFINED, primaryC->getSourceNum(), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryDist, primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, false, true,
106 secondaryDist, secondaryC->getTolerance(), secondaryC->getAlwaysSnap());
107 }
108
109 // No intersection
111}
112
114{
115 // Calculate the intersections of a curve with a line, which are both within snapping range, and
116 // return only the closest intersection
117 // The point of intersection should be considered for snapping, but might be outside the snapping range
118 // PS: We need p (the location of the mouse pointer) to find out which intersection is the
119 // closest, as there might be multiple intersections of a single curve with a line
120
121 // 1) get a Geom::Line object from the SnappedLine
122 // 2) convert to document coordinates (line and p are in desktop coordinates, but the curves are in document coordinate)
123 // 3) create a Geom::LineSegment (i.e. a curve), because we cannot use a Geom::Line for calculating intersections
124 // (for this we will create a 2e6 pixels long linesegment, with t running from -1e6 to 1e6; this should be long
125 // enough for any practical purpose)
126 Geom::LineSegment line_segm = line.getLine().transformed(dt2doc).segment(-1e6, 1e6); //
127 const Geom::Curve *line_as_curve = dynamic_cast<Geom::Curve const*>(&line_segm);
128 Geom::Crossings cs = crossings(*(this->_curve), *line_as_curve);
129
130 if (cs.size() > 0) {
131 // There might be multiple intersections: find the closest
132 Geom::Coord best_dist = Geom::infinity();
134 for (const auto & c : cs) {
135 Geom::Point p_ix = this->_curve->pointAt(c.ta);
136 Geom::Coord dist = Geom::distance(p_ix, p);
137
138 if (dist < best_dist) {
139 best_dist = dist;
140 best_p = p_ix;
141 }
142 }
143
144 // The intersection should in fact be returned in desktop coordinates
145 best_p = best_p * dt2doc;
146
147 // Now we've found the closest intersection, return it as a SnappedPoint
148 if (_distance < line.getSnapDistance()) {
149 // curve is the closest, so this is our primary snap target
151 Geom::L2(best_p - this->getPoint()), this->getTolerance(), this->getAlwaysSnap(), true, false, true,
152 Geom::L2(best_p - line.getPoint()), line.getTolerance(), line.getAlwaysSnap());
153 } else {
155 Geom::L2(best_p - line.getPoint()), line.getTolerance(), line.getAlwaysSnap(), true, false, true,
156 Geom::L2(best_p - this->getPoint()), this->getTolerance(), this->getAlwaysSnap());
157 }
158 }
159
160 // No intersection
162}
163
164
165// search for the closest snapped line
166bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result, bool exclude_paths, bool paths_only)
167{
168 bool success = false;
169
170 for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); ++i) {
171 if (exclude_paths && ((*i).getTarget() == Inkscape::SNAPTARGET_PATH)) {
172 continue;
173 }
174 if (paths_only && (not (*i).getOnPath())) {
175 // This discards for example bounding box edges, page border, and text baseline
176 continue;
177 }
178 if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) {
179 result = *i;
180 success = true;
181 }
182 }
183
184 return success;
185}
186
187// search for the closest intersection of two snapped curves, which are both member of the same collection
188bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geom::Point const &p, Inkscape::SnappedPoint &result, Geom::Affine dt2doc)
189{
190 bool success = false;
191
192 for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); ++i) {
193 if ((*i).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes,
194 // as this would require two bboxes two be flashed in the snap indicator
195 std::list<Inkscape::SnappedCurve>::const_iterator j = i;
196 ++j;
197 for (; j != list.end(); ++j) {
198 if ((*j).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes
199 Inkscape::SnappedPoint sp = (*i).intersect(*j, p, dt2doc);
200 if (sp.getAtIntersection()) {
201 // if it's the first point
202 bool const c1 = !success;
203 // or, if it's closer
204 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
205 // or, if it's just as close then look at the other distance
206 // (only relevant for snapped points which are at an intersection)
207 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance());
208 // then prefer this point over the previous one
209 if (c1 || c2 || c3) {
210 result = sp;
211 success = true;
212 }
213 }
214 }
215 }
216 }
217 }
218
219 return success;
220}
221
222// search for the closest intersection of two snapped curves, which are member of two different collections
223bool getClosestIntersectionCL(std::list<Inkscape::SnappedCurve> const &curve_list, std::list<Inkscape::SnappedLine> const &line_list, Geom::Point const &p, Inkscape::SnappedPoint &result, Geom::Affine dt2doc)
224{
225 bool success = false;
226
227 for (const auto & i : curve_list) {
228 if (i.getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes,
229 // as this would require two bboxes two be flashed in the snap indicator
230 for (const auto & j : line_list) {
231 if (j.getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes
232 Inkscape::SnappedPoint sp = i.intersect(j, p, dt2doc);
233 if (sp.getAtIntersection()) {
234 // if it's the first point
235 bool const c1 = !success;
236 // or, if it's closer
237 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
238 // or, if it's just as close then look at the other distance
239 // (only relevant for snapped points which are at an intersection)
240 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance());
241 // then prefer this point over the previous one
242 if (c1 || c2 || c3) {
243 result = sp;
244 success = true;
245 }
246 }
247 }
248 }
249 }
250 }
251
252 return success;
253}
254
255/*
256 Local Variables:
257 mode:c++
258 c-file-style:"stroustrup"
259 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
260 indent-tabs-mode:nil
261 fill-column:99
262 End:
263*/
264// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
3x3 matrix representing an affine transformation.
Definition affine.h:70
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
Line transformed(Affine const &m) const
Create a line transformed by an affine transformation.
Definition line.h:312
LineSegment segment(Coord f, Coord t) const
Create a segment of this line.
Definition line.h:283
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Class describing the result of an attempt to snap to a curve.
Inkscape::SnappedPoint intersect(SnappedCurve const &curve, Geom::Point const &p, Geom::Affine dt2doc) const
Geom::Curve const * _curve
Class describing the result of an attempt to snap to a line.
Geom::Line getLine() const
Class describing the result of an attempt to snap.
Geom::OptRect _target_bbox
Geom::Point getPoint() const
Geom::Coord getTolerance() const
bool getAlwaysSnap() const
Geom::Coord getSnapDistance() const
bool getAtIntersection() const
Geom::Coord getSecondSnapDistance() const
Geom::Coord _second_tolerance
Css & result
double c[8][4]
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
std::vector< Crossing > Crossings
Definition crossing.h:126
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
SnapSourceType
enumerations of snap source types and snap target types.
Definition snap-enums.h:18
@ SNAPSOURCE_UNDEFINED
Definition snap-enums.h:19
@ SNAPTARGET_UNDEFINED
Definition snap-enums.h:71
@ SNAPTARGET_PATH_GUIDE_INTERSECTION
Definition snap-enums.h:89
@ SNAPTARGET_PATH
Definition snap-enums.h:85
@ SNAPTARGET_BBOX_EDGE
Definition snap-enums.h:77
@ SNAPTARGET_PATH_INTERSECTION
Definition snap-enums.h:88
Path intersection.
bool getClosestIntersectionCS(std::list< Inkscape::SnappedCurve > const &list, Geom::Point const &p, Inkscape::SnappedPoint &result, Geom::Affine dt2doc)
bool getClosestIntersectionCL(std::list< Inkscape::SnappedCurve > const &curve_list, std::list< Inkscape::SnappedLine > const &line_list, Geom::Point const &p, Inkscape::SnappedPoint &result, Geom::Affine dt2doc)
bool getClosestCurve(std::list< Inkscape::SnappedCurve > const &list, Inkscape::SnappedCurve &result, bool exclude_paths, bool paths_only)
SnappedCurve class.
Definition curve.h:24