summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/effect.cpp2
-rw-r--r--src/live_effects/lpe-jointype.cpp45
-rw-r--r--src/live_effects/lpe-powerstroke.cpp3
-rw-r--r--src/live_effects/lpe-taperstroke.cpp306
-rwxr-xr-xsrc/live_effects/pathoutlineprovider.cpp1017
-rw-r--r--src/live_effects/pathoutlineprovider.h6
6 files changed, 511 insertions, 868 deletions
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index d6840e5b8..337fe631f 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -5,7 +5,7 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-//#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects
+#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects
#ifdef HAVE_CONFIG_H
# include "config.h"
diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp
index 93e645783..3b2887bb5 100644
--- a/src/live_effects/lpe-jointype.cpp
+++ b/src/live_effects/lpe-jointype.cpp
@@ -158,46 +158,13 @@ void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem)
}
}
-
+//NOTE: I originally had all the outliner functions defined in here, but they were actually useful
+//enough for other LPEs so I moved them all into pathoutlineprovider.cpp. The code here is just a
+//wrapper around it.
std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in)
-{
- std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
- if (path_in.empty())
- {
- return path_out;
- }
- Path p = Path();
- Path outlinepath = Path();
- for (unsigned i = 0; i < path_in.size(); i++)
- {
- p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
- }
-
- #define miter_lim ( (attempt_force_join) ? std::numeric_limits<double>::max() : fabs(line_width * miter_limit))
-
- //magic!
- if (linejoin_type.get_value() <= 2)
- {
- p.Outline(&outlinepath, line_width / 2, static_cast<JoinType>( linejoin_type.get_value() ),
- static_cast<ButtType>( linecap_type.get_value() ), miter_lim);
- //fix memory leak
- std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
- path_out = *pv_p;
- delete pv_p;
-
- } else if (linejoin_type.get_value() == 3) {
- //reflected arc join
- path_out = Outline::outlinePath(path_in, line_width, static_cast<JoinType>( linejoin_type.get_value() ),
- static_cast<ButtType>( linecap_type.get_value() ), miter_lim);
-
- } else if (linejoin_type.get_value() == 4) {
- //extrapolated arc join
- path_out = Outline::outlinePath_extr(path_in, line_width, LINEJOIN_STRAIGHT, static_cast<ButtType>(linecap_type.get_value()), miter_lim);
-
- }
-
- #undef miter_lim
- return path_out;
+{
+ return Outline::PathVectorOutline(path_in, line_width, static_cast<ButtType>(linecap_type.get_value()),
+ static_cast<LineJoinType>(linejoin_type.get_value()), miter_limit);
}
} //namespace LivePathEffect
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index f6bc1de65..d17def471 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -380,7 +380,6 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
Geom::Piecewise<Geom::SBasis> const & y, // width path
LineJoinType jointype,
double miter_limit,
- bool /*forward_direction*/,
double tol=Geom::EPSILON)
{
/* per definition, each discontinuity should be fixed with a join-ending, as defined by linejoin_type
@@ -683,7 +682,7 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
Piecewise<D2<SBasis> > pwd2_out = compose(pwd2_in,x) + y*compose(n,x);
Piecewise<D2<SBasis> > mirrorpath = reverse(compose(pwd2_in,x) - y*compose(n,x));
- Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, y, jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
+ Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, y, jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, reverse(y), jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
if (path_in[0].closed()) {
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index 0e709f3ac..91b8fd7bc 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -232,52 +232,21 @@ Geom::Path return_at_first_cusp (Geom::Path const & path_in, double smooth_toler
return path_out;
}
-Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first)
-{
- //the only reason for this function is the lack of a subdivide function in the Curve class.
- //you have to cast to Beziers to be able to use subdivide(t)
- unsigned order = Outline::bezierOrder(curve_in);
- Geom::Curve* curve_out = curve_in->duplicate();
- switch (order)
- {
- //these need to be scoped because of the variable 'c'
- case 3:
- {
- Geom::CubicBezier c = first ? (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- case 2:
- {
- Geom::QuadraticBezier c = first ? (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- case 1:
- {
- Geom::BezierCurveN<1> c = first ? (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).first :
- (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).second;
- if (curve_out) delete curve_out;
- curve_out = c.duplicate();
- break;
- }
- }
- return curve_out;
-}
-
+Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first);
Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double width);
Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
{
+ Geom::Path first_cusp = return_at_first_cusp(path_in[0]);
+ Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse());
+
+ 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
unsigned size = path_in[0].size();
- if (size == return_at_first_cusp(path_in[0]).size()) {
+ if (size == first_cusp.size()) {
//check to see if the knots were dragged over each other
//if so, reset the end offset
if ( attach_start >= (size - attach_end) ) {
@@ -287,176 +256,213 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
//don't ever let it be zero
if (attach_start <= 0) {
- attach_start.param_set_value( 0.0001 );
+ attach_start.param_set_value( 0.00000001 );
}
if (attach_end <= 0) {
- attach_end.param_set_value( 0.0001 );
+ attach_end.param_set_value( 0.00000001 );
}
//don't let it be integer
if (double(unsigned(attach_start)) == attach_start) {
- attach_start.param_set_value(attach_start - 0.0001);
+ attach_start.param_set_value(attach_start - 0.00000001);
+ zeroStart = true;
}
if (double(unsigned(attach_end)) == attach_end) {
- attach_end.param_set_value(attach_end - 0.0001);
+ attach_end.param_set_value(attach_end - 0.00000001);
+ zeroStart = true;
}
- unsigned allowed_start = return_at_first_cusp(path_in[0]).size();
-
- unsigned allowed_end = return_at_first_cusp(path_in[0].reverse()).size();
+ unsigned allowed_start = first_cusp.size();
+ unsigned allowed_end = last_cusp.size();
+ //don't let the knots be farther than they are allowed to be
if ((unsigned)attach_start >= allowed_start) {
- attach_start.param_set_value((double)allowed_start - 0.0001);
+ attach_start.param_set_value((double)allowed_start - 0.00000001);
}
if ((unsigned)attach_end >= allowed_end) {
- attach_end.param_set_value((double)allowed_end - 0.0001);
+ attach_end.param_set_value((double)allowed_end - 0.00000001);
}
- //Path::operator () means get point at time t
- start_attach_point = return_at_first_cusp(path_in[0])(attach_start);
- end_attach_point = return_at_first_cusp(path_in[0].reverse())(attach_end);
+ //remember, Path::operator () means get point at time t
+ start_attach_point = first_cusp(attach_start);
+ end_attach_point = last_cusp(attach_end);
Geom::PathVector pathv_out;
+ //the following function just splits it up into three pieces.
pathv_out = doEffect_simplePath(path_in);
+ //now for the actual tapering. We use a Pattern Along Path method to get this done.
- //now for the fun stuff. Right? RIGHT?
-
- if (true) {
- Geom::PathVector real_pathv;
- Geom::Path real_path;
+ Geom::PathVector real_pathv;
+ Geom::Path real_path;
+ Geom::PathVector pat_vec;
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2;
+ Geom::Path throwaway_path;
+
+ if (!zeroStart) {
+ //Construct the pattern (pat_str stands for pattern string) (yes, this is easier, trust me)
+ std::stringstream pat_str;
+ pat_str << "M 1,0 1,1 C " << 1 - (double)smoothing << ",1 0,0.5 0,0.5 0,0.5 " << 1 - double(smoothing) << ",0 1,0";
+
+ pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
- //Construct the pattern (pat_str stands for pattern string)
- std::stringstream pat_str;
- pat_str << "M 1,0 1,1 C " << 1 - (double)smoothing << ",1 0,0.5 0,0.5 0,0.5 " << 1 - double(smoothing) << ",0 1,0";
-
- Geom::PathVector pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
+ pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], line_width));
- Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2;
- pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], line_width));
+ throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0].reverse();
+ throwaway_path.erase_last(); //wtf
- Geom::Path throwaway_path = path_from_piecewise(pwd2, 0.001)[0].reverse();
- throwaway_path.erase_last();
-
- real_path.append(throwaway_path); //wtf
-
- throwaway_path = Outline::PathOutsideOutline(pathv_out[1],
- line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
+ real_path.append(throwaway_path);
+ }
+ //append the outside outline of the path (with direction)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1],
+ line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
- throwaway_path.setInitial(real_path.finalPoint());
- real_path.append(throwaway_path);
-
- std::stringstream pat_str_1;
- 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";
- pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
-
- pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > ();
- pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], line_width));
+ throwaway_path.setInitial(real_path.finalPoint());
+ real_path.append(throwaway_path);
+
+ if (!zeroEnd) {
+ //append the ending taper
+ std::stringstream pat_str_1;
+ 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";
+ pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
- throwaway_path = Geom::Path();
- throwaway_path = path_from_piecewise(pwd2, 0.001)[0];
- throwaway_path.setInitial(real_path.finalPoint());
- real_path.append(throwaway_path);
+ pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > ();
+ pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], line_width));
- throwaway_path = Geom::Path();
- throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(),
- line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
-
- //throwaway_path = throwaway_path.reverse();
- throwaway_path.setInitial(real_path.finalPoint());
+ throwaway_path = Geom::path_from_piecewise(pwd2, 0.001)[0];
+ throwaway_path.setInitial(real_path.finalPoint());
+ real_path.append(throwaway_path);
+ }
+ //append the inside outline of the path (against direction)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(),
+ line_width, static_cast<LineJoinType>(join_type.get_value()), miter_limit);
- real_path.append(throwaway_path);
- //real_path.close();
+ throwaway_path.setInitial(real_path.finalPoint());
- real_pathv.push_back(real_path);
+ real_path.append(throwaway_path);
+ real_path.close();
- //real_pathv.push_back(path_from_piecewise(pwd2, 0.001)[0].reverse());
+ real_pathv.push_back(real_path);
- return real_pathv;
- }
-
- return pathv_out;
+ return real_pathv;
}
//in all cases, this should return a PathVector with three elements.
Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in)
{
- unsigned size = path_in[0].size();
+ unsigned size = path_in[0].size();
- //do subdivision and get out
- unsigned loc = (unsigned)attach_start;
- Geom::Curve * curve_start = path_in[0] [loc].duplicate();
+ //do subdivision and get out
+ unsigned loc = (unsigned)attach_start;
+ Geom::Curve * curve_start = path_in[0] [loc].duplicate();
- std::vector<Geom::Path> pathv_out;
- Geom::Path path_out = Geom::Path();
+ std::vector<Geom::Path> pathv_out;
+ Geom::Path path_out = Geom::Path();
- Geom::Path trimmed_start = Geom::Path();
- Geom::Path trimmed_end = Geom::Path();
+ Geom::Path trimmed_start = Geom::Path();
+ Geom::Path trimmed_end = Geom::Path();
- for (unsigned i = 0; i < loc; i++) {
- trimmed_start.append(path_in[0] [i]);
- }
+ for (unsigned i = 0; i < loc; i++) {
+ trimmed_start.append(path_in[0] [i]);
+ }
- #define OVERLAP 0 /*(0.001 / (line_width < 1 ? 1 : line_width))*/
- trimmed_start.append(*subdivide_at(curve_start, (attach_start - loc) + OVERLAP, true));
- curve_start = subdivide_at(curve_start, attach_start - loc, false);
+ trimmed_start.append(*subdivide_at(curve_start, (attach_start - loc), true));
+ curve_start = subdivide_at(curve_start, attach_start - loc, false);
- //special case: path is one segment long
- //special case: what if the two knots occupy the same segment?
- if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc ))
- {
- Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
- //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.
- trimmed_end.append(*subdivide_at(curve_start, t - OVERLAP, false));
- for (unsigned j = (size - attach_end) + 1; j < size; j++) {
- trimmed_end.append(path_in[0] [j]);
- }
-
- curve_start = subdivide_at(curve_start, t, true);
- path_out.append(*curve_start);
- pathv_out.push_back(trimmed_start);
- pathv_out.push_back(path_out);
- pathv_out.push_back(trimmed_end);
- return pathv_out;
+ //special case: path is one segment long
+ //special case: what if the two knots occupy the same segment?
+ if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc ))
+ {
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
+ //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.
+ trimmed_end.append(*subdivide_at(curve_start, t, false));
+ for (unsigned j = (size - attach_end) + 1; j < size; j++) {
+ trimmed_end.append(path_in[0] [j]);
}
-
+
+ curve_start = subdivide_at(curve_start, t, true);
+ path_out.append(*curve_start);
pathv_out.push_back(trimmed_start);
+ pathv_out.push_back(path_out);
+ pathv_out.push_back(trimmed_end);
+ 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)
- path_out.append(*curve_start);
+ pathv_out.push_back(trimmed_start);
+
+ //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)
+ path_out.append(*curve_start);
- for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) {
- path_out.append(path_in[0] [k]);
- }
+ for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) {
+ path_out.append(path_in[0] [k]);
+ }
- //deal with the last segment in a very similar fashion to the first
- loc = size - attach_end;
+ //deal with the last segment in a very similar fashion to the first
+ loc = size - attach_end;
- Geom::Curve * curve_end = path_in[0] [loc].duplicate();
+ Geom::Curve * curve_end = path_in[0] [loc].duplicate();
- Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
- trimmed_end.append(*subdivide_at(curve_end, t - OVERLAP, false));
- curve_end = subdivide_at(curve_end, t, true);
+ trimmed_end.append(*subdivide_at(curve_end, t, false));
+ curve_end = subdivide_at(curve_end, t, true);
- for (unsigned j = (size - attach_end) + 1; j < size; j++) {
- trimmed_end.append(path_in[0] [j]);
- }
+ for (unsigned j = (size - attach_end) + 1; j < size; j++) {
+ trimmed_end.append(path_in[0] [j]);
+ }
- path_out.append(*curve_end);
- pathv_out.push_back(path_out);
+ path_out.append(*curve_end);
+ pathv_out.push_back(path_out);
- pathv_out.push_back(trimmed_end);
+ pathv_out.push_back(trimmed_end);
- if (curve_end) delete curve_end;
- if (curve_start) delete curve_start;
- return pathv_out;
+ if (curve_end) delete curve_end;
+ if (curve_start) delete curve_start;
+ return pathv_out;
+}
+
+Geom::Curve * subdivide_at(const Geom::Curve* curve_in, Geom::Coord time, bool first)
+{
+ //the only reason for this function is the lack of a subdivide function in the Curve class.
+ //you have to cast to Beziers to be able to use subdivide(t)
+ unsigned order = Outline::bezierOrder(curve_in);
+ Geom::Curve* curve_out = curve_in->duplicate();
+ switch (order)
+ {
+ //these need to be scoped because of the variable 'c'
+ case 3:
+ {
+ Geom::CubicBezier c = first ? (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::CubicBezier*> (curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ case 2:
+ {
+ Geom::QuadraticBezier c = first ? (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::QuadraticBezier*>(curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ case 1:
+ {
+ Geom::BezierCurveN<1> c = first ? (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).first :
+ (dynamic_cast<Geom::BezierCurveN<1>* >(curve_out))->subdivide(time).second;
+ if (curve_out) delete curve_out;
+ curve_out = c.duplicate();
+ break;
+ }
+ }
+ return curve_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)
diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp
index 302756c7c..3565fabec 100755
--- a/src/live_effects/pathoutlineprovider.cpp
+++ b/src/live_effects/pathoutlineprovider.cpp
@@ -141,721 +141,392 @@ typedef Geom::Piecewise<D2SB> PWD2;
unsigned bezierOrder (const Geom::Curve* curve_in)
{
- using namespace Geom;
- //cast it
- const CubicBezier *cbc = dynamic_cast<const CubicBezier*>(curve_in);
- if (cbc) return 3;
- const QuadraticBezier * qbc = dynamic_cast<const QuadraticBezier*>(curve_in);
- if (qbc) return 2;
- const BezierCurveN<1U> * lbc = dynamic_cast<const BezierCurveN<1U> *>(curve_in);
- if (lbc) return 1;
- return 0;
+ using namespace Geom;
+ //cast it
+ const CubicBezier *cbc = dynamic_cast<const CubicBezier*>(curve_in);
+ if (cbc) return 3;
+ const QuadraticBezier * qbc = dynamic_cast<const QuadraticBezier*>(curve_in);
+ if (qbc) return 2;
+ const BezierCurveN<1U> * lbc = dynamic_cast<const BezierCurveN<1U> *>(curve_in);
+ if (lbc) return 1;
+ return 0;
}
//returns true if the angle formed by the curves and their handles
//is >180 clockwise, otherwise false.
bool outside_angle (const Geom::Curve* cbc1, const Geom::Curve* cbc2)
{
- Geom::Point start_point = cbc1->initialPoint();
- Geom::Point end_point = cbc2->finalPoint();
- unsigned order = bezierOrder(cbc1);
- switch (order)
- {
- case 3:
- start_point = ( dynamic_cast<const Geom::CubicBezier*>(cbc1) )->operator [] (2);
- break;
- case 2:
- start_point = ( dynamic_cast<const Geom::QuadraticBezier*>(cbc1) )->operator [] (1);
- break;
- }
- order = bezierOrder(cbc2);
- switch (order)
- {
- case 3:
- end_point = ( dynamic_cast<const Geom::CubicBezier*>(cbc2) )->operator [] (1);
- break;
- case 2:
- end_point = ( dynamic_cast<const Geom::QuadraticBezier*>(cbc2) )->operator[] (1);
- break;
- }
- return false;
+ Geom::Point start_point = cbc1->initialPoint();
+ Geom::Point end_point = cbc2->finalPoint();
+ unsigned order = bezierOrder(cbc1);
+ switch (order) {
+ case 3:
+ start_point = ( dynamic_cast<const Geom::CubicBezier*>(cbc1) )->operator [] (2);
+ break;
+ case 2:
+ start_point = ( dynamic_cast<const Geom::QuadraticBezier*>(cbc1) )->operator [] (1);
+ break;
+ }
+ order = bezierOrder(cbc2);
+ switch (order) {
+ case 3:
+ end_point = ( dynamic_cast<const Geom::CubicBezier*>(cbc2) )->operator [] (1);
+ break;
+ case 2:
+ end_point = ( dynamic_cast<const Geom::QuadraticBezier*>(cbc2) )->operator[] (1);
+ break;
+ }
+ return false;
}
-void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve*cbc2, Geom::Point endPt, double miter_limit)
+void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit)
{
-
- Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
- if (cross.empty())
- {
- Geom::Path pth;
- pth.append(*cbc1);
-
- //Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
- pth = Geom::Path();
- pth.append( *cbc2 );
- Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+ if (cross.empty()) {
+ Geom::Path pth;
+ pth.append(*cbc1);
+
+ //Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
+
+ pth = Geom::Path();
+ pth.append( *cbc2 );
+ Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
+
+
+ Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(cbc1->toSBasis()), 0.);
+ Geom::Circle circle2 = Geom::touching_circle(cbc2->toSBasis(), 0);
+
+ Geom::Point points[2];
+ int solutions = Geom::circle_circle_intersection(circle1, circle2, points[0], points[1]);
+ if (solutions == 2) {
+ Geom::Point sol(0,0);
+ if ( dot(tang2,points[0]-endPt) > 0 ) {
+ // points[0] is bad, choose points[1]
+ sol = points[1];
+ } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1]
+ // points[1] is bad, choose points[0]
+ sol = points[0];
+ } else {
+ // both points are good, choose nearest
+ sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ?
+ points[0] : points[1];
+ }
+ Geom::EllipticalArc *arc0 = circle1.arc(cbc1->finalPoint(), 0.5*(cbc1->finalPoint()+sol), sol, true);
+ Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+
+ if (arc0) {
+ path_builder.append (arc0->toSBasis());
+ delete arc0;
+ arc0 = NULL;
+ }
+
+ if (arc1) {
+ path_builder.append (arc1->toSBasis());
+ delete arc1;
+ arc1 = NULL;
+ }
+ } else {
+ path_builder.appendNew<Geom::LineSegment> (endPt);
+ }
+ } else {
+ path_builder.appendNew<Geom::LineSegment> (endPt);
+ }
+}
+void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit)
+{
+ //the most important work for the reflected join is done here
- Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(cbc1->toSBasis()), 0.);
- Geom::Circle circle2 = Geom::touching_circle(cbc2->toSBasis(), 0);
+ //determine where we are in the path. If we're on the inside, ignore
+ //and just lineTo. On the outside, we'll do a little reflection magic :)
+
+ //note: this is TERRIBLY inaccurate.
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+ if (cross.empty()) {
+ //probably on the outside of the corner
+ Geom::Path pth;
+ pth.append(*cbc1);
+
+ Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
+
+ //reflect curves along the bevel
+ D2SB newcurve1 = pth.toPwSb()[0] *
+ Geom::reflection ( -Geom::rot90(tang1) ,
+ cbc1->finalPoint() );
+
+ Geom::CubicBezier bzr1 = sbasis_to_cubicbezier(Geom::reverse(newcurve1));
+
+ pth = Geom::Path();
+ pth.append( *cbc2 );
+ Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
+
+ D2SB newcurve2 = pth.toPwSb()[0] *
+ Geom::reflection ( -Geom::rot90(tang2) ,
+ cbc2->initialPoint() );
+ Geom::CubicBezier bzr2 = sbasis_to_cubicbezier(Geom::reverse(newcurve2));
+
+ cross = Geom::crossings(bzr1, bzr2);
+ if ( cross.empty() ) {
+ //curves didn't cross; default to miter
+ /*boost::optional <Geom::Point> p = intersection_point (cbc1->finalPoint(), tang1,
+ cbc2->initialPoint(), tang2);
+ if (p)
+ {
+ path_builder.appendNew<Geom::LineSegment> (*p);
+ }*/
+ //bevel
+ path_builder.appendNew<Geom::LineSegment>( endPt );
+ } else {
+ //join
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> sub1 = bzr1.subdivide(cross[0].ta);
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> sub2 = bzr2.subdivide(cross[0].tb);
+
+ //@TODO joins have a strange tendency to cross themselves twice. Check this.
+
+ //sections commented out are for general stability
+ path_builder.appendNew <Geom::CubicBezier> (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] );
+ path_builder.appendNew <Geom::CubicBezier> (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt );
+ }
+ } else { // cross.empty()
+ //probably on the inside of the corner
+ path_builder.appendNew<Geom::LineSegment> ( endPt );
+ }
+}
- Geom::Point points[2];
- int solutions = Geom::circle_circle_intersection(circle1, circle2, points[0], points[1]);
- if (solutions == 2)
- {
- Geom::Point sol(0,0);
- if ( dot(tang2,points[0]-endPt) > 0 )
- {
- // points[0] is bad, choose points[1]
- sol = points[1];
- }
- else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1]
- // points[1] is bad, choose points[0]
- sol = points[0];
- }
- else
- {
- // both points are good, choose nearest
- sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ?
- points[0] : points[1];
- }
- Geom::EllipticalArc *arc0 = circle1.arc(cbc1->finalPoint(), 0.5*(cbc1->finalPoint()+sol), sol, true);
- Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
-
- if (arc0)
- {
- path_builder.append (arc0->toSBasis());
- delete arc0;
- arc0 = NULL;
- }
-
- if (arc1)
- {
- path_builder.append (arc1->toSBasis());
- delete arc1;
- arc1 = NULL;
- }
- }
- else
- {
- path_builder.appendNew<Geom::LineSegment> (endPt);
- }
- }
- else
- {
- path_builder.appendNew<Geom::LineSegment> (endPt);
- }
+/** @brief Converts a path to one half of an outline.
+* path_in: The input path to use. (To create the other side use path_in.reverse() )
+* line_width: the line width to use (usually you want to divide this by 2)
+* miter_limit: the miter parameter
+* extrapolate: whether the join should be extrapolated instead of reflected
+*/
+Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double miter_limit, bool extrapolate = false)
+{
+ // NOTE: it is important to notice the distinction between a Geom::Path and a livarot Path here!
+ // if you do not see "Geom::" there is a different function set!
+ Geom::PathVector pv = split_at_cusps(path_in);
+
+ Path to_outline;
+ Path outlined_result;
+
+ Geom::Path path_builder = Geom::Path(); //the path to store the result in
+ Geom::PathVector * path_vec; //needed because livarot returns a goddamn pointer
+
+ const unsigned k = path_in.size();
+
+ for (unsigned u = 0; u < k; u+=2)
+ {
+ to_outline = Path();
+ outlined_result = Path();
+
+ to_outline.LoadPath(pv[u], Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+ //now a curve has been outside outlined and loaded into outlined_result
+
+ //get the Geom::Path
+ path_vec = outlined_result.MakePathVector();
+
+ //thing to do on the first run through
+ if (u == 0) {
+ //I could use the pv->operator[] (0) notation but that looks terrible
+ path_builder.start( (*path_vec)[0].initialPoint() );
+ } else {
+ //get the curves ready for the operation
+ Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate();
+
+ //do the reflection/extrapolation:
+ if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit); }
+ else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit); }
+ }
+
+ path_builder.append( (*path_vec)[0] );
+
+ //outline the next segment, but don't store it yet
+ if (path_vec) delete path_vec;
+
+ if (u < k - 1) {
+ outlined_result = Path();
+ to_outline = Path();
+
+ to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+
+ path_vec = outlined_result.MakePathVector();
+
+ //get the curves ready for the operation
+ Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate();
+
+ //do the reflection/extrapolation:
+ if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit); }
+ else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit); }
+
+ //Now we can store it.
+ path_builder.append( (*path_vec)[0] );
+
+ if (cbc1) delete cbc1;
+ if (cbc2) delete cbc2;
+ if (path_vec) delete path_vec;
+ }
+ }
+
+ return path_builder;
}
- Geom::Path half_outline_extrp(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit)
- {
- Geom::PathVector pv = split_at_cusps(path_in);
- unsigned m;
- Path path_outline = Path();
- Path path_tangent = Path();
-
- Geom::Point initialPoint;
- Geom::Point endPoint;
-
- Geom::Path path_builder = Geom::Path();
- Geom::PathVector * pathvec;
-
- //load the first portion in before the loop starts
- {
- path_outline = Path();
- path_outline.LoadPath(pv[0], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
- //now half of first cusp has been loaded
-
- pathvec = path_tangent.MakePathVector();
- path_tangent = Path();
-
- //instead of array accessing twice, dereferencing used for clarity
- initialPoint = (*pathvec)[0].initialPoint();
-
- path_builder.start(initialPoint);
- path_builder.append( (*pathvec)[0] );
-
- path_outline = Path();
- path_outline.LoadPath(pv[1], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
-
- delete pathvec; pathvec = NULL;
- pathvec = path_tangent.MakePathVector();
- path_tangent = Path();
-
- Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
- Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
-
- extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
-
- path_builder.append( (*pathvec)[0] );
-
- //always set pointers null after deleting
- delete pathvec; pathvec = NULL;
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
-
- for (m = 2; m < pv.size(); m++)
- {
- path_outline = Path();
- path_outline.LoadPath(pv[m], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
-
- delete pathvec; pathvec = NULL;
- pathvec = path_tangent.MakePathVector();
-
- Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
- Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
-
- extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
- path_builder.append( (*pathvec)[0] );
-
- delete pathvec; pathvec = NULL;
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
-
- return path_builder;
- }
- //Create a reflected outline join.
- //Note: it is generally recommended to let half_outline do this for you!
- //path_builder: the path to append the curves to
- //cbc1: the curve before the join
- //cbc2: the curve after the join
- //endPt: the point to end at
- //miter_limit: the miter parameter
- void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit)
- {
- //the most important work for the reflected join is done here
+Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join,
+ ButtType butt, double miter_lim, bool extrapolate)
+{
+ Geom::PathVector path_out;
+
+ unsigned pv_size = path_in.size();
+ for (unsigned i = 0; i < pv_size; i++) {
+
+ if (path_in[i].size() > 1) {
+ //since you've made it this far, hopefully all this is obvious :P
+ Geom::Path with_direction;
+ Geom::Path against_direction;
+
+ with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate );
+ against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, extrapolate );
+
+ Geom::PathBuilder pb;
+
+ //add in the...do I really need to say this?
+ pb.moveTo(with_direction.initialPoint());
+ pb.append(with_direction);
+
+ //add in our line caps
+ if (!path_in[i].closed()) {
+ switch (butt) {
+ case butt_straight:
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ case butt_round:
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() );
+ break;
+ case butt_pointy:
+ //I have ZERO idea what to do here.
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ case butt_square:
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ }
+ } else {
+ pb.moveTo(against_direction.initialPoint());
+ }
+
+ pb.append(against_direction);
+
+ //cap (if necessary)
+ if (!path_in[i].closed()) {
+ switch (butt) {
+ case butt_straight:
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ case butt_round:
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() );
+ break;
+ case butt_pointy:
+ //I have ZERO idea what to do here.
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ case butt_square:
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ }
+ }
+ pb.flush();
+ for (unsigned m = 0; i < pb.peek().size(); i++) {
+ path_out.push_back(pb.peek()[m]);
+ }
+ } else {
+ Path p = Path();
+ Path outlinepath = Path();
+
+ p.LoadPath(path_in[i], Geom::Affine(), false, false);
+ p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(join), butt, miter_lim);
+ Geom::PathVector *pv_p = outlinepath.MakePathVector();
+ //somewhat hack-ish
+ path_out.push_back( (*pv_p)[0].reverse() );
+ if (pv_p) delete pv_p;
+ }
+ }
+ return path_out;
+}
- //determine where we are in the path. If we're on the inside, ignore
- //and just lineTo. On the outside, we'll do a little reflection magic :)
- Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
- if (cross.empty())
- {
- //probably on the outside of the corner
- Geom::Path pth;
- pth.append(*cbc1);
-
- Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
-
- //reflect curves along the bevel
- D2SB newcurve1 = pth.toPwSb()[0] *
- Geom::reflection ( -Geom::rot90(tang1) ,
- cbc1->finalPoint() );
-
- Geom::CubicBezier bzr1 = sbasis_to_cubicbezier(Geom::reverse(newcurve1));
-
- pth = Geom::Path();
- pth.append( *cbc2 );
- Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
-
- D2SB newcurve2 = pth.toPwSb()[0] *
- Geom::reflection ( -Geom::rot90(tang2) ,
- cbc2->initialPoint() );
- Geom::CubicBezier bzr2 = sbasis_to_cubicbezier(Geom::reverse(newcurve2));
-
- cross = Geom::crossings(bzr1, bzr2);
- if ( cross.empty() )
- {
- //std::cout << "Oops, no crossings!" << std::endl;
- //curves didn't cross; default to miter
- /*boost::optional <Geom::Point> p = intersection_point (cbc1->finalPoint(), tang1,
- cbc2->initialPoint(), tang2);
- if (p)
- {
- path_builder.appendNew<Geom::LineSegment> (*p);
- }*/
- //bevel
- path_builder.appendNew<Geom::LineSegment>( endPt );
- }
- else
- {
- //join
- std::pair<Geom::CubicBezier, Geom::CubicBezier> sub1 = bzr1.subdivide(cross[0].ta);
- std::pair<Geom::CubicBezier, Geom::CubicBezier> sub2 = bzr2.subdivide(cross[0].tb);
-
- //@TODO joins have a strange tendency to cross themselves twice. Check this.
-
- //sections commented out are for general stability
- path_builder.appendNew <Geom::CubicBezier> (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] );
- path_builder.appendNew <Geom::CubicBezier> (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt );
- }
- }
- else // cross.empty()
- {
- //probably on the inside of the corner
- path_builder.appendNew<Geom::LineSegment> ( endPt );
- }
- }
+Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtType linecap_type,
+ LineJoinType linejoin_type, double miter_limit)
+{
+ std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+ if (path_in.empty()) {
+ return path_out;
+ }
+ Path p = Path();
+ Path outlinepath = Path();
+ for (unsigned i = 0; i < path_in.size(); i++) {
+ p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
+ }
- /** @brief Converts a path to one half of an outline.
- * path_in: The input path to use. (To create the other side use path_in.reverse() )
- * line_width: the line width to use (usually you want to divide this by 2)
- * linecap_type: (not used here) the cap to apply. Passed to libvarot.
- * miter_limit: the miter parameter
- */
- Geom::Path half_outline(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit)
- {
- Geom::PathVector pv = split_at_cusps(path_in);
- unsigned m;
- Path path_outline = Path();
- Path path_tangent = Path();
- //needed for closing the path
- Geom::Point initialPoint;
- Geom::Point endPoint;
-
- //some issues prevented me from using a PathBuilder here
- //it seems like PathBuilder::peek() gave me a null reference exception
- //and I was unable to get a stack trace on Windows, so had to switch to Linux
- //to see what the hell was wrong. :(
- //I wasted five hours opening it in IDAPro, VS2012, and GDB Windows
-
- /*Program received signal SIGSEGV, Segmentation fault.
- 0x00000000006539ac in get_curves (this=0x0)
- at /usr/include/c++/4.6/bits/locale_facets.h:1077
- 1077 { return __c; }
- */
-
- Geom::Path path_builder = Geom::Path();
- Geom::PathVector * pathvec;
-
- //load the first portion in before the loop starts
- {
- path_outline = Path();
- path_outline.LoadPath(pv[0], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
- //now half of first cusp has been loaded
-
- pathvec = path_tangent.MakePathVector();
- path_tangent = Path();
-
- //instead of array accessing twice, dereferencing used for clarity
- initialPoint = (*pathvec)[0].initialPoint();
-
- path_builder.start(initialPoint);
- path_builder.append( (*pathvec)[0] );
-
- path_outline = Path();
- path_outline.LoadPath(pv[1], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
-
- delete pathvec; pathvec = NULL;
- pathvec = path_tangent.MakePathVector();
- path_tangent = Path();
-
- Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
- Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
-
- reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
-
- path_builder.append( (*pathvec)[0] );
-
- //always set pointers null after deleting
- delete pathvec; pathvec = NULL;
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
-
- for (m = 2; m < pv.size(); m++)
- {
- path_outline = Path();
- path_outline.LoadPath(pv[m], Geom::Affine(), false, false);
- path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
-
- delete pathvec; pathvec = NULL;
- pathvec = path_tangent.MakePathVector();
-
- Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
- Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
-
- reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
- path_builder.append( (*pathvec)[0] );
-
- delete pathvec; pathvec = NULL;
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
-
- return path_builder;
- }
+#define miter_lim fabs(line_width * miter_limit)
- Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, JoinType join, ButtType butt, double miter_lim)
- {
- Path p = Path();
- Path outlinepath = Path();
- for (unsigned i = 0; i < path_in.size(); i++)
- {
- p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
- }
+ //magic!
+ if (linejoin_type <= 2) {
+ p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(linejoin_type),
+ linecap_type, miter_lim);
+ //fix memory leak
+ std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
+ path_out = *pv_p;
+ delete pv_p;
- Geom::PathVector path_out;
- for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++)
- {
- if (path_in[lmnop].size() > 1)
- {
- Geom::Path p_init;
- Geom::Path p_rev;
- Geom::PathBuilder pb = Geom::PathBuilder();
-
- if ( !path_in[lmnop].closed() )
- {
- p_init = Outline::half_outline( path_in[lmnop], -line_width, butt,
- miter_lim );
- p_rev = Outline::half_outline( path_in[lmnop].reverse(), -line_width, butt,
- miter_lim );
-
- pb.moveTo(p_init.initialPoint() );
- pb.append(p_init);
-
- //cap
- if (butt == butt_straight) {
- pb.lineTo(p_rev.initialPoint() );
- } else if (butt == butt_round) {
- pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() );
- } else if (butt == butt_square) {
- //don't know what to do
- pb.lineTo(p_rev.initialPoint() );
- } else if (butt == butt_pointy) {
- //don't know what to do
- pb.lineTo(p_rev.initialPoint() );
- }
-
- pb.append(p_rev);
-
- if (butt == butt_straight) {
- pb.lineTo(p_init.initialPoint() );
- } else if (butt == butt_round) {
- pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() );
- } else if (butt == butt_square) {
- //don't know what to do
- pb.lineTo(p_init.initialPoint() );
- } else if (butt == butt_pointy) {
- //don't know what to do
- //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0);
- //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint());
-
- pb.lineTo(p_init.initialPoint() );
- }
- }
- else
- {
- //final join
- //refer to half_outline for documentation
- Geom::Path p_almost = path_in[lmnop];
- p_almost.appendNew<Geom::LineSegment> ( path_in[lmnop].initialPoint() );
- p_init = Outline::half_outline( p_almost, -line_width, butt,
- miter_lim );
- p_rev = Outline::half_outline( p_almost.reverse(), -line_width, butt,
- miter_lim );
- p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
-
- //this is a kludge, because I can't find how to make this work properly
- bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(),
- path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) ==
- (path_in[lmnop] [path_in[lmnop].size()].length()));
-
- p_almost = p_init;
- if (lastIsLinear)
- {
- p_almost.erase_last(); p_almost.erase_last();
- }
-
- //outside test
- Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate();
- Geom::Curve* cbc2 = p_almost[0].duplicate();
-
- Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
-
- if (cross.empty())
- {
- //this is the outside path
-
- //reuse the old one
- p_init = p_almost;
- Outline::reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
- pb.moveTo(p_init.initialPoint()); pb.append(p_init);
- }
- else
- {
- //inside, carry on :-)
- pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
- }
-
- p_almost = p_rev;
- if (lastIsLinear)
- {
- p_almost.erase(p_almost.begin() );
- p_almost.erase(p_almost.begin() );
- }
-
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
-
- cbc1 = p_almost[p_almost.size() - 1].duplicate();
- cbc2 = p_almost[0].duplicate();
-
- cross = Geom::crossings(*cbc1, *cbc2);
-
- if (cross.empty())
- {
- //outside path
-
- p_init = p_almost;
- reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
- pb.moveTo(p_init.initialPoint()); pb.append(p_init);
- }
- else
- {
- //inside
- pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
- }
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
- //pb.closePath();
- pb.flush();
- Geom::PathVector pv_np = pb.peek();
- //hack
- for (unsigned abcd = 0; abcd < pv_np.size(); abcd++)
- {
- path_out.push_back( pv_np[abcd] );
- }
- }
- else
- {
- p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
- p.Outline(&outlinepath, line_width / 2, join, butt, miter_lim);
- std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
- //hack
- path_out.push_back( (*pv_p)[0].reverse() );
- delete pv_p;
- }
- }
- return path_out;
- }
- Geom::PathVector outlinePath_extr(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim)
- {
- Path p = Path();
- Path outlinepath = Path();
- for (unsigned i = 0; i < path_in.size(); i++)
- {
- p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
- }
+ } else if (linejoin_type == 3) {
+ //reflected arc join
+ path_out = outlinePath(path_in, line_width, static_cast<LineJoinType>(linejoin_type),
+ linecap_type , miter_lim, false);
- Geom::PathVector path_out;
- for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++)
- {
- if (path_in[lmnop].size() > 1)
- {
- Geom::Path p_init;
- Geom::Path p_rev;
- Geom::PathBuilder pb = Geom::PathBuilder();
-
- if ( !path_in[lmnop].closed() )
- {
- p_init = Outline::half_outline_extrp( path_in[lmnop], -line_width, butt,
- miter_lim );
- p_rev = Outline::half_outline_extrp( path_in[lmnop].reverse(), -line_width, butt,
- miter_lim );
-
- pb.moveTo(p_init.initialPoint() );
- pb.append(p_init);
-
- //cap
- if (butt == butt_straight) {
- pb.lineTo(p_rev.initialPoint() );
- } else if (butt == butt_round) {
- pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() );
- } else if (butt == butt_square) {
- //don't know what to do
- pb.lineTo(p_rev.initialPoint() );
- } else if (butt == butt_pointy) {
- //don't know what to do
- pb.lineTo(p_rev.initialPoint() );
- }
-
- pb.append(p_rev);
-
- if (butt == butt_straight) {
- pb.lineTo(p_init.initialPoint() );
- } else if (butt == butt_round) {
- pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() );
- } else if (butt == butt_square) {
- //don't know what to do
- pb.lineTo(p_init.initialPoint() );
- } else if (butt == butt_pointy) {
- //don't know what to do
- //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0);
- //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint());
-
- pb.lineTo(p_init.initialPoint() );
- }
- }
- else
- {
- //final join
- //refer to half_outline for documentation
- Geom::Path p_almost = path_in[lmnop];
- p_almost.appendNew<Geom::LineSegment> ( path_in[lmnop].initialPoint() );
- p_init = Outline::half_outline_extrp( p_almost, -line_width, butt,
- miter_lim );
- p_rev = Outline::half_outline_extrp( p_almost.reverse(), -line_width, butt,
- miter_lim );
- p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
-
- //this is a kludge, because I can't find how to make this work properly
- bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(),
- path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) ==
- (path_in[lmnop] [path_in[lmnop].size()].length()));
-
- p_almost = p_init;
- if (lastIsLinear)
- {
- p_almost.erase_last(); p_almost.erase_last();
- }
-
- //outside test
- Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate();
- Geom::Curve* cbc2 = p_almost[0].duplicate();
-
- Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
-
- if (cross.empty())
- {
- //this is the outside path
-
- //reuse the old one
- p_init = p_almost;
- Outline::extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
- pb.moveTo(p_init.initialPoint()); pb.append(p_init);
- }
- else
- {
- //inside, carry on :-)
- pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
- }
-
- p_almost = p_rev;
- if (lastIsLinear)
- {
- p_almost.erase(p_almost.begin() );
- p_almost.erase(p_almost.begin() );
- }
-
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
-
- cbc1 = p_almost[p_almost.size() - 1].duplicate();
- cbc2 = p_almost[0].duplicate();
-
- cross = Geom::crossings(*cbc1, *cbc2);
-
- if (cross.empty())
- {
- //outside path
-
- p_init = p_almost;
- extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
- pb.moveTo(p_init.initialPoint()); pb.append(p_init);
- }
- else
- {
- //inside
- pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
- }
- delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
- }
- //pb.closePath();
- pb.flush();
- Geom::PathVector pv_np = pb.peek();
- //hack
- for (unsigned abcd = 0; abcd < pv_np.size(); abcd++)
- {
- path_out.push_back( pv_np[abcd] );
- }
- }
- else
- {
- p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
- p.Outline(&outlinepath, line_width / 2, join_pointy, butt, miter_lim);
- std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
- //hack
- path_out.push_back( (*pv_p)[0].reverse() );
- delete pv_p;
- }
- }
- return path_out;
- }
-
-Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtType linecap_type,
- join_typ linejoin_type, double miter_limit)
-{
- std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
- if (path_in.empty())
- {
- return path_out;
- }
- Path p = Path();
- Path outlinepath = Path();
- for (unsigned i = 0; i < path_in.size(); i++)
- {
- p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
- }
+ } else if (linejoin_type == 4) {
+ //extrapolated arc join
+ path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true);
- #define miter_lim fabs(line_width * miter_limit)
+ }
- //magic!
- if (linejoin_type <= 2)
- {
- p.Outline(&outlinepath, line_width / 2, linejoin_type,
- linecap_type, miter_lim);
- //fix memory leak
- std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
- path_out = *pv_p;
- delete pv_p;
-
- } else if (linejoin_type == 3) {
- //reflected arc join
- path_out = outlinePath(path_in, line_width, linejoin_type,
- linecap_type , miter_lim);
-
- } else if (linejoin_type == 4) {
- //extrapolated arc join
- path_out = outlinePath_extr(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim);
-
- }
-
- #undef miter_lim
- return path_out;
+#undef miter_lim
+ return path_out;
}
+
Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit)
{
- #define miter_lim fabs(line_width * miter_limit)
-
+#define miter_lim fabs(line_width * miter_limit)
+
Geom::Path path_out;
if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) {
-
+
Geom::PathVector * pathvec;
-
+
Path path_tangent = Path();
Path path_outline = Path();
path_outline.LoadPath(path_in, Geom::Affine(), false, false);
path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast<join_typ>(linejoin_type), butt_straight, miter_lim);
-
+
pathvec = path_tangent.MakePathVector();
path_out = pathvec[0]/* deref pointer */[0]/*actual object ref*/;
delete pathvec;
return path_out;
- }
- else if (linejoin_type == LINEJOIN_REFLECTED) {
+ } else if (linejoin_type == LINEJOIN_REFLECTED) {
//reflected half outline
- Geom::PathVector pathvec; pathvec.push_back(path_in);
- path_out = half_outline(path_in, line_width, butt_straight, miter_lim);
+ Geom::PathVector pathvec;
+ pathvec.push_back(path_in);
+ path_out = doAdvHalfOutline(path_in, line_width, miter_lim, false);
return path_out;
- }
- else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
- path_out = half_outline_extrp(path_in, line_width, butt_straight, miter_lim);
+ } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
+ //what the hell do you think this is? :P
+ path_out = doAdvHalfOutline(path_in, line_width, miter_lim, true);
return path_out;
}
+#undef miter_lim
return path_out;
}
diff --git a/src/live_effects/pathoutlineprovider.h b/src/live_effects/pathoutlineprovider.h
index 4133f47c2..23cc7e2c4 100644
--- a/src/live_effects/pathoutlineprovider.h
+++ b/src/live_effects/pathoutlineprovider.h
@@ -16,10 +16,10 @@ namespace Outline
{
unsigned bezierOrder (const Geom::Curve* curve_in);
std::vector<Geom::Path> PathVectorOutline(std::vector<Geom::Path> const & path_in, double line_width, ButtType linecap_type,
- join_typ linejoin_type, double miter_limit);
+ LineJoinType linejoin_type, double miter_limit);
- Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, JoinType join, ButtType butt, double miter_lim);
- Geom::PathVector outlinePath_extr(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim);
+ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join,
+ ButtType butt, double miter_lim, bool extrapolate = false);
Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit);
}