summaryrefslogtreecommitdiffstats
path: root/src/snapped-curve.cpp
blob: dfed84531d807e3bb23736d4f6a5bf24333dedb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/**
 *    \file src/snapped-curve.cpp
 *    \brief SnappedCurve class.
 *
 *    Authors:
 *      Diederik van Lierop <mail@diedenrezi.nl>
 *
 *    Released under GNU GPL, read the file 'COPYING' for more information.
 */

#include "snapped-curve.h"
#include "libnr/nr-values.h"
#include <2geom/crossing.h>
#include <2geom/path-intersection.h>
#include <libnr/nr-convert2geom.h>

// These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack
#include "desktop.h"
#include "inkscape.h"

Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve)
{
	_distance = snapped_distance;
    _tolerance = snapped_tolerance;
    _always_snap = always_snap;
    _curve = curve;
	_second_distance = NR_HUGE;
    _second_tolerance = 0;
    _second_always_snap = false;
	_point = snapped_point;
	_at_intersection = false;
}

Inkscape::SnappedCurve::SnappedCurve() 
{
	_distance = NR_HUGE;
    _tolerance = 0;
    _always_snap = false;
    _curve = NULL;
	_second_distance = NR_HUGE;
    _second_tolerance = 0;
    _second_always_snap = false;
	_point = Geom::Point(0,0);
	_at_intersection = false;
}

Inkscape::SnappedCurve::~SnappedCurve()
{
}

Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, Geom::Point const &p) const 
{
    // 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
    // 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 (std::vector<Geom::Crossing>::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;
            }
        }
        
        // Now we've found the closests intersection, return it as a SnappedPoint
        bool const use_this_as_primary = _distance < curve.getDistance();
        Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;
        Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;
        // The intersection should in fact be returned in desktop coordinates, but for this
        // we need a desktop: this is a dirty hack
        SPDesktop const *desktop = SP_ACTIVE_DESKTOP;
        best_p = desktop->dt2doc(best_p);
        // TODO: Investigate whether it is possible to use document coordinates everywhere
        // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.
        // All paths are already in document coords and we are certainly not going to change THAT.
        return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, 
                                          secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());
    }
    
    // No intersection
    return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
}

// search for the closest snapped line
bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) 
{
    bool success = false;
    
    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {
        if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {
            result = *i;
            success = true;
        }   
    }
    
    return success; 
}

// search for the closest intersection of two snapped curves, which are both member of the same collection
bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geom::Point const &p, Inkscape::SnappedPoint &result)
{
    bool success = false;
    
    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {
        std::list<Inkscape::SnappedCurve>::const_iterator j = i;
        j++;
        for (; j != list.end(); j++) {
            Inkscape::SnappedPoint sp = (*i).intersect(*j, p);
            if (sp.getAtIntersection()) {
                // if it's the first point
                bool const c1 = !success;
                // or, if it's closer             
                bool const c2 = sp.getDistance() < result.getDistance();
                // or, if it's just then look at the other distance 
                // (only relevant for snapped points which are at an intersection
                bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); 
                // then prefer this point over the previous one
                if (c1 || c2 || c3) {  
                    result = sp;
                    success = true;
                }
            }               
        }
    }
    
    return success; 
}
/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :