Code

Node tool: fix snapping of node rotation center
[inkscape.git] / src / ui / tool / node.h
1 /** @file
2  * Editable node and associated data structures.
3  */
4 /* Authors:
5  *   Krzysztof KosiƄski <tweenk.pl@gmail.com>
6  *
7  * Copyright (C) 2009 Authors
8  * Released under GNU GPL, read the file 'COPYING' for more information
9  */
11 #ifndef SEEN_UI_TOOL_NODE_H
12 #define SEEN_UI_TOOL_NODE_H
14 #include <glib.h>
15 #include <iterator>
16 #include <iosfwd>
17 #include <stdexcept>
18 #include <tr1/functional>
19 #include <boost/utility.hpp>
20 #include <boost/shared_ptr.hpp>
21 #include <boost/optional.hpp>
22 #include <boost/operators.hpp>
23 #include "snapped-point.h"
24 #include "ui/tool/selectable-control-point.h"
25 #include "ui/tool/node-types.h"
28 namespace Inkscape {
29 namespace UI {
30 template <typename> class NodeIterator;
31 }
32 }
34 namespace std {
35 namespace tr1 {
36 template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >;
37 }
38 }
40 namespace Inkscape {
41 namespace UI {
43 class PathManipulator;
44 class MultiPathManipulator;
46 class Node;
47 class Handle;
48 class NodeList;
49 class SubpathList;
50 template <typename> class NodeIterator;
52 std::ostream &operator<<(std::ostream &, NodeType);
54 /*
55 template <typename T>
56 struct ListMember {
57     T *next;
58     T *prev;
59 };
60 struct SubpathMember : public ListMember<NodeListMember> {
61     Subpath *list;
62 };
63 struct SubpathListMember : public ListMember<SubpathListMember> {
64     SubpathList *list;
65 };
66 */
68 struct ListNode {
69     ListNode *ln_next;
70     ListNode *ln_prev;
71     NodeList *ln_list;
72 };
74 struct NodeSharedData {
75     SPDesktop *desktop;
76     ControlPointSelection *selection;
77     SPCanvasGroup *node_group;
78     SPCanvasGroup *handle_group;
79     SPCanvasGroup *handle_line_group;
80 };
82 class Handle : public ControlPoint {
83 public:
84     virtual ~Handle();
85     inline Geom::Point relativePos();
86     inline double length();
87     bool isDegenerate() { return _degenerate; }
89     virtual void setVisible(bool);
90     virtual void move(Geom::Point const &p);
92     virtual void setPosition(Geom::Point const &p);
93     inline void setRelativePos(Geom::Point const &p);
94     void setLength(double len);
95     void retract();
96     void setDirection(Geom::Point const &from, Geom::Point const &to);
97     void setDirection(Geom::Point const &dir);
98     Node *parent() { return _parent; }
99     Handle *other();
101     static char const *handle_type_to_localized_string(NodeType type);
102 protected:
103     Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent);
105     virtual bool _eventHandler(GdkEvent *event);
106     virtual void dragged(Geom::Point &, GdkEventMotion *);
107     virtual bool grabbed(GdkEventMotion *);
108     virtual void ungrabbed(GdkEventButton *);
109     virtual bool clicked(GdkEventButton *);
111     virtual Glib::ustring _getTip(unsigned state);
112     virtual Glib::ustring _getDragTip(GdkEventMotion *event);
113     virtual bool _hasDragTips() { return true; }
114 private:
115     inline PathManipulator &_pm();
116     Node *_parent; // the handle's lifetime does not extend beyond that of the parent node,
117     // so a naked pointer is OK and allows setting it during Node's construction
118     SPCanvasItem *_handle_line;
119     bool _degenerate; // this is used often internally so it makes sense to cache this
121     static Geom::Point _saved_other_pos;
122     static double _saved_length;
123     static bool _drag_out;
124     friend class Node;
125 };
127 class Node : ListNode, public SelectableControlPoint {
128 public:
129     Node(NodeSharedData const &data, Geom::Point const &pos);
130     virtual void move(Geom::Point const &p);
131     virtual void transform(Geom::Matrix const &m);
132     virtual Geom::Rect bounds();
134     NodeType type() { return _type; }
135     void setType(NodeType type, bool update_handles = true);
136     void showHandles(bool v);
137     void pickBestType(); // automatically determine the type from handle positions
138     bool isDegenerate() { return _front.isDegenerate() && _back.isDegenerate(); }
139     bool isEndNode();
140     Handle *front() { return &_front; }
141     Handle *back()  { return &_back;  }
142     Handle *handleToward(Node *to);
143     Node *nodeToward(Handle *h);
144     Handle *handleAwayFrom(Node *to);
145     Node *nodeAwayFrom(Handle *h);
146     NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); }
147     void sink();
149     static NodeType parse_nodetype(char x);
150     static char const *node_type_to_localized_string(NodeType type);
151     // temporarily public
152     virtual bool _eventHandler(GdkEvent *event);
153 protected:
154     virtual void dragged(Geom::Point &, GdkEventMotion *);
155     virtual bool grabbed(GdkEventMotion *);
156     virtual bool clicked(GdkEventButton *);
158     virtual void _setState(State state);
159     virtual Glib::ustring _getTip(unsigned state);
160     virtual Glib::ustring _getDragTip(GdkEventMotion *event);
161     virtual bool _hasDragTips() { return true; }
162 private:
163     Node(Node const &);
164     void _fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos);
165     void _updateAutoHandles();
166     void _linearGrow(int dir);
167     Node *_next();
168     Node *_prev();
169     Inkscape::SnapSourceType _snapSourceType();
170     Inkscape::SnapTargetType _snapTargetType();
171     inline PathManipulator &_pm();
172     static SPCtrlShapeType _node_type_to_shape(NodeType type);
173     static bool _is_line_segment(Node *first, Node *second);
175     // Handles are always present, but are not visible if they coincide with the node
176     // (are degenerate). A segment that has both handles degenerate is always treated
177     // as a line segment
178     Handle _front; ///< Node handle in the backward direction of the path
179     Handle _back; ///< Node handle in the forward direction of the path
180     NodeType _type; ///< Type of node - cusp, smooth...
181     bool _handles_shown;
182     friend class Handle;
183     friend class NodeList;
184     friend class NodeIterator<Node>;
185     friend class NodeIterator<Node const>;
186 };
188 /// Iterator for editable nodes
189 /** Use this class for all operations that require some knowledge about the node's
190  * neighbors. It is a bidirectional iterator.
191  *
192  * Because paths can be cyclic, node iterators have two different ways to
193  * increment and decrement them. When using ++/--, the end iterator will eventually
194  * be returned. Whent using advance()/retreat(), the end iterator will only be returned
195  * when the path is open. If it's closed, calling advance() will cycle indefinitely.
196  * This is particularly useful for cases where the adjacency of nodes is more important
197  * than their sequence order.
198  *
199  * When @a i is a node iterator, then:
200  * - <code>++i</code> moves the iterator to the next node in sequence order;
201  * - <code>--i</code> moves the iterator to the previous node in sequence order;
202  * - <code>i.next()</code> returns the next node with wrap-around;
203  * - <code>i.prev()</code> returns the previous node with wrap-around;
204  * - <code>i.advance()</code> moves the iterator to the next node with wrap-around;
205  * - <code>i.retreat()</code> moves the iterator to the previous node with wrap-around.
206  *
207  * next() and prev() do not change their iterator. They can return the end iterator
208  * if the path is open.
209  *
210  * Unlike most other iterators, you can check whether you've reached the end of the list
211  * without having access to the iterator's container.
212  * Simply use <code>if (i) { ...</code>
213  * */
214 template <typename N>
215 class NodeIterator
216     : public boost::bidirectional_iterator_helper<NodeIterator<N>, N, std::ptrdiff_t,
217         N *, N &>
219 public:
220     typedef NodeIterator self;
221     NodeIterator()
222         : _node(0)
223     {}
224     // default copy, default assign
226     self &operator++() {
227         _node = _node->ln_next;
228         return *this;
229     }
230     self &operator--() {
231         _node = _node->ln_prev;
232         return *this;
233     }
234     bool operator==(self const &other) const { return _node == other._node; }
235     N &operator*() const { return *static_cast<N*>(_node); }
236     inline operator bool() const; // define after NodeList
237     /// Get a pointer to the underlying node. Equivalent to <code>&*i</code>.
238     N *get_pointer() const { return static_cast<N*>(_node); }
239     /// @see get_pointer()
240     N *ptr() const { return static_cast<N*>(_node); }
242     self next() const {
243         self r(*this);
244         r.advance();
245         return r;
246     }
247     self prev() const {
248         self r(*this);
249         r.retreat();
250         return r;
251     }
252     self &advance();
253     self &retreat();
254 private:
255     NodeIterator(ListNode const *n)
256         : _node(const_cast<ListNode*>(n))
257     {}
258     ListNode *_node;
259     friend class NodeList;
260 };
262 class NodeList : ListNode, boost::noncopyable, public boost::enable_shared_from_this<NodeList> {
263 public:
264     typedef std::size_t size_type;
265     typedef Node &reference;
266     typedef Node const &const_reference;
267     typedef Node *pointer;
268     typedef Node const *const_pointer;
269     typedef Node value_type;
270     typedef NodeIterator<value_type> iterator;
271     typedef NodeIterator<value_type const> const_iterator;
273     // TODO Lame. Make this private and make SubpathList a factory
274     NodeList(SubpathList &_list);
275     ~NodeList();
277     // iterators
278     iterator begin() { return iterator(ln_next); }
279     iterator end() { return iterator(this); }
280     const_iterator begin() const { return const_iterator(ln_next); }
281     const_iterator end() const { return const_iterator(this); }
283     // size
284     bool empty();
285     size_type size();
287     // extra node-specific methods
288     bool closed();
289     bool degenerate();
290     void setClosed(bool c) { _closed = c; }
291     iterator before(double t, double *fracpart = NULL);
292     const_iterator before(double t, double *fracpart = NULL) const {
293         return const_iterator(before(t, fracpart)._node);
294     }
296     // list operations
297     iterator insert(iterator pos, Node *x);
298     template <class InputIterator>
299     void insert(iterator pos, InputIterator first, InputIterator last) {
300         for (; first != last; ++first) insert(pos, *first);
301     }
302     void splice(iterator pos, NodeList &list);
303     void splice(iterator pos, NodeList &list, iterator i);
304     void splice(iterator pos, NodeList &list, iterator first, iterator last);
305     void reverse();
306     void shift(int n);
307     void push_front(Node *x) { insert(begin(), x); }
308     void pop_front() { erase(begin()); }
309     void push_back(Node *x) { insert(end(), x); }
310     void pop_back() { erase(--end()); }
311     void clear();
312     iterator erase(iterator pos);
313     iterator erase(iterator first, iterator last) {
314         NodeList::iterator ret = first;
315         while (first != last) ret = erase(first++);
316         return ret;
317     }
319     // member access - undefined results when the list is empty
320     Node &front() { return *static_cast<Node*>(ln_next); }
321     Node &back() { return *static_cast<Node*>(ln_prev); }
323     // HACK remove this subpath from its path. This will be removed later.
324     void kill();
325     SubpathList &subpathList() { return _list; }
327     static iterator get_iterator(Node *n) { return iterator(n); }
328     static const_iterator get_iterator(Node const *n) { return const_iterator(n); }
329     static NodeList &get(Node *n);
330     static NodeList &get(iterator const &i);
331 private:
332     // no copy or assign
333     NodeList(NodeList const &);
334     void operator=(NodeList const &);
336     SubpathList &_list;
337     bool _closed;
339     friend class Node;
340     friend class Handle; // required to access handle and handle line groups
341     friend class NodeIterator<Node>;
342     friend class NodeIterator<Node const>;
343 };
345 /** List of node lists. Represents an editable path. */
346 class SubpathList : public std::list< boost::shared_ptr<NodeList> > {
347 public:
348     typedef std::list< boost::shared_ptr<NodeList> > list_type;
350     SubpathList(PathManipulator &pm) : _path_manipulator(pm) {}
351     PathManipulator &pm() { return _path_manipulator; }
353 private:
354     list_type _nodelists;
355     PathManipulator &_path_manipulator;
356     friend class NodeList;
357     friend class Node;
358     friend class Handle;
359 };
363 // define inline Handle funcs after definition of Node
364 inline Geom::Point Handle::relativePos() {
365     return position() - _parent->position();
367 inline void Handle::setRelativePos(Geom::Point const &p) {
368     setPosition(_parent->position() + p);
370 inline double Handle::length() {
371     return relativePos().length();
373 inline PathManipulator &Handle::_pm() {
374     return _parent->_pm();
376 inline PathManipulator &Node::_pm() {
377     return nodeList().subpathList().pm();
380 // definitions for node iterator
381 template <typename N>
382 NodeIterator<N>::operator bool() const {
383     return _node && static_cast<ListNode*>(_node->ln_list) != _node;
385 template <typename N>
386 NodeIterator<N> &NodeIterator<N>::advance() {
387     ++(*this);
388     if (G_UNLIKELY(!*this) && _node->ln_list->closed()) ++(*this);
389     return *this;
391 template <typename N>
392 NodeIterator<N> &NodeIterator<N>::retreat() {
393     --(*this);
394     if (G_UNLIKELY(!*this) && _node->ln_list->closed()) --(*this);
395     return *this;
398 } // namespace UI
399 } // namespace Inkscape
401 #endif
403 /*
404   Local Variables:
405   mode:c++
406   c-file-style:"stroustrup"
407   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
408   indent-tabs-mode:nil
409   fill-column:99
410   End:
411 */
412 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :