1 /*
2 * A class for handling shape interaction with libavoid.
3 *
4 * Authors:
5 * Michael Wybrow <mjwybrow@users.sourceforge.net>
6 *
7 * Copyright (C) 2005 Michael Wybrow
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
14 #include "sp-item.h"
15 #include "conn-avoid-ref.h"
16 #include "libnr/nr-rect-ops.h"
17 #include "libavoid/polyutil.h"
18 #include "libavoid/incremental.h"
19 #include "xml/simple-node.cpp"
20 #include "document.h"
23 static Avoid::Polygn avoid_item_poly(SPItem const *item);
24 static void avoid_item_move(NR::Matrix const *mp, SPItem *moved_item);
27 SPAvoidRef::SPAvoidRef(SPItem *spitem)
28 : shapeRef(NULL)
29 , item(spitem)
30 , setting(false)
31 , new_setting(false)
32 , _transformed_connection()
33 {
34 }
37 SPAvoidRef::~SPAvoidRef()
38 {
39 _transformed_connection.disconnect();
40 if (shapeRef) {
41 // shapeRef is finalised by delShape,
42 // so no memory is lost here.
43 Avoid::delShape(shapeRef);
44 shapeRef = NULL;
45 }
46 }
49 void SPAvoidRef::setAvoid(char const *value)
50 {
51 if (SP_OBJECT_IS_CLONED(item)) {
52 // Don't keep avoidance information for cloned objects.
53 return;
54 }
55 new_setting = false;
56 if (value && (strcmp(value, "true") == 0)) {
57 new_setting = true;
58 }
59 }
62 void SPAvoidRef::handleSettingChange(void)
63 {
64 if (new_setting == setting) {
65 // Don't need to make any changes
66 return;
67 }
69 _transformed_connection.disconnect();
70 if (new_setting) {
71 _transformed_connection = item->connectTransformed(
72 sigc::ptr_fun(&avoid_item_move));
74 Avoid::Polygn poly = avoid_item_poly(item);
75 if (poly.pn > 0) {
76 const char *id = SP_OBJECT_REPR(item)->attribute("id");
77 g_assert(id != NULL);
79 // Get a unique ID for the item.
80 GQuark itemID = g_quark_from_string(id);
82 shapeRef = new Avoid::ShapeRef(itemID, poly);
83 Avoid::freePoly(poly);
85 Avoid::addShape(shapeRef);
86 }
87 }
88 else
89 {
90 g_assert(shapeRef);
92 // shapeRef is finalised by delShape,
93 // so no memory is lost here.
94 Avoid::delShape(shapeRef);
95 shapeRef = NULL;
96 }
97 setting = new_setting;
98 }
101 static Avoid::Polygn avoid_item_poly(SPItem const *item)
102 {
103 Avoid::Polygn poly;
105 // TODO: The right way to do this is to return the convex hull of
106 // the object, or an approximation in the case of a rounded
107 // object. Specific SPItems will need to have a new
108 // function that returns points for the convex hull.
109 // For some objects it is enough to feed the snappoints to
110 // some convex hull code, though not NR::ConvexHull as this
111 // only keeps the bounding box of the convex hull currently.
113 // TODO: SPItem::invokeBbox gives the wrong result for some objects
114 // that have internal representations that are updated later
115 // by the sp_*_update functions, e.g., text.
116 sp_document_ensure_up_to_date(item->document);
118 NR::Rect rHull = item->invokeBbox(sp_item_i2doc_affine(item));
120 // Add a little buffer around the edge of each object.
121 NR::Rect rExpandedHull = NR::expand(rHull, -10.0);
122 poly = Avoid::newPoly(4);
124 for (unsigned n = 0; n < 4; ++n) {
125 // TODO: I think the winding order in libavoid or inkscape might
126 // be backwards, probably due to the inverse y co-ordinates
127 // used for the screen. The '3 - n' reverses the order.
128 /* On "correct" winding order: Winding order of NR::Rect::corner is in a positive
129 * direction, like libart. "Positive direction" means the same as in most of Inkscape and
130 * SVG: if you visualize y as increasing upwards, as is the convention in mathematics, then
131 * positive angle is visualized as anticlockwise, as in mathematics; so if you visualize y
132 * as increasing downwards, as is common outside of mathematics, then positive angle
133 * direction is visualized as clockwise, as is common outside of mathematics. This
134 * convention makes it easier mix pure mathematics code with graphics code: the important
135 * thing when mixing code is that the number values stored in variables (representing y
136 * coordinate, angle) match up; variables store numbers, not visualized positions, and the
137 * programmer is free to switch between visualizations when thinking about a given piece of
138 * code.
139 *
140 * MathWorld, libart and NR::Rect::corner all seem to take positive winding (i.e. winding
141 * that yields +1 winding number inside a simple closed shape) to mean winding in a
142 * positive angle. This, together with the observation that variables store numbers rather
143 * than positions, suggests that NR::Rect::corner uses the right direction.
144 */
145 NR::Point hullPoint = rExpandedHull.corner(3 - n);
146 poly.ps[n].x = hullPoint[NR::X];
147 poly.ps[n].y = hullPoint[NR::Y];
148 }
150 return poly;
151 }
154 static void avoid_item_move(NR::Matrix const *mp, SPItem *moved_item)
155 {
156 Avoid::ShapeRef *shapeRef = moved_item->avoidRef->shapeRef;
157 g_assert(shapeRef);
159 Avoid::Polygn poly = avoid_item_poly(moved_item);
160 if (poly.pn > 0) {
161 // moveShape actually destroys the old shapeRef and returns a new one.
162 moved_item->avoidRef->shapeRef = Avoid::moveShape(shapeRef, &poly);
163 Avoid::freePoly(poly);
164 }
165 }
167 /*
168 Local Variables:
169 mode:c++
170 c-file-style:"stroustrup"
171 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
172 indent-tabs-mode:nil
173 fill-column:99
174 End:
175 */
176 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :