lpe-powerstroke.cpp revision e736a7d039d68eaf2a38a02e97587ec19c43898e
359a38ce40498397028473d956691915ed3e849atavmjong-free#define INKSCAPE_LPE_POWERSTROKE_CPP
359a38ce40498397028473d956691915ed3e849atavmjong-free/** \file
359a38ce40498397028473d956691915ed3e849atavmjong-free * @brief PowerStroke LPE implementation. Creates curves with modifiable stroke width.
359a38ce40498397028473d956691915ed3e849atavmjong-free */
359a38ce40498397028473d956691915ed3e849atavmjong-free/* Authors:
359a38ce40498397028473d956691915ed3e849atavmjong-free * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Copyright (C) 2010-2011 Authors
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Released under GNU GPL, read the file 'COPYING' for more information
359a38ce40498397028473d956691915ed3e849atavmjong-free */
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "live_effects/lpe-powerstroke.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "sp-shape.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "display/curve.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <2geom/path.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <2geom/piecewise.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <2geom/sbasis-geometric.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <2geom/transforms.h>
3fad3df12ae2d320c12871d471eb0faf5f187cbdAlex Valavanis#include <2geom/bezier-utils.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <2geom/svg-elliptical-arc.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "live_effects/bezctx.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "live_effects/bezctx_intf.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "live_effects/spiro.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free/// @TODO Move this to 2geom
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Geom {
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Interpolate {
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeenum InterpolatorType {
359a38ce40498397028473d956691915ed3e849atavmjong-free INTERP_LINEAR,
359a38ce40498397028473d956691915ed3e849atavmjong-free INTERP_CUBICBEZIER,
359a38ce40498397028473d956691915ed3e849atavmjong-free INTERP_CUBICBEZIER_JOHAN,
359a38ce40498397028473d956691915ed3e849atavmjong-free INTERP_SPIRO
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeclass Interpolator {
359a38ce40498397028473d956691915ed3e849atavmjong-freepublic:
359a38ce40498397028473d956691915ed3e849atavmjong-free Interpolator() {};
d1561c248f49dc3508ae9e6557fc0d371928e394Markus Engel virtual ~Interpolator() {};
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free static Interpolator* create(InterpolatorType type);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free// virtual Piecewise<D2<SBasis> > interpolateToPwD2Sb(std::vector<Point> points) = 0;
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual Geom::Path interpolateToPath(std::vector<Point> points) = 0;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeprivate:
359a38ce40498397028473d956691915ed3e849atavmjong-free Interpolator(const Interpolator&);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Interpolator& operator=(const Interpolator&);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White};
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. Whiteclass Linear : public Interpolator {
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. Whitepublic:
359a38ce40498397028473d956691915ed3e849atavmjong-free Linear() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual ~Linear() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual Path interpolateToPath(std::vector<Point> points) {
359a38ce40498397028473d956691915ed3e849atavmjong-free Path path;
359a38ce40498397028473d956691915ed3e849atavmjong-free path.start( points.at(0) );
359a38ce40498397028473d956691915ed3e849atavmjong-free for (unsigned int i = 1 ; i < points.size(); ++i) {
359a38ce40498397028473d956691915ed3e849atavmjong-free path.appendNew<Geom::LineSegment>(points.at(i));
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free return path;
359a38ce40498397028473d956691915ed3e849atavmjong-free };
359a38ce40498397028473d956691915ed3e849atavmjong-free
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. Whiteprivate:
359a38ce40498397028473d956691915ed3e849atavmjong-free Linear(const Linear&);
359a38ce40498397028473d956691915ed3e849atavmjong-free Linear& operator=(const Linear&);
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free// this class is terrible
359a38ce40498397028473d956691915ed3e849atavmjong-freeclass CubicBezierFit : public Interpolator {
359a38ce40498397028473d956691915ed3e849atavmjong-freepublic:
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierFit() {};
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz virtual ~CubicBezierFit() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual Path interpolateToPath(std::vector<Point> points) {
359a38ce40498397028473d956691915ed3e849atavmjong-free unsigned int n_points = points.size();
359a38ce40498397028473d956691915ed3e849atavmjong-free // worst case gives us 2 segment per point
359a38ce40498397028473d956691915ed3e849atavmjong-free int max_segs = 8*n_points;
359a38ce40498397028473d956691915ed3e849atavmjong-free Geom::Point * b = g_new(Geom::Point, max_segs);
359a38ce40498397028473d956691915ed3e849atavmjong-free Geom::Point * points_array = g_new(Geom::Point, 4*n_points);
359a38ce40498397028473d956691915ed3e849atavmjong-free for (unsigned i = 0; i < n_points; ++i) {
359a38ce40498397028473d956691915ed3e849atavmjong-free points_array[i] = points.at(i);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free double tolerance_sq = 0; // this value is just a random guess
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free int const n_segs = Geom::bezier_fit_cubic_r(b, points_array, n_points,
359a38ce40498397028473d956691915ed3e849atavmjong-free tolerance_sq, max_segs);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free Geom::Path fit;
5f19756f48574526dda8abedebf811c9d1456e80Markus Engel if ( n_segs > 0)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free fit.start(b[0]);
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int c = 0; c < n_segs; c++) {
359a38ce40498397028473d956691915ed3e849atavmjong-free fit.appendNew<Geom::CubicBezier>(b[4*c+1], b[4*c+2], b[4*c+3]);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free g_free(b);
359a38ce40498397028473d956691915ed3e849atavmjong-free g_free(points_array);
359a38ce40498397028473d956691915ed3e849atavmjong-free return fit;
359a38ce40498397028473d956691915ed3e849atavmjong-free };
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeprivate:
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierFit(const CubicBezierFit&);
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierFit& operator=(const CubicBezierFit&);
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free/// @todo invent name for this class
359a38ce40498397028473d956691915ed3e849atavmjong-freeclass CubicBezierJohan : public Interpolator {
359a38ce40498397028473d956691915ed3e849atavmjong-freepublic:
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierJohan() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual ~CubicBezierJohan() {};
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual Path interpolateToPath(std::vector<Point> points) {
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz Path fit;
359a38ce40498397028473d956691915ed3e849atavmjong-free fit.start(points.at(0));
359a38ce40498397028473d956691915ed3e849atavmjong-free for (unsigned int i = 1; i < points.size(); ++i) {
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Point p0 = points.at(i-1);
359a38ce40498397028473d956691915ed3e849atavmjong-free Point p1 = points.at(i);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Point dx = Point(p1[X] - p0[X], 0);
359a38ce40498397028473d956691915ed3e849atavmjong-free fit.appendNew<CubicBezier>(p0+0.2*dx, p1-0.2*dx, p1);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free return fit;
359a38ce40498397028473d956691915ed3e849atavmjong-free };
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeprivate:
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierJohan(const CubicBezierJohan&);
359a38ce40498397028473d956691915ed3e849atavmjong-free CubicBezierJohan& operator=(const CubicBezierJohan&);
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
359a38ce40498397028473d956691915ed3e849atavmjong-freeclass SpiroInterpolator : public Interpolator {
359a38ce40498397028473d956691915ed3e849atavmjong-freepublic:
359a38ce40498397028473d956691915ed3e849atavmjong-free SpiroInterpolator() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual ~SpiroInterpolator() {};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free virtual Path interpolateToPath(std::vector<Point> points) {
359a38ce40498397028473d956691915ed3e849atavmjong-free Path fit;
359a38ce40498397028473d956691915ed3e849atavmjong-free
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz Coord scale_y = 100.;
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz
359a38ce40498397028473d956691915ed3e849atavmjong-free guint len = points.size();
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx *bc = new_bezctx_ink(&fit);
359a38ce40498397028473d956691915ed3e849atavmjong-free spiro_cp *controlpoints = g_new (spiro_cp, len);
359a38ce40498397028473d956691915ed3e849atavmjong-free for (unsigned int i = 0; i < len; ++i) {
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[i].x = points[i][X];
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[i].y = points[i][Y] / scale_y;
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[i].ty = 'c';
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[0].ty = '{';
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[1].ty = 'v';
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[len-2].ty = 'v';
359a38ce40498397028473d956691915ed3e849atavmjong-free controlpoints[len-1].ty = '}';
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free spiro_seg *s = run_spiro(controlpoints, len);
359a38ce40498397028473d956691915ed3e849atavmjong-free spiro_to_bpath(s, len, bc);
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz free(s);
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz free(bc);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free fit *= Scale(1,scale_y);
359a38ce40498397028473d956691915ed3e849atavmjong-free return fit;
359a38ce40498397028473d956691915ed3e849atavmjong-free };
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeprivate:
359a38ce40498397028473d956691915ed3e849atavmjong-free typedef struct {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx base;
359a38ce40498397028473d956691915ed3e849atavmjong-free Path *path;
359a38ce40498397028473d956691915ed3e849atavmjong-free int is_open;
359a38ce40498397028473d956691915ed3e849atavmjong-free } bezctx_ink;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free static void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx_ink *bi = (bezctx_ink *) bc;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White if ( IS_FINITE(x) && IS_FINITE(y) ) {
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White bi->path->start(Point(x, y));
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
359a38ce40498397028473d956691915ed3e849atavmjong-free else {
359a38ce40498397028473d956691915ed3e849atavmjong-free g_message("spiro moveto not finite");
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #endif
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free static void bezctx_ink_lineto(bezctx *bc, double x, double y)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx_ink *bi = (bezctx_ink *) bc;
359a38ce40498397028473d956691915ed3e849atavmjong-free if ( IS_FINITE(x) && IS_FINITE(y) ) {
359a38ce40498397028473d956691915ed3e849atavmjong-free bi->path->appendNew<LineSegment>( Point(x, y) );
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
359a38ce40498397028473d956691915ed3e849atavmjong-free else {
359a38ce40498397028473d956691915ed3e849atavmjong-free g_message("spiro lineto not finite");
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #endif
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free static void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx_ink *bi = (bezctx_ink *) bc;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
359a38ce40498397028473d956691915ed3e849atavmjong-free bi->path->appendNew<QuadraticBezier>(Point(xm, ym), Point(x3, y3));
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
359a38ce40498397028473d956691915ed3e849atavmjong-free else {
359a38ce40498397028473d956691915ed3e849atavmjong-free g_message("spiro quadto not finite");
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #endif
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free static void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
359a38ce40498397028473d956691915ed3e849atavmjong-free double x3, double y3)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx_ink *bi = (bezctx_ink *) bc;
359a38ce40498397028473d956691915ed3e849atavmjong-free if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
359a38ce40498397028473d956691915ed3e849atavmjong-free bi->path->appendNew<CubicBezier>(Point(x1, y1), Point(x2, y2), Point(x3, y3));
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
359a38ce40498397028473d956691915ed3e849atavmjong-free else {
359a38ce40498397028473d956691915ed3e849atavmjong-free g_message("spiro curveto not finite");
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free #endif
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx *
359a38ce40498397028473d956691915ed3e849atavmjong-free new_bezctx_ink(Geom::Path *path) {
359a38ce40498397028473d956691915ed3e849atavmjong-free bezctx_ink *result = g_new(bezctx_ink, 1);
359a38ce40498397028473d956691915ed3e849atavmjong-free result->base.moveto = bezctx_ink_moveto;
359a38ce40498397028473d956691915ed3e849atavmjong-free result->base.lineto = bezctx_ink_lineto;
359a38ce40498397028473d956691915ed3e849atavmjong-free result->base.quadto = bezctx_ink_quadto;
359a38ce40498397028473d956691915ed3e849atavmjong-free result->base.curveto = bezctx_ink_curveto;
359a38ce40498397028473d956691915ed3e849atavmjong-free result->base.mark_knot = NULL;
359a38ce40498397028473d956691915ed3e849atavmjong-free result->path = path;
359a38ce40498397028473d956691915ed3e849atavmjong-free return &result->base;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free SpiroInterpolator(const SpiroInterpolator&);
359a38ce40498397028473d956691915ed3e849atavmjong-free SpiroInterpolator& operator=(const SpiroInterpolator&);
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeInterpolator*
359a38ce40498397028473d956691915ed3e849atavmjong-freeInterpolator::create(InterpolatorType type) {
359a38ce40498397028473d956691915ed3e849atavmjong-free switch (type) {
359a38ce40498397028473d956691915ed3e849atavmjong-free case INTERP_LINEAR:
359a38ce40498397028473d956691915ed3e849atavmjong-free return new Geom::Interpolate::Linear();
359a38ce40498397028473d956691915ed3e849atavmjong-free case INTERP_CUBICBEZIER:
359a38ce40498397028473d956691915ed3e849atavmjong-free return new Geom::Interpolate::CubicBezierFit();
359a38ce40498397028473d956691915ed3e849atavmjong-free case INTERP_CUBICBEZIER_JOHAN:
359a38ce40498397028473d956691915ed3e849atavmjong-free return new Geom::Interpolate::CubicBezierJohan();
359a38ce40498397028473d956691915ed3e849atavmjong-free case INTERP_SPIRO:
359a38ce40498397028473d956691915ed3e849atavmjong-free return new Geom::Interpolate::SpiroInterpolator();
359a38ce40498397028473d956691915ed3e849atavmjong-free default:
bf9ec3e969ba6b11cbbc613545aedc63cc886973Matthew Petroff return new Geom::Interpolate::Linear();
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free}
add38d633bbf8ef881bdb908735ea27385c554b8Matthew Petroff
359a38ce40498397028473d956691915ed3e849atavmjong-free} //namespace Interpolate
359a38ce40498397028473d956691915ed3e849atavmjong-free} //namespace Geom
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Inkscape {
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace LivePathEffect {
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic const Util::EnumData<unsigned> InterpolatorTypeData[] = {
359a38ce40498397028473d956691915ed3e849atavmjong-free {Geom::Interpolate::INTERP_LINEAR , N_("Linear"), "Linear"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}
359a38ce40498397028473d956691915ed3e849atavmjong-free};
bf9ec3e969ba6b11cbbc613545aedc63cc886973Matthew Petroffstatic const Util::EnumDataConverter<unsigned> InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData));
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeenum LineCapType {
add38d633bbf8ef881bdb908735ea27385c554b8Matthew Petroff LINECAP_BUTT,
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECAP_SQUARE,
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECAP_ROUND,
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECAP_PEAK
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic const Util::EnumData<unsigned> LineCapTypeData[] = {
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECAP_BUTT , N_("Butt"), "butt"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECAP_SQUARE, N_("Square"), "square"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECAP_ROUND , N_("Round"), "round"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECAP_PEAK , N_("Peak"), "peak"}
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic const Util::EnumDataConverter<unsigned> LineCapTypeConverter(LineCapTypeData, sizeof(LineCapTypeData)/sizeof(*LineCapTypeData));
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeenum LineCuspType {
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECUSP_BEVEL,
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECUSP_ROUND,
359a38ce40498397028473d956691915ed3e849atavmjong-free LINECUSP_SHARP
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic const Util::EnumData<unsigned> LineCuspTypeData[] = {
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECUSP_BEVEL , N_("Beveled"), "bevel"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECUSP_ROUND , N_("Rounded"), "round"},
359a38ce40498397028473d956691915ed3e849atavmjong-free {LINECUSP_SHARP , N_("Sharp"), "sharp"}
359a38ce40498397028473d956691915ed3e849atavmjong-free};
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic const Util::EnumDataConverter<unsigned> LineCuspTypeConverter(LineCuspTypeData, sizeof(LineCuspTypeData)/sizeof(*LineCuspTypeData));
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freeLPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
359a38ce40498397028473d956691915ed3e849atavmjong-free Effect(lpeobject),
359a38ce40498397028473d956691915ed3e849atavmjong-free offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),
359a38ce40498397028473d956691915ed3e849atavmjong-free sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve."), "sort_points", &wr, this, true),
359a38ce40498397028473d956691915ed3e849atavmjong-free 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),
359a38ce40498397028473d956691915ed3e849atavmjong-free start_linecap_type(_("Start line cap type"), _("Determines the shape of the path's start."), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND),
359a38ce40498397028473d956691915ed3e849atavmjong-free cusp_linecap_type(_("Cusp line cap type"), _("Determines the shape of the cusps along the path."), "cusp_linecap_type", LineCuspTypeConverter, &wr, this, LINECUSP_ROUND),
359a38ce40498397028473d956691915ed3e849atavmjong-free end_linecap_type(_("End line cap type"), _("Determines the shape of the path's end."), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND)
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free show_orig_path = true;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /// @todo offset_points are initialized with empty path, is that bug-save?
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free registerParameter( dynamic_cast<Parameter *>(&offset_points) );
359a38ce40498397028473d956691915ed3e849atavmjong-free registerParameter( dynamic_cast<Parameter *>(&sort_points) );
359a38ce40498397028473d956691915ed3e849atavmjong-free registerParameter( dynamic_cast<Parameter *>(&interpolator_type) );
359a38ce40498397028473d956691915ed3e849atavmjong-free registerParameter( dynamic_cast<Parameter *>(&start_linecap_type) );
359a38ce40498397028473d956691915ed3e849atavmjong-free //registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) );
359a38ce40498397028473d956691915ed3e849atavmjong-free registerParameter( dynamic_cast<Parameter *>(&end_linecap_type) );
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-freeLPEPowerStroke::~LPEPowerStroke()
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. Whitevoid
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhiteLPEPowerStroke::doOnApply(SPLPEItem *lpeitem)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White std::vector<Geom::Point> points;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Geom::Path::size_type size = SP_SHAPE(lpeitem)->curve->get_pathvector().front().size_open();
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White points.push_back( Geom::Point(0,0) );
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White points.push_back( Geom::Point(0.5*size,0) );
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White points.push_back( Geom::Point(size,0) );
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White offset_points.param_set_and_write_new_value(points);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. Whitestatic bool compare_offsets (Geom::Point first, Geom::Point second)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White return first[Geom::X] < second[Geom::X];
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhiteGeom::Piecewise<Geom::D2<Geom::SBasis> >
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhiteLPEPowerStroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White using namespace Geom;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White offset_points.set_pwd2(pwd2_in);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Piecewise<D2<SBasis> > der = unitVector(derivative(pwd2_in));
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White Piecewise<D2<SBasis> > n = rot90(der);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White offset_points.set_pwd2_normal(n);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free // see if we should treat the path as being closed.
359a38ce40498397028473d956691915ed3e849atavmjong-free bool closed_path = false;
359a38ce40498397028473d956691915ed3e849atavmjong-free if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
359a38ce40498397028473d956691915ed3e849atavmjong-free closed_path = true;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free Piecewise<D2<SBasis> > output;
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!closed_path) {
359a38ce40498397028473d956691915ed3e849atavmjong-free LineCapType start_linecap = static_cast<LineCapType>(start_linecap_type.get_value());
359a38ce40498397028473d956691915ed3e849atavmjong-free LineCapType end_linecap = static_cast<LineCapType>(end_linecap_type.get_value());
359a38ce40498397028473d956691915ed3e849atavmjong-free
// perhaps use std::list instead of std::vector?
std::vector<Geom::Point> ts(offset_points.data().size() + 2);
for (unsigned int i = 0; i < offset_points.data().size(); ++i) {
ts.at(i+1) = offset_points.data().at(i);
}
if (sort_points) {
sort(ts.begin()+1, ts.end()-1, compare_offsets);
}
switch (start_linecap) {
/*
case LINECAP_SHARP:
// first and last point coincide with input path to make sharp points on ends
ts.front() = Point(pwd2_in.domain().min(),0);
break;
*/
case LINECAP_PEAK:
case LINECAP_SQUARE:
case LINECAP_BUTT:
case LINECAP_ROUND:
default:
// first and last point have same distance from path as second and second to last points, respectively.
ts.front() = Point(pwd2_in.domain().min(), (*(ts.begin()+1))[Geom::Y] );
break;
}
switch (end_linecap) {
/*
case LINECAP_SHARP:
ts.back() = Point(pwd2_in.domain().max(),0);
break;
*/
case LINECAP_PEAK:
case LINECAP_SQUARE:
case LINECAP_BUTT:
case LINECAP_ROUND:
default:
ts.back() = Point(pwd2_in.domain().max(), (*(ts.end()-2))[Geom::Y] );
break;
}
// create stroke path where points (x,y) := (t, offset)
Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value()));
Geom::Path strokepath = interpolator->interpolateToPath(ts);
delete interpolator;
D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb());
Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
// find time values for which x lies outside path domain
// and only take portion of x and y that lies within those time values
std::vector< double > rtsmin = roots (x - pwd2_in.domain().min());
std::vector< double > rtsmax = roots (x - pwd2_in.domain().max());
if ( !rtsmin.empty() && !rtsmax.empty() ) {
x = portion(x, rtsmin.at(0), rtsmax.at(0));
y = portion(y, rtsmin.at(0), rtsmax.at(0));
}
output = compose(pwd2_in,x) + y*compose(n,x);
x = reverse(x);
y = reverse(y);
Piecewise<D2<SBasis> > mirrorpath = compose(pwd2_in,x) - y*compose(n,x);
switch (end_linecap) {
case LINECAP_PEAK:
{
Geom::Point end_deriv = der.lastValue();
double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue());
Geom::Point midpoint = 0.5*(output.lastValue() + mirrorpath.firstValue()) + radius*end_deriv;
Geom::LineSegment cap11(output.lastValue(), midpoint);
Geom::LineSegment cap12(midpoint, mirrorpath.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis()));
break;
}
case LINECAP_SQUARE:
{
Geom::Point end_deriv = der.lastValue();
double radius = 0.5 * distance(output.lastValue(), mirrorpath.firstValue());
Geom::LineSegment cap11(output.lastValue(), output.lastValue() + radius*end_deriv);
Geom::LineSegment cap12(output.lastValue() + radius*end_deriv, mirrorpath.firstValue() + radius*end_deriv);
Geom::LineSegment cap13(mirrorpath.firstValue() + radius*end_deriv, mirrorpath.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap11.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap12.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap13.toSBasis()));
break;
}
case LINECAP_BUTT:
{
Geom::LineSegment cap1(output.lastValue(), mirrorpath.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis()));
break;
}
case LINECAP_ROUND:
default:
{
double radius1 = 0.5 * distance(output.lastValue(), mirrorpath.firstValue());
Geom::SVGEllipticalArc cap1(output.lastValue(), radius1, radius1, M_PI/2., false, y.firstValue() < 0, mirrorpath.firstValue()); // note that y is reversed above!
output.continuousConcat(Piecewise<D2<SBasis> >(cap1.toSBasis()));
break;
}
}
output.continuousConcat(mirrorpath);
switch (start_linecap) {
case LINECAP_PEAK:
{
Geom::Point start_deriv = der.firstValue();
double radius = 0.5 * distance(output.firstValue(), output.lastValue());
Geom::Point midpoint = 0.5*(output.lastValue() + output.firstValue()) - radius*start_deriv;
Geom::LineSegment cap21(output.lastValue(), midpoint);
Geom::LineSegment cap22(midpoint, output.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis()));
break;
}
case LINECAP_SQUARE:
{
Geom::Point start_deriv = der.firstValue();
double radius = 0.5 * distance(output.firstValue(), output.lastValue());
Geom::LineSegment cap21(output.lastValue(), output.lastValue() - radius*start_deriv);
Geom::LineSegment cap22(output.lastValue() - radius*start_deriv, output.firstValue() - radius*start_deriv);
Geom::LineSegment cap23(output.firstValue() - radius*start_deriv, output.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap21.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap22.toSBasis()));
output.continuousConcat(Piecewise<D2<SBasis> >(cap23.toSBasis()));
break;
}
case LINECAP_BUTT:
{
Geom::LineSegment cap2(output.lastValue(), output.firstValue());
output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis()));
break;
}
case LINECAP_ROUND:
default:
{
double radius2 = 0.5 * distance(output.firstValue(), output.lastValue());
Geom::SVGEllipticalArc cap2(output.lastValue(), radius2, radius2, M_PI/2., false, y.lastValue() < 0, output.firstValue()); // note that y is reversed above!
output.continuousConcat(Piecewise<D2<SBasis> >(cap2.toSBasis()));
break;
}
}
} else {
// path is closed
// linecap parameter can be ignored
// perhaps use std::list instead of std::vector?
std::vector<Geom::Point> ts = offset_points.data();
if (sort_points) {
sort(ts.begin(), ts.end(), compare_offsets);
}
// add extra points for interpolation between first and last point
Point first_point = ts.front();
Point last_point = ts.back();
ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0));
ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) );
// create stroke path where points (x,y) := (t, offset)
Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value()));
Geom::Path strokepath = interpolator->interpolateToPath(ts);
delete interpolator;
// output 2 separate paths
D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb());
Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
// find time values for which x lies outside path domain
// and only take portion of x and y that lies within those time values
std::vector< double > rtsmin = roots (x - pwd2_in.domain().min());
std::vector< double > rtsmax = roots (x - pwd2_in.domain().max());
if ( !rtsmin.empty() && !rtsmax.empty() ) {
x = portion(x, rtsmin.at(0), rtsmax.at(0));
y = portion(y, rtsmin.at(0), rtsmax.at(0));
}
output = compose(pwd2_in,x) + y*compose(n,x);
x = reverse(x);
y = reverse(y);
output.concat(compose(pwd2_in,x) - y*compose(n,x));
}
return output;
}
/* ######################## */
} //namespace LivePathEffect
} /* namespace Inkscape */
/*
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:fileencoding=utf-8:textwidth=99 :