diff options
Diffstat (limited to 'src/snapped-curve.cpp')
| -rw-r--r-- | src/snapped-curve.cpp | 117 |
1 files changed, 103 insertions, 14 deletions
diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp index 25bde8105..8fdf1d46f 100644 --- a/src/snapped-curve.cpp +++ b/src/snapped-curve.cpp @@ -1,6 +1,6 @@ /** * \file src/snapped-curve.cpp - * \brief SnappedCurve class. + * SnappedCurve class. * * Authors: * Diederik van Lierop <mail@diedenrezi.nl> @@ -20,7 +20,7 @@ Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, int num_p _tolerance = std::max(snapped_tolerance, 1.0); _always_snap = always_snap; _curve = curve; - _second_distance = NR_HUGE; + _second_distance = Geom::infinity(); _second_tolerance = 1; _second_always_snap = false; _point = snapped_point; @@ -36,11 +36,11 @@ Inkscape::SnappedCurve::SnappedCurve() { _num_path = 0; _num_segm = 0; - _distance = NR_HUGE; + _distance = Geom::infinity(); _tolerance = 1; _always_snap = false; _curve = NULL; - _second_distance = NR_HUGE; + _second_distance = Geom::infinity(); _second_tolerance = 1; _second_always_snap = false; _point = Geom::Point(0,0); @@ -61,15 +61,15 @@ Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &cur // Calculate the intersections of two curves, which are both within snapping range, and // return only the closest intersection // The point of intersection should be considered for snapping, but might be outside the snapping range - // PS: We need p (the location of the mouse pointer) for find out which intersection is the + // PS: We need p (the location of the mouse pointer) to find out which intersection is the // closest, as there might be multiple intersections of two curves Geom::Crossings cs = crossings(*(this->_curve), *(curve._curve)); if (cs.size() > 0) { // There might be multiple intersections: find the closest - Geom::Coord best_dist = NR_HUGE; - Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE); - for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) { + Geom::Coord best_dist = Geom::infinity(); + Geom::Point best_p = Geom::Point(Geom::infinity(), Geom::infinity()); + for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); ++i) { Geom::Point p_ix = this->_curve->pointAt((*i).ta); Geom::Coord dist = Geom::distance(p_ix, p); @@ -106,15 +106,70 @@ Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &cur } // No intersection - return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, false, NR_HUGE, 0, false); + return SnappedPoint(Geom::Point(Geom::infinity(), Geom::infinity()), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, Geom::infinity(), 0, false, false, false, false, Geom::infinity(), 0, false); } +Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedLine const &line, Geom::Point const &p, Geom::Affine dt2doc) const +{ + // Calculate the intersections of a curve with a line, which are both within snapping range, and + // return only the closest intersection + // The point of intersection should be considered for snapping, but might be outside the snapping range + // PS: We need p (the location of the mouse pointer) to find out which intersection is the + // closest, as there might be multiple intersections of a single curve with a line + + // 1) get a Geom::Line object from the SnappedLine + // 2) convert to document coordinates (line and p are in desktop coordinates, but the curves are in document coordinate) + // 3) create a Geom::LineSegment (i.e. a curve), because we cannot use a Geom::Line for calculating intersections + // (for this we will create a 2e6 pixels long linesegment, with t running from -1e6 to 1e6; this should be long + // enough for any practical purpose) + Geom::LineSegment line_segm = line.getLine().transformed(dt2doc).segment(-1e6, 1e6); // + const Geom::Curve *line_as_curve = dynamic_cast<Geom::Curve const*>(&line_segm); + Geom::Crossings cs = crossings(*(this->_curve), *line_as_curve); + + if (cs.size() > 0) { + // There might be multiple intersections: find the closest + Geom::Coord best_dist = Geom::infinity(); + Geom::Point best_p = Geom::Point(Geom::infinity(), Geom::infinity()); + for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); ++i) { + Geom::Point p_ix = this->_curve->pointAt((*i).ta); + Geom::Coord dist = Geom::distance(p_ix, p); + + if (dist < best_dist) { + best_dist = dist; + best_p = p_ix; + } + } + + // The intersection should in fact be returned in desktop coordinates + best_p = best_p * dt2doc; + + // Now we've found the closest intersection, return it as a SnappedPoint + if (_distance < line.getSnapDistance()) { + // curve is the closest, so this is our primary snap target + return SnappedPoint(best_p, Inkscape::SNAPSOURCE_UNDEFINED, this->getSourceNum(), Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION, + Geom::L2(best_p - this->getPoint()), this->getTolerance(), this->getAlwaysSnap(), true, false, true, + Geom::L2(best_p - line.getPoint()), line.getTolerance(), line.getAlwaysSnap()); + } else { + return SnappedPoint(best_p, Inkscape::SNAPSOURCE_UNDEFINED, line.getSourceNum(), Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION, + Geom::L2(best_p - line.getPoint()), line.getTolerance(), line.getAlwaysSnap(), true, false, true, + Geom::L2(best_p - this->getPoint()), this->getTolerance(), this->getAlwaysSnap()); + } + } + + // No intersection + return SnappedPoint(Geom::Point(Geom::infinity(), Geom::infinity()), SNAPSOURCE_UNDEFINED, 0, SNAPTARGET_UNDEFINED, Geom::infinity(), 0, false, false, false, false, Geom::infinity(), 0, false); +} + + // search for the closest snapped line -bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) +bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result, bool exclude_paths) { bool success = false; - for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) { + for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); ++i) { + if (exclude_paths && ((*i).getTarget() == Inkscape::SNAPTARGET_PATH)) { + continue; + } if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) { result = *i; success = true; @@ -129,12 +184,45 @@ bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geo { bool success = false; - for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) { + for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); ++i) { if ((*i).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes, // as this would require two bboxes two be flashed in the snap indicator std::list<Inkscape::SnappedCurve>::const_iterator j = i; - j++; - for (; j != list.end(); j++) { + ++j; + for (; j != list.end(); ++j) { + if ((*j).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes + Inkscape::SnappedPoint sp = (*i).intersect(*j, p, dt2doc); + if (sp.getAtIntersection()) { + // if it's the first point + bool const c1 = !success; + // or, if it's closer + bool const c2 = sp.getSnapDistance() < result.getSnapDistance(); + // or, if it's just as close then look at the other distance + // (only relevant for snapped points which are at an intersection) + bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); + // then prefer this point over the previous one + if (c1 || c2 || c3) { + result = sp; + success = true; + } + } + } + } + } + } + + return success; +} + +// search for the closest intersection of two snapped curves, which are member of two different collections +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 success = false; + + for (std::list<Inkscape::SnappedCurve>::const_iterator i = curve_list.begin(); i != curve_list.end(); ++i) { + if ((*i).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes, + // as this would require two bboxes two be flashed in the snap indicator + for (std::list<Inkscape::SnappedLine>::const_iterator j = line_list.begin(); j != line_list.end(); ++j) { if ((*j).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes Inkscape::SnappedPoint sp = (*i).intersect(*j, p, dt2doc); if (sp.getAtIntersection()) { @@ -158,6 +246,7 @@ bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geo return success; } + /* Local Variables: mode:c++ |
