summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/CMakeLists.txt8
-rw-r--r--src/live_effects/Makefile_insert5
-rw-r--r--src/live_effects/bezctx.cpp48
-rw-r--r--src/live_effects/bezctx.h10
-rw-r--r--src/live_effects/bezctx_intf.h20
-rw-r--r--src/live_effects/lpe-powerstroke-interpolators.h83
-rw-r--r--src/live_effects/lpe-powerstroke.cpp193
-rw-r--r--src/live_effects/lpe-powerstroke.h2
-rw-r--r--src/live_effects/lpe-spiro.cpp85
-rw-r--r--src/live_effects/spiro-converters.cpp123
-rw-r--r--src/live_effects/spiro-converters.h67
-rw-r--r--src/live_effects/spiro.cpp69
-rw-r--r--src/live_effects/spiro.h54
13 files changed, 450 insertions, 317 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index e066e7f43..a5f50a69d 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -1,6 +1,5 @@
set(live_effects_SRC
- bezctx.cpp
effect.cpp
lpe-angle_bisector.cpp
lpe-bendpath.cpp
@@ -41,6 +40,7 @@ set(live_effects_SRC
lpeobject-reference.cpp
lpeobject.cpp
spiro.cpp
+ spiro-converters.cpp
parameter/array.cpp
parameter/bool.cpp
@@ -58,8 +58,6 @@ set(live_effects_SRC
# -------
# Headers
- bezctx.h
- bezctx_intf.h
effect-enum.h
effect.h
lpe-angle_bisector.h
@@ -101,6 +99,8 @@ set(live_effects_SRC
lpegroupbbox.h
lpeobject-reference.h
lpeobject.h
+ spiro.h
+ spiro-converters.h
parameter/array.h
parameter/bool.h
@@ -115,7 +115,7 @@ set(live_effects_SRC
parameter/text.h
parameter/unit.h
parameter/vector.h
- spiro.h
+
)
# add_inkscape_lib(live_effects_LIB "${live_effects_SRC}")
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index 74356f563..9c3c171f2 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -50,9 +50,8 @@ ink_common_sources += \
live_effects/lpe-perp_bisector.h \
live_effects/spiro.h \
live_effects/spiro.cpp \
- live_effects/bezctx.h \
- live_effects/bezctx_intf.h \
- live_effects/bezctx.cpp \
+ live_effects/spiro-converters.h \
+ live_effects/spiro-converters.cpp \
live_effects/lpe-circle_with_radius.cpp \
live_effects/lpe-circle_with_radius.h \
live_effects/lpe-perspective_path.cpp \
diff --git a/src/live_effects/bezctx.cpp b/src/live_effects/bezctx.cpp
deleted file mode 100644
index 722f5dbaf..000000000
--- a/src/live_effects/bezctx.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-ppedit - A pattern plate editor for Spiro splines.
-Copyright (C) 2007 Raph Levien
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.
-
-*/
-#include "bezctx.h"
-
-void bezctx_moveto(bezctx *bc, double x, double y, int is_open)
-{
- bc->moveto(bc, x, y, is_open);
-}
-
-void bezctx_lineto(bezctx *bc, double x, double y)
-{
- bc->lineto(bc, x, y);
-}
-
-void bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2)
-{
- bc->quadto(bc, x1, y1, x2, y2);
-}
-
-void bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
- double x3, double y3)
-{
- bc->curveto(bc, x1, y1, x2, y2, x3, y3);
-}
-
-void bezctx_mark_knot(bezctx *bc, int knot_idx)
-{
- if (bc->mark_knot)
- bc->mark_knot(bc, knot_idx);
-}
diff --git a/src/live_effects/bezctx.h b/src/live_effects/bezctx.h
deleted file mode 100644
index 057312e5a..000000000
--- a/src/live_effects/bezctx.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "bezctx_intf.h"
-
-struct _bezctx {
- void (*moveto)(bezctx *bc, double x, double y, int is_open);
- void (*lineto)(bezctx *bc, double x, double y);
- void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2);
- void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2,
- double x3, double y3);
- void (*mark_knot)(bezctx *bc, int knot_idx);
-};
diff --git a/src/live_effects/bezctx_intf.h b/src/live_effects/bezctx_intf.h
deleted file mode 100644
index a47b8ef5a..000000000
--- a/src/live_effects/bezctx_intf.h
+++ /dev/null
@@ -1,20 +0,0 @@
-typedef struct _bezctx bezctx;
-
-bezctx *
-new_bezctx(void);
-
-void
-bezctx_moveto(bezctx *bc, double x, double y, int is_open);
-
-void
-bezctx_lineto(bezctx *bc, double x, double y);
-
-void
-bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2);
-
-void
-bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
- double x3, double y3);
-
-void
-bezctx_mark_knot(bezctx *bc, int knot_idx);
diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h
index 7f9cb3ddb..6f5b75af8 100644
--- a/src/live_effects/lpe-powerstroke-interpolators.h
+++ b/src/live_effects/lpe-powerstroke-interpolators.h
@@ -16,8 +16,6 @@
#include <2geom/bezier-utils.h>
#include <2geom/sbasis-to-bezier.h>
-#include "live_effects/bezctx.h"
-#include "live_effects/bezctx_intf.h"
#include "live_effects/spiro.h"
@@ -136,7 +134,6 @@ private:
};
-#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
class SpiroInterpolator : public Interpolator {
public:
SpiroInterpolator() {};
@@ -148,8 +145,7 @@ public:
Coord scale_y = 100.;
guint len = points.size();
- bezctx *bc = new_bezctx_ink(&fit);
- spiro_cp *controlpoints = g_new (spiro_cp, len);
+ Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, len);
for (unsigned int i = 0; i < len; ++i) {
controlpoints[i].x = points[i][X];
controlpoints[i].y = points[i][Y] / scale_y;
@@ -160,88 +156,13 @@ public:
controlpoints[len-2].ty = 'v';
controlpoints[len-1].ty = '}';
- spiro_seg *s = run_spiro(controlpoints, len);
- spiro_to_bpath(s, len, bc);
- free(s);
- free(bc);
+ Spiro::spiro_run(controlpoints, len, fit);
fit *= Scale(1,scale_y);
return fit;
};
private:
- typedef struct {
- bezctx base;
- Path *path;
- int is_open;
- } bezctx_ink;
-
- static void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
- {
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x) && IS_FINITE(y) ) {
- bi->path->start(Point(x, y));
- }
- #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("spiro moveto not finite");
- }
- #endif
- }
-
- static void bezctx_ink_lineto(bezctx *bc, double x, double y)
- {
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x) && IS_FINITE(y) ) {
- bi->path->appendNew<LineSegment>( Point(x, y) );
- }
- #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("spiro lineto not finite");
- }
- #endif
- }
-
- static void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
- {
- bezctx_ink *bi = (bezctx_ink *) bc;
-
- if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
- bi->path->appendNew<QuadraticBezier>(Point(xm, ym), Point(x3, y3));
- }
- #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("spiro quadto not finite");
- }
- #endif
- }
-
- static void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
- double x3, double y3)
- {
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
- bi->path->appendNew<CubicBezier>(Point(x1, y1), Point(x2, y2), Point(x3, y3));
- }
- #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("spiro curveto not finite");
- }
- #endif
- }
-
- bezctx *
- new_bezctx_ink(Geom::Path *path) const {
- bezctx_ink *result = g_new(bezctx_ink, 1);
- result->base.moveto = bezctx_ink_moveto;
- result->base.lineto = bezctx_ink_lineto;
- result->base.quadto = bezctx_ink_quadto;
- result->base.curveto = bezctx_ink_curveto;
- result->base.mark_knot = NULL;
- result->path = path;
- return &result->base;
- }
-
SpiroInterpolator(const SpiroInterpolator&);
SpiroInterpolator& operator=(const SpiroInterpolator&);
};
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index 257dba8ea..d3843dc92 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -5,7 +5,7 @@
/* Authors:
* Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
*
- * Copyright (C) 2010-2011 Authors
+ * Copyright (C) 2010-2012 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -26,9 +26,12 @@
#include <2geom/svg-path.h>
#include <2geom/path-intersection.h>
#include <2geom/crossing.h>
+#include <2geom/ellipse.h>
-namespace Geom {
+#include "spiro.h"
+namespace Geom {
+// should all be moved to 2geom at some point
Point unitTangentAt( D2<SBasis> const & a, Coord t, unsigned n = 3) {
std::vector<Point> derivs = a.valueAndDerivatives(t, n);
@@ -55,8 +58,48 @@ boost::optional<Point> intersection_point( Point const & origin_a, Point const &
return boost::none;
}
+Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in)
+{
+ std::vector<Geom::Point> temp;
+ sbasis_to_bezier(temp, sbasis_in, 4);
+ return Geom::CubicBezier( temp );
+}
+
+/**
+ * document this!
+ * very quick: this finds the ellipse with minimum eccentricity
+ passing through point P and Q, with tangent PO at P and QO at Q
+ http://mathforum.org/kb/message.jspa?messageID=7471596&tstart=0
+ */
+static Ellipse find_ellipse(Point P, Point Q, Point O)
+{
+ Point p = P - O;
+ Point q = Q - O;
+ Coord K = 4 * dot(p,q) / (L2sq(p) + L2sq(q));
+
+ double cross = p[Y]*q[X] - p[X]*q[Y];
+ double a = -q[Y]/cross;
+ double b = q[X]/cross;
+ double c = (O[X]*q[Y] - O[Y]*q[X])/cross;
+
+ double d = p[Y]/cross;
+ double e = -p[X]/cross;
+ double f = (-O[X]*p[Y] + O[Y]*p[X])/cross;
+
+ // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
+ double A = (a*d*K+d*d+a*a);
+ double B = (a*e*K+b*d*K+2*d*e+2*a*b);
+ double C = (b*e*K+e*e+b*b);
+ double D = (a*f*K+c*d*K+2*d*f-2*d+2*a*c-2*a);
+ double E = (b*f*K+c*e*K+2*e*f-2*e+2*b*c-2*b);
+ double F = c*f*K+f*f-2*f+c*c-2*c+1;
+
+ return Ellipse(A, B, C, D, E, F);
}
+
+} // namespace Geom
+
namespace Inkscape {
namespace LivePathEffect {
@@ -84,19 +127,21 @@ static const Util::EnumData<unsigned> LineCapTypeData[] = {
};
static const Util::EnumDataConverter<unsigned> LineCapTypeConverter(LineCapTypeData, sizeof(LineCapTypeData)/sizeof(*LineCapTypeData));
-enum LineCuspType {
- LINECUSP_BEVEL,
- LINECUSP_ROUND,
- LINECUSP_EXTRP_MITER,
- LINECUSP_MITER
+enum LineJoinType {
+ LINEJOIN_BEVEL,
+ LINEJOIN_ROUND,
+ LINEJOIN_EXTRP_MITER,
+ LINEJOIN_MITER,
+ LINEJOIN_SPIRO
};
-static const Util::EnumData<unsigned> LineCuspTypeData[] = {
- {LINECUSP_BEVEL, N_("Beveled"), "bevel"},
- {LINECUSP_ROUND, N_("Rounded"), "round"},
- {LINECUSP_EXTRP_MITER, N_("Extrapolated"), "extrapolated"},
- {LINECUSP_MITER, N_("Miter"), "miter"},
+static const Util::EnumData<unsigned> LineJoinTypeData[] = {
+ {LINEJOIN_BEVEL, N_("Beveled"), "bevel"},
+ {LINEJOIN_ROUND, N_("Rounded"), "round"},
+ {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"},
+ {LINEJOIN_MITER, N_("Miter"), "miter"},
+ {LINEJOIN_SPIRO, N_("Spiro"), "spiro"},
};
-static const Util::EnumDataConverter<unsigned> LineCuspTypeConverter(LineCuspTypeData, sizeof(LineCuspTypeData)/sizeof(*LineCuspTypeData));
+static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData));
LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
@@ -105,7 +150,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
interpolator_type(_("Interpolator type"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path."), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN),
interpolator_beta(_("Smoothness"), _("Sets the smoothness for the CubicBezierJohan interpolator. 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2),
start_linecap_type(_("Start cap"), _("Determines the shape of the path's start."), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND),
- cusp_linecap_type(_("Join"), _("Specifies the shape of the path's corners."), "cusp_linecap_type", LineCuspTypeConverter, &wr, this, LINECUSP_ROUND),
+ linejoin_type(_("Join"), _("Specifies the shape of the path's corners."), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND),
miter_limit(_("Miter limit"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.),
end_linecap_type(_("End cap"), _("Determines the shape of the path's end."), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND)
{
@@ -121,7 +166,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
registerParameter( dynamic_cast<Parameter *>(&interpolator_type) );
registerParameter( dynamic_cast<Parameter *>(&interpolator_beta) );
registerParameter( dynamic_cast<Parameter *>(&start_linecap_type) );
- registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) );
+ registerParameter( dynamic_cast<Parameter *>(&linejoin_type) );
registerParameter( dynamic_cast<Parameter *>(&miter_limit) );
registerParameter( dynamic_cast<Parameter *>(&end_linecap_type) );
}
@@ -163,9 +208,9 @@ static bool compare_offsets (Geom::Point first, Geom::Point second)
// find discontinuities in input path
struct discontinuity_data {
- Geom::Point der0; // unit derivative of 'left' side of cusp
- Geom::Point der1; // unit derivative of 'right' side of cusp
- double width; // intended stroke width at cusp
+ Geom::Point der0; // unit derivative of 'left' side of join
+ Geom::Point der1; // unit derivative of 'right' side of join
+ double width; // intended stroke width at join
};
std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der,
Geom::Piecewise<Geom::SBasis> const & x,
@@ -176,8 +221,16 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G
for(unsigned i = 1; i < der.size(); i++) {
if ( ! are_near(der[i-1].at1(), der[i].at0(), eps) ) {
discontinuity_data data;
+
data.der0 = der[i-1].at1();
data.der1 = der[i].at0();
+ if ( Geom::are_near(data.der0.length(), 0) ) {
+ data.der0 = unitTangentAt(der[i-1], 1, 2);
+ }
+ if ( Geom::are_near(data.der1.length(), 0) ) {
+ data.der1 = unitTangentAt(der[i], 0, 2);
+ }
+
double t = der.cuts[i];
std::vector< double > rts = roots (x - t); /// @todo this has multiple solutions for general strokewidth paths (generated by spiro interpolator...), ignore for now
if (!rts.empty()) {
@@ -194,12 +247,12 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G
Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & B,
std::vector<discontinuity_data> const & cusps,
- LineCuspType cusp_linecap,
+ LineJoinType jointype,
double miter_limit,
bool forward_direction,
double tol=Geom::EPSILON)
{
-/* per definition, each discontinuity should be fixed with a cusp-ending, as defined by cusp_linecap_type
+/* per definition, each discontinuity should be fixed with a join-ending, as defined by linejoin_type
*/
Geom::PathBuilder pb;
if (B.size() == 0) {
@@ -223,14 +276,41 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis>
{ // discontinuity found, so fix it :-)
discontinuity_data cusp = cusps[cusp_i];
- switch (cusp_linecap) {
- case LINECUSP_ROUND: // properly bugged ^_^
- pb.arcTo( abs(cusp.width), abs(cusp.width),
- angle_between(cusp.der0, cusp.der1), false, cusp.width < 0,
- B[i].at0() );
+ bool on_outside = ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0. );
+
+ switch (jointype) {
+ case LINEJOIN_ROUND: {
+ if (on_outside) {
+ // we are on the outside: round corner
+ /* for constant width paths, the rounding is a circular arc (rx == ry),
+ for non-constant width paths, the rounding can be done with an ellipse but is hard and ambiguous.
+ The elliptical arc should go through the discontinuity's start and end points (of course!)
+ and also should match the discontinuity tangents at those start and end points.
+ To resolve the ambiguity, the elliptical arc with minimal eccentricity should be chosen.
+ A 2Geom method was created to do exactly this :)
+ */
+
+ Geom::Point tang1 = unitTangentAt(B[prev_i],1);
+ Geom::Point tang2 = unitTangentAt(B[i],0);
+ boost::optional<Geom::Point> O = intersection_point( B[prev_i].at1(), tang1,
+ B[i].at0(), tang2 );
+ if (!O) {
+ // no center found, i.e. 180 degrees round
+ pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles
+ break;
+ }
+
+ Geom::Ellipse ellipse = find_ellipse(B[prev_i].at1(), B[i].at0(), *O);
+ pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rot_angle(),
+ false, cusp.width < 0, B[i].at0() );
+ } else {
+ // we are on the inside, do a simple bevel to connect the paths
+ pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles
+ }
break;
-/* case LINECUSP_NONE: {
- if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) {
+ }
+/* case LINEJOIN_NONE: {
+ if ( on_outside ) {
// we are on the outside
Geom::Point der1 = unitTangentAt(B[prev_i],1);
Geom::Point point_on_path = B[prev_i].at1() - rot90(der1) * cusp.width;
@@ -241,24 +321,18 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis>
pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles
}
} */
- case LINECUSP_EXTRP_MITER: {
- // first figure out whether we are on the outside or inside of the corner in the path
- if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) {
+ case LINEJOIN_EXTRP_MITER: {
+ if (on_outside) {
// we are on the outside, do something complicated to make it look good ;)
Geom::Point der1 = unitTangentAt(B[prev_i],1);
Geom::Point der2 = unitTangentAt(B[i],0);
Geom::D2<Geom::SBasis> newcurve1 = B[prev_i] * Geom::reflection(rot90(der1), B[prev_i].at1());
- newcurve1 = reverse(newcurve1);
- std::vector<Geom::Point> temp;
- sbasis_to_bezier(temp, newcurve1, 4);
- Geom::CubicBezier bzr1( temp );
+ Geom::CubicBezier bzr1 = sbasis_to_cubicbezier( reverse(newcurve1) );
Geom::D2<Geom::SBasis> newcurve2 = B[i] * Geom::reflection(rot90(der2), B[i].at0());
- newcurve2 = reverse(newcurve2);
- sbasis_to_bezier(temp, newcurve2, 4);
- Geom::CubicBezier bzr2( temp );
+ Geom::CubicBezier bzr2 = sbasis_to_cubicbezier( reverse(newcurve2) );
Geom::Crossings cross = crossings(bzr1, bzr2);
if (cross.empty()) {
@@ -285,9 +359,8 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis>
}
break;
}
- case LINECUSP_MITER: {
- // first figure out whether we are on the outside or inside of the corner in the path
- if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) {
+ case LINEJOIN_MITER: {
+ if (on_outside) {
// we are on the outside, do something complicated to make it look good ;)
Geom::Point der1 = unitTangentAt(B[prev_i],1);
@@ -310,7 +383,39 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis>
}
break;
}
- case LINECUSP_BEVEL:
+ case LINEJOIN_SPIRO: {
+ if (on_outside) {
+ Geom::Point tang1 = unitTangentAt(B[prev_i],1);
+ Geom::Point tang2 = unitTangentAt(B[i],0);
+
+ Geom::Point direction = B[i].at0() - B[prev_i].at1();
+ double tang1_sign = dot(direction,tang1);
+ double tang2_sign = dot(direction,tang2);
+
+ Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, 4);
+ controlpoints[0].x = (B[prev_i].at1() - tang1_sign*tang1)[Geom::X];
+ controlpoints[0].y = (B[prev_i].at1() - tang1_sign*tang1)[Geom::Y];
+ controlpoints[0].ty = '{';
+ controlpoints[1].x = B[prev_i].at1()[Geom::X];
+ controlpoints[1].y = B[prev_i].at1()[Geom::Y];
+ controlpoints[1].ty = ']';
+ controlpoints[2].x = B[i].at0()[Geom::X];
+ controlpoints[2].y = B[i].at0()[Geom::Y];
+ controlpoints[2].ty = '[';
+ controlpoints[3].x = (B[i].at0() + tang2_sign*tang2)[Geom::X];
+ controlpoints[3].y = (B[i].at0() + tang2_sign*tang2)[Geom::Y];
+ controlpoints[3].ty = '}';
+
+ Geom::Path spiro;
+ Spiro::spiro_run(controlpoints, 4, spiro);
+ pb.append(spiro.portion(1,spiro.size_open()-1), Geom::Path::STITCH_DISCONTINUOUS);
+ } else {
+ // we are on the inside, do a simple bevel to connect the paths
+ pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles
+ }
+ break;
+ }
+ case LINEJOIN_BEVEL:
default:
pb.lineTo(B[i].at0());
break;
@@ -388,13 +493,13 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
}
std::vector<discontinuity_data> cusps = find_discontinuities(der, x, y);
- LineCuspType cusp_linecap = static_cast<LineCuspType>(cusp_linecap_type.get_value());
+ LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value());
Piecewise<D2<SBasis> > pwd2_out = compose(pwd2_in,x) + y*compose(n,x);
Piecewise<D2<SBasis> > mirrorpath = reverse(compose(pwd2_in,x) - y*compose(n,x));
- Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, cusp_linecap, miter_limit, true, LPE_CONVERSION_TOLERANCE);
- Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, cusp_linecap, miter_limit, false, LPE_CONVERSION_TOLERANCE);
+ Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, jointype, miter_limit, true, LPE_CONVERSION_TOLERANCE);
+ Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, jointype, miter_limit, false, LPE_CONVERSION_TOLERANCE);
if (path_in[0].closed()) {
fixed_path.close(true);
diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h
index a5bb8c836..e6c915234 100644
--- a/src/live_effects/lpe-powerstroke.h
+++ b/src/live_effects/lpe-powerstroke.h
@@ -39,7 +39,7 @@ private:
EnumParam<unsigned> interpolator_type;
ScalarParam interpolator_beta;
EnumParam<unsigned> start_linecap_type;
- EnumParam<unsigned> cusp_linecap_type;
+ EnumParam<unsigned> linejoin_type;
ScalarParam miter_limit;
EnumParam<unsigned> end_linecap_type;
diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp
index 22974fe13..8b4274ab2 100644
--- a/src/live_effects/lpe-spiro.cpp
+++ b/src/live_effects/lpe-spiro.cpp
@@ -15,8 +15,6 @@
#include "helper/geom-nodetype.h"
#include "helper/geom-curves.h"
-#include "live_effects/bezctx.h"
-#include "live_effects/bezctx_intf.h"
#include "live_effects/spiro.h"
// For handling un-continuous paths:
@@ -24,82 +22,6 @@
#include "inkscape.h"
#include "desktop.h"
-#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
-
-typedef struct {
- bezctx base;
- SPCurve *curve;
- int is_open;
-} bezctx_ink;
-
-void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
-{
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x) && IS_FINITE(y) ) {
- bi->curve->moveto(x, y);
- }
-#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("lpe moveto not finite");
- }
-#endif
-}
-
-void bezctx_ink_lineto(bezctx *bc, double x, double y)
-{
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x) && IS_FINITE(y) ) {
- bi->curve->lineto(x, y);
- }
-#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("lpe lineto not finite");
- }
-#endif
-}
-
-void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
-{
- bezctx_ink *bi = (bezctx_ink *) bc;
-
- if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
- bi->curve->quadto(xm, ym, x3, y3);
- }
-#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("lpe quadto not finite");
- }
-#endif
-}
-
-void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
- double x3, double y3)
-{
- bezctx_ink *bi = (bezctx_ink *) bc;
- if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
- bi->curve->curveto(x1, y1, x2, y2, x3, y3);
- }
-#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
- else {
- g_message("lpe curveto not finite");
- }
-#endif
-}
-
-bezctx *
-new_bezctx_ink(SPCurve *curve) {
- bezctx_ink *result = g_new(bezctx_ink, 1);
- result->base.moveto = bezctx_ink_moveto;
- result->base.lineto = bezctx_ink_lineto;
- result->base.quadto = bezctx_ink_quadto;
- result->base.curveto = bezctx_ink_curveto;
- result->base.mark_knot = NULL;
- result->curve = curve;
- return &result->base;
-}
-
-
-
namespace Inkscape {
namespace LivePathEffect {
@@ -124,8 +46,7 @@ LPESpiro::doEffect(SPCurve * curve)
guint len = curve->get_segment_count() + 2;
curve->reset();
- bezctx *bc = new_bezctx_ink(curve);
- spiro_cp *path = g_new (spiro_cp, len);
+ Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len);
int ip = 0;
for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
@@ -218,9 +139,7 @@ LPESpiro::doEffect(SPCurve * curve)
// run subpath through spiro
int sp_len = ip;
- spiro_seg *s = run_spiro(path, sp_len);
- spiro_to_bpath(s, sp_len, bc);
- free(s);
+ Spiro::spiro_run(path, sp_len, *curve);
ip = 0;
}
diff --git a/src/live_effects/spiro-converters.cpp b/src/live_effects/spiro-converters.cpp
new file mode 100644
index 000000000..3c7bdf99e
--- /dev/null
+++ b/src/live_effects/spiro-converters.cpp
@@ -0,0 +1,123 @@
+/* Authors:
+ * Johan Engelen
+ *
+ * Copyright (C) 2010-2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "spiro-converters.h"
+#include <2geom/path.h>
+#include "display/curve.h"
+#include <glib.h>
+
+#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+# define SPIRO_G_MESSAGE(x) g_message(x)
+#else
+# define SPIRO_G_MESSAGE(x)
+#endif
+
+namespace Spiro {
+
+void
+ConverterSPCurve::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.moveto(x, y);
+ if (!is_open) {
+ _curve.closepath();
+ }
+ } else {
+ SPIRO_G_MESSAGE("Spiro: moveto not finite");
+ }
+}
+
+void
+ConverterSPCurve::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.lineto(x, y);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: lineto not finite");
+ }
+}
+
+void
+ConverterSPCurve::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _curve.quadto(xm, ym, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: quadto not finite");
+ }
+}
+
+void
+ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _curve.curveto(x1, y1, x2, y2, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: curveto not finite");
+ }
+}
+
+
+
+
+void
+ConverterPath::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.start(Geom::Point(x, y));
+ _path.close(!is_open);
+ } else {
+ SPIRO_G_MESSAGE("spiro moveto not finite");
+ }
+}
+
+void
+ConverterPath::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.appendNew<Geom::LineSegment>( Geom::Point(x, y) );
+ } else {
+ SPIRO_G_MESSAGE("spiro lineto not finite");
+ }
+}
+
+void
+ConverterPath::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _path.appendNew<Geom::QuadraticBezier>(Geom::Point(xm, ym), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro quadto not finite");
+ }
+}
+
+void
+ConverterPath::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _path.appendNew<Geom::CubicBezier>(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro curveto not finite");
+ }
+}
+
+} // namespace Spiro
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/spiro-converters.h b/src/live_effects/spiro-converters.h
new file mode 100644
index 000000000..83f6ebbc3
--- /dev/null
+++ b/src/live_effects/spiro-converters.h
@@ -0,0 +1,67 @@
+#ifndef INKSCAPE_SPIRO_CONVERTERS_H
+#define INKSCAPE_SPIRO_CONVERTERS_H
+
+#include <2geom/forward.h>
+class SPCurve;
+
+namespace Spiro {
+
+class ConverterBase {
+public:
+ ConverterBase() {};
+ virtual ~ConverterBase() {};
+
+ virtual void moveto(double x, double y, bool is_open) = 0;
+ virtual void lineto(double x, double y) = 0;
+ virtual void quadto(double x1, double y1, double x2, double y2) = 0;
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3) = 0;
+};
+
+
+/**
+ * Converts Spiro to Inkscape's SPCurve
+ */
+class ConverterSPCurve : public ConverterBase {
+public:
+ ConverterSPCurve(SPCurve &curve)
+ : _curve(curve)
+ {} ;
+
+ virtual void moveto(double x, double y, bool is_open);
+ virtual void lineto(double x, double y);
+ virtual void quadto(double x1, double y1, double x2, double y2);
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3);
+
+private:
+ SPCurve &_curve;
+
+ ConverterSPCurve(const ConverterSPCurve&);
+ ConverterSPCurve& operator=(const ConverterSPCurve&);
+};
+
+
+/**
+ * Converts Spiro to 2Geom's Path
+ */
+class ConverterPath : public ConverterBase {
+public:
+ ConverterPath(Geom::Path &path)
+ : _path(path)
+ {} ;
+
+ virtual void moveto(double x, double y, bool is_open);
+ virtual void lineto(double x, double y);
+ virtual void quadto(double x1, double y1, double x2, double y2);
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3);
+
+private:
+ Geom::Path &_path;
+
+ ConverterPath(const ConverterPath&);
+ ConverterPath& operator=(const ConverterPath&);
+};
+
+
+} // namespace Spiro
+
+#endif
diff --git a/src/live_effects/spiro.cpp b/src/live_effects/spiro.cpp
index b98e12213..e4b72793c 100644
--- a/src/live_effects/spiro.cpp
+++ b/src/live_effects/spiro.cpp
@@ -1,6 +1,8 @@
/*
-ppedit - A pattern plate editor for Spiro splines.
-Copyright (C) 2007 Raph Levien
+Copyright (C) 2007-2012 Authors
+
+Authors: Raph Levien
+ Johan Engelen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -20,12 +22,39 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
*/
/* C implementation of third-order polynomial spirals. */
+#include "spiro.h"
+
#include <math.h>
#include <stdlib.h>
#include <string.h>
-#include "bezctx_intf.h"
-#include "spiro.h"
+#include "display/curve.h"
+#include <2geom/math-utils.h>
+
+#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+
+namespace Spiro {
+
+void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve)
+{
+ spiro_seg *s = Spiro::run_spiro(src, src_len);
+ Spiro::ConverterSPCurve bc(curve);
+ Spiro::spiro_to_otherpath(s, src_len, bc);
+ free(s);
+}
+
+void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path)
+{
+ spiro_seg *s = Spiro::run_spiro(src, src_len);
+ Spiro::ConverterPath bc(path);
+ Spiro::spiro_to_otherpath(s, src_len, bc);
+ free(s);
+}
+
+
+/************************************
+ * Spiro math
+ */
struct spiro_seg_s {
double x;
@@ -814,15 +843,15 @@ solve_spiro(spiro_seg *s, int nseg)
}
static void
-spiro_seg_to_bpath(const double ks[4],
+spiro_seg_to_otherpath(const double ks[4],
double x0, double y0, double x1, double y1,
- bezctx *bc, int depth)
+ ConverterBase &bc, int depth)
{
double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) +
fabs((1./48) * ks[3]);
if (!bend > 1e-8) {
- bezctx_lineto(bc, x1, y1);
+ bc.lineto(x1, y1);
} else {
double seg_ch = hypot(x1 - x0, y1 - y0);
double seg_th = atan2(y1 - y0, x1 - x0);
@@ -845,7 +874,7 @@ spiro_seg_to_bpath(const double ks[4],
vl = (scale * (1./3)) * sin(th_even - th_odd);
ur = (scale * (1./3)) * cos(th_even + th_odd);
vr = (scale * (1./3)) * sin(th_even + th_odd);
- bezctx_curveto(bc, x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1);
+ bc.curveto(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1);
} else {
/* subdivide */
double ksub[4];
@@ -864,11 +893,11 @@ spiro_seg_to_bpath(const double ks[4],
integrate_spiro(ksub, xysub);
xmid = x0 + cth * xysub[0] - sth * xysub[1];
ymid = y0 + cth * xysub[1] + sth * xysub[0];
- spiro_seg_to_bpath(ksub, x0, y0, xmid, ymid, bc, depth + 1);
+ spiro_seg_to_otherpath(ksub, x0, y0, xmid, ymid, bc, depth + 1);
ksub[0] += .25 * ks[1] + (1./384) * ks[3];
ksub[1] += .125 * ks[2];
ksub[2] += (1./16) * ks[3];
- spiro_seg_to_bpath(ksub, xmid, ymid, x1, y1, bc, depth + 1);
+ spiro_seg_to_otherpath(ksub, xmid, ymid, x1, y1, bc, depth + 1);
}
}
}
@@ -890,7 +919,7 @@ free_spiro(spiro_seg *s)
}
void
-spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc)
+spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc)
{
int i;
int nsegs = s[n - 1].ty == '}' ? n - 1 : n;
@@ -901,10 +930,10 @@ spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc)
double x1 = s[i + 1].x;
double y1 = s[i + 1].y;
- if (i == 0)
- bezctx_moveto(bc, x0, y0, s[0].ty == '{');
- bezctx_mark_knot(bc, i);
- spiro_seg_to_bpath(s[i].ks, x0, y0, x1, y1, bc, 0);
+ if (i == 0) {
+ bc.moveto(x0, y0, s[0].ty == '{');
+ }
+ spiro_seg_to_otherpath(s[i].ks, x0, y0, x1, y1, bc, 0);
}
}
@@ -922,10 +951,20 @@ get_knot_th(const spiro_seg *s, int i)
}
}
+
+} // namespace Spiro
+
+/************************************
+ * Unit_test code
+ */
+
+
#ifdef UNIT_TEST
#include <stdio.h>
#include <sys/time.h> /* for gettimeofday */
+using namespace Spiro;
+
static double
get_time (void)
{
diff --git a/src/live_effects/spiro.h b/src/live_effects/spiro.h
index 54de40465..0d85da74b 100644
--- a/src/live_effects/spiro.h
+++ b/src/live_effects/spiro.h
@@ -1,18 +1,56 @@
+/*
+Copyright (C) 2007-2012 Authors
+
+Authors: Raph Levien
+ Johan Engelen
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+*/
+
+#ifndef INKSCAPE_SPIRO_H
+#define INKSCAPE_SPIRO_H
+
+#include "live_effects/spiro-converters.h"
+
+class SPCurve;
+namespace Geom {
+ class Path;
+}
+
+namespace Spiro {
+
typedef struct {
double x;
double y;
char ty;
} spiro_cp;
-typedef struct spiro_seg_s spiro_seg;
-spiro_seg *
-run_spiro(const spiro_cp *src, int n);
+void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve);
+void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path);
-void
-free_spiro(spiro_seg *s);
+/* the following methods are only for expert use: */
+typedef struct spiro_seg_s spiro_seg;
+spiro_seg * run_spiro(const spiro_cp *src, int n);
+void free_spiro(spiro_seg *s);
+void spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc);
+double get_knot_th(const spiro_seg *s, int i);
-void
-spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc);
-double get_knot_th(const spiro_seg *s, int i);
+} // namespace Spiro
+
+#endif // INKSCAPE_SPIRO_H \ No newline at end of file