15
16
* Released under GNU GPL
18
#include "knot-holder-entity.h"
19
19
#include "knotholder.h"
20
20
#include "sp-item.h"
22
22
#include "preferences.h"
23
23
#include "macros.h"
24
#include <libnr/nr-matrix-ops.h>
24
25
#include "sp-pattern.h"
26
27
#include "desktop.h"
27
28
#include "sp-namedview.h"
28
#include <2geom/affine.h>
29
#include <2geom/matrix.h>
29
30
#include <2geom/transforms.h>
31
32
int KnotHolderEntity::counter = 0;
33
void KnotHolderEntity::create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, Inkscape::ControlType type,
35
SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
35
KnotHolderEntity::create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, const gchar *tip,
36
SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
37
knot = new SPKnot(desktop, tip);
38
knot = sp_knot_new(desktop, tip);
39
40
this->parent_holder = parent;
40
41
this->item = item; // TODO: remove the item either from here or from knotholder.cpp
43
44
my_counter = KnotHolderEntity::counter++;
45
g_object_set(G_OBJECT(knot->item), "shape", shape, NULL);
46
g_object_set(G_OBJECT(knot->item), "mode", mode, NULL);
48
// TODO base more appearance from this type instead of passing in arbitrary values.
49
knot->item->ctrlType = type;
46
g_object_set(G_OBJECT (knot->item), "shape", shape, NULL);
47
g_object_set(G_OBJECT (knot->item), "mode", mode, NULL);
51
49
knot->fill [SP_KNOT_STATE_NORMAL] = color;
52
g_object_set (G_OBJECT(knot->item), "fill_color", color, NULL);
50
g_object_set (G_OBJECT (knot->item), "fill_color", color, NULL);
57
_moved_connection = knot->moved_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_moved_handler));
58
_click_connection = knot->click_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_clicked_handler));
59
_ungrabbed_connection = knot->ungrabbed_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_ungrabbed_handler));
55
_moved_connection = knot->_moved_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_moved_handler));
56
_click_connection = knot->_click_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_clicked_handler));
57
_ungrabbed_connection = knot->_ungrabbed_signal.connect(sigc::mem_fun(*parent_holder, &KnotHolder::knot_ungrabbed_handler));
81
78
KnotHolderEntity::update_knot()
83
Geom::Point knot_pos(knot_get());
84
if (knot_pos.isFinite()) {
85
Geom::Point dp(knot_pos * item->i2dt_affine());
87
_moved_connection.block();
88
knot->setPosition(dp, SP_KNOT_STATE_NORMAL);
89
_moved_connection.unblock();
91
// knot coords are non-finite, hide knot
97
KnotHolderEntity::snap_knot_position(Geom::Point const &p, guint state)
99
if (state & GDK_SHIFT_MASK) { // Don't snap when shift-key is held
103
Geom::Affine const i2dt (item->i2dt_affine());
104
Geom::Point s = p * i2dt;
106
SnapManager &m = desktop->namedview->snap_manager;
107
m.setup(desktop, true, item);
108
m.freeSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE);
111
return s * i2dt.inverse();
115
KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint, guint state)
117
if (state & GDK_SHIFT_MASK) { // Don't snap when shift-key is held
121
Geom::Affine const i2d (item->i2dt_affine());
122
Geom::Point s = p * i2d;
124
SnapManager &m = desktop->namedview->snap_manager;
125
m.setup(desktop, true, item);
127
// constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line.
128
// This way the constraint is already enforced, no need to worry about that later on
129
Inkscape::Snapper::SnapConstraint transformed_constraint = Inkscape::Snapper::SnapConstraint(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d);
130
m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint);
80
Geom::Matrix const i2d(sp_item_i2d_affine(item));
82
Geom::Point dp(knot_get() * i2d);
84
_moved_connection.block();
85
sp_knot_set_position(knot, dp, SP_KNOT_STATE_NORMAL);
86
_moved_connection.unblock();
90
KnotHolderEntity::snap_knot_position(Geom::Point const &p)
92
Geom::Matrix const i2d (sp_item_i2d_affine(item));
93
Geom::Point s = p * i2d;
94
SnapManager &m = desktop->namedview->snap_manager;
95
m.setup(desktop, true, item);
96
m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, s, Inkscape::SNAPSOURCE_HANDLE);
97
return s * i2d.inverse();
101
KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint)
103
Geom::Matrix const i2d (sp_item_i2d_affine(item));
104
Geom::Point s = p * i2d;
105
Inkscape::Snapper::ConstraintLine transformed_constraint = Inkscape::Snapper::ConstraintLine(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d);
106
SnapManager &m = desktop->namedview->snap_manager;
107
m.setup(desktop, true, item);
108
m.constrainedSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, s, Inkscape::SNAPSOURCE_HANDLE, transformed_constraint);
133
109
return s * i2d.inverse();
139
115
/* TODO: this pattern manipulation is not able to handle general transformation matrices. Only matrices that are the result of a pure scale times a pure rotation. */
141
static gdouble sp_pattern_extract_theta(SPPattern const *pat)
117
static gdouble sp_pattern_extract_theta(SPPattern *pat)
143
Geom::Affine transf = pat->getTransform();
119
Geom::Matrix transf = pat->patternTransform;
144
120
return Geom::atan2(transf.xAxis());
147
static Geom::Point sp_pattern_extract_scale(SPPattern const *pat)
123
static Geom::Point sp_pattern_extract_scale(SPPattern *pat)
149
Geom::Affine transf = pat->getTransform();
125
Geom::Matrix transf = pat->patternTransform;
150
126
return Geom::Point( transf.expansionX(), transf.expansionY() );
153
129
static Geom::Point sp_pattern_extract_trans(SPPattern const *pat)
155
return Geom::Point(pat->getTransform()[4], pat->getTransform()[5]);
131
return Geom::Point(pat->patternTransform[4], pat->patternTransform[5]);
159
135
PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
161
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
137
SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style));
163
139
// FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used.
164
Geom::Point p_snapped = snap_knot_position(p, state);
140
Geom::Point p_snapped = snap_knot_position(p);
166
142
if ( state & GDK_CONTROL_MASK ) {
167
143
if (fabs((p - origin)[Geom::X]) > fabs((p - origin)[Geom::Y])) {
175
151
Geom::Point const q = p_snapped - sp_pattern_extract_trans(pat);
176
item->adjust_pattern(Geom::Translate(q), false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
152
sp_item_adjust_pattern(item, Geom::Matrix(Geom::Translate(q)));
179
155
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
183
PatternKnotHolderEntityXY::knot_get() const
159
PatternKnotHolderEntityXY::knot_get()
185
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
161
SPPattern const *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style));
186
162
return sp_pattern_extract_trans(pat);
190
PatternKnotHolderEntityAngle::knot_get() const
166
PatternKnotHolderEntityAngle::knot_get()
192
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
168
SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style));
194
gdouble x = pat->width();
170
gdouble x = (pattern_width(pat));
196
172
Geom::Point delta = Geom::Point(x,y);
197
173
Geom::Point scale = sp_pattern_extract_scale(pat);
198
174
gdouble theta = sp_pattern_extract_theta(pat);
199
delta = delta * Geom::Affine(Geom::Scale(scale))*Geom::Affine(Geom::Rotate(theta));
175
delta = delta * Geom::Matrix(Geom::Scale(scale))*Geom::Matrix(Geom::Rotate(theta));
200
176
delta = delta + sp_pattern_extract_trans(pat);
220
196
// get the scale from the current transform so we can keep it.
221
197
Geom::Point scl = sp_pattern_extract_scale(pat);
222
Geom::Affine rot = Geom::Affine(Geom::Scale(scl)) * Geom::Affine(Geom::Rotate(theta));
198
Geom::Matrix rot = Geom::Matrix(Geom::Scale(scl)) * Geom::Matrix(Geom::Rotate(theta));
223
199
Geom::Point const t = sp_pattern_extract_trans(pat);
224
200
rot[4] = t[Geom::X];
225
201
rot[5] = t[Geom::Y];
226
item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
202
sp_item_adjust_pattern(item, rot, true);
227
203
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
231
207
PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
233
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
209
SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style));
235
211
// FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used.
236
Geom::Point p_snapped = snap_knot_position(p, state);
212
Geom::Point p_snapped = snap_knot_position(p);
238
214
// get angle from current transform
239
215
gdouble theta = sp_pattern_extract_theta(pat);
241
217
// Get the new scale from the position of the knotholder
242
218
Geom::Point d = p_snapped - sp_pattern_extract_trans(pat);
243
gdouble pat_x = pat->width();
244
gdouble pat_y = pat->height();
219
gdouble pat_x = pattern_width(pat);
220
gdouble pat_y = pattern_height(pat);
245
221
Geom::Scale scl(1);
246
222
if ( state & GDK_CONTROL_MASK ) {
247
223
// if ctrl is pressed: use 1:1 scaling
252
228
scl = Geom::Scale(d[Geom::X] / pat_x, d[Geom::Y] / pat_y);
255
Geom::Affine rot = (Geom::Affine)scl * Geom::Rotate(theta);
231
Geom::Matrix rot = (Geom::Matrix)scl * Geom::Rotate(theta);
257
233
Geom::Point const t = sp_pattern_extract_trans(pat);
258
234
rot[4] = t[Geom::X];
259
235
rot[5] = t[Geom::Y];
260
item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE);
236
sp_item_adjust_pattern(item, rot, true);
261
237
item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
266
PatternKnotHolderEntityScale::knot_get() const
242
PatternKnotHolderEntityScale::knot_get()
268
SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer());
244
SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style));
270
gdouble x = pat->width();
271
gdouble y = pat->height();
246
gdouble x = pattern_width(pat);
247
gdouble y = pattern_height(pat);
272
248
Geom::Point delta = Geom::Point(x,y);
273
Geom::Affine a = pat->getTransform();
249
Geom::Matrix a = pat->patternTransform;
276
252
delta = delta * a;