Code

Node tool: fix moving multiple nodes along handles (Ctrl+Alt)
[inkscape.git] / src / live_effects / lpe-copy_rotate.cpp
1 #define INKSCAPE_LPE_COPY_ROTATE_CPP
2 /** \file
3  * LPE <copy_rotate> implementation
4  */
5 /*
6  * Authors:
7  *   Maximilian Albert
8  *
9  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
10  * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include "live_effects/lpe-copy_rotate.h"
16 #include "sp-shape.h"
17 #include "display/curve.h"
19 #include <2geom/path.h>
20 #include <2geom/transforms.h>
21 #include <2geom/d2-sbasis.h>
22 #include <2geom/angle.h>
24 namespace Inkscape {
25 namespace LivePathEffect {
27 namespace CR {
29 class KnotHolderEntityStartingAngle : public LPEKnotHolderEntity
30 {
31 public:
32     virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
33     virtual Geom::Point knot_get();
34 };
36 class KnotHolderEntityRotationAngle : public LPEKnotHolderEntity
37 {
38 public:
39     virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
40     virtual Geom::Point knot_get();
41 };
43 } // namespace CR
45 LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) :
46     Effect(lpeobject),
47     starting_angle(_("Starting"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0),
48     rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 30.0),
49     num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 5),
50     origin(_("Origin"), _("Origin of the rotation"), "origin", &wr, this, "Adjust the origin of the rotation"),
51     dist_angle_handle(100)
52 {
53     show_orig_path = true;
55     // register all your parameters here, so Inkscape knows which parameters this effect has:
56     registerParameter( dynamic_cast<Parameter *>(&starting_angle) );
57     registerParameter( dynamic_cast<Parameter *>(&rotation_angle) );
58     registerParameter( dynamic_cast<Parameter *>(&num_copies) );
59     registerParameter( dynamic_cast<Parameter *>(&origin) );
61     registerKnotHolderHandle(new CR::KnotHolderEntityStartingAngle(), _("Adjust the starting angle"));
62     registerKnotHolderHandle(new CR::KnotHolderEntityRotationAngle(), _("Adjust the rotation angle"));
64     num_copies.param_make_integer(true);
65     num_copies.param_set_range(0, 1000);
67 }
69 LPECopyRotate::~LPECopyRotate()
70 {
72 }
74 void
75 LPECopyRotate::doOnApply(SPLPEItem *lpeitem)
76 {
77     SPCurve *curve = SP_SHAPE(lpeitem)->curve;
79     A = *(curve->first_point());
80     B = *(curve->last_point());
82     origin.param_setValue(A);
84     dir = unit_vector(B - A);
85     dist_angle_handle = L2(B - A);
86 }
88 Geom::Piecewise<Geom::D2<Geom::SBasis> >
89 LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
90 {
91     using namespace Geom;
93     // I first suspected the minus sign to be a bug in 2geom but it is
94     // likely due to SVG's choice of coordinate system orientation (max)
95     start_pos = origin + dir * Rotate(-deg_to_rad(starting_angle)) * dist_angle_handle;
96     rot_pos = origin + dir * Rotate(-deg_to_rad(starting_angle + rotation_angle)) * dist_angle_handle;
98     A = pwd2_in.firstValue();
99     B = pwd2_in.lastValue();
100     dir = unit_vector(B - A);
102     Piecewise<D2<SBasis> > output;
104     Matrix pre = Translate(-origin) * Rotate(-deg_to_rad(starting_angle));
105     for (int i = 0; i < num_copies; ++i) {
106         // I first suspected the minus sign to be a bug in 2geom but it is
107         // likely due to SVG's choice of coordinate system orientation (max)
108         Rotate rot(-deg_to_rad(rotation_angle * i));
109         Matrix t = pre * rot * Translate(origin);
110         output.concat(pwd2_in * t);
111     }
113     return output;
116 void
117 LPECopyRotate::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
119     using namespace Geom;
121     Path path(start_pos);
122     path.appendNew<LineSegment>((Geom::Point) origin);
123     path.appendNew<LineSegment>(rot_pos);
125     PathVector pathv;
126     pathv.push_back(path);
127     hp_vec.push_back(pathv);
130 namespace CR {
132 using namespace Geom;
134 // TODO: make this more generic
135 static LPECopyRotate *
136 get_effect(SPItem *item)
138     Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
139     if (effect->effectType() != COPY_ROTATE) {
140         g_print ("Warning: Effect is not of type LPECopyRotate!\n");
141         return NULL;
142     }
143     return static_cast<LPECopyRotate *>(effect);
146 void
147 KnotHolderEntityStartingAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
149     LPECopyRotate* lpe = get_effect(item);
151     Geom::Point const s = snap_knot_position(p);
153     // I first suspected the minus sign to be a bug in 2geom but it is
154     // likely due to SVG's choice of coordinate system orientation (max)
155     lpe->starting_angle.param_set_value(rad_to_deg(-angle_between(lpe->dir, s - lpe->origin)));
156     if (state & GDK_SHIFT_MASK) {
157         lpe->dist_angle_handle = L2(lpe->B - lpe->A);
158     } else {
159         lpe->dist_angle_handle = L2(p - lpe->origin);
160     }
162     // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
163     sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
166 void
167 KnotHolderEntityRotationAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
169     LPECopyRotate* lpe = get_effect(item);
171     Geom::Point const s = snap_knot_position(p);
173     // I first suspected the minus sign to be a bug in 2geom but it is
174     // likely due to SVG's choice of coordinate system orientation (max)
175     lpe->rotation_angle.param_set_value(rad_to_deg(-angle_between(lpe->dir, s - lpe->origin)) - lpe->starting_angle);
176     if (state & GDK_SHIFT_MASK) {
177         lpe->dist_angle_handle = L2(lpe->B - lpe->A);
178     } else {
179         lpe->dist_angle_handle = L2(p - lpe->origin);
180     }
182     // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
183     sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
186 Geom::Point
187 KnotHolderEntityStartingAngle::knot_get()
189     LPECopyRotate* lpe = get_effect(item);
190     return snap_knot_position(lpe->start_pos);
193 Geom::Point
194 KnotHolderEntityRotationAngle::knot_get()
196     LPECopyRotate* lpe = get_effect(item);
197     return snap_knot_position(lpe->rot_pos);
200 } // namespace CR
202 /* ######################## */
204 } //namespace LivePathEffect
205 } /* namespace Inkscape */
207 /*
208   Local Variables:
209   mode:c++
210   c-file-style:"stroustrup"
211   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
212   indent-tabs-mode:nil
213   fill-column:99
214   End:
215 */
216 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :