lpe-taperstroke.cpp revision 79ecb5032b06c1516abd8c3559688d2eeb9f9105
/**
* @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 "pathoutlineprovider.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"
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, 3),
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, LINEJOIN_EXTRAPOLATED),
{
/* uncomment the following line to have the original path displayed while the item is selected */
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 {
}
} else {
g_warning("LPE Join Type can only be applied to paths (not groups).");
}
}
//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->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
} else {
}
} else {
}
item->updateRepr();
}
}
//actual effect impl here
{
{
break;
//determine order of curve
//can you tell that the following expressions have been shaped by
//repeated compiler errors? ;)
switch (order)
{
case 3:
//major league b***f***ing
}
break;
case 2:
//this never happens
break;
case 1:
default:
}
switch (order)
{
case 3:
}
break;
case 2:
break;
case 1:
default:
}
break;
}
return path_out;
}
Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double width);
{
bool zeroStart = false;
bool zeroEnd = false;
//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
}
}
//don't ever let it be zero
if (attach_start <= 0.00000001) {
zeroStart = true;
}
if (attach_end <= 0.00000001) {
zeroEnd = true;
}
//don't let it be integer
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) {
}
//remember, Path::operator () means get point at time t
//the following function just splits it up into three pieces.
//now for the actual tapering. We use a Pattern Along Path method to get this done.
if (!zeroStart) {
//Construct the pattern (pat_str stands for pattern string) (yes, this is easier, trust me)
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";
}
//append the outside outline of the path (with direction)
if (!zeroStart) {
} else {
}
if (!zeroEnd) {
//append the ending taper
pat_str_1 << "M 0,0 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
}
//append the inside outline of the path (against direction)
if (!zeroEnd) {
//throwaway_path.setInitial(real_path.finalPoint());
} else {
}
return real_pathv;
}
//in all cases, this should return a PathVector with three elements.
{
//do subdivision and get out
unsigned loc = (unsigned)attach_start;
for (unsigned i = 0; i < loc; i++) {
}
//this is pretty annoying
//previously I wrote a function for this but it wasted a lot of time
//so I optimized it back into here.
switch (order) {
case 3: {
break;
}
case 2: {
std::pair<Geom::QuadraticBezier, Geom::QuadraticBezier> qb_pair = qb->subdivide((attach_start - loc));
break;
}
case 1: {
std::pair<Geom::BezierCurveN<1>, Geom::BezierCurveN<1> > lb_pair = lb->subdivide((attach_start - loc));
break;
}
}
//special case: path is one segment long
//special case: what if the two knots occupy the same segment?
{
//it is just a dumb segment
//we have to do some shifting here because the value changed when we reduced the length
//of the previous segment.
switch (order) {
case 3: {
break;
}
case 2: {
break;
}
case 1: {
break;
}
}
}
return pathv_out;
}
//append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute)
}
//deal with the last segment in a very similar fashion to the first
switch (order) {
case 3: {
break;
}
case 2: {
break;
}
case 1: {
break;
}
}
}
if (curve_start) delete curve_start;
return pathv_out;
}
//most of the below code is verbatim from Pattern Along Path. However, it needed a little
//tweaking to get it to work right in this case.
Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::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;
/*if (prop_units.get_value() && pattBndsY){
xspace *= pattBndsX->extent();
noffset *= pattBndsY->extent();
toffset *= pattBndsX->extent();
}*/
//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){
Geom::Piecewise<Geom::D2<Geom::SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs);
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > splited_output_piece = split_at_discontinuities(output_piece);
}else{
}
}
}
/*if (false){
pre_output = fuse_nearby_ends(pre_output, fuse_tolerance);
for (unsigned i=0; i<pre_output.size(); i++){
output.concat(pre_output[i]);
}
}*/
return output;
} else {
return pwd2_in;
}
}
void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
{
{
_("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE );
knotholder->add(e);
}
{
_("End point of the taper"), SP_KNOT_SHAPE_CIRCLE );
knotholder->add(e);
}
}
namespace TpS {
void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
{
using namespace Geom;
// 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;
// FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
}
{
return lpe->start_attach_point;
}
{
return lpe->end_attach_point;
}
}
/* ######################## */
} //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 :