lpe-spiro.cpp revision 1071e61e46ddf04389109cf2ef25618e6e0dae4c
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#define INKSCAPE_LPE_SPIRO_C
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove/*
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove * Released under GNU GPL, read the file 'COPYING' for more information
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove */
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "live_effects/lpe-spiro.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "display/curve.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "nodepath.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include <typeinfo>
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include <2geom/pathvector.h>
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include <2geom/matrix.h>
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include <2geom/bezier-curve.h>
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include <2geom/hvlinesegment.h>
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "helper/geom-nodetype.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "live_effects/bezctx.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "live_effects/bezctx_intf.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "live_effects/spiro.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove// For handling un-continuous paths:
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "message-stack.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "inkscape.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove#include "desktop.h"
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grovetypedef struct {
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bezctx base;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove SPCurve *curve;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove int is_open;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove} bezctx_ink;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grovevoid bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove{
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bezctx_ink *bi = (bezctx_ink *) bc;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bi->curve->moveto(x, y);
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove}
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grovevoid bezctx_ink_lineto(bezctx *bc, double x, double y)
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove{
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bezctx_ink *bi = (bezctx_ink *) bc;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bi->curve->lineto(x, y);
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove}
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grovevoid bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove{
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove bezctx_ink *bi = (bezctx_ink *) bc;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove double x0, y0;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove double x1, y1;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove double x2, y2;
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove Geom::Point last = bi->curve->last_point();
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove x0 = last[Geom::X];
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove y0 = last[Geom::Y];
5f9cae5c825d76bdc95b78301e460a46ec5fbdf4Ryan Grove x1 = xm + (1./3) * (x0 - xm);
y1 = ym + (1./3) * (y0 - ym);
x2 = xm + (1./3) * (x3 - xm);
y2 = ym + (1./3) * (y3 - ym);
bi->curve->curveto(x1, y1, x2, y2, x3, y3);
}
void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
double x3, double y3)
{
bezctx_ink *bi = (bezctx_ink *) bc;
bi->curve->curveto(x1, y1, x2, y2, x3, y3);
}
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 {
LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
Effect(lpeobject)
{
}
LPESpiro::~LPESpiro()
{
}
void
LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np)
{
sp_nodepath_show_handles(np, false);
// sp_nodepath_show_helperpath(np, false);
}
void
LPESpiro::doEffect(SPCurve * curve)
{
using Geom::X;
using Geom::Y;
// Make copy of old path as it is changed during processing
Geom::PathVector const original_pathv = curve->get_pathvector();
guint len = curve->get_segment_count() + 2;
curve->reset();
bezctx *bc = new_bezctx_ink(curve);
spiro_cp *path = g_new (spiro_cp, len);
int ip = 0;
for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
if (path_it->empty())
continue;
// start of path
{
Geom::Point p = path_it->front().pointAt(0);
path[ip].x = p[X];
path[ip].y = p[Y];
path[ip].ty = '{' ; // for closed paths, this is overwritten
ip++;
}
// midpoints
Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
if (path_it->closed()) {
// if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
if (path_it->back_closed().isDegenerate()) {
// the closing line segment has zero-length. So stop before that one!
curve_endit = path_it->end_open();
}
}
while ( curve_it2 != curve_endit )
{
/* This deals with the node between curve_it1 and curve_it2.
* Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
* and then curve_it1 points to end or closing segment */
Geom::Point p = curve_it1->finalPoint();
path[ip].x = p[X];
path[ip].y = p[Y];
// Determine type of spiro node this is, determined by the tangents (angles) of the curves
// TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
bool this_is_line = ( dynamic_cast<Geom::LineSegment const *>(&*curve_it1) ||
dynamic_cast<Geom::HLineSegment const *>(&*curve_it1) ||
dynamic_cast<Geom::VLineSegment const *>(&*curve_it1) );
bool next_is_line = ( dynamic_cast<Geom::LineSegment const *>(&*curve_it2) ||
dynamic_cast<Geom::HLineSegment const *>(&*curve_it2) ||
dynamic_cast<Geom::VLineSegment const *>(&*curve_it2) );
Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
{
if (this_is_line && !next_is_line) {
path[ip].ty = ']';
} else if (next_is_line && !this_is_line) {
path[ip].ty = '[';
} else {
path[ip].ty = 'c';
}
} else {
path[ip].ty = 'v';
}
++curve_it1;
++curve_it2;
ip++;
}
// add last point to the spiropath
Geom::Point p = curve_it1->finalPoint();
path[ip].x = p[X];
path[ip].y = p[Y];
if (path_it->closed()) {
// curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
switch (nodetype) {
case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
path[ip].ty = '}';
ip++;
break;
case Geom::NODE_CUSP:
path[0].ty = path[ip].ty = 'v';
break;
case Geom::NODE_SMOOTH:
case Geom::NODE_SYMM:
path[0].ty = path[ip].ty = 'c';
break;
}
} else {
// set type to path closer
path[ip].ty = '}';
ip++;
}
// 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);
ip = 0;
}
g_free (path);
// FIXME: refactor the spiro code such that it cannot generate non-continous paths!
// sometimes, the code above generates a path that is not continuous: caused by chaotic algorithm?
// The continuity error always happens after a lot of curveto calls (a big path probably that spins to infinity?)
// if so, undo the effect by resetting the original path.
try {
curve->get_pathvector() * Geom::identity(); // tests for continuity, this makes LPE Spiro slower of course :-(
}
catch (Geom::ContinuityError & e) {
g_warning("Exception during LPE Spiro execution. \n %s", e.what());
SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
_("An exception occurred during execution of the Spiro Path Effect.") );
curve->set_pathvector(original_pathv);
}
}
}; //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 :