lpe-taperstroke.cpp revision 76addc201c409e81eaaa73fe27cc0f79c4db097c
/**
* @file
* Taper Stroke path effect, provided as an alternative to Power Strokes
* for otherwise constant-width paths.
*
* Authors:
* Liam P White <inkscapebrony@gmail.com>
*
* Copyright (C) 2014 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "live_effects/lpe-taperstroke.h"
#include "helper/geom-nodetype.h"
#include "helper/geom-pathstroke.h"
#include "sp-shape.h"
#include "style.h"
#include "sp-paint-server.h"
#include "svg/svg-color.h"
#include "desktop-style.h"
#include "svg/css-ostringstream.h"
#include "knot-holder-entity.h"
#include "knotholder.h"
template<typename T>
}
namespace Inkscape {
namespace LivePathEffect {
namespace TpS {
class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity {
public:
};
class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity {
public:
};
} // TpS
};
static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType));
line_width(_("Stroke width:"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 1.),
attach_start(_("Start offset:"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2),
smoothing(_("Taper smoothing:"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5),
join_type(_("Join type:"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, JOIN_EXTRAPOLATE),
{
show_orig_path = true;
_provides_knotholder_entities = true;
}
// from LPEPowerStroke -- sets fill if stroke color because we will
// be converting to a fill to make the new join.
{
if (SP_IS_SHAPE(lpeitem)) {
if (server) {
str += "url(#";
str += ")";
}
gchar c[64];
sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
} else {
}
} else {
printf("WARNING: It only makes sense to apply Taper stroke to paths (not groups).\n");
}
}
// from LPEPowerStroke -- sets stroke color from existing fill color
{
if (SP_IS_SHAPE(lpeitem)) {
if (server) {
str += "url(#";
str += ")";
}
gchar c[64];
sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value)));
} else {
}
}
}
// leave Geom::Path
static Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05)
{
break;
}
}
return temp;
}
Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double width);
// actual effect
{
bool zeroStart = false; // [distance from start taper knot -> start of path] == 0
bool zeroEnd = false; // [distance from end taper knot -> end of path] == 0
bool metInMiddle = false; // knots are touching
// there is a pretty good chance that people will try to drag the knots
// on top of each other, so block it
// check to see if the knots were dragged over each other
// if so, reset the end offset, but still allow the start offset.
metInMiddle = true;
}
}
metInMiddle = true;
}
metInMiddle = true;
}
// don't let it be integer (TODO this is stupid!)
{
if (double(unsigned(attach_start)) == attach_start) {
}
if (double(unsigned(attach_end)) == attach_end) {
}
}
// don't let the knots be farther than they are allowed to be
{
if ((unsigned)attach_start >= allowed_start) {
}
if ((unsigned)attach_end >= allowed_end) {
}
}
// don't let it be zero (this is stupid too!)
zeroStart = true;
}
zeroEnd = true;
}
// Path::operator () means get point at time t
// the following function just splits it up into three pieces.
// now for the actual tapering. the stretch_along method (stolen from PaP) is used to accomplish this
if (!zeroStart) {
// Construct the pattern
pat_str << "M 1,0 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1";
}
// if this condition happens to evaluate false, i.e. there was no space for a path to be drawn, it is simply skipped.
// although this seems obvious, it can probably lead to bugs.
if (!metInMiddle) {
// append the outside outline of the path (goes with the direction of the path)
throwaway_path = half_outline(pathv_out[1], fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
} else {
}
}
}
if (!zeroEnd) {
// append the ending taper
pat_str_1 << "M 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
} else {
}
}
if (!metInMiddle) {
// append the inside outline of the path (against direction)
throwaway_path = half_outline(pathv_out[1].reversed(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
} else {
}
}
} else {
}
return real_pathv;
}
/**
* @return Always returns a PathVector with three elements.
*
* The positions of the effect knots are accessed to determine
* where exactly the input path should be split.
*/
{
return out;
}
/**
* Most of the below function is verbatim from Pattern Along Path. However, it needed a little
* tweaking to get it to work right in this case. Also, large portions of the effect have been
* stripped out as I deemed them unnecessary for the relative simplicity of this effect.
*/
Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double prop_scale)
{
using namespace Geom;
// Don't allow empty path parameter:
return pwd2_in;
}
/* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
double xspace = 0;
double noffset = 0;
double toffset = 0;
// Prevent more than 90% overlap...
}
int nbCopies = 0;
double scaling = 1;
nbCopies = 1;
if (scaling != 1.0) {
x*=scaling;
}
if ( false ) {
y*=(scaling*prop_scale);
} else {
}
x += toffset;
double offs = 0;
for (int i=0; i<nbCopies; i++) {
if (false) {
} else {
}
}
}
return output;
} else {
return pwd2_in;
}
}
void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
{
e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE);
knotholder->add(e);
f->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE);
knotholder->add(f);
}
namespace TpS {
void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const&/*origin*/, guint state)
{
using namespace Geom;
printf("WARNING: LPEItem is not a path!\n");
return;
}
// oops
return;
}
// in case you are wondering, the above are simply sanity checks. we never want to actually
// use that object.
// FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
}
void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
{
using namespace Geom;
printf("WARNING: LPEItem is not a path!\n");
return;
}
// oops
return;
}
}
{
return lpe->start_attach_point;
}
{
return lpe->end_attach_point;
}
} // namespace TpS
} // 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 :