lpe-powerstroke.cpp revision e358a7299c524797a09d195a8bd5248d9250682f
/**
* @file
* PowerStroke LPE implementation. Creates curves with modifiable stroke width.
*/
/* Authors:
* Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
*
* Copyright (C) 2010-2011 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "live_effects/lpe-powerstroke.h"
#include "live_effects/lpe-powerstroke-interpolators.h"
#include "sp-shape.h"
namespace Geom {
// length of derivative is non-zero, so return unit vector
}
}
return Point (0,0);
}
/** Find the point where two straight lines cross.
*/
{
}
}
}
namespace Inkscape {
namespace LivePathEffect {
};
static const Util::EnumDataConverter<unsigned> InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData));
enum LineCapType {
};
};
static const Util::EnumDataConverter<unsigned> LineCapTypeConverter(LineCapTypeData, sizeof(LineCapTypeData)/sizeof(*LineCapTypeData));
enum LineCuspType {
};
};
static const Util::EnumDataConverter<unsigned> LineCuspTypeConverter(LineCuspTypeData, sizeof(LineCuspTypeData)/sizeof(*LineCuspTypeData));
sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve."), "sort_points", &wr, this, true),
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),
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)
{
show_orig_path = true;
/// @todo offset_points are initialized with empty path, is that bug-save?
interpolator_beta.addSlider(true);
}
{
}
void
{
if (SP_IS_SHAPE(lpeitem)) {
} else {
g_warning("LPE Powerstroke can only be applied to shapes (not groups).");
}
}
void
{
}
}
{
}
// find discontinuities in input path
struct discontinuity_data {
double width; // intended stroke width at cusp
};
std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der,
{
std::vector< double > rts = roots (x - t); /// @todo this has multiple solutions for general strokewidth paths (generated by spiro interpolator...), ignore for now
} else {
}
}
}
return vect;
}
double miter_limit,
bool forward_direction,
{
/* per definition, each discontinuity should be fixed with a cusp-ending, as defined by cusp_linecap_type
*/
if (B.size() == 0) {
}
unsigned prev_i = 0;
for (unsigned i=1; i < B.size(); i++) {
// if segment is degenerate, skip it
// the degeneracy/constancy test had to be loosened (eps > 1e-5)
if (B[i].isConstant(1e-4)) {
continue;
}
{ // discontinuity found, so fix it :-)
switch (cusp_linecap) {
case LINECUSP_ROUND: // properly bugged ^_^
B[i].at0() );
break;
/* case LINECUSP_NONE: {
if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) {
// 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;
pb.lineTo(point_on_path);
pb.lineTo(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
}
} */
case LINECUSP_EXTRP_MITER: {
// first figure out whether we are on the outside or inside of the corner in the path
// we are on the outside, do something complicated to make it look good ;)
// empty crossing: default to bevel
} else {
// check size of miter
// miter too big: default to bevel
} else {
}
}
} else {
// we are on the inside, do a simple bevel to connect the paths
}
break;
}
case LINECUSP_MITER: {
// first figure out whether we are on the outside or inside of the corner in the path
// we are on the outside, do something complicated to make it look good ;)
if (p) {
// check size of miter
// miter OK
}
}
} else {
// we are on the inside, do a simple bevel to connect the paths
}
break;
}
case LINECUSP_BEVEL:
default:
break;
}
}
prev_i = i;
}
}
{
using namespace Geom;
return path_out;
}
// for now, only regard first subpath and ignore the rest
return path_out;
}
if (sort_points) {
}
// add extra points for interpolation between first and last point
} else {
// add width data for first and last point on the path
// depending on cap type, these first and last points have width zero or take the width from the closest width point.
}
// 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()));
if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {
}
delete interpolator;
// find time values for which x lies outside path domain
// and only take portion of x and y that lies within those time values
}
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);
fixed_path.close(true);
fixed_mirrorpath.close(true);
} else {
// add linecaps...
switch (end_linecap) {
case LINECAP_ZERO_WIDTH:
// do nothing
break;
case LINECAP_PEAK:
{
break;
}
case LINECAP_SQUARE:
{
break;
}
case LINECAP_BUTT:
{
break;
}
case LINECAP_ROUND:
default:
{
fixed_path.appendNew<SVGEllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() );
break;
}
}
switch (start_linecap) {
case LINECAP_ZERO_WIDTH:
// do nothing
break;
case LINECAP_PEAK:
{
break;
}
case LINECAP_SQUARE:
{
break;
}
case LINECAP_BUTT:
{
break;
}
case LINECAP_ROUND:
default:
{
fixed_path.appendNew<SVGEllipticalArc>( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() );
break;
}
}
fixed_path.close(true);
}
return path_out;
}
/* ######################## */
} //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 :