Merge lp:~inkscape.dev/inkscape/fillet-chamfer into lp:~inkscape.dev/inkscape/trunk
- fillet-chamfer
- Merge into trunk
Status: | Merged |
---|---|
Merge reported by: | su_v |
Merged at revision: | not available |
Proposed branch: | lp:~inkscape.dev/inkscape/fillet-chamfer |
Merge into: | lp:~inkscape.dev/inkscape/trunk |
Diff against target: |
2687 lines (+2421/-12) 19 files modified
src/2geom/pointwise.h (+90/-0) src/2geom/satellite.h (+146/-0) src/knotholder.h (+2/-0) src/live_effects/CMakeLists.txt (+4/-0) src/live_effects/Makefile_insert (+2/-0) src/live_effects/effect-enum.h (+1/-0) src/live_effects/effect.cpp (+5/-5) src/live_effects/lpe-fillet-chamfer.cpp (+649/-0) src/live_effects/lpe-fillet-chamfer.h (+101/-0) src/live_effects/parameter/Makefile_insert (+2/-0) src/live_effects/parameter/filletchamferpointarray.cpp (+882/-0) src/live_effects/parameter/filletchamferpointarray.h (+123/-0) src/ui/CMakeLists.txt (+2/-0) src/ui/dialog/Makefile_insert (+2/-0) src/ui/dialog/lpe-fillet-chamfer-properties.cpp (+278/-0) src/ui/dialog/lpe-fillet-chamfer-properties.h (+110/-0) src/ui/tool/multi-path-manipulator.h (+2/-1) src/ui/tool/path-manipulator.cpp (+15/-5) src/ui/tools/node-tool.cpp (+5/-1) |
To merge this branch: | bzr merge lp:~inkscape.dev/inkscape/fillet-chamfer |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Inkscape Developers | Pending | ||
Review via email: mp+221308@code.launchpad.net |
Commit message
Description of the change
Add fillet and chanfer LPE, here is a video working: https:/
njh (njh-njhurst) wrote : | # |
Jabiertxof (jabiertxof) wrote : | # |
Hi Nathan i go on:
Think the first is solve the class substitute to the vector<point>.
In my mind there is a object, like you say Pointwise that handles:
#######
Pointwise<
enum typeof /*handle the type of fillet,chamfer.. and can be extended
for other uses*/,
bool active /**/,
bool hidden /**/,
?double time /**/
double size /*one, time or size need to be 0 to get the other value,
you know the right way to handle this? Now is handled negative values
are size and positive times.*/
>
#######
Where is the better place for the class? Im not sure if is correct
create a class like that. Please tell me your opinion. Also we can
expand it.
For the moment thats all, I read all your comments, thanks for the time
on it! I correct all i can while refactor with the new class.
Regards, Jabier.
Jabiertxof (jabiertxof) wrote : | # |
Also have a question about handle subpaths.
Is ok something like: std::vector<
There is a standar way to apply this data to svg string?
I need 3 separators and i only see 2 ", and |", can use for example + or #?
Regards,Jabier.
njh (njh-njhurst) wrote : | # |
On Sat, May 31, 2014 at 06:45:40PM -0000, Jabiertxof wrote:
> Hi Nathan i go on:
> Think the first is solve the class substitute to the vector<point>.
> In my mind there is a object, like you say Pointwise that handles:
>
> #######
> Pointwise<
> enum typeof /*handle the type of fillet,chamfer.. and can be extended
> for other uses*/,
> bool active /**/,
> bool hidden /**/,
> ?double time /**/
> double size /*one, time or size need to be 0 to get the other value,
> you know the right way to handle this? Now is handled negative values
> are size and positive times.*/
You just want an extra bit to specify the correct choice.
> >
> #######
>
> Where is the better place for the class? Im not sure if is correct
> create a class like that. Please tell me your opinion. Also we can
> expand it.
So I think Pointwise itself should go in 2geom, because it is a common
thing to do relating to paths and geometry. The class that you put in
the <T> is much like you say, but you might like to do the
polymorphism using a disjuntive type. In C++ you do this like in the
Path class, you make a base class (like Curve), and everything that
goes into pointwise is a reference to a subclass of it. The advantage
of this is that you can mix and match, your dispatch can handle a lot
of the selection problem (which method to call for which code). The
main downside is that it has a much higher memory management
overhead. Otherwise put everything you need in a single class.
I'll ask around and see if there are any other good choices.
> For the moment thats all, I read all your comments, thanks for the time
> on it! I correct all i can while refactor with the new class.
That's ok, I was up and didn't have anything important to do, so I
think it was time well spent (it took me about 2 hours total, which
goes to show how important good code readability is - I've spent much
more time reading your code than writing it :)
njh
MenTaLguY (mental-deactivatedaccount) wrote : | # |
Rather than an enum template parameter, consider a policy. https:/
Jabiertxof (jabiertxof) wrote : | # |
> Rather than an enum template parameter, consider a policy.
> https:/
Thanks very much, I consider it.
Jabiertxof (jabiertxof) wrote : | # |
> So I think Pointwise itself should go in 2geom
OK
> you make a base class (like Curve) [...]
> Otherwise put everything you need in a single class.
Not sure whats better, i like the first one but no have real expertize.
> I'll ask around and see if there are any other good choices.
Perfect, please tell me about, im waiting if yes or not.
> it was time well spent
Thanks!
Jabiertxof (jabiertxof) wrote : | # |
Another question, maybe this class can be used also in the bspline branch, it remove all extra staff of the node related tool. And maybe simplify the pen related staff.
njh (njh-njhurst) wrote : | # |
> Also have a question about handling subpaths.
> Is something like: std::vector<
Sure, you might call it PointwiseVector like PathVector.
> There is a standard way to apply this data to an svg string?
> I need 3 separators and I only see 2 ", and |", can I use, for example, + or #?
I'm not quite sure what you're asking for, do you want to store the data in an attribute in svg with your own namespace? Then you can use any xml valid string you like. You could use Aaron's ragel parser that he wrote for svg d attribute parsing as a starting point. You will need to check that inkscape correctly maintains namespaced attributes (I'm pretty sure it does).
I don't think you should store anything in the svg:d attribute itself.
njh (njh-njhurst) wrote : | # |
> > So I think Pointwise itself should go in 2geom
>
> OK
>
> > you make a base class (like Curve) [...]
> > Otherwise put everything you need in a single class.
>
> Not sure whats better, i like the first one but no have real expertize.
The first is a good option. But before you commit to a design, can you write up a class with exactly the data you want to store so I can take a look?
> > I'll ask around and see if there are any other good choices.
>
> Perfect, please tell me about, im waiting if yes or not.
I asked Mental to take a look, he thinks the first option is good.
njh (njh-njhurst) wrote : | # |
> Another question, maybe this class can be used also in the bspline branch, it
> remove all extra staff of the node related tool. And maybe simplify the pen
> related staff.
That's a great idea, if you know those pieces of code well you should try porting all of them to use your pointwise class. I would suggest you get it working for fillet-chamfer first, then look at what needs to be added for each of the others. That way you won't spend the rest of your life trying to cover every possible case.
It's certainly something that we've thought about a lot when we were working on 2geom - how to attach 'sidecar data' to paths. I think your pointwise class is a very nice start to solving that problem.
(grandpa story time:) When I was thinking about this and related problems I could only think about representing the data as a row-store. That is, all the data for the pen size, markers etc had to be stored in the same Curve object. This is how databases are often implemented. But it's very hard for paths because of things like ranges and functions of arclength. I've since realised that a column store representation is the right solution. The hard part is making sure that all edits are correctly applied to all columns. We can talk about that once you've got the basic Pointwise stuff done. The more I think about this, the more I'm convinced this is the right solution!
Jabiertxof (jabiertxof) wrote : | # |
> > Also have a question about handling subpaths.
> > Is something like: std::vector<
>
> Sure, you might call it PointwiseVector like PathVector.
OK.
> > There is a standard way to apply this data to an svg string?
> > I need 3 separators and I only see 2 ", and |", can I use, for example, + or
> #?
>
> I'm not quite sure what you're asking for, do you want to store the data in an
> attribute in svg with your own namespace? Then you can use any xml valid
> string you like. You could use Aaron's ragel parser that he wrote for svg d
> attribute parsing as a starting point. You will need to check that inkscape
> correctly maintains namespaced attributes (I'm pretty sure it does).
>
> I don't think you should store anything in the svg:d attribute itself.
I want to strore a paremeter LPE.
njh (njh-njhurst) wrote : | # |
> I want to strore a paremeter LPE.
Ask on the inkscape-devel mailing list. Johan might have an idea, I don't know anything about that stuff.
Jabiertxof (jabiertxof) wrote : | # |
> The first is a good option. But before you commit to a design, can you write
> up a class with exactly the data you want to store so I can take a look?
Yes send you soon.
> I asked Mental to take a look, he thinks the first option is good.
Ok Thanks
Jabiertxof (jabiertxof) wrote : | # |
> > I want to strore a paremeter LPE.
>
> Ask on the inkscape-devel mailing list. Johan might have an idea, I don't
> know anything about that stuff.
OK , i ask soon.
Regards.
Jabiertxof (jabiertxof) wrote : | # |
One question im on the tree of the class and have a question.
How many "SPoints" "have a node", think 2 are enougth but maybe is beter make it flexible inside a vector.
njh (njh-njhurst) wrote : | # |
> One question im on the tree of the class and have a question.
> How many "SPoints" "have a node", think 2 are enougth but maybe is beter make
> it flexible inside a vector.
What are SPoints? how do you maintain the relationship between them and the curve? Keeping things in sorted order makes merging much easier (like you already have).
The two obvious ways to me are a) have a 1:1 mapping between curve nodes and these SPoints (that's a fairly poor name btw, it doesn't mean anything to me at all). b) include the corresponding node index in the SPoint.
Jabiertxof (jabiertxof) wrote : | # |
Spoint -> NodePointExtend
I think can be cases with more than required two "extenders" per node, so, if i undertand it well, i prefear the second one.
- 13425. By Jabiertxof <email address hidden>
-
update to trunk
- 13426. By Jabiertxof <email address hidden>
-
Fixed problems in Nathan review, todo: add PointWise class
- 13427. By Jabiertxof <email address hidden>
-
update to trunk
- 13428. By Jabiertxof <email address hidden>
-
Full remove knot doubleclick
- 13429. By Jabiertxof <email address hidden>
-
change isDegenerate by are_near
Jabiertxof (jabiertxof) wrote : | # |
Saving diff reply comments
- 13430. By Jabiertxof <email address hidden>
-
remove percent const
- 13431. By Jabiertxof <email address hidden>
-
update to trunk
- 13432. By Jabiertxof <email address hidden>
-
Fixed CMakeLists.txt pointed by Liam
- 13433. By Jabiertxof <email address hidden>
-
Put correctly doubles in a function
- 13434. By Jabiertxof <email address hidden>
-
Add LiamW changes
- 13435. By Jabiertxof <email address hidden>
-
update to trunk
- 13436. By Jabiertxof <email address hidden>
-
Fix bug on closed nodes in fillet-chamfer LPE
- 13437. By Jabiertxof <email address hidden>
-
update to trunk
- 13438. By Jabiertxof <email address hidden>
-
Fix a bug on type of 'fillet' on the closing node
- 13439. By Jabiertxof <email address hidden>
-
fix bug on open paths
- 13440. By root <email address hidden>
-
update to trunk
- 13441. By Jabiertxof <email address hidden>
-
update to trunk
- 13442. By Jabiertxof <email address hidden>
-
Added reset modifier to knotas pointed by suv
- 13443. By Jabiertxof <email address hidden>
-
add a missing line
- 13444. By Jabiertxof <email address hidden>
-
Fix LPE filet-chamfer name to sweet experimental branch
- 13445. By Jabiertxof <email address hidden>
-
update to trunk
- 13446. By Jabiertxof <email address hidden>
-
fix a bug knots resetting
- 13447. By Jabiertxof <email address hidden>
-
update to trunk
- 13448. By Jabiertxof <email address hidden>
-
Updated to use Arcs instead Bezier in fillet and inverse fillet
- 13449. By root <email address hidden>
-
adde force arc bool parameter in cubic bezier
- 13450. By root <email address hidden>
-
fixed locale in popup knot
- 13451. By root <email address hidden>
-
Added real radius and optionaly ditance to knots, also fixed subpaths bug
- 13452. By Jabiertxof <email address hidden>
-
migrate some code to a reusable function
- 13453. By Jabiertxof <email address hidden>
-
migrate more code to a reusable function
- 13454. By Jabiertxof <email address hidden>
-
update to trunk
- 13455. By Jabiertxof <email address hidden>
-
fixing bugs in fillet chamfer
- 13456. By Jabiertxof <email address hidden>
-
working on radious fillet chamfer
- 13457. By Jabiertxof <email address hidden>
-
Add HAVE_GLIBMM_
THREADS_ H check to fillet-chamfer by Liam - 13458. By Jabiertxof <email address hidden>
-
fixing radious problems
- 13459. By Jabiertxof <email address hidden>
-
update to trunk
- 13460. By Jabiertxof <email address hidden>
-
updated for use also radius
- 13461. By Jabiertxof <email address hidden>
-
update to trunk
- 13462. By Jabiertxof <email address hidden>
-
Fixed code in locale, get from LiamW fix
- 13463. By Jabiertxof <email address hidden>
-
update to trunk
- 13464. By Jabiertxof <email address hidden>
-
Fix locale and bugs applied to arcs
- 13465. By Jabiertxof <email address hidden>
-
Fix locale, for the moment use classic locale
- 13466. By Jabiertxof <email address hidden>
-
Add numeric input in knot click
- 13467. By Jabiertxof <email address hidden>
-
update to trunk
- 13468. By Jabiertxof <email address hidden>
-
Fillet-Chamfer update. Two things.
Helper paths on knots: now work well in rect and curves
Method: Now the method ase selectable. Three methods:
Auto: Straight lines whith arcs and curves whith bezier
Arc: Always use arc.
Bezier: Always use bezier. - 13469. By Jabiertxof <email address hidden>
-
A little fix in fillet-chamfer to hide knot helper path at hide the knots
- 13470. By Jabiertxof <email address hidden>
-
update to trunk
- 13471. By Jabiertxof <email address hidden>
-
Starting pointwise refactor
- 13472. By Jabiertxof <email address hidden>
-
pointwise dfinitions
su_v (suv-lp) wrote : | # |
fillet-chamfer was added to experimental in r13415
http://
experimental was merged into trunk in r13641
http://
Changing status to 'Merged'.
Preview Diff
1 | === added file 'src/2geom/pointwise.h' |
2 | --- src/2geom/pointwise.h 1970-01-01 00:00:00 +0000 |
3 | +++ src/2geom/pointwise.h 2014-10-22 21:40:37 +0000 |
4 | @@ -0,0 +1,90 @@ |
5 | +/** |
6 | + * \file |
7 | + * \brief Pointwise |
8 | + *//* |
9 | + * Authors: |
10 | + * |
11 | + * Copyright 2014 authors |
12 | + * |
13 | + * |
14 | + * This library is free software; you can redistribute it and/or |
15 | + * modify it either under the terms of the GNU Lesser General Public |
16 | + * License version 2.1 as published by the Free Software Foundation |
17 | + * (the "LGPL") or, at your option, under the terms of the Mozilla |
18 | + * Public License Version 1.1 (the "MPL"). If you do not alter this |
19 | + * notice, a recipient may use your version of this file under either |
20 | + * the MPL or the LGPL. |
21 | + * |
22 | + * You should have received a copy of the LGPL along with this library |
23 | + * in the file COPYING-LGPL-2.1; if not, output to the Free Software |
24 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
25 | + * You should have received a copy of the MPL along with this library |
26 | + * in the file COPYING-MPL-1.1 |
27 | + * |
28 | + * The contents of this file are subject to the Mozilla Public License |
29 | + * Version 1.1 (the "License"); you may not use this file except in |
30 | + * compliance with the License. You may obtain a copy of the License at |
31 | + * http://www.mozilla.org/MPL/ |
32 | + * |
33 | + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
34 | + * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
35 | + * the specific language governing rights and limitations. |
36 | + * |
37 | + */ |
38 | + |
39 | +#ifndef SEEN_GEOM_POINTWISE_H |
40 | +#define SEEN_GEOM_POINTWISE_H |
41 | + |
42 | +#include <vector> |
43 | +#include <2geom/sbasis.h> |
44 | +#include <2geom/sbasis-2d.h> |
45 | +#include <2geom/piecewise.h> |
46 | +#include <2geom/satellite.h> |
47 | +#include <2geom/affine.h> |
48 | + |
49 | +namespace Geom { |
50 | +/** |
51 | + * %Pointwise function class. |
52 | + */ |
53 | + |
54 | +class Pointwise |
55 | +{ |
56 | + public: |
57 | + Pointwise(){} |
58 | + |
59 | + Pointwise(Piecewise<D2<SBasis> > pwd2, std::vector<std::pair<int,satelite> > satellites) |
60 | + : _pwd2(pwd2), _satellites(satellites) |
61 | + { |
62 | + } |
63 | + |
64 | + virtual ~Pointwise(); |
65 | + |
66 | + std::pair<int,satelite> findSatellites(int A) const; |
67 | + |
68 | + double toTime(std::pair<int,satelite> A, double B); |
69 | + double toSize(std::pair<int,satelite> A, double B); |
70 | + double len_to_rad(int index, double len); |
71 | + double rad_to_len(int index, double rad); |
72 | + std::vector<double> get_times(int index, bool last); |
73 | + std::pair<std::size_t, std::size_t> get_positions(int index) |
74 | + int last_index(int index); |
75 | + double time_to_len(int index, double time); |
76 | + double len_to_time(int index, double len); |
77 | + Pointwise recalculate_for_new_pwd2(Piecewise<D2<SBasis> > A); |
78 | + |
79 | + private: |
80 | + std::vector<std::pair<int,satelite> > _satellites; |
81 | + Piecewise<D2<SBasis> > _pwd2; |
82 | +}; |
83 | + |
84 | +#endif //SEEN_GEOM_PW_SB_H |
85 | +/* |
86 | + Local Variables: |
87 | + mode:c++ |
88 | + c-file-style:"stroustrup" |
89 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
90 | + indent-tabs-mode:nil |
91 | + fill-column:99 |
92 | + End: |
93 | +*/ |
94 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
95 | |
96 | === added file 'src/2geom/satellite.h' |
97 | --- src/2geom/satellite.h 1970-01-01 00:00:00 +0000 |
98 | +++ src/2geom/satellite.h 2014-10-22 21:40:37 +0000 |
99 | @@ -0,0 +1,146 @@ |
100 | +/** |
101 | + * \file |
102 | + * \brief Satellite |
103 | + *//* |
104 | + * Authors: |
105 | + * |
106 | + * Copyright 2014 authors |
107 | + * |
108 | + * This library is free software; you can redistribute it and/or |
109 | + * modify it either under the terms of the GNU Lesser General Public |
110 | + * License version 2.1 as published by the Free Software Foundation |
111 | + * (the "LGPL") or, at your option, under the terms of the Mozilla |
112 | + * Public License Version 1.1 (the "MPL"). If you do not alter this |
113 | + * notice, a recipient may use your version of this file under either |
114 | + * the MPL or the LGPL. |
115 | + * |
116 | + * You should have received a copy of the LGPL along with this library |
117 | + * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
118 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
119 | + * You should have received a copy of the MPL along with this library |
120 | + * in the file COPYING-MPL-1.1 |
121 | + * |
122 | + * The contents of this file are subject to the Mozilla Public License |
123 | + * Version 1.1 (the "License"); you may not use this file except in |
124 | + * compliance with the License. You may obtain a copy of the License at |
125 | + * http://www.mozilla.org/MPL/ |
126 | + * |
127 | + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
128 | + * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
129 | + * the specific language governing rights and limitations. |
130 | + */ |
131 | + |
132 | +#ifndef LIB2GEOM_SEEN_SATELLITE_H |
133 | +#define LIB2GEOM_SEEN_SATELLITE_H |
134 | + |
135 | +namespace Geom { |
136 | + |
137 | +class Satellite |
138 | +{ |
139 | + public: |
140 | + Satellite() |
141 | + {} |
142 | + |
143 | + virtual ~Sattelite(); |
144 | + |
145 | + Satellite(typeSatellite ts, bool isTime, bool active, bool after, bool hidden, double size, double time) |
146 | + : _ts(ts), _time(time), _active(active), _after(after), _hidden(hidden), _size(size), _time(time) |
147 | + { |
148 | + } |
149 | + |
150 | + void setTypeSatellite(typeSatellite A) |
151 | + { |
152 | + _ts = A; |
153 | + } |
154 | + |
155 | + void setActive(bool A) |
156 | + { |
157 | + _active = A; |
158 | + } |
159 | + |
160 | + void setAfter(bool A) |
161 | + { |
162 | + _after = A; |
163 | + } |
164 | + |
165 | + void setHidden(bool A) |
166 | + { |
167 | + _hidden = A; |
168 | + } |
169 | + |
170 | + void setTime(double A) |
171 | + { |
172 | + _isTime = true; |
173 | + _time = A; |
174 | + } |
175 | + |
176 | + void setSize(double A) |
177 | + { |
178 | + _isTime = false; |
179 | + _size = A; |
180 | + } |
181 | + |
182 | + typeSatellite ts() const |
183 | + { |
184 | + return _ts; |
185 | + } |
186 | + |
187 | + bool isTime() const |
188 | + { |
189 | + return _isTime; |
190 | + } |
191 | + |
192 | + bool active() const |
193 | + { |
194 | + return _active; |
195 | + } |
196 | + |
197 | + bool after() const |
198 | + { |
199 | + return _after; |
200 | + } |
201 | + |
202 | + bool hidden() const |
203 | + { |
204 | + return _hidden; |
205 | + } |
206 | + |
207 | + double size() const |
208 | + { |
209 | + return _size; |
210 | + } |
211 | + |
212 | + double time() const |
213 | + { |
214 | + return _time; |
215 | + } |
216 | + |
217 | + bool isDegenerate() const |
218 | + { |
219 | + return _size = 0 && _time = 0; |
220 | + } |
221 | + |
222 | + private: |
223 | + typeSatellite _ts; |
224 | + bool _isTime; |
225 | + bool _active; |
226 | + bool _after; |
227 | + bool _hidden; |
228 | + double _size; |
229 | + double _time; |
230 | +}; |
231 | + |
232 | +} // end namespace Geom |
233 | + |
234 | +#endif // LIB2GEOM_SEEN_SATELLITE_H |
235 | + |
236 | +/* |
237 | + Local Variables: |
238 | + mode:c++ |
239 | + c-file-style:"stroustrup" |
240 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
241 | + indent-tabs-mode:nil |
242 | + fill-column:99 |
243 | + End: |
244 | +*/ |
245 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
246 | |
247 | === modified file 'src/knotholder.h' |
248 | --- src/knotholder.h 2014-03-30 12:10:32 +0000 |
249 | +++ src/knotholder.h 2014-10-22 21:40:37 +0000 |
250 | @@ -28,6 +28,7 @@ |
251 | } |
252 | namespace LivePathEffect { |
253 | class PowerStrokePointArrayParamKnotHolderEntity; |
254 | +class FilletPointArrayParamKnotHolderEntity; |
255 | } |
256 | } |
257 | |
258 | @@ -60,6 +61,7 @@ |
259 | |
260 | friend class ShapeEditor; |
261 | friend class Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity; |
262 | + friend class Inkscape::LivePathEffect::FilletPointArrayParamKnotHolderEntity; |
263 | |
264 | protected: |
265 | |
266 | |
267 | === modified file 'src/live_effects/CMakeLists.txt' |
268 | --- src/live_effects/CMakeLists.txt 2012-03-23 21:02:03 +0000 |
269 | +++ src/live_effects/CMakeLists.txt 2014-10-22 21:40:37 +0000 |
270 | @@ -13,6 +13,7 @@ |
271 | lpe-dynastroke.cpp |
272 | lpe-envelope.cpp |
273 | lpe-extrude.cpp |
274 | + lpe-fillet-chamfer.cpp |
275 | lpe-gears.cpp |
276 | lpe-interpolate.cpp |
277 | lpe-knot.cpp |
278 | @@ -44,6 +45,7 @@ |
279 | |
280 | parameter/array.cpp |
281 | parameter/bool.cpp |
282 | + parameter/powerstrokepointarray.cpp |
283 | parameter/parameter.cpp |
284 | parameter/path.cpp |
285 | parameter/originalpath.cpp |
286 | @@ -72,6 +74,7 @@ |
287 | lpe-dynastroke.h |
288 | lpe-envelope.h |
289 | lpe-extrude.h |
290 | + lpe-fillet-chamfer.h |
291 | lpe-gears.h |
292 | lpe-interpolate.h |
293 | lpe-knot.h |
294 | @@ -104,6 +107,7 @@ |
295 | |
296 | parameter/array.h |
297 | parameter/bool.h |
298 | + parameter/powerstrokepointarray.h |
299 | parameter/enum.h |
300 | parameter/parameter.h |
301 | parameter/path-reference.h |
302 | |
303 | === modified file 'src/live_effects/Makefile_insert' |
304 | --- src/live_effects/Makefile_insert 2012-03-23 21:02:03 +0000 |
305 | +++ src/live_effects/Makefile_insert 2014-10-22 21:40:37 +0000 |
306 | @@ -32,6 +32,8 @@ |
307 | live_effects/lpe-curvestitch.h \ |
308 | live_effects/lpe-constructgrid.cpp \ |
309 | live_effects/lpe-constructgrid.h \ |
310 | + live_effects/lpe-fillet-chamfer.cpp \ |
311 | + live_effects/lpe-fillet-chamfer.h \ |
312 | live_effects/lpe-gears.cpp \ |
313 | live_effects/lpe-gears.h \ |
314 | live_effects/lpe-interpolate.cpp \ |
315 | |
316 | === modified file 'src/live_effects/effect-enum.h' |
317 | --- src/live_effects/effect-enum.h 2012-01-12 21:06:16 +0000 |
318 | +++ src/live_effects/effect-enum.h 2014-10-22 21:40:37 +0000 |
319 | @@ -50,6 +50,7 @@ |
320 | EXTRUDE, |
321 | POWERSTROKE, |
322 | CLONE_ORIGINAL, |
323 | + FILLET_CHAMFER, |
324 | INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan) |
325 | }; |
326 | |
327 | |
328 | === modified file 'src/live_effects/effect.cpp' |
329 | --- src/live_effects/effect.cpp 2014-05-05 08:40:12 +0000 |
330 | +++ src/live_effects/effect.cpp 2014-10-22 21:40:37 +0000 |
331 | @@ -25,6 +25,7 @@ |
332 | #include "live_effects/lpe-perspective_path.h" |
333 | #include "live_effects/lpe-spiro.h" |
334 | #include "live_effects/lpe-lattice.h" |
335 | +#include "live_effects/lpe-fillet-chamfer.h" |
336 | #include "live_effects/lpe-envelope.h" |
337 | #include "live_effects/lpe-constructgrid.h" |
338 | #include "live_effects/lpe-perp_bisector.h" |
339 | @@ -120,6 +121,7 @@ |
340 | /* 0.49 */ |
341 | {POWERSTROKE, N_("Power stroke"), "powerstroke"}, |
342 | {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"}, |
343 | + {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"}, |
344 | }; |
345 | const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData)); |
346 | |
347 | @@ -243,6 +245,9 @@ |
348 | case CLONE_ORIGINAL: |
349 | neweffect = static_cast<Effect*> ( new LPECloneOriginal(lpeobj) ); |
350 | break; |
351 | + case FILLET_CHAMFER: |
352 | + neweffect = static_cast<Effect*> ( new LPEFilletChamfer(lpeobj) ); |
353 | + break; |
354 | default: |
355 | g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); |
356 | neweffect = NULL; |
357 | @@ -494,11 +499,6 @@ |
358 | { |
359 | std::vector<Geom::PathVector> hp_vec; |
360 | |
361 | - if (!SP_IS_SHAPE(lpeitem)) { |
362 | -// g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups. |
363 | - return hp_vec; |
364 | - } |
365 | - |
366 | // add indicators provided by the effect itself |
367 | addCanvasIndicators(lpeitem, hp_vec); |
368 | |
369 | |
370 | === added file 'src/live_effects/lpe-fillet-chamfer.cpp' |
371 | --- src/live_effects/lpe-fillet-chamfer.cpp 1970-01-01 00:00:00 +0000 |
372 | +++ src/live_effects/lpe-fillet-chamfer.cpp 2014-10-22 21:40:37 +0000 |
373 | @@ -0,0 +1,649 @@ |
374 | +/* |
375 | + * Author(s): |
376 | + * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es> |
377 | + * |
378 | + * Copyright (C) 2014 Author(s) |
379 | + * |
380 | + * Special thanks to Johan Engelen for the base of the effect -powerstroke- |
381 | + * Also to ScislaC for point me to the idea |
382 | + * Also su_v for his construvtive feedback and time |
383 | + * Also to Mc- (IRC nick) for his important contribution to find real time |
384 | + * values based on |
385 | + * and finaly to Liam P. White for his big help on coding, that save me a lot of hours |
386 | + * |
387 | + * Released under GNU GPL, read the file 'COPYING' for more information |
388 | + */ |
389 | +#include "live_effects/lpe-fillet-chamfer.h" |
390 | + |
391 | +#include <2geom/sbasis-to-bezier.h> |
392 | +#include <2geom/svg-elliptical-arc.h> |
393 | +#include <2geom/line.h> |
394 | + |
395 | +#include "desktop.h" |
396 | +#include "display/curve.h" |
397 | +#include "helper/geom-nodetype.h" |
398 | +#include "helper/geom-curves.h" |
399 | +#include "helper/geom.h" |
400 | + |
401 | +#include "live_effects/parameter/filletchamferpointarray.h" |
402 | + |
403 | +// for programmatically updating knots |
404 | +#include "selection.h" |
405 | +#include "tools-switch.h" |
406 | +#include "ui/tool/control-point-selection.h" |
407 | +#include "ui/tool/selectable-control-point.h" |
408 | +#include "ui/tool/node.h" |
409 | +#include "ui/tools/node-tool.h" |
410 | + |
411 | +// TODO due to internal breakage in glibmm headers, this must be last: |
412 | +#include <glibmm/i18n.h> |
413 | + |
414 | +using namespace Geom; |
415 | +namespace Inkscape { |
416 | +namespace LivePathEffect { |
417 | + |
418 | +static const Util::EnumData<FilletMethod> FilletMethodData[FM_END] = { |
419 | + { FM_AUTO, N_("Auto"), "auto" }, |
420 | + { FM_ARC, N_("Force arc"), "arc" }, |
421 | + { FM_BEZIER, N_("Force bezier"), "bezier" } |
422 | +}; |
423 | +static const Util::EnumDataConverter<FilletMethod> |
424 | +FMConverter(FilletMethodData, FM_END); |
425 | + |
426 | +const double tolerance = 0.001; |
427 | +const double gapHelper = 0.00001; |
428 | + |
429 | +LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) : |
430 | + Effect(lpeobject), |
431 | + fillet_chamfer_values(_("Fillet point"), _("Fillet point"), "fillet_chamfer_values", &wr, this), |
432 | + hide_knots(_("Hide knots"), _("Hide knots"), "hide_knots", &wr, this, false), |
433 | + ignore_radius_0(_("Ignore 0 radius knots"), _("Ignore 0 radius knots"), "ignore_radius_0", &wr, this, false), |
434 | + only_selected(_("Change only selected nodes"), _("Change only selected nodes"), "only_selected", &wr, this, false), |
435 | + flexible(_("Flexible radius size (%)"), _("Flexible radius size (%)"), "flexible", &wr, this, false), |
436 | + use_knot_distance(_("Use knots distance instead radius"), _("Use knots distance instead radius"), "use_knot_distance", &wr, this, false), |
437 | + unit(_("Unit"), _("Unit"), "unit", &wr, this), |
438 | + method(_("Method"), _("Fillets methods"), "method", FMConverter, &wr, this, FM_AUTO), |
439 | + radius(_("Radius (unit or %)"), _("Radius, in unit or %"), "radius", &wr, this, 0.), |
440 | + helper_size(_("Helper size with direction"), _("Helper size with direction"), "helper_size", &wr, this, 0) |
441 | +{ |
442 | + registerParameter(&fillet_chamfer_values); |
443 | + registerParameter(&unit); |
444 | + registerParameter(&method); |
445 | + registerParameter(&radius); |
446 | + registerParameter(&helper_size); |
447 | + registerParameter(&flexible); |
448 | + registerParameter(&use_knot_distance); |
449 | + registerParameter(&ignore_radius_0); |
450 | + registerParameter(&only_selected); |
451 | + registerParameter(&hide_knots); |
452 | + |
453 | + radius.param_set_range(0., infinity()); |
454 | + radius.param_set_increments(1, 1); |
455 | + radius.param_set_digits(4); |
456 | + helper_size.param_set_range(0, infinity()); |
457 | + helper_size.param_set_increments(5, 5); |
458 | + helper_size.param_set_digits(0); |
459 | +} |
460 | + |
461 | +LPEFilletChamfer::~LPEFilletChamfer() {} |
462 | + |
463 | +Gtk::Widget *LPEFilletChamfer::newWidget() |
464 | +{ |
465 | + // use manage here, because after deletion of Effect object, others might |
466 | + // still be pointing to this widget. |
467 | + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); |
468 | + |
469 | + vbox->set_border_width(5); |
470 | + vbox->set_homogeneous(false); |
471 | + vbox->set_spacing(2); |
472 | + std::vector<Parameter *>::iterator it = param_vector.begin(); |
473 | + while (it != param_vector.end()) { |
474 | + if ((*it)->widget_is_visible) { |
475 | + Parameter *param = *it; |
476 | + Gtk::Widget *widg = param->param_newWidget(); |
477 | + if (param->param_key == "radius") { |
478 | + Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg)); |
479 | + widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::updateFillet)); |
480 | + widg = widgRegistered; |
481 | + if (widg) { |
482 | + Gtk::HBox *scalarParameter = dynamic_cast<Gtk::HBox *>(widg); |
483 | + std::vector<Gtk::Widget *> childList = scalarParameter->get_children(); |
484 | + Gtk::Entry *entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]); |
485 | + entryWidg->set_width_chars(6); |
486 | + } |
487 | + } else if (param->param_key == "flexible") { |
488 | + Gtk::CheckButton *widgRegistered = Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg)); |
489 | + widgRegistered->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::toggleFlexFixed)); |
490 | + } else if (param->param_key == "helper_size") { |
491 | + Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg)); |
492 | + widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::refreshKnots)); |
493 | + } else if (param->param_key == "hide_knots") { |
494 | + Gtk::CheckButton *widgRegistered = Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg)); |
495 | + widgRegistered->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::toggleHide)); |
496 | + } else if (param->param_key == "only_selected") { |
497 | + Gtk::manage(widg); |
498 | + } else if (param->param_key == "ignore_radius_0") { |
499 | + Gtk::manage(widg); |
500 | + } |
501 | + |
502 | + Glib::ustring *tip = param->param_getTooltip(); |
503 | + if (widg) { |
504 | + vbox->pack_start(*widg, true, true, 2); |
505 | + if (tip) { |
506 | + widg->set_tooltip_text(*tip); |
507 | + } else { |
508 | + widg->set_tooltip_text(""); |
509 | + widg->set_has_tooltip(false); |
510 | + } |
511 | + } |
512 | + } |
513 | + |
514 | + ++it; |
515 | + } |
516 | + |
517 | + Gtk::HBox *filletButtonsContainer = Gtk::manage(new Gtk::HBox(true, 0)); |
518 | + Gtk::Button *fillet = Gtk::manage(new Gtk::Button(Glib::ustring(_("Fillet")))); |
519 | + fillet->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::fillet)); |
520 | + |
521 | + filletButtonsContainer->pack_start(*fillet, true, true, 2); |
522 | + Gtk::Button *inverse = Gtk::manage(new Gtk::Button(Glib::ustring(_("Inverse fillet")))); |
523 | + inverse->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::inverse)); |
524 | + filletButtonsContainer->pack_start(*inverse, true, true, 2); |
525 | + |
526 | + Gtk::HBox *chamferButtonsContainer = Gtk::manage(new Gtk::HBox(true, 0)); |
527 | + Gtk::Button *chamfer = Gtk::manage(new Gtk::Button(Glib::ustring(_("Chamfer")))); |
528 | + chamfer->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::chamfer)); |
529 | + |
530 | + chamferButtonsContainer->pack_start(*chamfer, true, true, 2); |
531 | + Gtk::Button *doubleChamfer = Gtk::manage(new Gtk::Button(Glib::ustring(_("Double chamfer")))); |
532 | + doubleChamfer->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::doubleChamfer)); |
533 | + chamferButtonsContainer->pack_start(*doubleChamfer, true, true, 2); |
534 | + |
535 | + vbox->pack_start(*filletButtonsContainer, true, true, 2); |
536 | + vbox->pack_start(*chamferButtonsContainer, true, true, 2); |
537 | + |
538 | + return vbox; |
539 | +} |
540 | + |
541 | +void LPEFilletChamfer::toggleHide() |
542 | +{ |
543 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
544 | + std::vector<Geom::Point> result; |
545 | + for (std::vector<Point>::const_iterator point_it = filletChamferData.begin(); |
546 | + point_it != filletChamferData.end(); ++point_it) { |
547 | + if (hide_knots) { |
548 | + result.push_back(Point((*point_it)[X], abs((*point_it)[Y]) * -1)); |
549 | + } else { |
550 | + result.push_back(Point((*point_it)[X], abs((*point_it)[Y]))); |
551 | + } |
552 | + } |
553 | + fillet_chamfer_values.param_set_and_write_new_value(result); |
554 | + refreshKnots(); |
555 | +} |
556 | + |
557 | +void LPEFilletChamfer::toggleFlexFixed() |
558 | +{ |
559 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
560 | + std::vector<Geom::Point> result; |
561 | + unsigned int i = 0; |
562 | + for (std::vector<Point>::const_iterator point_it = filletChamferData.begin(); |
563 | + point_it != filletChamferData.end(); ++point_it) { |
564 | + if (flexible) { |
565 | + result.push_back(Point(fillet_chamfer_values.to_time(i, (*point_it)[X]), |
566 | + (*point_it)[Y])); |
567 | + } else { |
568 | + result.push_back(Point(fillet_chamfer_values.to_len(i, (*point_it)[X]), |
569 | + (*point_it)[Y])); |
570 | + } |
571 | + i++; |
572 | + } |
573 | + if (flexible) { |
574 | + radius.param_set_range(0., 100); |
575 | + radius.param_set_value(0); |
576 | + } else { |
577 | + radius.param_set_range(0., infinity()); |
578 | + radius.param_set_value(0); |
579 | + } |
580 | + fillet_chamfer_values.param_set_and_write_new_value(result); |
581 | +} |
582 | + |
583 | +void LPEFilletChamfer::updateFillet() |
584 | +{ |
585 | + double power = 0; |
586 | + if (!flexible) { |
587 | + power = Inkscape::Util::Quantity::convert(radius, unit.get_abbreviation(), "px") * -1; |
588 | + } else { |
589 | + power = radius; |
590 | + } |
591 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
592 | + doUpdateFillet(path_from_piecewise(pwd2, tolerance), power); |
593 | +} |
594 | + |
595 | +void LPEFilletChamfer::fillet() |
596 | +{ |
597 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
598 | + doChangeType(path_from_piecewise(pwd2, tolerance), 1); |
599 | +} |
600 | + |
601 | +void LPEFilletChamfer::inverse() |
602 | +{ |
603 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
604 | + doChangeType(path_from_piecewise(pwd2, tolerance), 2); |
605 | +} |
606 | + |
607 | +void LPEFilletChamfer::chamfer() |
608 | +{ |
609 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
610 | + doChangeType(path_from_piecewise(pwd2, tolerance), 3); |
611 | +} |
612 | + |
613 | +void LPEFilletChamfer::doubleChamfer() |
614 | +{ |
615 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
616 | + doChangeType(path_from_piecewise(pwd2, tolerance), 4); |
617 | +} |
618 | + |
619 | +void LPEFilletChamfer::refreshKnots() |
620 | +{ |
621 | + Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2(); |
622 | + fillet_chamfer_values.recalculate_knots(pwd2); |
623 | + SPDesktop *desktop = inkscape_active_desktop(); |
624 | + if (tools_isactive(desktop, TOOLS_NODES)) { |
625 | + tools_switch(desktop, TOOLS_SELECT); |
626 | + tools_switch(desktop, TOOLS_NODES); |
627 | + } |
628 | +} |
629 | + |
630 | +bool LPEFilletChamfer::nodeIsSelected(Geom::Point nodePoint, std::vector<Geom::Point> point) |
631 | +{ |
632 | + if (point.size() > 0) { |
633 | + for (std::vector<Geom::Point>::iterator i = point.begin(); i != point.end(); |
634 | + ++i) { |
635 | + Geom::Point p = *i; |
636 | + if (Geom::are_near(p, nodePoint, 2)) { |
637 | + return true; |
638 | + } |
639 | + } |
640 | + } |
641 | + return false; |
642 | +} |
643 | +void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power) |
644 | +{ |
645 | + std::vector<Geom::Point> point; |
646 | + SPDesktop *desktop = inkscape_active_desktop(); |
647 | + if (INK_IS_NODE_TOOL(desktop->event_context)) { |
648 | + Inkscape::UI::Tools::NodeTool *nodeTool = INK_NODE_TOOL(desktop->event_context); |
649 | + Inkscape::UI::ControlPointSelection::Set &selection = nodeTool->_selected_nodes->allPoints(); |
650 | + std::vector<Geom::Point>::iterator pBegin; |
651 | + for (Inkscape::UI::ControlPointSelection::Set::iterator i = selection.begin(); i != selection.end(); ++i) { |
652 | + if ((*i)->selected()) { |
653 | + Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i); |
654 | + pBegin = point.begin(); |
655 | + point.insert(pBegin, desktop->doc2dt(n->position())); |
656 | + } |
657 | + } |
658 | + } |
659 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
660 | + std::vector<Geom::Point> result; |
661 | + std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); |
662 | + int counter = 0; |
663 | + for (PathVector::const_iterator path_it = original_pathv_processed.begin(); |
664 | + path_it != original_pathv_processed.end(); ++path_it) { |
665 | + if (path_it->empty()) |
666 | + continue; |
667 | + |
668 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
669 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
670 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
671 | + if (path_it->closed() && path_it->back_closed().isDegenerate()) { |
672 | + const Curve &closingline = path_it->back_closed(); |
673 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
674 | + curve_endit = path_it->end_open(); |
675 | + } |
676 | + } |
677 | + double powerend = 0; |
678 | + while (curve_it1 != curve_endit) { |
679 | + powerend = power; |
680 | + if (power < 0 && !use_knot_distance) { |
681 | + powerend = fillet_chamfer_values.rad_to_len(counter,powerend); |
682 | + } |
683 | + if (power > 0) { |
684 | + powerend = counter + (power / 100); |
685 | + } |
686 | + if (ignore_radius_0 && (filletChamferData[counter][X] == 0 || |
687 | + filletChamferData[counter][X] == counter)) { |
688 | + powerend = filletChamferData[counter][X]; |
689 | + } |
690 | + if (filletChamferData[counter][Y] == 0) { |
691 | + powerend = filletChamferData[counter][X]; |
692 | + } |
693 | + if (only_selected && !nodeIsSelected(curve_it1->initialPoint(), point)) { |
694 | + powerend = filletChamferData[counter][X]; |
695 | + } |
696 | + result.push_back(Point(powerend, filletChamferData[counter][Y])); |
697 | + ++curve_it1; |
698 | + ++curve_it2; |
699 | + counter++; |
700 | + } |
701 | + } |
702 | + fillet_chamfer_values.param_set_and_write_new_value(result); |
703 | +} |
704 | + |
705 | +void LPEFilletChamfer::doChangeType(std::vector<Geom::Path> const& original_pathv, int type) |
706 | +{ |
707 | + std::vector<Geom::Point> point; |
708 | + SPDesktop *desktop = inkscape_active_desktop(); |
709 | + if (INK_IS_NODE_TOOL(desktop->event_context)) { |
710 | + Inkscape::UI::Tools::NodeTool *nodeTool = |
711 | + INK_NODE_TOOL(desktop->event_context); |
712 | + Inkscape::UI::ControlPointSelection::Set &selection = |
713 | + nodeTool->_selected_nodes->allPoints(); |
714 | + std::vector<Geom::Point>::iterator pBegin; |
715 | + for (Inkscape::UI::ControlPointSelection::Set::iterator i = |
716 | + selection.begin(); |
717 | + i != selection.end(); ++i) { |
718 | + if ((*i)->selected()) { |
719 | + Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i); |
720 | + pBegin = point.begin(); |
721 | + point.insert(pBegin, desktop->doc2dt(n->position())); |
722 | + } |
723 | + } |
724 | + } |
725 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
726 | + std::vector<Geom::Point> result; |
727 | + std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); |
728 | + int counter = 0; |
729 | + for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) { |
730 | + int pathCounter = 0; |
731 | + if (path_it->empty()) |
732 | + continue; |
733 | + |
734 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
735 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
736 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
737 | + if (path_it->closed() && path_it->back_closed().isDegenerate()) { |
738 | + const Curve &closingline = path_it->back_closed(); |
739 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
740 | + curve_endit = path_it->end_open(); |
741 | + } |
742 | + } |
743 | + while (curve_it1 != curve_endit) { |
744 | + bool toggle = true; |
745 | + if (filletChamferData[counter][Y] == 0 || |
746 | + (ignore_radius_0 && (filletChamferData[counter][X] == 0 || |
747 | + filletChamferData[counter][X] == counter)) || |
748 | + (only_selected && |
749 | + !nodeIsSelected(curve_it1->initialPoint(), point))) { |
750 | + toggle = false; |
751 | + } |
752 | + if (toggle) { |
753 | + result.push_back(Point(filletChamferData[counter][X], type)); |
754 | + } else { |
755 | + result.push_back(filletChamferData[counter]); |
756 | + } |
757 | + ++curve_it1; |
758 | + if (curve_it2 != curve_endit) { |
759 | + ++curve_it2; |
760 | + } |
761 | + counter++; |
762 | + pathCounter++; |
763 | + } |
764 | + } |
765 | + fillet_chamfer_values.param_set_and_write_new_value(result); |
766 | +} |
767 | + |
768 | +void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem) |
769 | +{ |
770 | + if (SP_IS_SHAPE(lpeItem)) { |
771 | + std::vector<Point> point; |
772 | + PathVector const &original_pathv = pathv_to_linear_and_cubic_beziers(SP_SHAPE(lpeItem)->_curve->get_pathvector()); |
773 | + Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv); |
774 | + for (PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { |
775 | + if (path_it->empty()) |
776 | + continue; |
777 | + |
778 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
779 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
780 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
781 | + if (path_it->closed()) { |
782 | + const Geom::Curve &closingline = path_it->back_closed(); |
783 | + // the closing line segment is always of type |
784 | + // Geom::LineSegment. |
785 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
786 | + // closingline.isDegenerate() did not work, because it only checks for |
787 | + // *exact* zero length, which goes wrong for relative coordinates and |
788 | + // rounding errors... |
789 | + // the closing line segment has zero-length. So stop before that one! |
790 | + curve_endit = path_it->end_open(); |
791 | + } |
792 | + } |
793 | + int counter = 0; |
794 | + while (curve_it1 != curve_endit) { |
795 | + std::pair<std::size_t, std::size_t> positions = fillet_chamfer_values.get_positions(counter, original_pathv); |
796 | + Geom::NodeType nodetype; |
797 | + if (positions.second == 0) { |
798 | + if (path_it->closed()) { |
799 | + Piecewise<D2<SBasis> > u; |
800 | + u.push_cut(0); |
801 | + u.push(pwd2_in[fillet_chamfer_values.last_index(counter, original_pathv)], 1); |
802 | + Geom::Curve const * A = path_from_piecewise(u, 0.1)[0][0].duplicate(); |
803 | + nodetype = get_nodetype(*A, *curve_it1); |
804 | + } else { |
805 | + nodetype = NODE_NONE; |
806 | + } |
807 | + } else { |
808 | + nodetype = get_nodetype((*path_it)[counter - 1], *curve_it1); |
809 | + } |
810 | + if (nodetype == NODE_CUSP) { |
811 | + point.push_back(Point(0, 1)); |
812 | + } else { |
813 | + point.push_back(Point(0, 0)); |
814 | + } |
815 | + ++curve_it1; |
816 | + if (curve_it2 != curve_endit) { |
817 | + ++curve_it2; |
818 | + } |
819 | + counter++; |
820 | + } |
821 | + } |
822 | + fillet_chamfer_values.param_set_and_write_new_value(point); |
823 | + } else { |
824 | + g_warning("LPE Fillet can only be applied to shapes (not groups)."); |
825 | + } |
826 | +} |
827 | + |
828 | +void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem) |
829 | +{ |
830 | + if (SP_IS_SHAPE(lpeItem)) { |
831 | + if(hide_knots){ |
832 | + fillet_chamfer_values.set_helper_size(0); |
833 | + } else { |
834 | + fillet_chamfer_values.set_helper_size(helper_size); |
835 | + } |
836 | + fillet_chamfer_values.set_use_distance(use_knot_distance); |
837 | + fillet_chamfer_values.set_unit(unit.get_abbreviation()); |
838 | + SPCurve *c = SP_IS_PATH(lpeItem) ? static_cast<SPPath const *>(lpeItem) |
839 | + ->get_original_curve() |
840 | + : SP_SHAPE(lpeItem)->getCurve(); |
841 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
842 | + if (!filletChamferData.empty() && getKnotsNumber(c) != (int) |
843 | + filletChamferData.size()) { |
844 | + PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector()); |
845 | + Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv); |
846 | + fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pwd2_in); |
847 | + } |
848 | + } else { |
849 | + g_warning("LPE Fillet can only be applied to shapes (not groups)."); |
850 | + } |
851 | +} |
852 | + |
853 | +int LPEFilletChamfer::getKnotsNumber(SPCurve const *c) |
854 | +{ |
855 | + int nKnots = c->nodes_in_path(); |
856 | + PathVector const pv = pathv_to_linear_and_cubic_beziers(c->get_pathvector()); |
857 | + for (std::vector<Geom::Path>::const_iterator path_it = pv.begin(); |
858 | + path_it != pv.end(); ++path_it) { |
859 | + if (!(*path_it).closed()) { |
860 | + nKnots--; |
861 | + } |
862 | + } |
863 | + return nKnots; |
864 | +} |
865 | + |
866 | +void |
867 | +LPEFilletChamfer::adjustForNewPath(std::vector<Geom::Path> const &path_in) |
868 | +{ |
869 | + if (!path_in.empty()) { |
870 | + fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pathv_to_linear_and_cubic_beziers(path_in)[0].toPwSb()); |
871 | + } |
872 | +} |
873 | + |
874 | +std::vector<Geom::Path> |
875 | +LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) |
876 | +{ |
877 | + std::vector<Geom::Path> pathvector_out; |
878 | + Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(pathv_to_linear_and_cubic_beziers(path_in)); |
879 | + pwd2_in = remove_short_cuts(pwd2_in, .01); |
880 | + Piecewise<D2<SBasis> > der = derivative(pwd2_in); |
881 | + Piecewise<D2<SBasis> > n = rot90(unitVector(der)); |
882 | + fillet_chamfer_values.set_pwd2(pwd2_in, n); |
883 | + std::vector<Point> filletChamferData = fillet_chamfer_values.data(); |
884 | + unsigned int counter = 0; |
885 | + const double K = (4.0 / 3.0) * (sqrt(2.0) - 1.0); |
886 | + std::vector<Geom::Path> path_in_processed = pathv_to_linear_and_cubic_beziers(path_in); |
887 | + for (PathVector::const_iterator path_it = path_in_processed.begin(); |
888 | + path_it != path_in_processed.end(); ++path_it) { |
889 | + if (path_it->empty()) |
890 | + continue; |
891 | + Geom::Path path_out; |
892 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
893 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
894 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
895 | + if (path_it->closed()) { |
896 | + const Geom::Curve &closingline = path_it->back_closed(); |
897 | + // the closing line segment is always of type |
898 | + // Geom::LineSegment. |
899 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
900 | + // closingline.isDegenerate() did not work, because it only checks for |
901 | + // *exact* zero length, which goes wrong for relative coordinates and |
902 | + // rounding errors... |
903 | + // the closing line segment has zero-length. So stop before that one! |
904 | + curve_endit = path_it->end_open(); |
905 | + } |
906 | + } |
907 | + unsigned int counterCurves = 0; |
908 | + while (curve_it1 != curve_endit) { |
909 | + Curve *curve_it2Fixed = (*path_it->begin()).duplicate(); |
910 | + if(!path_it->closed() || curve_it2 != curve_endit){ |
911 | + curve_it2Fixed = (*curve_it2).duplicate(); |
912 | + } |
913 | + bool last = curve_it2 == curve_endit; |
914 | + std::vector<double> times = fillet_chamfer_values.get_times(counter, path_in, last); |
915 | + Curve *knotCurve1 = curve_it1->portion(times[0], times[1]); |
916 | + if (counterCurves > 0) { |
917 | + knotCurve1->setInitial(path_out.finalPoint()); |
918 | + } else { |
919 | + path_out.start((*curve_it1).pointAt(times[0])); |
920 | + } |
921 | + Curve *knotCurve2 = curve_it2Fixed->portion(times[2], 1); |
922 | + Point startArcPoint = knotCurve1->finalPoint(); |
923 | + Point endArcPoint = curve_it2Fixed->pointAt(times[2]); |
924 | + double k1 = distance(startArcPoint, curve_it1->finalPoint()) * K; |
925 | + double k2 = distance(endArcPoint, curve_it1->finalPoint()) * K; |
926 | + Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1); |
927 | + Ray ray1(startArcPoint, curve_it1->finalPoint()); |
928 | + if (cubic1) { |
929 | + ray1.setPoints((*cubic1)[2], startArcPoint); |
930 | + } |
931 | + Point handle1 = Point::polar(ray1.angle(),k1) + startArcPoint; |
932 | + Geom::CubicBezier const *cubic2 = |
933 | + dynamic_cast<Geom::CubicBezier const *>(&*knotCurve2); |
934 | + Ray ray2(curve_it1->finalPoint(), endArcPoint); |
935 | + if (cubic2) { |
936 | + ray2.setPoints(endArcPoint, (*cubic2)[1]); |
937 | + } |
938 | + Point handle2 = endArcPoint - Point::polar(ray2.angle(),k2); |
939 | + bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0; |
940 | + double angle = angle_between(ray1, ray2, ccwToggle); |
941 | + double handleAngle = ray1.angle() - angle; |
942 | + if (ccwToggle) { |
943 | + handleAngle = ray1.angle() + angle; |
944 | + } |
945 | + Point inverseHandle1 = Point::polar(handleAngle,k1) + startArcPoint; |
946 | + handleAngle = ray2.angle() + angle; |
947 | + if (ccwToggle) { |
948 | + handleAngle = ray2.angle() - angle; |
949 | + } |
950 | + Point inverseHandle2 = endArcPoint - Point::polar(handleAngle,k2); |
951 | + //straigth lines arc based |
952 | + Line const x_line(Geom::Point(0,0),Geom::Point(1,0)); |
953 | + Line const angled_line(startArcPoint,endArcPoint); |
954 | + double angleArc = Geom::angle_between( x_line,angled_line); |
955 | + double radius = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint))/sin(angle/2.0); |
956 | + Coord rx = radius; |
957 | + Coord ry = rx; |
958 | + |
959 | + if (times[1] != 1) { |
960 | + if (times[1] != gapHelper && times[1] != times[0] + gapHelper) { |
961 | + path_out.append(*knotCurve1); |
962 | + } |
963 | + int type = 0; |
964 | + if(path_it->closed() && last){ |
965 | + type = abs(filletChamferData[counter - counterCurves][Y]); |
966 | + } else if (!path_it->closed() && last){ |
967 | + //0 |
968 | + } else { |
969 | + type = abs(filletChamferData[counter + 1][Y]); |
970 | + } |
971 | + if (type == 3 || type == 4) { |
972 | + if (type == 4) { |
973 | + Geom::Point central = middle_point(startArcPoint, endArcPoint); |
974 | + LineSegment chamferCenter(central, curve_it1->finalPoint()); |
975 | + path_out.appendNew<Geom::LineSegment>(chamferCenter.pointAt(0.5)); |
976 | + } |
977 | + path_out.appendNew<Geom::LineSegment>(endArcPoint); |
978 | + } else if (type == 2) { |
979 | + if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ |
980 | + ccwToggle = ccwToggle?0:1; |
981 | + path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); |
982 | + }else{ |
983 | + path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint); |
984 | + } |
985 | + } else { |
986 | + if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ |
987 | + path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); |
988 | + } else { |
989 | + path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint); |
990 | + } |
991 | + } |
992 | + } else { |
993 | + path_out.append(*knotCurve1); |
994 | + } |
995 | + if (path_it->closed() && last) { |
996 | + path_out.close(); |
997 | + } |
998 | + ++curve_it1; |
999 | + if (curve_it2 != curve_endit) { |
1000 | + ++curve_it2; |
1001 | + } |
1002 | + counter++; |
1003 | + counterCurves++; |
1004 | + } |
1005 | + pathvector_out.push_back(path_out); |
1006 | + } |
1007 | + return pathvector_out; |
1008 | +} |
1009 | + |
1010 | +}; //namespace LivePathEffect |
1011 | +}; /* namespace Inkscape */ |
1012 | + |
1013 | +/* |
1014 | + Local Variables: |
1015 | + mode:c++ |
1016 | + c-file-style:"stroustrup" |
1017 | + c-file-offset:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1018 | + indent-tabs-mode:nil |
1019 | + fill-column:99 |
1020 | + End: |
1021 | +*/ |
1022 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1023 | |
1024 | === added file 'src/live_effects/lpe-fillet-chamfer.h' |
1025 | --- src/live_effects/lpe-fillet-chamfer.h 1970-01-01 00:00:00 +0000 |
1026 | +++ src/live_effects/lpe-fillet-chamfer.h 2014-10-22 21:40:37 +0000 |
1027 | @@ -0,0 +1,101 @@ |
1028 | +#ifndef INKSCAPE_LPE_FILLET_CHAMFER_H |
1029 | +#define INKSCAPE_LPE_FILLET_CHAMFER_H |
1030 | + |
1031 | +/* |
1032 | + * Author(s): |
1033 | + * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es> |
1034 | + * |
1035 | + * Copyright (C) 2014 Author(s) |
1036 | + * |
1037 | + * Special thanks to Johan Engelen for the base of the effect -powerstroke- |
1038 | + * Also to ScislaC for point me to the idea |
1039 | + * Also su_v for his construvtive feedback and time |
1040 | + * and finaly to Liam P. White for his big help on coding, that save me a lot of hours |
1041 | + * |
1042 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1043 | + */ |
1044 | + |
1045 | +#if HAVE_CONFIG_H |
1046 | +# include "config.h" |
1047 | +#endif |
1048 | + |
1049 | +#if defined(GLIBMM_DISABLE_DEPRECATED) && defined(HAVE_GLIBMM_THREADS_H) |
1050 | +# include <glibmm/threads.h> |
1051 | +#endif |
1052 | + |
1053 | +#include "live_effects/parameter/enum.h" |
1054 | +#include "live_effects/parameter/bool.h" |
1055 | +#include "live_effects/parameter/unit.h" |
1056 | + |
1057 | +#include "live_effects/parameter/filletchamferpointarray.h" |
1058 | +#include "live_effects/effect.h" |
1059 | + |
1060 | +namespace Inkscape { |
1061 | +namespace LivePathEffect { |
1062 | + |
1063 | +enum FilletMethod { |
1064 | + FM_AUTO, |
1065 | + FM_ARC, |
1066 | + FM_BEZIER, |
1067 | + FM_END |
1068 | +}; |
1069 | + |
1070 | +class LPEFilletChamfer : public Effect { |
1071 | +public: |
1072 | + LPEFilletChamfer(LivePathEffectObject *lpeobject); |
1073 | + virtual ~LPEFilletChamfer(); |
1074 | + |
1075 | + virtual std::vector<Geom::Path> doEffect_path(std::vector<Geom::Path> const &path_in); |
1076 | + |
1077 | + virtual void doOnApply(SPLPEItem const *lpeItem); |
1078 | + virtual void doBeforeEffect(SPLPEItem const *lpeItem); |
1079 | + virtual void adjustForNewPath(std::vector<Geom::Path> const &path_in); |
1080 | + virtual Gtk::Widget* newWidget(); |
1081 | + |
1082 | + int getKnotsNumber(SPCurve const *c); |
1083 | + void toggleHide(); |
1084 | + void toggleFlexFixed(); |
1085 | + void chamfer(); |
1086 | + void fillet(); |
1087 | + void doubleChamfer(); |
1088 | + void inverse(); |
1089 | + void updateFillet(); |
1090 | + void doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power); |
1091 | + void doChangeType(std::vector<Geom::Path> const& original_pathv, int type); |
1092 | + bool nodeIsSelected(Geom::Point nodePoint, std::vector<Geom::Point> points); |
1093 | + void refreshKnots(); |
1094 | + |
1095 | + FilletChamferPointArrayParam fillet_chamfer_values; |
1096 | + |
1097 | +private: |
1098 | + |
1099 | + BoolParam hide_knots; |
1100 | + BoolParam ignore_radius_0; |
1101 | + BoolParam only_selected; |
1102 | + BoolParam flexible; |
1103 | + BoolParam use_knot_distance; |
1104 | + UnitParam unit; |
1105 | + EnumParam<FilletMethod> method; |
1106 | + ScalarParam radius; |
1107 | + ScalarParam helper_size; |
1108 | + |
1109 | + LPEFilletChamfer(const LPEFilletChamfer &); |
1110 | + LPEFilletChamfer &operator=(const LPEFilletChamfer &); |
1111 | + |
1112 | +}; |
1113 | + |
1114 | +} //namespace LivePathEffect |
1115 | +} //namespace Inkscape |
1116 | + |
1117 | +#endif |
1118 | + |
1119 | +/* |
1120 | + Local Variables: |
1121 | + mode:c++ |
1122 | + c-file-style:"stroustrup" |
1123 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1124 | + indent-tabs-mode:nil |
1125 | + fill-column:99 |
1126 | + End: |
1127 | +*/ |
1128 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1129 | |
1130 | === modified file 'src/live_effects/parameter/Makefile_insert' |
1131 | --- src/live_effects/parameter/Makefile_insert 2012-01-12 21:06:16 +0000 |
1132 | +++ src/live_effects/parameter/Makefile_insert 2014-10-22 21:40:37 +0000 |
1133 | @@ -20,6 +20,8 @@ |
1134 | live_effects/parameter/originalpath.h \ |
1135 | live_effects/parameter/powerstrokepointarray.cpp \ |
1136 | live_effects/parameter/powerstrokepointarray.h \ |
1137 | + live_effects/parameter/filletchamferpointarray.cpp \ |
1138 | + live_effects/parameter/filletchamferpointarray.h \ |
1139 | live_effects/parameter/text.cpp \ |
1140 | live_effects/parameter/text.h \ |
1141 | live_effects/parameter/unit.cpp \ |
1142 | |
1143 | === added file 'src/live_effects/parameter/filletchamferpointarray.cpp' |
1144 | --- src/live_effects/parameter/filletchamferpointarray.cpp 1970-01-01 00:00:00 +0000 |
1145 | +++ src/live_effects/parameter/filletchamferpointarray.cpp 2014-10-22 21:40:37 +0000 |
1146 | @@ -0,0 +1,882 @@ |
1147 | +/* |
1148 | + * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es> |
1149 | + * Special thanks to Johan Engelen for the base of the effect -powerstroke- |
1150 | + * Also to ScislaC for point me to the idea |
1151 | + * Also su_v for his construvtive feedback and time |
1152 | + * and finaly to Liam P. White for his big help on coding, that save me a lot of |
1153 | + * hours |
1154 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1155 | + */ |
1156 | + |
1157 | +#include <2geom/piecewise.h> |
1158 | +#include <2geom/sbasis-to-bezier.h> |
1159 | +#include <2geom/sbasis-geometric.h> |
1160 | +#include <2geom/svg-elliptical-arc.h> |
1161 | +#include <2geom/line.h> |
1162 | +#include <2geom/path-intersection.h> |
1163 | + |
1164 | +#include "ui/dialog/lpe-fillet-chamfer-properties.h" |
1165 | +#include "live_effects/parameter/filletchamferpointarray.h" |
1166 | +#include "live_effects/effect.h" |
1167 | +#include "svg/svg.h" |
1168 | +#include "svg/stringstream.h" |
1169 | +#include "knotholder.h" |
1170 | +#include "sp-lpe-item.h" |
1171 | +#include "selection.h" |
1172 | + |
1173 | +// needed for on-canvas editting: |
1174 | +#include "desktop.h" |
1175 | +#include "live_effects/lpeobject.h" |
1176 | +#include "helper/geom-nodetype.h" |
1177 | +#include "helper/geom-curves.h" |
1178 | +#include "ui/tools/node-tool.h" |
1179 | + |
1180 | +// TODO due to internal breakage in glibmm headers, |
1181 | +// this has to be included last. |
1182 | +#include <glibmm/i18n.h> |
1183 | + |
1184 | + |
1185 | +using namespace Geom; |
1186 | + |
1187 | +namespace Inkscape { |
1188 | + |
1189 | +namespace LivePathEffect { |
1190 | + |
1191 | +FilletChamferPointArrayParam::FilletChamferPointArrayParam( |
1192 | + const Glib::ustring &label, const Glib::ustring &tip, |
1193 | + const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr, |
1194 | + Effect *effect) |
1195 | + : ArrayParam<Point>(label, tip, key, wr, effect, 0) |
1196 | +{ |
1197 | + knot_shape = SP_KNOT_SHAPE_DIAMOND; |
1198 | + knot_mode = SP_KNOT_MODE_XOR; |
1199 | + knot_color = 0x00ff0000; |
1200 | +} |
1201 | + |
1202 | +FilletChamferPointArrayParam::~FilletChamferPointArrayParam() {} |
1203 | + |
1204 | +Gtk::Widget *FilletChamferPointArrayParam::param_newWidget() |
1205 | +{ |
1206 | + return NULL; |
1207 | + /* |
1208 | + Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = |
1209 | + Gtk::manage( |
1210 | + new Inkscape::UI::Widget::RegisteredTransformedPoint( |
1211 | + param_label, |
1212 | + param_tooltip, |
1213 | + param_key, |
1214 | + *param_wr, |
1215 | + param_effect->getRepr(), |
1216 | + param_effect->getSPDoc() |
1217 | + ) ); |
1218 | + // TODO: fix to get correct desktop (don't use SP_ACTIVE_DESKTOP) |
1219 | + SPDesktop *desktop = SP_ACTIVE_DESKTOP; |
1220 | + Affine transf = desktop->doc2dt(); |
1221 | + pointwdg->setTransform(transf); |
1222 | + pointwdg->setValue( *this ); |
1223 | + pointwdg->clearProgrammatically(); |
1224 | + pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
1225 | + _("Change point parameter")); |
1226 | + |
1227 | + Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() ); |
1228 | + static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true); |
1229 | + static_cast<Gtk::HBox*>(hbox)->show_all_children(); |
1230 | + |
1231 | + return dynamic_cast<Gtk::Widget *> (hbox); |
1232 | + */ |
1233 | +} |
1234 | + |
1235 | +void |
1236 | +FilletChamferPointArrayParam::param_transform_multiply(Affine const &postmul, |
1237 | + bool /*set*/) |
1238 | +{ |
1239 | + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); |
1240 | + |
1241 | + if (prefs->getBool("/options/transform/rectcorners", true) && |
1242 | + _vector[1][X] <= 0) { |
1243 | + std::vector<Geom::Point> result; |
1244 | + for (std::vector<Point>::const_iterator point_it = _vector.begin(); |
1245 | + point_it != _vector.end(); ++point_it) { |
1246 | + Coord A = |
1247 | + (*point_it)[X] * ((postmul.expansionX() + postmul.expansionY()) / 2); |
1248 | + result.push_back(Point(A, (*point_it)[Y])); |
1249 | + } |
1250 | + param_set_and_write_new_value(result); |
1251 | + } |
1252 | + |
1253 | + // param_set_and_write_new_value( (*this) * postmul ); |
1254 | +} |
1255 | + |
1256 | +/** call this method to recalculate the controlpoints such that they stay at the |
1257 | + * same location relative to the new path. Useful after adding/deleting nodes to |
1258 | + * the path.*/ |
1259 | +void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2( |
1260 | + Piecewise<D2<SBasis> > const &pwd2_in) |
1261 | +{ |
1262 | + if (!last_pwd2.empty()) { |
1263 | + PathVector const pathv = |
1264 | + path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001); |
1265 | + PathVector last_pathv = |
1266 | + path_from_piecewise(remove_short_cuts(last_pwd2, 0.1), 0.001); |
1267 | + std::vector<Point> result; |
1268 | + unsigned long counter = 0; |
1269 | + unsigned long counterPaths = 0; |
1270 | + unsigned long counterCurves = 0; |
1271 | + long offset = 0; |
1272 | + long offsetPaths = 0; |
1273 | + Geom::NodeType nodetype; |
1274 | + for (PathVector::const_iterator path_it = pathv.begin(); |
1275 | + path_it != pathv.end(); ++path_it) { |
1276 | + if (path_it->empty()) { |
1277 | + counterPaths++; |
1278 | + counter++; |
1279 | + continue; |
1280 | + } |
1281 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
1282 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
1283 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
1284 | + if (path_it->closed() && path_it->back_closed().isDegenerate()) { |
1285 | + const Curve &closingline = path_it->back_closed(); |
1286 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
1287 | + curve_endit = path_it->end_open(); |
1288 | + } |
1289 | + } |
1290 | + counterCurves = 0; |
1291 | + while (curve_it1 != curve_endit) { |
1292 | + //if start a path get node type |
1293 | + if (counterCurves == 0) { |
1294 | + if (path_it->closed()) { |
1295 | + if (path_it->back_closed().isDegenerate()) { |
1296 | + nodetype = get_nodetype(path_it->back_open(), *curve_it1); |
1297 | + } else { |
1298 | + nodetype = get_nodetype(path_it->back_closed(), *curve_it1); |
1299 | + } |
1300 | + } else { |
1301 | + nodetype = NODE_NONE; |
1302 | + } |
1303 | + } else { |
1304 | + //check node type also whith straight lines because get_nodetype |
1305 | + //return non cusp node in a node inserted inside a straight line |
1306 | + //todo: if the path remove some nodes whith the result of a straight |
1307 | + //line but with handles, the node inserted into dont fire the knot |
1308 | + // because is not handle as cusp node by get_nodetype function |
1309 | + bool this_is_line = true; |
1310 | + bool next_is_line = is_straight_curve(*curve_it1); |
1311 | + this_is_line = is_straight_curve((*path_it)[counterCurves - 1]); |
1312 | + nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1); |
1313 | + if (this_is_line || next_is_line) { |
1314 | + nodetype = NODE_CUSP; |
1315 | + } |
1316 | + } |
1317 | + if (last_pathv.size() > pathv.size() || |
1318 | + (last_pathv.size() > counterPaths && |
1319 | + last_pathv[counterPaths].size() > counter - offset && |
1320 | + !are_near(curve_it1->initialPoint(), |
1321 | + last_pathv[counterPaths][counter - offset].initialPoint(), |
1322 | + 0.1))) { |
1323 | + if ( curve_it2 == curve_endit) { |
1324 | + if (last_pathv[counterPaths].size() < pathv[counterPaths].size()) { |
1325 | + offset = abs(last_pathv[counterPaths].size() - |
1326 | + pathv[counterPaths].size()); |
1327 | + } else if (last_pathv[counterPaths].size() > |
1328 | + pathv[counterPaths].size()) { |
1329 | + offset = (abs(last_pathv[counterPaths].size() - |
1330 | + pathv[counterPaths].size())) * -1; |
1331 | + } else { |
1332 | + offset = 0; |
1333 | + } |
1334 | + offsetPaths += offset; |
1335 | + offset = offsetPaths; |
1336 | + } else if (counterCurves == 0 && last_pathv.size() <= pathv.size() && |
1337 | + counter - offset <= last_pathv[counterPaths].size() && |
1338 | + are_near(curve_it1->initialPoint(), |
1339 | + last_pathv[counterPaths].finalPoint(), 0.1) && |
1340 | + !last_pathv[counterPaths].closed()) { |
1341 | + long e = counter - offset + 1; |
1342 | + std::vector<Point> tmp = _vector; |
1343 | + for (unsigned long i = |
1344 | + last_pathv[counterPaths].size() + counter - offset; |
1345 | + i > counterCurves - offset + 1; i--) { |
1346 | + |
1347 | + if (tmp[i - 1][X] > 0) { |
1348 | + double fractpart, intpart; |
1349 | + fractpart = modf(tmp[i - 1][X], &intpart); |
1350 | + _vector[e] = Point(e + fractpart, tmp[i - 1][Y]); |
1351 | + } else { |
1352 | + _vector[e] = Point(tmp[i - 1][X], tmp[i - 1][Y]); |
1353 | + } |
1354 | + e++; |
1355 | + } |
1356 | + //delete temp vector |
1357 | + std::vector<Point>().swap(tmp); |
1358 | + if (last_pathv.size() > counterPaths) { |
1359 | + last_pathv[counterPaths] = last_pathv[counterPaths].reverse(); |
1360 | + } |
1361 | + } else { |
1362 | + if (last_pathv.size() > counterPaths) { |
1363 | + if (last_pathv[counterPaths].size() < |
1364 | + pathv[counterPaths].size()) { |
1365 | + offset++; |
1366 | + } else if (last_pathv[counterPaths].size() > |
1367 | + pathv[counterPaths].size()) { |
1368 | + offset--; |
1369 | + continue; |
1370 | + } |
1371 | + } else { |
1372 | + offset++; |
1373 | + } |
1374 | + } |
1375 | + double xPos = 0; |
1376 | + if (_vector[1][X] > 0) { |
1377 | + xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); |
1378 | + } |
1379 | + if (nodetype == NODE_CUSP) { |
1380 | + result.push_back(Point(xPos, 1)); |
1381 | + } else { |
1382 | + result.push_back(Point(xPos, 0)); |
1383 | + } |
1384 | + } else { |
1385 | + double xPos = _vector[counter - offset][X]; |
1386 | + if (_vector.size() <= (unsigned)(counter - offset)) { |
1387 | + if (_vector[1][X] > 0) { |
1388 | + xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); |
1389 | + } else { |
1390 | + xPos = 0; |
1391 | + } |
1392 | + } |
1393 | + if (nodetype == NODE_CUSP) { |
1394 | + double vectorY = _vector[counter - offset][Y]; |
1395 | + if (_vector.size() <= (unsigned)(counter - offset) || vectorY == 0) { |
1396 | + vectorY = 1; |
1397 | + } |
1398 | + result.push_back(Point(xPos, vectorY)); |
1399 | + } else { |
1400 | + if (_vector[1][X] < 0) { |
1401 | + xPos = 0; |
1402 | + } |
1403 | + result.push_back(Point(floor(xPos), 0)); |
1404 | + } |
1405 | + } |
1406 | + ++curve_it1; |
1407 | + if (curve_it2 != curve_endit) { |
1408 | + ++curve_it2; |
1409 | + } |
1410 | + counter++; |
1411 | + counterCurves++; |
1412 | + } |
1413 | + counterPaths++; |
1414 | + } |
1415 | + _vector = result; |
1416 | + write_to_SVG(); |
1417 | + } |
1418 | +} |
1419 | + |
1420 | +void FilletChamferPointArrayParam::recalculate_knots( |
1421 | + Piecewise<D2<SBasis> > const &pwd2_in) |
1422 | +{ |
1423 | + bool change = false; |
1424 | + PathVector pathv = path_from_piecewise(pwd2_in, 0.001); |
1425 | + if (!pathv.empty()) { |
1426 | + std::vector<Point> result; |
1427 | + int counter = 0; |
1428 | + int counterCurves = 0; |
1429 | + Geom::NodeType nodetype; |
1430 | + for (PathVector::const_iterator path_it = pathv.begin(); |
1431 | + path_it != pathv.end(); ++path_it) { |
1432 | + if (path_it->empty()) { |
1433 | + counter++; |
1434 | + continue; |
1435 | + } |
1436 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
1437 | + Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); |
1438 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
1439 | + if (path_it->closed() && path_it->back_closed().isDegenerate()) { |
1440 | + const Curve &closingline = path_it->back_closed(); |
1441 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
1442 | + curve_endit = path_it->end_open(); |
1443 | + } |
1444 | + } |
1445 | + counterCurves = 0; |
1446 | + while (curve_it1 != curve_endit) { |
1447 | + //if start a path get node type |
1448 | + if (counterCurves == 0) { |
1449 | + if (path_it->closed()) { |
1450 | + if (path_it->back_closed().isDegenerate()) { |
1451 | + nodetype = get_nodetype(path_it->back_open(), *curve_it1); |
1452 | + } else { |
1453 | + nodetype = get_nodetype(path_it->back_closed(), *curve_it1); |
1454 | + } |
1455 | + } else { |
1456 | + nodetype = NODE_NONE; |
1457 | + } |
1458 | + } else { |
1459 | + bool this_is_line = true; |
1460 | + bool next_is_line = is_straight_curve(*curve_it1); |
1461 | + this_is_line = is_straight_curve((*path_it)[counterCurves - 1]); |
1462 | + nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1); |
1463 | + if (this_is_line || next_is_line) { |
1464 | + nodetype = NODE_CUSP; |
1465 | + } |
1466 | + } |
1467 | + if (nodetype == NODE_CUSP) { |
1468 | + double vectorY = _vector[counter][Y]; |
1469 | + if (vectorY == 0) { |
1470 | + vectorY = 1; |
1471 | + change = true; |
1472 | + } |
1473 | + result.push_back(Point(_vector[counter][X], vectorY)); |
1474 | + } else { |
1475 | + double xPos = floor(_vector[counter][X]); |
1476 | + if (_vector[1][X] < 0) { |
1477 | + xPos = 0; |
1478 | + } |
1479 | + double vectorY = _vector[counter][Y]; |
1480 | + if (vectorY != 0) { |
1481 | + change = true; |
1482 | + } |
1483 | + result.push_back(Point(xPos, 0)); |
1484 | + } |
1485 | + ++curve_it1; |
1486 | + counter++; |
1487 | + if (curve_it2 != curve_endit) { |
1488 | + ++curve_it2; |
1489 | + } |
1490 | + counterCurves++; |
1491 | + } |
1492 | + } |
1493 | + if (change) { |
1494 | + _vector = result; |
1495 | + write_to_SVG(); |
1496 | + } |
1497 | + } |
1498 | +} |
1499 | + |
1500 | +void FilletChamferPointArrayParam::set_pwd2( |
1501 | + Piecewise<D2<SBasis> > const &pwd2_in, |
1502 | + Piecewise<D2<SBasis> > const &pwd2_normal_in) |
1503 | +{ |
1504 | + last_pwd2 = pwd2_in; |
1505 | + last_pwd2_normal = pwd2_normal_in; |
1506 | +} |
1507 | + |
1508 | +void FilletChamferPointArrayParam::set_helper_size(int hs) |
1509 | +{ |
1510 | + helper_size = hs; |
1511 | +} |
1512 | + |
1513 | +void FilletChamferPointArrayParam::set_use_distance(bool use_knot_distance ) |
1514 | +{ |
1515 | + use_distance = use_knot_distance; |
1516 | +} |
1517 | + |
1518 | +void FilletChamferPointArrayParam::set_unit(const gchar *abbr) |
1519 | +{ |
1520 | + unit = abbr; |
1521 | +} |
1522 | + |
1523 | +void FilletChamferPointArrayParam::updateCanvasIndicators() |
1524 | +{ |
1525 | + std::vector<Point> ts = data(); |
1526 | + hp.clear(); |
1527 | + unsigned int i = 0; |
1528 | + for (std::vector<Point>::const_iterator point_it = ts.begin(); |
1529 | + point_it != ts.end(); ++point_it) { |
1530 | + double Xvalue = to_time(i, (*point_it)[X]) -i; |
1531 | + if (Xvalue == 0) { |
1532 | + i++; |
1533 | + continue; |
1534 | + } |
1535 | + Geom::Point ptA = last_pwd2[i].valueAt(Xvalue); |
1536 | + Geom::Point derivA = unit_vector(derivative(last_pwd2[i]).valueAt(Xvalue)); |
1537 | + Geom::Rotate rot(Geom::Rotate::from_degrees(-90)); |
1538 | + derivA = derivA * rot; |
1539 | + Geom::Point C = ptA - derivA * helper_size; |
1540 | + Geom::Point D = ptA + derivA * helper_size; |
1541 | + Geom::Ray ray1(C, D); |
1542 | + char const * svgd = "M 1,0.25 0.5,0 1,-0.25 M 1,0.5 0,0 1,-0.5"; |
1543 | + Geom::PathVector pathv = sp_svg_read_pathv(svgd); |
1544 | + Geom::Affine aff = Geom::Affine(); |
1545 | + aff *= Geom::Scale(helper_size); |
1546 | + aff *= Geom::Rotate(ray1.angle() - deg_to_rad(270)); |
1547 | + pathv *= aff; |
1548 | + pathv += last_pwd2[i].valueAt(Xvalue); |
1549 | + hp.push_back(pathv[0]); |
1550 | + hp.push_back(pathv[1]); |
1551 | + i++; |
1552 | + } |
1553 | +} |
1554 | + |
1555 | +void FilletChamferPointArrayParam::addCanvasIndicators( |
1556 | + SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) |
1557 | +{ |
1558 | + hp_vec.push_back(hp); |
1559 | +} |
1560 | + |
1561 | +double FilletChamferPointArrayParam::rad_to_len(int index, double rad) |
1562 | +{ |
1563 | + double len = 0; |
1564 | + std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1); |
1565 | + std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths); |
1566 | + D2<SBasis> A = last_pwd2[last_index(index, subpaths)]; |
1567 | + if(positions.second != 0){ |
1568 | + A = last_pwd2[index-1]; |
1569 | + }else{ |
1570 | + if(!subpaths[positions.first].closed()){ |
1571 | + return len; |
1572 | + } |
1573 | + } |
1574 | + D2<SBasis> B = last_pwd2[index]; |
1575 | + Piecewise<D2<SBasis> > offset_curve0 = Piecewise<D2<SBasis> >(A)+rot90(unitVector(derivative(A)))*(rad); |
1576 | + Piecewise<D2<SBasis> > offset_curve1 = Piecewise<D2<SBasis> >(B)+rot90(unitVector(derivative(B)))*(rad); |
1577 | + Geom::Path p0 = path_from_piecewise(offset_curve0, 0.1)[0]; |
1578 | + Geom::Path p1 = path_from_piecewise(offset_curve1, 0.1)[0]; |
1579 | + Geom::Crossings cs = Geom::crossings(p0, p1); |
1580 | + if(cs.size() > 0){ |
1581 | + Point cp =p0(cs[0].ta); |
1582 | + double p0pt = nearest_point(cp, B); |
1583 | + len = time_to_len(index,p0pt); |
1584 | + } else { |
1585 | + if(rad < 0){ |
1586 | + len = rad_to_len(index, rad * -1); |
1587 | + } |
1588 | + } |
1589 | + return len; |
1590 | +} |
1591 | + |
1592 | +double FilletChamferPointArrayParam::len_to_rad(int index, double len) |
1593 | +{ |
1594 | + double rad = 0; |
1595 | + double tmp_len = _vector[index][X]; |
1596 | + _vector[index] = Geom::Point(len,_vector[index][Y]); |
1597 | + std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1); |
1598 | + std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths); |
1599 | + Piecewise<D2<SBasis> > u; |
1600 | + u.push_cut(0); |
1601 | + u.push(last_pwd2[last_index(index, subpaths)], 1); |
1602 | + Geom::Curve * A = path_from_piecewise(u, 0.1)[0][0].duplicate(); |
1603 | + Geom::Curve * B = subpaths[positions.first][positions.second].duplicate(); |
1604 | + std::vector<double> times; |
1605 | + if(positions.second != 0){ |
1606 | + A = subpaths[positions.first][positions.second-1].duplicate(); |
1607 | + times = get_times(index-1, subpaths, false); |
1608 | + }else{ |
1609 | + if(!subpaths[positions.first].closed()){ |
1610 | + return rad; |
1611 | + } |
1612 | + times = get_times(last_index(index, subpaths), subpaths, true); |
1613 | + } |
1614 | + _vector[index] = Geom::Point(tmp_len,_vector[index][Y]); |
1615 | + Geom::Point startArcPoint = A->toSBasis().valueAt(times[1]); |
1616 | + Geom::Point endArcPoint = B->toSBasis().valueAt(times[2]); |
1617 | + Curve *knotCurve1 = A->portion(times[0], times[1]); |
1618 | + Curve *knotCurve2 = B->portion(times[2], 1); |
1619 | + Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1); |
1620 | + Ray ray1(startArcPoint, A->finalPoint()); |
1621 | + if (cubic1) { |
1622 | + ray1.setPoints((*cubic1)[2], startArcPoint); |
1623 | + } |
1624 | + Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve2); |
1625 | + Ray ray2(B->initialPoint(), endArcPoint); |
1626 | + if (cubic2) { |
1627 | + ray2.setPoints(endArcPoint, (*cubic2)[1]); |
1628 | + } |
1629 | + bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0; |
1630 | + double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint)); |
1631 | + double angleBetween = angle_between(ray1, ray2, ccwToggle); |
1632 | + rad = distanceArc/sin(angleBetween/2.0); |
1633 | + return rad * -1; |
1634 | +} |
1635 | + |
1636 | +std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vector<Geom::Path> subpaths, bool last) |
1637 | +{ |
1638 | + const double tolerance = 0.001; |
1639 | + const double gapHelper = 0.00001; |
1640 | + std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths); |
1641 | + Curve *curve_it1; |
1642 | + curve_it1 = subpaths[positions.first][positions.second].duplicate(); |
1643 | + Coord it1_length = (*curve_it1).length(tolerance); |
1644 | + double time_it1, time_it2, time_it1_B, intpart; |
1645 | + time_it1 = modf(to_time(index, _vector[index][X]), &intpart); |
1646 | + if (_vector[index][Y] == 0) { |
1647 | + time_it1 = 0; |
1648 | + } |
1649 | + double resultLenght = 0; |
1650 | + time_it1_B = 1; |
1651 | + if (subpaths[positions.first].closed() && last) { |
1652 | + time_it2 = modf(to_time(index - positions.second , _vector[index - positions.second ][X]), &intpart); |
1653 | + resultLenght = it1_length + to_len(index - positions.second, _vector[index - positions.second ][X]); |
1654 | + } else if (!subpaths[positions.first].closed() && last){ |
1655 | + time_it2 = 0; |
1656 | + resultLenght = 0; |
1657 | + } else { |
1658 | + time_it2 = modf(to_time(index + 1, _vector[index + 1][X]), &intpart); |
1659 | + resultLenght = it1_length + to_len( index + 1, _vector[index + 1][X]); |
1660 | + } |
1661 | + if (resultLenght > 0 && time_it2 != 0) { |
1662 | + time_it1_B = modf(to_time(index, -resultLenght), &intpart); |
1663 | + } else { |
1664 | + if (time_it2 == 0) { |
1665 | + time_it1_B = 1; |
1666 | + } else { |
1667 | + time_it1_B = gapHelper; |
1668 | + } |
1669 | + } |
1670 | + |
1671 | + if ((subpaths[positions.first].closed() && last && _vector[index - positions.second][Y] == 0) || (subpaths[positions.first].size() > positions.second + 1 && _vector[index + 1][Y] == 0)) { |
1672 | + time_it1_B = 1; |
1673 | + time_it2 = 0; |
1674 | + } |
1675 | + if (time_it1_B < time_it1) { |
1676 | + time_it1_B = time_it1 + gapHelper; |
1677 | + } |
1678 | + std::vector<double> out; |
1679 | + out.push_back(time_it1); |
1680 | + out.push_back(time_it1_B); |
1681 | + out.push_back(time_it2); |
1682 | + return out; |
1683 | +} |
1684 | + |
1685 | +std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, std::vector<Geom::Path> subpaths) |
1686 | +{ |
1687 | + int counter = -1; |
1688 | + std::size_t first = 0; |
1689 | + std::size_t second = 0; |
1690 | + for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) { |
1691 | + if (path_it->empty()) |
1692 | + continue; |
1693 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
1694 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
1695 | + if (path_it->closed()) { |
1696 | + const Geom::Curve &closingline = path_it->back_closed(); |
1697 | + // the closing line segment is always of type |
1698 | + // Geom::LineSegment. |
1699 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
1700 | + // closingline.isDegenerate() did not work, because it only checks for |
1701 | + // *exact* zero length, which goes wrong for relative coordinates and |
1702 | + // rounding errors... |
1703 | + // the closing line segment has zero-length. So stop before that one! |
1704 | + curve_endit = path_it->end_open(); |
1705 | + } |
1706 | + } |
1707 | + first++; |
1708 | + second = 0; |
1709 | + while (curve_it1 != curve_endit) { |
1710 | + counter++; |
1711 | + second++; |
1712 | + if(counter == index){ |
1713 | + break; |
1714 | + } |
1715 | + ++curve_it1; |
1716 | + } |
1717 | + if(counter == index){ |
1718 | + break; |
1719 | + } |
1720 | + } |
1721 | + first--; |
1722 | + second--; |
1723 | + std::pair<std::size_t, std::size_t> out(first, second); |
1724 | + return out; |
1725 | +} |
1726 | + |
1727 | +int FilletChamferPointArrayParam::last_index(int index, std::vector<Geom::Path> subpaths) |
1728 | +{ |
1729 | + int counter = -1; |
1730 | + bool inSubpath = false; |
1731 | + for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) { |
1732 | + if (path_it->empty()) |
1733 | + continue; |
1734 | + Geom::Path::const_iterator curve_it1 = path_it->begin(); |
1735 | + Geom::Path::const_iterator curve_endit = path_it->end_default(); |
1736 | + if (path_it->closed()) { |
1737 | + const Geom::Curve &closingline = path_it->back_closed(); |
1738 | + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { |
1739 | + curve_endit = path_it->end_open(); |
1740 | + } |
1741 | + } |
1742 | + while (curve_it1 != curve_endit) { |
1743 | + counter++; |
1744 | + if(counter == index){ |
1745 | + inSubpath = true; |
1746 | + } |
1747 | + ++curve_it1; |
1748 | + } |
1749 | + if(inSubpath){ |
1750 | + break; |
1751 | + } |
1752 | + } |
1753 | + if(!inSubpath){ |
1754 | + counter = -1; |
1755 | + } |
1756 | + return counter; |
1757 | +} |
1758 | + |
1759 | + |
1760 | +double FilletChamferPointArrayParam::len_to_time(int index, double len) |
1761 | +{ |
1762 | + double t = 0; |
1763 | + if (last_pwd2.size() > (unsigned) index) { |
1764 | + if (len != 0) { |
1765 | + if (last_pwd2[index][0].degreesOfFreedom() != 2) { |
1766 | + Piecewise<D2<SBasis> > u; |
1767 | + u.push_cut(0); |
1768 | + u.push(last_pwd2[index], 1); |
1769 | + std::vector<double> t_roots = roots(arcLengthSb(u) - std::abs(len)); |
1770 | + if (t_roots.size() > 0) { |
1771 | + t = t_roots[0]; |
1772 | + } |
1773 | + } else { |
1774 | + double lenghtPart = 0; |
1775 | + if (last_pwd2.size() > (unsigned) index) { |
1776 | + lenghtPart = length(last_pwd2[index], EPSILON); |
1777 | + } |
1778 | + if (std::abs(len) < lenghtPart && lenghtPart != 0) { |
1779 | + t = std::abs(len) / lenghtPart; |
1780 | + } |
1781 | + } |
1782 | + } |
1783 | + t = double(index) + t; |
1784 | + } else { |
1785 | + t = double(last_pwd2.size() - 1); |
1786 | + } |
1787 | + |
1788 | + return t; |
1789 | +} |
1790 | + |
1791 | +double FilletChamferPointArrayParam::time_to_len(int index, double time) |
1792 | +{ |
1793 | + double intpart; |
1794 | + double len = 0; |
1795 | + time = modf(time, &intpart); |
1796 | + double lenghtPart = 0; |
1797 | + if (last_pwd2.size() <= (unsigned) index || time == 0) { |
1798 | + return len; |
1799 | + } |
1800 | + if (last_pwd2[index][0].degreesOfFreedom() != 2) { |
1801 | + Piecewise<D2<SBasis> > u; |
1802 | + u.push_cut(0); |
1803 | + u.push(last_pwd2[index], 1); |
1804 | + u = portion(u, 0, time); |
1805 | + return length(u, 0.001) * -1; |
1806 | + } |
1807 | + lenghtPart = length(last_pwd2[index], EPSILON); |
1808 | + return (time * lenghtPart) * -1; |
1809 | +} |
1810 | + |
1811 | +double FilletChamferPointArrayParam::to_time(int index, double A) |
1812 | +{ |
1813 | + if (A > 0) { |
1814 | + return A; |
1815 | + } else { |
1816 | + return len_to_time(index, A); |
1817 | + } |
1818 | +} |
1819 | + |
1820 | +double FilletChamferPointArrayParam::to_len(int index, double A) |
1821 | +{ |
1822 | + if (A > 0) { |
1823 | + return time_to_len(index, A); |
1824 | + } else { |
1825 | + return A; |
1826 | + } |
1827 | +} |
1828 | + |
1829 | +void FilletChamferPointArrayParam::set_oncanvas_looks(SPKnotShapeType shape, |
1830 | + SPKnotModeType mode, |
1831 | + guint32 color) |
1832 | +{ |
1833 | + knot_shape = shape; |
1834 | + knot_mode = mode; |
1835 | + knot_color = color; |
1836 | +} |
1837 | +/* |
1838 | +class FilletChamferPointArrayParamKnotHolderEntity : public KnotHolderEntity { |
1839 | +public: |
1840 | + FilletChamferPointArrayParamKnotHolderEntity(FilletChamferPointArrayParam |
1841 | +*p, unsigned int index); |
1842 | + virtual ~FilletChamferPointArrayParamKnotHolderEntity() {} |
1843 | + |
1844 | + virtual void knot_set(Point const &p, Point const &origin, guint state); |
1845 | + virtual Point knot_get() const; |
1846 | + virtual void knot_click(guint state); |
1847 | + virtual void knot_doubleclicked(guint state); |
1848 | + |
1849 | + /Checks whether the index falls within the size of the parameter's vector/ |
1850 | + bool valid_index(unsigned int index) const { |
1851 | + return (_pparam->_vector.size() > index); |
1852 | + }; |
1853 | + |
1854 | +private: |
1855 | + FilletChamferPointArrayParam *_pparam; |
1856 | + unsigned int _index; |
1857 | +}; |
1858 | +/*/ |
1859 | + |
1860 | +FilletChamferPointArrayParamKnotHolderEntity:: |
1861 | +FilletChamferPointArrayParamKnotHolderEntity( |
1862 | + FilletChamferPointArrayParam *p, unsigned int index) |
1863 | + : _pparam(p), _index(index) {} |
1864 | + |
1865 | +void FilletChamferPointArrayParamKnotHolderEntity::knot_set(Point const &p, |
1866 | + Point const &origin, |
1867 | + guint state) |
1868 | +{ |
1869 | + using namespace Geom; |
1870 | + |
1871 | + if (!valid_index(_index)) { |
1872 | + return; |
1873 | + } |
1874 | + /// @todo how about item transforms??? |
1875 | + Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2(); |
1876 | + //todo: add snapping |
1877 | + //Geom::Point const s = snap_knot_position(p, state); |
1878 | + double t = nearest_point(p, pwd2[_index]); |
1879 | + if (t == 1) { |
1880 | + t = 0.9999; |
1881 | + } |
1882 | + t += _index; |
1883 | + |
1884 | + if (_pparam->_vector.at(_index)[X] <= 0) { |
1885 | + _pparam->_vector.at(_index) = |
1886 | + Point(_pparam->time_to_len(_index, t), _pparam->_vector.at(_index)[Y]); |
1887 | + } else { |
1888 | + _pparam->_vector.at(_index) = Point(t, _pparam->_vector.at(_index)[Y]); |
1889 | + } |
1890 | + sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); |
1891 | +} |
1892 | + |
1893 | +Point FilletChamferPointArrayParamKnotHolderEntity::knot_get() const |
1894 | +{ |
1895 | + using namespace Geom; |
1896 | + |
1897 | + if (!valid_index(_index)) { |
1898 | + return Point(infinity(), infinity()); |
1899 | + } |
1900 | + |
1901 | + Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2(); |
1902 | + |
1903 | + double time_it = _pparam->to_time(_index, _pparam->_vector.at(_index)[X]); |
1904 | + Point canvas_point = pwd2.valueAt(time_it); |
1905 | + |
1906 | + _pparam->updateCanvasIndicators(); |
1907 | + return canvas_point; |
1908 | + |
1909 | +} |
1910 | + |
1911 | +void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state) |
1912 | +{ |
1913 | + if (state & GDK_CONTROL_MASK) { |
1914 | + if (state & GDK_MOD1_MASK) { |
1915 | + _pparam->_vector.at(_index) = Point(_index, _pparam->_vector.at(_index)[Y]); |
1916 | + _pparam->param_set_and_write_new_value(_pparam->_vector); |
1917 | + sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); |
1918 | + }else{ |
1919 | + using namespace Geom; |
1920 | + double type = _pparam->_vector.at(_index)[Y] + 1; |
1921 | + if (type > 4) { |
1922 | + type = 1; |
1923 | + } |
1924 | + _pparam->_vector.at(_index) = Point(_pparam->_vector.at(_index)[X], type); |
1925 | + _pparam->param_set_and_write_new_value(_pparam->_vector); |
1926 | + sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); |
1927 | + const gchar *tip; |
1928 | + if (type == 3) { |
1929 | + tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toogle type, " |
1930 | + "<b>Shift+Click</b> open dialog, " |
1931 | + "<b>Ctrl+Alt+Click</b> reset"); |
1932 | + } else if (type == 2) { |
1933 | + tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toogle type, " |
1934 | + "<b>Shift+Click</b> open dialog, " |
1935 | + "<b>Ctrl+Alt+Click</b> reset"); |
1936 | + } else if (type == 1) { |
1937 | + tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toogle type, " |
1938 | + "<b>Shift+Click</b> open dialog, " |
1939 | + "<b>Ctrl+Alt+Click</b> reset"); |
1940 | + } else { |
1941 | + tip = _("<b>Double Chamfer</b>: <b>Ctrl+Click</b> toogle type, " |
1942 | + "<b>Shift+Click</b> open dialog, " |
1943 | + "<b>Ctrl+Alt+Click</b> reset"); |
1944 | + } |
1945 | + this->knot->tip = g_strdup(tip); |
1946 | + this->knot->show(); |
1947 | + } |
1948 | + } else if (state & GDK_SHIFT_MASK) { |
1949 | + double xModified = _pparam->_vector.at(_index).x(); |
1950 | + if(xModified < 0 && !_pparam->use_distance){ |
1951 | + xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x()); |
1952 | + } |
1953 | + std::vector<Geom::Path> subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1); |
1954 | + std::pair<std::size_t, std::size_t> positions = _pparam->get_positions(_index, subpaths); |
1955 | + D2<SBasis> A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)]; |
1956 | + if(positions.second != 0){ |
1957 | + A = _pparam->last_pwd2[_index-1]; |
1958 | + } |
1959 | + D2<SBasis> B = _pparam->last_pwd2[_index]; |
1960 | + bool aprox = (A[0].degreesOfFreedom() != 2 || B[0].degreesOfFreedom() != 2) && !_pparam->use_distance?true:false; |
1961 | + Geom::Point offset = Geom::Point(xModified, _pparam->_vector.at(_index).y()); |
1962 | + Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog( |
1963 | + this->desktop, offset, this, _pparam->unit, _pparam->use_distance, aprox); |
1964 | + } |
1965 | + |
1966 | +} |
1967 | + |
1968 | +void FilletChamferPointArrayParamKnotHolderEntity::knot_set_offset( |
1969 | + Geom::Point offset) |
1970 | +{ |
1971 | + double xModified = offset.x(); |
1972 | + if(xModified < 0 && !_pparam->use_distance){ |
1973 | + xModified = _pparam->rad_to_len(_index, offset.x()); |
1974 | + } |
1975 | + _pparam->_vector.at(_index) = Geom::Point(xModified, offset.y()); |
1976 | + this->parent_holder->knot_ungrabbed_handler(this->knot, 0); |
1977 | +} |
1978 | + |
1979 | +void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, |
1980 | + SPDesktop *desktop, |
1981 | + SPItem *item) |
1982 | +{ |
1983 | + recalculate_knots(get_pwd2()); |
1984 | + for (unsigned int i = 0; i < _vector.size(); ++i) { |
1985 | + if (_vector[i][Y] <= 0) { |
1986 | + continue; |
1987 | + } |
1988 | + const gchar *tip; |
1989 | + if (_vector[i][Y] == 3) { |
1990 | + tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toogle type, " |
1991 | + "<b>Shift+Click</b> open dialog, " |
1992 | + "<b>Ctrl+Alt+Click</b> reset"); |
1993 | + } else if (_vector[i][Y] == 2) { |
1994 | + tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toogle type, " |
1995 | + "<b>Shift+Click</b> open dialog, " |
1996 | + "<b>Ctrl+Alt+Click</b> reset"); |
1997 | + } else if (_vector[i][Y] == 1) { |
1998 | + tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toogle type, " |
1999 | + "<b>Shift+Click</b> open dialog, " |
2000 | + "<b>Ctrl+Alt+Click</b> reset"); |
2001 | + } else { |
2002 | + tip = _("<b>Double Chamfer</b>: <b>Ctrl+Click</b> toogle type, " |
2003 | + "<b>Shift+Click</b> open dialog, " |
2004 | + "<b>Ctrl+Alt+Click</b> reset"); |
2005 | + } |
2006 | + FilletChamferPointArrayParamKnotHolderEntity *e = |
2007 | + new FilletChamferPointArrayParamKnotHolderEntity(this, i); |
2008 | + e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip), |
2009 | + knot_shape, knot_mode, knot_color); |
2010 | + knotholder->add(e); |
2011 | + } |
2012 | + updateCanvasIndicators(); |
2013 | +} |
2014 | + |
2015 | +} /* namespace LivePathEffect */ |
2016 | + |
2017 | +} /* namespace Inkscape */ |
2018 | + |
2019 | +/* |
2020 | + Local Variables: |
2021 | + mode:c++ |
2022 | + c-file-style:"stroustrup" |
2023 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
2024 | + indent-tabs-mode:nil |
2025 | + fill-column:99 |
2026 | + End: |
2027 | +*/ |
2028 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
2029 | |
2030 | === added file 'src/live_effects/parameter/filletchamferpointarray.h' |
2031 | --- src/live_effects/parameter/filletchamferpointarray.h 1970-01-01 00:00:00 +0000 |
2032 | +++ src/live_effects/parameter/filletchamferpointarray.h 2014-10-22 21:40:37 +0000 |
2033 | @@ -0,0 +1,123 @@ |
2034 | +#ifndef INKSCAPE_LIVEPATHEFFECT_FILLET_CHAMFER_POINT_ARRAY_H |
2035 | +#define INKSCAPE_LIVEPATHEFFECT_FILLET_CHAMFER_POINT_ARRAY_H |
2036 | + |
2037 | +/* |
2038 | + * Inkscape::LivePathEffectParameters |
2039 | + * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es> |
2040 | + * Special thanks to Johan Engelen for the base of the effect -powerstroke- |
2041 | + * Also to ScislaC for point me to the idea |
2042 | + * Also su_v for his construvtive feedback and time |
2043 | + * and finaly to Liam P. White for his big help on coding, that save me a lot of |
2044 | + * hours |
2045 | + * Released under GNU GPL, read the file 'COPYING' for more information |
2046 | + */ |
2047 | + |
2048 | +#include <glib.h> |
2049 | +#include <2geom/point.h> |
2050 | + |
2051 | +#include "live_effects/parameter/array.h" |
2052 | + |
2053 | +#include "knot-holder-entity.h" |
2054 | + |
2055 | +namespace Inkscape { |
2056 | + |
2057 | +namespace LivePathEffect { |
2058 | + |
2059 | +class FilletChamferPointArrayParamKnotHolderEntity; |
2060 | + |
2061 | +class FilletChamferPointArrayParam : public ArrayParam<Geom::Point> { |
2062 | +public: |
2063 | + FilletChamferPointArrayParam(const Glib::ustring &label, |
2064 | + const Glib::ustring &tip, |
2065 | + const Glib::ustring &key, |
2066 | + Inkscape::UI::Widget::Registry *wr, |
2067 | + Effect *effect); |
2068 | + virtual ~FilletChamferPointArrayParam(); |
2069 | + |
2070 | + virtual Gtk::Widget *param_newWidget(); |
2071 | + |
2072 | + virtual void param_transform_multiply(Geom::Affine const &postmul, |
2073 | + bool /*set*/); |
2074 | + |
2075 | + void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, |
2076 | + guint32 color); |
2077 | + virtual double to_time(int index, double A); |
2078 | + virtual double to_len(int index, double A); |
2079 | + virtual double rad_to_len(int index, double rad); |
2080 | + virtual double len_to_rad(int index, double len); |
2081 | + virtual double len_to_time(int index, double len); |
2082 | + virtual double time_to_len(int index, double time); |
2083 | + virtual std::pair<std::size_t, std::size_t> get_positions(int index, std::vector<Geom::Path> subpaths); |
2084 | + virtual int last_index(int index, std::vector<Geom::Path> subpaths); |
2085 | + std::vector<double> get_times(int index, std::vector<Geom::Path> subpaths, bool last); |
2086 | + virtual void set_helper_size(int hs); |
2087 | + virtual void set_use_distance(bool use_knot_distance); |
2088 | + virtual void set_unit(const gchar *abbr); |
2089 | + virtual void addCanvasIndicators(SPLPEItem const *lpeitem, |
2090 | + std::vector<Geom::PathVector> &hp_vec); |
2091 | + virtual bool providesKnotHolderEntities() const { |
2092 | + return true; |
2093 | + } |
2094 | + virtual void updateCanvasIndicators(); |
2095 | + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, |
2096 | + SPItem *item); |
2097 | + |
2098 | + void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in, |
2099 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_normal_in); |
2100 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > const &get_pwd2() const { |
2101 | + return last_pwd2; |
2102 | + } |
2103 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > const &get_pwd2_normal() const { |
2104 | + return last_pwd2_normal; |
2105 | + } |
2106 | + |
2107 | + void recalculate_controlpoints_for_new_pwd2( |
2108 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in); |
2109 | + void recalculate_knots( |
2110 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in); |
2111 | + friend class FilletChamferPointArrayParamKnotHolderEntity; |
2112 | + |
2113 | +private: |
2114 | + FilletChamferPointArrayParam(const FilletChamferPointArrayParam &); |
2115 | + FilletChamferPointArrayParam &operator=(const FilletChamferPointArrayParam &); |
2116 | + |
2117 | + SPKnotShapeType knot_shape; |
2118 | + SPKnotModeType knot_mode; |
2119 | + guint32 knot_color; |
2120 | + int helper_size; |
2121 | + bool use_distance; |
2122 | + const gchar *unit; |
2123 | + Geom::PathVector hp; |
2124 | + |
2125 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2; |
2126 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal; |
2127 | +}; |
2128 | + |
2129 | +class FilletChamferPointArrayParamKnotHolderEntity : public KnotHolderEntity { |
2130 | +public: |
2131 | + FilletChamferPointArrayParamKnotHolderEntity(FilletChamferPointArrayParam *p, |
2132 | + unsigned int index); |
2133 | + virtual ~FilletChamferPointArrayParamKnotHolderEntity() {} |
2134 | + |
2135 | + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, |
2136 | + guint state); |
2137 | + virtual Geom::Point knot_get() const; |
2138 | + virtual void knot_click(guint state); |
2139 | + virtual void knot_set_offset(Geom::Point offset); |
2140 | + |
2141 | + /*Checks whether the index falls within the size of the parameter's vector*/ |
2142 | + bool valid_index(unsigned int index) const { |
2143 | + return (_pparam->_vector.size() > index); |
2144 | + } |
2145 | + ; |
2146 | + |
2147 | +private: |
2148 | + FilletChamferPointArrayParam *_pparam; |
2149 | + unsigned int _index; |
2150 | +}; |
2151 | + |
2152 | +} //namespace LivePathEffect |
2153 | + |
2154 | +} //namespace Inkscape |
2155 | + |
2156 | +#endif |
2157 | |
2158 | === modified file 'src/ui/CMakeLists.txt' |
2159 | --- src/ui/CMakeLists.txt 2014-09-02 23:51:04 +0000 |
2160 | +++ src/ui/CMakeLists.txt 2014-10-22 21:40:37 +0000 |
2161 | @@ -78,6 +78,7 @@ |
2162 | dialog/layers.cpp |
2163 | dialog/livepatheffect-add.cpp |
2164 | dialog/livepatheffect-editor.cpp |
2165 | + dialog/lpe-fillet-chamfer-properties.cpp |
2166 | dialog/memory.cpp |
2167 | dialog/messages.cpp |
2168 | dialog/new-from-template.cpp |
2169 | @@ -194,6 +195,7 @@ |
2170 | dialog/layers.h |
2171 | dialog/livepatheffect-add.h |
2172 | dialog/livepatheffect-editor.h |
2173 | + dialog/lpe-fillet-chamfer-properties.cpp |
2174 | dialog/memory.h |
2175 | dialog/messages.h |
2176 | dialog/new-from-template.h |
2177 | |
2178 | === modified file 'src/ui/dialog/Makefile_insert' |
2179 | --- src/ui/dialog/Makefile_insert 2014-03-27 01:33:44 +0000 |
2180 | +++ src/ui/dialog/Makefile_insert 2014-10-22 21:40:37 +0000 |
2181 | @@ -114,4 +114,6 @@ |
2182 | ui/dialog/undo-history.h \ |
2183 | ui/dialog/xml-tree.cpp \ |
2184 | ui/dialog/xml-tree.h \ |
2185 | + ui/dialog/lpe-fillet-chamfer-properties.cpp \ |
2186 | + ui/dialog/lpe-fillet-chamfer-properties.h \ |
2187 | $(inkboard_dialogs) |
2188 | |
2189 | === added file 'src/ui/dialog/lpe-fillet-chamfer-properties.cpp' |
2190 | --- src/ui/dialog/lpe-fillet-chamfer-properties.cpp 1970-01-01 00:00:00 +0000 |
2191 | +++ src/ui/dialog/lpe-fillet-chamfer-properties.cpp 2014-10-22 21:40:37 +0000 |
2192 | @@ -0,0 +1,278 @@ |
2193 | +/** |
2194 | + * From the code of Liam P.White from his Power Stroke Knot dialog |
2195 | + * |
2196 | + * Released under GNU GPL. Read the file 'COPYING' for more information |
2197 | + */ |
2198 | + |
2199 | +#ifdef HAVE_CONFIG_H |
2200 | +#include <config.h> |
2201 | +#endif |
2202 | + |
2203 | +#if GLIBMM_DISABLE_DEPRECATED &&HAVE_GLIBMM_THREADS_H |
2204 | +#include <glibmm/threads.h> |
2205 | +#endif |
2206 | + |
2207 | +#include <gtkmm.h> |
2208 | +#include "lpe-fillet-chamfer-properties.h" |
2209 | +#include <boost/lexical_cast.hpp> |
2210 | +#include <glibmm/main.h> |
2211 | +#include <glibmm/i18n.h> |
2212 | +#include "inkscape.h" |
2213 | +#include "desktop.h" |
2214 | +#include "document.h" |
2215 | +#include "document-undo.h" |
2216 | +#include "layer-manager.h" |
2217 | +#include "message-stack.h" |
2218 | +#include "desktop-handles.h" |
2219 | +#include "sp-object.h" |
2220 | +#include "sp-item.h" |
2221 | +#include "verbs.h" |
2222 | +#include "selection.h" |
2223 | +#include "selection-chemistry.h" |
2224 | +#include "ui/icon-names.h" |
2225 | +#include "ui/widget/imagetoggler.h" |
2226 | +#include "util/units.h" |
2227 | +#include <cmath> |
2228 | + |
2229 | +//#include "event-context.h" |
2230 | + |
2231 | +namespace Inkscape { |
2232 | +namespace UI { |
2233 | +namespace Dialogs { |
2234 | + |
2235 | +FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() |
2236 | + : _desktop(NULL), _knotpoint(NULL), _position_visible(false) |
2237 | +{ |
2238 | + Gtk::Box *mainVBox = get_vbox(); |
2239 | + mainVBox->set_homogeneous(false); |
2240 | + _layout_table.set_spacings(4); |
2241 | + _layout_table.resize(2, 2); |
2242 | + |
2243 | + // Layer name widgets |
2244 | + _fillet_chamfer_position_numeric.set_digits(4); |
2245 | + _fillet_chamfer_position_numeric.set_increments(1,1); |
2246 | + //todo: get tha max aloable infinity freeze the widget |
2247 | + _fillet_chamfer_position_numeric.set_range(0., 999999999999999999.); |
2248 | + |
2249 | + _fillet_chamfer_position_label.set_label(_("Radius (pixels):")); |
2250 | + _fillet_chamfer_position_label.set_alignment(1.0, 0.5); |
2251 | + |
2252 | + _layout_table.attach(_fillet_chamfer_position_label, 0, 1, 0, 1, Gtk::FILL, |
2253 | + Gtk::FILL); |
2254 | + _layout_table.attach(_fillet_chamfer_position_numeric, 1, 2, 0, 1, |
2255 | + Gtk::FILL | Gtk::EXPAND, Gtk::FILL); |
2256 | + _fillet_chamfer_type_fillet.set_label(_("Fillet")); |
2257 | + _fillet_chamfer_type_fillet.set_group(_fillet_chamfer_type_group); |
2258 | + _fillet_chamfer_type_inverse_fillet.set_label(_("Inverse fillet")); |
2259 | + _fillet_chamfer_type_inverse_fillet.set_group(_fillet_chamfer_type_group); |
2260 | + _fillet_chamfer_type_chamfer.set_label(_("Chamfer")); |
2261 | + _fillet_chamfer_type_chamfer.set_group(_fillet_chamfer_type_group); |
2262 | + _fillet_chamfer_type_double_chamfer.set_label(_("Double chamfer")); |
2263 | + _fillet_chamfer_type_double_chamfer.set_group(_fillet_chamfer_type_group); |
2264 | + |
2265 | + mainVBox->pack_start(_layout_table, true, true, 4); |
2266 | + mainVBox->pack_start(_fillet_chamfer_type_fillet, true, true, 4); |
2267 | + mainVBox->pack_start(_fillet_chamfer_type_inverse_fillet, true, true, 4); |
2268 | + mainVBox->pack_start(_fillet_chamfer_type_chamfer, true, true, 4); |
2269 | + mainVBox->pack_start(_fillet_chamfer_type_double_chamfer, true, true, 4); |
2270 | + |
2271 | + // Buttons |
2272 | + _close_button.set_use_stock(true); |
2273 | + _close_button.set_label(Gtk::Stock::CANCEL.id); |
2274 | + _close_button.set_can_default(); |
2275 | + |
2276 | + _apply_button.set_use_underline(true); |
2277 | + _apply_button.set_can_default(); |
2278 | + |
2279 | + _close_button.signal_clicked() |
2280 | + .connect(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_close)); |
2281 | + _apply_button.signal_clicked() |
2282 | + .connect(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_apply)); |
2283 | + |
2284 | + signal_delete_event().connect(sigc::bind_return( |
2285 | + sigc::hide(sigc::mem_fun(*this, &FilletChamferPropertiesDialog::_close)), |
2286 | + true)); |
2287 | + |
2288 | + add_action_widget(_close_button, Gtk::RESPONSE_CLOSE); |
2289 | + add_action_widget(_apply_button, Gtk::RESPONSE_APPLY); |
2290 | + |
2291 | + _apply_button.grab_default(); |
2292 | + |
2293 | + show_all_children(); |
2294 | + |
2295 | + set_focus(_fillet_chamfer_position_numeric); |
2296 | +} |
2297 | + |
2298 | +FilletChamferPropertiesDialog::~FilletChamferPropertiesDialog() |
2299 | +{ |
2300 | + |
2301 | + _setDesktop(NULL); |
2302 | +} |
2303 | + |
2304 | +void FilletChamferPropertiesDialog::showDialog( |
2305 | + SPDesktop *desktop, Geom::Point knotpoint, |
2306 | + const Inkscape::LivePathEffect:: |
2307 | + FilletChamferPointArrayParamKnotHolderEntity *pt, |
2308 | + const gchar *unit, |
2309 | + bool use_distance, |
2310 | + bool aprox_radius) |
2311 | +{ |
2312 | + FilletChamferPropertiesDialog *dialog = new FilletChamferPropertiesDialog(); |
2313 | + |
2314 | + dialog->_setDesktop(desktop); |
2315 | + dialog->_setUnit(unit); |
2316 | + dialog->_set_use_distance(use_distance); |
2317 | + dialog->_set_aprox(aprox_radius); |
2318 | + dialog->_setKnotPoint(knotpoint); |
2319 | + dialog->_setPt(pt); |
2320 | + |
2321 | + dialog->set_title(_("Modify Fillet-Chamfer")); |
2322 | + dialog->_apply_button.set_label(_("_Modify")); |
2323 | + |
2324 | + dialog->set_modal(true); |
2325 | + desktop->setWindowTransient(dialog->gobj()); |
2326 | + dialog->property_destroy_with_parent() = true; |
2327 | + |
2328 | + dialog->show(); |
2329 | + dialog->present(); |
2330 | +} |
2331 | + |
2332 | +void FilletChamferPropertiesDialog::_apply() |
2333 | +{ |
2334 | + double d_width; |
2335 | + double d_pos = _fillet_chamfer_position_numeric.get_value(); |
2336 | + if (d_pos) { |
2337 | + if (_fillet_chamfer_type_fillet.get_active() == true) { |
2338 | + d_width = 1; |
2339 | + } else if (_fillet_chamfer_type_inverse_fillet.get_active() == true) { |
2340 | + d_width = 2; |
2341 | + } else if (_fillet_chamfer_type_chamfer.get_active() == true) { |
2342 | + d_width = 3; |
2343 | + } else { |
2344 | + d_width = 4; |
2345 | + } |
2346 | + if (_flexible) { |
2347 | + if (d_pos > 99.99999 || d_pos < 0) { |
2348 | + d_pos = 0; |
2349 | + } |
2350 | + d_pos = _index + (d_pos / 100); |
2351 | + } else { |
2352 | + d_pos = Inkscape::Util::Quantity::convert(d_pos, unit, "px"); |
2353 | + d_pos = d_pos * -1; |
2354 | + } |
2355 | + _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); |
2356 | + } |
2357 | + _close(); |
2358 | +} |
2359 | + |
2360 | +void FilletChamferPropertiesDialog::_close() |
2361 | +{ |
2362 | + _setDesktop(NULL); |
2363 | + destroy_(); |
2364 | + Glib::signal_idle().connect( |
2365 | + sigc::bind_return( |
2366 | + sigc::bind(sigc::ptr_fun(&::operator delete), this), |
2367 | + false |
2368 | + ) |
2369 | + ); |
2370 | +} |
2371 | + |
2372 | +bool FilletChamferPropertiesDialog::_handleKeyEvent(GdkEventKey *event) |
2373 | +{ |
2374 | + return false; |
2375 | +} |
2376 | + |
2377 | +void FilletChamferPropertiesDialog::_handleButtonEvent(GdkEventButton *event) |
2378 | +{ |
2379 | + if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1)) { |
2380 | + _apply(); |
2381 | + } |
2382 | +} |
2383 | + |
2384 | +void FilletChamferPropertiesDialog::_setKnotPoint(Geom::Point knotpoint) |
2385 | +{ |
2386 | + double position; |
2387 | + std::string distance_or_radius = std::string(_("Radius ")); |
2388 | + if(aprox){ |
2389 | + distance_or_radius = std::string(_("Radius aproximated ")); |
2390 | + } |
2391 | + if(use_distance){ |
2392 | + distance_or_radius = std::string(_("Knot distance ")); |
2393 | + } |
2394 | + if (knotpoint.x() > 0) { |
2395 | + double intpart; |
2396 | + position = modf(knotpoint[Geom::X], &intpart) * 100; |
2397 | + _flexible = true; |
2398 | + _index = intpart; |
2399 | + _fillet_chamfer_position_label.set_label(_("Position (%):")); |
2400 | + } else { |
2401 | + _flexible = false; |
2402 | + std::string posConcat = distance_or_radius + |
2403 | + std::string(_("(")) + std::string(unit) + std::string(")"); |
2404 | + _fillet_chamfer_position_label.set_label(_(posConcat.c_str())); |
2405 | + position = knotpoint[Geom::X] * -1; |
2406 | + position = Inkscape::Util::Quantity::convert(position, "px", unit); |
2407 | + } |
2408 | + _fillet_chamfer_position_numeric.set_value(position); |
2409 | + if (knotpoint.y() == 1) { |
2410 | + _fillet_chamfer_type_fillet.set_active(true); |
2411 | + } else if (knotpoint.y() == 2) { |
2412 | + _fillet_chamfer_type_inverse_fillet.set_active(true); |
2413 | + } else if (knotpoint.y() == 3) { |
2414 | + _fillet_chamfer_type_chamfer.set_active(true); |
2415 | + } else if (knotpoint.y() == 1) { |
2416 | + _fillet_chamfer_type_double_chamfer.set_active(true); |
2417 | + } |
2418 | +} |
2419 | + |
2420 | +void FilletChamferPropertiesDialog::_setPt( |
2421 | + const Inkscape::LivePathEffect:: |
2422 | + FilletChamferPointArrayParamKnotHolderEntity *pt) |
2423 | +{ |
2424 | + _knotpoint = const_cast< |
2425 | + Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity *>( |
2426 | + pt); |
2427 | +} |
2428 | + |
2429 | +void FilletChamferPropertiesDialog::_setUnit(const gchar *abbr) |
2430 | +{ |
2431 | + unit = abbr; |
2432 | +} |
2433 | + |
2434 | +void FilletChamferPropertiesDialog::_set_use_distance(bool use_knot_distance) |
2435 | +{ |
2436 | + use_distance = use_knot_distance; |
2437 | +} |
2438 | + |
2439 | +void FilletChamferPropertiesDialog::_set_aprox(bool aprox_radius) |
2440 | +{ |
2441 | + aprox = aprox_radius; |
2442 | +} |
2443 | + |
2444 | +void FilletChamferPropertiesDialog::_setDesktop(SPDesktop *desktop) |
2445 | +{ |
2446 | + if (desktop) { |
2447 | + Inkscape::GC::anchor(desktop); |
2448 | + } |
2449 | + if (_desktop) { |
2450 | + Inkscape::GC::release(_desktop); |
2451 | + } |
2452 | + _desktop = desktop; |
2453 | +} |
2454 | + |
2455 | +} // namespace |
2456 | +} // namespace |
2457 | +} // namespace |
2458 | + |
2459 | +/* |
2460 | + Local Variables: |
2461 | + mode:c++ |
2462 | + c-file-style:"stroustrup" |
2463 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
2464 | + indent-tabs-mode:nil |
2465 | + fill-column:99 |
2466 | + End: |
2467 | +*/ |
2468 | +// vim: |
2469 | +// filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 |
2470 | +// : |
2471 | |
2472 | === added file 'src/ui/dialog/lpe-fillet-chamfer-properties.h' |
2473 | --- src/ui/dialog/lpe-fillet-chamfer-properties.h 1970-01-01 00:00:00 +0000 |
2474 | +++ src/ui/dialog/lpe-fillet-chamfer-properties.h 2014-10-22 21:40:37 +0000 |
2475 | @@ -0,0 +1,110 @@ |
2476 | +/** |
2477 | + * |
2478 | + * From the code of Liam P.White from his Power Stroke Knot dialog |
2479 | + * |
2480 | + * Released under GNU GPL. Read the file 'COPYING' for more information |
2481 | + */ |
2482 | + |
2483 | +#ifndef INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H |
2484 | +#define INKSCAPE_DIALOG_FILLET_CHAMFER_PROPERTIES_H |
2485 | + |
2486 | +#include <2geom/point.h> |
2487 | +#include <gtkmm.h> |
2488 | +#include "live_effects/parameter/filletchamferpointarray.h" |
2489 | + |
2490 | +class SPDesktop; |
2491 | + |
2492 | +namespace Inkscape { |
2493 | +namespace UI { |
2494 | +namespace Dialogs { |
2495 | + |
2496 | +class FilletChamferPropertiesDialog : public Gtk::Dialog { |
2497 | +public: |
2498 | + FilletChamferPropertiesDialog(); |
2499 | + virtual ~FilletChamferPropertiesDialog(); |
2500 | + |
2501 | + Glib::ustring getName() const { |
2502 | + return "LayerPropertiesDialog"; |
2503 | + } |
2504 | + |
2505 | + static void showDialog(SPDesktop *desktop, Geom::Point knotpoint, |
2506 | + const Inkscape::LivePathEffect:: |
2507 | + FilletChamferPointArrayParamKnotHolderEntity *pt, |
2508 | + const gchar *unit, |
2509 | + bool use_distance, |
2510 | + bool aprox_radius); |
2511 | + |
2512 | +protected: |
2513 | + |
2514 | + SPDesktop *_desktop; |
2515 | + Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity * |
2516 | + _knotpoint; |
2517 | + |
2518 | + Gtk::Label _fillet_chamfer_position_label; |
2519 | + Gtk::SpinButton _fillet_chamfer_position_numeric; |
2520 | + Gtk::RadioButton::Group _fillet_chamfer_type_group; |
2521 | + Gtk::RadioButton _fillet_chamfer_type_fillet; |
2522 | + Gtk::RadioButton _fillet_chamfer_type_inverse_fillet; |
2523 | + Gtk::RadioButton _fillet_chamfer_type_chamfer; |
2524 | + Gtk::RadioButton _fillet_chamfer_type_double_chamfer; |
2525 | + |
2526 | + Gtk::Table _layout_table; |
2527 | + bool _position_visible; |
2528 | + double _index; |
2529 | + |
2530 | + Gtk::Button _close_button; |
2531 | + Gtk::Button _apply_button; |
2532 | + |
2533 | + sigc::connection _destroy_connection; |
2534 | + |
2535 | + static FilletChamferPropertiesDialog &_instance() { |
2536 | + static FilletChamferPropertiesDialog instance; |
2537 | + return instance; |
2538 | + } |
2539 | + |
2540 | + void _setDesktop(SPDesktop *desktop); |
2541 | + void _setPt(const Inkscape::LivePathEffect:: |
2542 | + FilletChamferPointArrayParamKnotHolderEntity *pt); |
2543 | + void _setUnit(const gchar *abbr); |
2544 | + void _set_use_distance(bool use_knot_distance); |
2545 | + void _set_aprox(bool aprox_radius); |
2546 | + void _apply(); |
2547 | + void _close(); |
2548 | + bool _flexible; |
2549 | + const gchar *unit; |
2550 | + bool use_distance; |
2551 | + bool aprox; |
2552 | + void _setKnotPoint(Geom::Point knotpoint); |
2553 | + void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row); |
2554 | + |
2555 | + bool _handleKeyEvent(GdkEventKey *event); |
2556 | + void _handleButtonEvent(GdkEventButton *event); |
2557 | + |
2558 | + friend class Inkscape::LivePathEffect:: |
2559 | + FilletChamferPointArrayParamKnotHolderEntity; |
2560 | + |
2561 | +private: |
2562 | + FilletChamferPropertiesDialog( |
2563 | + FilletChamferPropertiesDialog const &); // no copy |
2564 | + FilletChamferPropertiesDialog &operator=( |
2565 | + FilletChamferPropertiesDialog const &); // no assign |
2566 | +}; |
2567 | + |
2568 | +} // namespace |
2569 | +} // namespace |
2570 | +} // namespace |
2571 | + |
2572 | +#endif //INKSCAPE_DIALOG_LAYER_PROPERTIES_H |
2573 | + |
2574 | +/* |
2575 | + Local Variables: |
2576 | + mode:c++ |
2577 | + c-file-style:"stroustrup" |
2578 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
2579 | + indent-tabs-mode:nil |
2580 | + fill-column:99 |
2581 | + End: |
2582 | +*/ |
2583 | +// vim: |
2584 | +// filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 |
2585 | +// : |
2586 | |
2587 | === modified file 'src/ui/tool/multi-path-manipulator.h' |
2588 | --- src/ui/tool/multi-path-manipulator.h 2014-03-27 01:33:44 +0000 |
2589 | +++ src/ui/tool/multi-path-manipulator.h 2014-10-22 21:40:37 +0000 |
2590 | @@ -53,6 +53,7 @@ |
2591 | |
2592 | void insertNodesAtExtrema(ExtremumType extremum); |
2593 | void insertNodes(); |
2594 | + void alertLPE(); |
2595 | void duplicateNodes(); |
2596 | void joinNodes(); |
2597 | void breakNodes(); |
2598 | @@ -104,7 +105,7 @@ |
2599 | } |
2600 | |
2601 | void _commit(CommitEvent cps); |
2602 | - void _done(gchar const *reason, bool alert_LPE = false); |
2603 | + void _done(gchar const *reason, bool alert_LPE = true); |
2604 | void _doneWithCleanup(gchar const *reason, bool alert_LPE = false); |
2605 | guint32 _getOutlineColor(ShapeRole role); |
2606 | |
2607 | |
2608 | === modified file 'src/ui/tool/path-manipulator.cpp' |
2609 | --- src/ui/tool/path-manipulator.cpp 2014-03-27 01:33:44 +0000 |
2610 | +++ src/ui/tool/path-manipulator.cpp 2014-10-22 21:40:37 +0000 |
2611 | @@ -11,6 +11,7 @@ |
2612 | */ |
2613 | |
2614 | #include "live_effects/lpe-powerstroke.h" |
2615 | +#include "live_effects/lpe-fillet-chamfer.h" |
2616 | #include <string> |
2617 | #include <sstream> |
2618 | #include <deque> |
2619 | @@ -1207,11 +1208,20 @@ |
2620 | _spcurve->set_pathvector(pathv); |
2621 | if (alert_LPE) { |
2622 | /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming? |
2623 | - if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) { |
2624 | - PathEffectList effect_list = _path->getEffectList(); |
2625 | - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>( effect_list.front()->lpeobject->get_lpe() ); |
2626 | - if (lpe_pwr) { |
2627 | - lpe_pwr->adjustForNewPath(pathv); |
2628 | + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ |
2629 | + Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); |
2630 | + if(thisEffect){ |
2631 | + LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe()); |
2632 | + if (lpe_pwr) { |
2633 | + lpe_pwr->adjustForNewPath(pathv); |
2634 | + } |
2635 | + } |
2636 | + thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); |
2637 | + if(thisEffect){ |
2638 | + LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe()); |
2639 | + if (lpe_fll) { |
2640 | + lpe_fll->adjustForNewPath(pathv); |
2641 | + } |
2642 | } |
2643 | } |
2644 | } |
2645 | |
2646 | === modified file 'src/ui/tools/node-tool.cpp' |
2647 | --- src/ui/tools/node-tool.cpp 2014-08-31 21:19:39 +0000 |
2648 | +++ src/ui/tools/node-tool.cpp 2014-10-22 21:40:37 +0000 |
2649 | @@ -24,6 +24,8 @@ |
2650 | #include "message-context.h" |
2651 | #include "selection.h" |
2652 | #include "shape-editor.h" // temporary! |
2653 | +#include "live_effects/effect.h" |
2654 | +#include "display/curve.h" |
2655 | #include "sp-clippath.h" |
2656 | #include "sp-item-group.h" |
2657 | #include "sp-mask.h" |
2658 | @@ -167,6 +169,7 @@ |
2659 | if (this->flash_tempitem) { |
2660 | this->desktop->remove_temporary_canvasitem(this->flash_tempitem); |
2661 | } |
2662 | + |
2663 | if (this->helperpath_tmpitem) { |
2664 | this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); |
2665 | } |
2666 | @@ -250,13 +253,13 @@ |
2667 | ))) |
2668 | ); |
2669 | |
2670 | - this->helperpath_tmpitem = NULL; |
2671 | this->cursor_drag = false; |
2672 | this->show_transform_handles = true; |
2673 | this->single_node_transform_handles = false; |
2674 | this->flash_tempitem = NULL; |
2675 | this->flashed_item = NULL; |
2676 | this->_last_over = NULL; |
2677 | + this->helperpath_tmpitem = NULL; |
2678 | |
2679 | // read prefs before adding items to selection to prevent momentarily showing the outline |
2680 | sp_event_context_read(this, "show_handles"); |
2681 | @@ -473,6 +476,7 @@ |
2682 | case GDK_MOTION_NOTIFY: { |
2683 | this->update_helperpath(); |
2684 | combine_motion_events(desktop->canvas, event->motion, 0); |
2685 | + this->update_helperpath(); |
2686 | SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), |
2687 | FALSE, TRUE); |
2688 |
A very nice piece of work, congratulations. The video is great too. I've put a lot of comments in, I don't expect you to fix them all in this commit, but I want you to understand them all and work out which you are going to do in your next iteration. The biggest changes I see are around changing from storing data in an array of points to building a separate Pointwise abstract class similar to Piecewise which manages all of the slicing and search operations you currently have scattered through your code.
The main area you need to work on is learning to use more abstract concepts - it's great to understand how the mathematics works at the bottom level, but to become a great programmer/ mathematician you need to practice refactoring your code/ideas into simpler, more elegant forms. That way you can level-up to write more in less time.