summaryrefslogtreecommitdiffstats
path: root/src/ui/tool/node.h
blob: 0194f5053176936f7ee0ef62543cb02fa32d2403 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/** @file
 * Editable node and associated data structures.
 */
/* Authors:
 *   Krzysztof Kosiński <tweenk.pl@gmail.com>
 *
 * Copyright (C) 2009 Authors
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#ifndef SEEN_UI_TOOL_NODE_H
#define SEEN_UI_TOOL_NODE_H

#include <glib.h>
#include <iterator>
#include <iosfwd>
#include <stdexcept>
#include <tr1/functional>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
#include <boost/operators.hpp>
#include "snapped-point.h"
#include "ui/tool/selectable-control-point.h"
#include "ui/tool/node-types.h"


namespace Inkscape {
namespace UI {
template <typename> class NodeIterator;
}
}

namespace std {
namespace tr1 {
template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >;
}
}

namespace Inkscape {
namespace UI {

class PathManipulator;
class MultiPathManipulator;

class Node;
class Handle;
class NodeList;
class SubpathList;
template <typename> class NodeIterator;

std::ostream &operator<<(std::ostream &, NodeType);

/*
template <typename T>
struct ListMember {
    T *next;
    T *prev;
};
struct SubpathMember : public ListMember<NodeListMember> {
    Subpath *list;
};
struct SubpathListMember : public ListMember<SubpathListMember> {
    SubpathList *list;
};
*/

struct ListNode {
    ListNode *ln_next;
    ListNode *ln_prev;
    NodeList *ln_list;
};

struct NodeSharedData {
    SPDesktop *desktop;
    ControlPointSelection *selection;
    SPCanvasGroup *node_group;
    SPCanvasGroup *handle_group;
    SPCanvasGroup *handle_line_group;
};

class Handle : public ControlPoint {
public:
    virtual ~Handle();
    inline Geom::Point relativePos();
    inline double length();
    bool isDegenerate() { return _degenerate; }

    virtual void setVisible(bool);
    virtual void move(Geom::Point const &p);

    virtual void setPosition(Geom::Point const &p);
    inline void setRelativePos(Geom::Point const &p);
    void setLength(double len);
    void retract();
    void setDirection(Geom::Point const &from, Geom::Point const &to);
    void setDirection(Geom::Point const &dir);
    Node *parent() { return _parent; }
    Handle *other();

    static char const *handle_type_to_localized_string(NodeType type);
protected:
    Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent);

    virtual void dragged(Geom::Point &, GdkEventMotion *);
    virtual bool grabbed(GdkEventMotion *);
    virtual void ungrabbed(GdkEventButton *);
    virtual bool clicked(GdkEventButton *);

    virtual Glib::ustring _getTip(unsigned state);
    virtual Glib::ustring _getDragTip(GdkEventMotion *event);
    virtual bool _hasDragTips() { return true; }
private:
    inline PathManipulator &_pm();
    Node *_parent; // the handle's lifetime does not extend beyond that of the parent node,
    // so a naked pointer is OK and allows setting it during Node's construction
    SPCanvasItem *_handle_line;
    bool _degenerate; // this is used often internally so it makes sense to cache this

    static Geom::Point _saved_other_pos;
    static double _saved_length;
    static bool _drag_out;
    friend class Node;
};

class Node : ListNode, public SelectableControlPoint {
public:
    Node(NodeSharedData const &data, Geom::Point const &pos);
    virtual void move(Geom::Point const &p);
    virtual void transform(Geom::Matrix const &m);
    virtual Geom::Rect bounds();

    NodeType type() { return _type; }
    void setType(NodeType type, bool update_handles = true);
    void showHandles(bool v);
    void pickBestType(); // automatically determine the type from handle positions
    bool isDegenerate() { return _front.isDegenerate() && _back.isDegenerate(); }
    bool isEndNode();
    Handle *front() { return &_front; }
    Handle *back()  { return &_back;  }
    Handle *handleToward(Node *to);
    Node *nodeToward(Handle *h);
    Handle *handleAwayFrom(Node *to);
    Node *nodeAwayFrom(Handle *h);
    NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); }
    void sink();

    static NodeType parse_nodetype(char x);
    static char const *node_type_to_localized_string(NodeType type);
    // temporarily public
    virtual bool _eventHandler(GdkEvent *event);
protected:
    virtual void dragged(Geom::Point &, GdkEventMotion *);
    virtual bool grabbed(GdkEventMotion *);
    virtual bool clicked(GdkEventButton *);

    virtual void _setState(State state);
    virtual Glib::ustring _getTip(unsigned state);
    virtual Glib::ustring _getDragTip(GdkEventMotion *event);
    virtual bool _hasDragTips() { return true; }
private:
    Node(Node const &);
    void _fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos);
    void _updateAutoHandles();
    void _linearGrow(int dir);
    Node *_next();
    Node *_prev();
    Inkscape::SnapSourceType _snapSourceType();
    Inkscape::SnapTargetType _snapTargetType();
    inline PathManipulator &_pm();
    static SPCtrlShapeType _node_type_to_shape(NodeType type);
    static bool _is_line_segment(Node *first, Node *second);

    // Handles are always present, but are not visible if they coincide with the node
    // (are degenerate). A segment that has both handles degenerate is always treated
    // as a line segment
    Handle _front; ///< Node handle in the backward direction of the path
    Handle _back; ///< Node handle in the forward direction of the path
    NodeType _type; ///< Type of node - cusp, smooth...
    bool _handles_shown;
    friend class Handle;
    friend class NodeList;
    friend class NodeIterator<Node>;
    friend class NodeIterator<Node const>;
};

/// Iterator for editable nodes
/** Use this class for all operations that require some knowledge about the node's
 * neighbors. It is a bidirectional iterator.
 *
 * Because paths can be cyclic, node iterators have two different ways to
 * increment and decrement them. When using ++/--, the end iterator will eventually
 * be returned. Whent using advance()/retreat(), the end iterator will only be returned
 * when the path is open. If it's closed, calling advance() will cycle indefinitely.
 * This is particularly useful for cases where the adjacency of nodes is more important
 * than their sequence order.
 *
 * When @a i is a node iterator, then:
 * - <code>++i</code> moves the iterator to the next node in sequence order;
 * - <code>--i</code> moves the iterator to the previous node in sequence order;
 * - <code>i.next()</code> returns the next node with wrap-around;
 * - <code>i.prev()</code> returns the previous node with wrap-around;
 * - <code>i.advance()</code> moves the iterator to the next node with wrap-around;
 * - <code>i.retreat()</code> moves the iterator to the previous node with wrap-around.
 *
 * next() and prev() do not change their iterator. They can return the end iterator
 * if the path is open.
 *
 * Unlike most other iterators, you can check whether you've reached the end of the list
 * without having access to the iterator's container.
 * Simply use <code>if (i) { ...</code>
 * */
template <typename N>
class NodeIterator
    : public boost::bidirectional_iterator_helper<NodeIterator<N>, N, std::ptrdiff_t,
        N *, N &>
{
public:
    typedef NodeIterator self;
    NodeIterator()
        : _node(0)
    {}
    // default copy, default assign

    self &operator++() {
        _node = _node->ln_next;
        return *this;
    }
    self &operator--() {
        _node = _node->ln_prev;
        return *this;
    }
    bool operator==(self const &other) const { return _node == other._node; }
    N &operator*() const { return *static_cast<N*>(_node); }
    inline operator bool() const; // define after NodeList
    /// Get a pointer to the underlying node. Equivalent to <code>&*i</code>.
    N *get_pointer() const { return static_cast<N*>(_node); }
    /// @see get_pointer()
    N *ptr() const { return static_cast<N*>(_node); }

    self next() const {
        self r(*this);
        r.advance();
        return r;
    }
    self prev() const {
        self r(*this);
        r.retreat();
        return r;
    }
    self &advance();
    self &retreat();
private:
    NodeIterator(ListNode const *n)
        : _node(const_cast<ListNode*>(n))
    {}
    ListNode *_node;
    friend class NodeList;
};

class NodeList : ListNode, boost::noncopyable, public boost::enable_shared_from_this<NodeList> {
public:
    typedef std::size_t size_type;
    typedef Node &reference;
    typedef Node const &const_reference;
    typedef Node *pointer;
    typedef Node const *const_pointer;
    typedef Node value_type;
    typedef NodeIterator<value_type> iterator;
    typedef NodeIterator<value_type const> const_iterator;

    // TODO Lame. Make this private and make SubpathList a factory
    NodeList(SubpathList &_list);
    ~NodeList();

    // iterators
    iterator begin() { return iterator(ln_next); }
    iterator end() { return iterator(this); }
    const_iterator begin() const { return const_iterator(ln_next); }
    const_iterator end() const { return const_iterator(this); }

    // size
    bool empty();
    size_type size();

    // extra node-specific methods
    bool closed();
    bool degenerate();
    void setClosed(bool c) { _closed = c; }
    iterator before(double t, double *fracpart = NULL);
    const_iterator before(double t, double *fracpart = NULL) const {
        return const_iterator(before(t, fracpart)._node);
    }

    // list operations
    iterator insert(iterator pos, Node *x);
    template <class InputIterator>
    void insert(iterator pos, InputIterator first, InputIterator last) {
        for (; first != last; ++first) insert(pos, *first);
    }
    void splice(iterator pos, NodeList &list);
    void splice(iterator pos, NodeList &list, iterator i);
    void splice(iterator pos, NodeList &list, iterator first, iterator last);
    void reverse();
    void shift(int n);
    void push_front(Node *x) { insert(begin(), x); }
    void pop_front() { erase(begin()); }
    void push_back(Node *x) { insert(end(), x); }
    void pop_back() { erase(--end()); }
    void clear();
    iterator erase(iterator pos);
    iterator erase(iterator first, iterator last) {
        NodeList::iterator ret = first;
        while (first != last) ret = erase(first++);
        return ret;
    }

    // member access - undefined results when the list is empty
    Node &front() { return *static_cast<Node*>(ln_next); }
    Node &back() { return *static_cast<Node*>(ln_prev); }

    // HACK remove this subpath from its path. This will be removed later.
    void kill();
    SubpathList &subpathList() { return _list; }

    static iterator get_iterator(Node *n) { return iterator(n); }
    static const_iterator get_iterator(Node const *n) { return const_iterator(n); }
    static NodeList &get(Node *n);
    static NodeList &get(iterator const &i);
private:
    // no copy or assign
    NodeList(NodeList const &);
    void operator=(NodeList const &);

    SubpathList &_list;
    bool _closed;

    friend class Node;
    friend class Handle; // required to access handle and handle line groups
    friend class NodeIterator<Node>;
    friend class NodeIterator<Node const>;
};

/** List of node lists. Represents an editable path. */
class SubpathList : public std::list< boost::shared_ptr<NodeList> > {
public:
    typedef std::list< boost::shared_ptr<NodeList> > list_type;

    SubpathList(PathManipulator &pm) : _path_manipulator(pm) {}
    PathManipulator &pm() { return _path_manipulator; }

private:
    list_type _nodelists;
    PathManipulator &_path_manipulator;
    friend class NodeList;
    friend class Node;
    friend class Handle;
};



// define inline Handle funcs after definition of Node
inline Geom::Point Handle::relativePos() {
    return position() - _parent->position();
}
inline void Handle::setRelativePos(Geom::Point const &p) {
    setPosition(_parent->position() + p);
}
inline double Handle::length() {
    return relativePos().length();
}
inline PathManipulator &Handle::_pm() {
    return _parent->_pm();
}
inline PathManipulator &Node::_pm() {
    return nodeList().subpathList().pm();
}

// definitions for node iterator
template <typename N>
NodeIterator<N>::operator bool() const {
    return _node && static_cast<ListNode*>(_node->ln_list) != _node;
}
template <typename N>
NodeIterator<N> &NodeIterator<N>::advance() {
    ++(*this);
    if (G_UNLIKELY(!*this) && _node->ln_list->closed()) ++(*this);
    return *this;
}
template <typename N>
NodeIterator<N> &NodeIterator<N>::retreat() {
    --(*this);
    if (G_UNLIKELY(!*this) && _node->ln_list->closed()) --(*this);
    return *this;
}

} // namespace UI
} // namespace Inkscape

#endif

/*
  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:fileencoding=utf-8:textwidth=99 :