Merge lp:~inkscape.dev/inkscape/fillet-chamfer into lp:~inkscape.dev/inkscape/trunk

Proposed by Jabiertxof
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
Reviewer Review Type Date Requested Status
Inkscape Developers Pending
Review via email: mp+221308@code.launchpad.net

Description of the change

Add fillet and chanfer LPE, here is a video working: https://www.youtube.com/watch?v=wJKzGhJULfc

To post a comment you must log in.
13422. By Jabiertxof <email address hidden>

Fixed a bug that advert Josh Andler. Also fixed calculations in non straight lines

13423. By Jabiertxof <email address hidden>

update to trunk

13424. By Jabiertxof <email address hidden>

Overlaping knots/radius removed to more beauty result

Revision history for this message
njh (njh-njhurst) wrote :

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.

Revision history for this message
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.

Revision history for this message
Jabiertxof (jabiertxof) wrote :

Also have a question about handle subpaths.
Is ok something like: std::vector<Pointwise>?
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.

Revision history for this message
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

Revision history for this message
MenTaLguY (mental-deactivatedaccount) wrote :

Rather than an enum template parameter, consider a policy. https://en.wikipedia.org/wiki/Policy-based_design

Revision history for this message
Jabiertxof (jabiertxof) wrote :

> Rather than an enum template parameter, consider a policy.
> https://en.wikipedia.org/wiki/Policy-based_design

Thanks very much, I consider it.

Revision history for this message
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!

Revision history for this message
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.

Revision history for this message
njh (njh-njhurst) wrote :

> Also have a question about handling subpaths.
> Is something like: std::vector<Pointwise> ok?

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.

Revision history for this message
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.

Revision history for this message
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!

Revision history for this message
Jabiertxof (jabiertxof) wrote :

> > Also have a question about handling subpaths.
> > Is something like: std::vector<Pointwise> ok?
>
> 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.

Revision history for this message
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.

Revision history for this message
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

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Jabiertxof (jabiertxof) wrote :

Spoint -> NodePointExtender... Fell free to sugest other better.
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

Revision history for this message
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

Revision history for this message
su_v (suv-lp) wrote :

fillet-chamfer was added to experimental in r13415
http://bazaar.launchpad.net/~inkscape.dev/inkscape/experimental/revision/13415

experimental was merged into trunk in r13641
http://bazaar.launchpad.net/~inkscape.dev/inkscape/trunk/revision/13641

Changing status to 'Merged'.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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