summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2015-03-28 13:57:30 +0000
committerJabiertxof <jtx@jtx.marker.es>2015-03-28 13:57:30 +0000
commitd3117487bcf047050642b80a3f46acd811947105 (patch)
tree2e1d204fefe18c4d2e94053f71830a18bc4590e1 /src
parentadding rotation transform (diff)
parentFix calculation of miter limit. Implement 'miter-clip' line join. (diff)
downloadinkscape-d3117487bcf047050642b80a3f46acd811947105.tar.gz
inkscape-d3117487bcf047050642b80a3f46acd811947105.zip
update to trunk
(bzr r13708.1.21)
Diffstat (limited to 'src')
-rw-r--r--src/document.cpp11
-rw-r--r--src/document.h1
-rw-r--r--src/helper/geom-pathstroke.cpp442
-rw-r--r--src/helper/geom-pathstroke.h3
-rw-r--r--src/live_effects/lpe-bspline.cpp44
-rw-r--r--src/live_effects/lpe-bspline.h3
-rw-r--r--src/live_effects/lpe-jointype.cpp13
-rw-r--r--src/live_effects/lpe-jointype.h1
-rw-r--r--src/live_effects/lpe-perspective-envelope.cpp12
-rw-r--r--src/live_effects/lpe-taperstroke.cpp116
-rw-r--r--src/svg-profile.h2
-rw-r--r--src/ui/dialog/filter-effects-dialog.cpp2
-rw-r--r--src/ui/tool/curve-drag-point.cpp4
-rw-r--r--src/ui/tool/multi-path-manipulator.h2
-rw-r--r--src/ui/tool/node.cpp15
-rw-r--r--src/ui/tool/path-manipulator.cpp53
-rw-r--r--src/ui/tool/path-manipulator.h2
-rw-r--r--src/ui/widget/page-sizer.cpp169
-rw-r--r--src/ui/widget/page-sizer.h21
-rw-r--r--src/uri-references.cpp2
20 files changed, 561 insertions, 357 deletions
diff --git a/src/document.cpp b/src/document.cpp
index 62e2f5b46..1c6bb76de 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -764,6 +764,17 @@ void SPDocument::setHeight(const Inkscape::Util::Quantity &height, bool changeSi
root->updateRepr();
}
+Geom::Rect SPDocument::getViewBox() const
+{
+ Geom::Rect viewBox;
+ if (root->viewBox_set) {
+ viewBox = root->viewBox;
+ } else {
+ viewBox = Geom::Rect::from_xywh( 0, 0, getWidth().value("px"), getHeight().value("px"));
+ }
+ return viewBox;
+}
+
void SPDocument::setViewBox(const Geom::Rect &viewBox)
{
root->viewBox_set = true;
diff --git a/src/document.h b/src/document.h
index ed19d123b..74dcfa75e 100644
--- a/src/document.h
+++ b/src/document.h
@@ -247,6 +247,7 @@ public:
Geom::Scale getDocumentScale() const;
Inkscape::Util::Quantity getWidth() const;
Inkscape::Util::Quantity getHeight() const;
+ Geom::Rect getViewBox() const;
Geom::Point getDimensions() const;
Geom::OptRect preferredBounds() const;
void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true);
diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp
index 1b8f90104..1908f2db7 100644
--- a/src/helper/geom-pathstroke.cpp
+++ b/src/helper/geom-pathstroke.cpp
@@ -6,11 +6,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <iomanip>
#include <2geom/path-sink.h>
#include <2geom/point.h>
#include <2geom/bezier-curve.h>
#include <2geom/svg-elliptical-arc.h>
#include <2geom/sbasis-to-bezier.h> // cubicbezierpath_from_sbasis
+#include <2geom/path-intersection.h>
#include "helper/geom-pathstroke.h"
@@ -18,6 +20,74 @@ namespace Geom {
// 2geom/circle-circle.cpp, no header
int circle_circle_intersection(Point X0, double r0, Point X1, double r1, Point &p0, Point &p1);
+/**
+ * Determine the intersection points between a circle C0 and a line defined
+ * by two points, X0 and X1.
+ *
+ * Which intersection point is assigned to p0 or p1 is unspecified, and callers
+ * should not depend on any particular intersection always being assigned to p0.
+ *
+ * Returns:
+ * If the line and circle do not cross, 0 is returned.
+ * If solution(s) exist, 2 is returned, and the results are written to p0 and p1.
+ */
+static int circle_line_intersection(Circle C0, Point X0, Point X1, Point &p0, Point &p1)
+{
+ /* equation of a circle: (x - h)^2 + (y - k)^2 = r^2 */
+ Coord r = C0.ray();
+ Coord h = C0.center()[X];
+ Coord k = C0.center()[Y];
+
+ Coord x0, y0;
+ Coord x1, y1;
+
+ if (are_near(X1[X], X0[X])) {
+ /* slope is undefined (vertical line) */
+ Coord c = X0[X];
+ Coord det = r*r - (c-h)*(c-h);
+
+ /* no intersection */
+ if (det < 0)
+ return 0;
+
+ /* solve for y */
+ y0 = k + std::sqrt(det);
+ y1 = k - std::sqrt(det);
+
+ // x == c (always)
+ x0 = c;
+ x1 = c;
+ } else {
+ /* equation of a line: y = mx + b */
+ Coord m = (X1[Y] - X0[Y]) / (X1[X] - X0[X]);
+ Coord b = X0[Y] - m*X0[X];
+
+ /* obtain quadratic for x: */
+ Coord A = m*m + 1;
+ Coord B = 2*h - 2*b*m + 2*k*m;
+ Coord C = b*b + h*h + k*k - r*r - 2*b*k;
+
+ Coord det = B*B - 4*A*C;
+
+ /* no intersection, circle and line do not cross */
+ if (det < 0)
+ return 0;
+
+ /* solve quadratic */
+ x0 = (B + std::sqrt(det)) / (2*A);
+ x1 = (B - std::sqrt(det)) / (2*A);
+
+ /* substitute the calculated x times to determine the y values */
+ y0 = m*x0 + b;
+ y1 = m*x1 + b;
+ }
+
+ p0 = Point(x0, y0);
+ p1 = Point(x1, y1);
+
+ return 2;
+}
+
static Point intersection_point(Point origin_a, Point vector_a, Point origin_b, Point vector_b)
{
Coord denom = cross(vector_b, vector_a);
@@ -57,122 +127,221 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0
namespace {
+// Join functions may:
+// - inspect any curve of the current path
+// - append any type of curve to the current path
+// - inspect the outgoing path
+//
+// Join functions must:
+// - append the outgoing curve
+// OR
+// - end at outgoing.finalPoint
+
typedef void join_func(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width);
void bevel_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double /*width*/)
{
res.appendNew<Geom::LineSegment>(outgoing.initialPoint());
+ res.append(outgoing);
}
void round_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double width)
{
res.appendNew<Geom::SVGEllipticalArc>(width, width, 0, false, width <= 0, outgoing.initialPoint());
+ res.append(outgoing);
}
-void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width)
+void miter_join_internal(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width, bool clip)
{
Geom::Curve const& incoming = res.back();
Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.);
Geom::Point tang2 = outgoing.unitTangentAt(0);
Geom::Point p = Geom::intersection_point(incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2);
+
+ bool satisfied = false;
+
if (p.isFinite()) {
// check size of miter
- Geom::Point point_on_path = incoming.finalPoint() - Geom::rot90(tang1)*width;
- double len = Geom::distance(p, point_on_path);
- if (len <= miter) {
+ Geom::Point point_on_path = incoming.finalPoint() + Geom::rot90(tang1)*width;
+ satisfied = Geom::distance(p, point_on_path) <= miter * 2.0 * width;
+ if (satisfied) {
// miter OK, check to see if we can do a relocation
- // TODO FIXME
- /*if (auto line = cast(const(LineSegment))res.back_open) {
- Curve copy = line.duplicate;
- copy.setFinal(p);
- res.erase_last();
- res.append(copy);
- } else {*/
+ bool ls = res.back_open().degreesOfFreedom() <= 4;
+ if (ls) {
+ res.setFinal(p);
+ } else {
res.appendNew<Geom::LineSegment>(p);
- //}
+ }
+ } else if (clip) {
+ // miter needs clipping, find two points
+ Geom::Line bisector(point_on_path, p);
+ Geom::Point point_limit = point_on_path + miter * 2.0 * width * bisector.versor();
+
+ Geom::Line line_limit =
+ Geom::Line::from_origin_and_versor( point_limit, bisector.versor().cw() );
+
+ Geom::Line incoming_line( incoming.finalPoint(), p );
+ Geom::Line outgoing_line( p, outgoing.initialPoint() );
+
+ Geom::OptCrossing i1 = intersection( line_limit, incoming_line );
+ Geom::OptCrossing i2 = intersection( line_limit, outgoing_line );
+
+ // It would be nice to have a simple point returned by intersection!
+ Geom::Point p1 = line_limit.pointAt( (*i1).ta );
+ Geom::Point p2 = line_limit.pointAt( (*i2).ta );
+
+ bool ls = res.back_open().degreesOfFreedom() <= 4;
+ if (ls) {
+ res.setFinal(p1);
+ } else {
+ res.appendNew<Geom::LineSegment>(p1);
+ }
+ res.appendNew<Geom::LineSegment>(p2);
}
}
+
res.appendNew<Geom::LineSegment>(outgoing.initialPoint());
+
+ // check if we can do another relocation
+ bool ls = outgoing.degreesOfFreedom() <= 4;
+
+ if ( (satisfied || clip) && ls) {
+ res.setFinal(outgoing.finalPoint());
+ } else {
+ res.append(outgoing);
+ }
+}
+
+void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) {
+ miter_join_internal( res, outgoing, miter, width, false );
+}
+
+void miter_clip_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) {
+ miter_join_internal( res, outgoing, miter, width, true );
+}
+
+Geom::Point pick_solution(Geom::Point points[2], Geom::Point tang2, Geom::Point endPt)
+{
+ Geom::Point sol;
+ 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];
+ }
+ return sol;
}
-// might need a little reworking
void extrapolate_join(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width)
{
using namespace Geom;
Geom::Curve const& incoming = path_builder.back();
Geom::Point endPt = outgoing.initialPoint();
+ Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0);
+
+ Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.);
+ Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0);
- // The method used when extrapolating curves fails to work when either side of the join to be extrapolated
- // is a line segment. When this situation is encountered, fall back to a regular miter join.
- bool lineProblem = (dynamic_cast<LineSegment const *>(&incoming)) || (dynamic_cast<LineSegment const*>(&outgoing));
- if (lineProblem == false) {
- // Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.);
- Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0);
+ bool inc_ls = !circle1.center().isFinite();
+ bool out_ls = !circle2.center().isFinite();
- Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.);
- Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0);
+ Geom::Point points[2];
- Geom::Point points[2];
- int solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(),
- circle2.center(), circle2.ray(),
- points[0], points[1]);
+ int solutions = 0;
+ Geom::EllipticalArc *arc0 = NULL;
+ Geom::EllipticalArc *arc1 = NULL;
+
+ if (!inc_ls && !out_ls) {
+ solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(),
+ circle2.center(), circle2.ray(),
+ 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::Point sol = pick_solution(points, tang2, endPt);
- Geom::EllipticalArc *arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true);
- Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
- try {
- if (arc0) {
- path_builder.append(*arc0);
- delete arc0;
- arc0 = NULL;
- } else {
- throw std::exception();
- }
-
- if (arc1) {
- path_builder.append(*arc1);
- delete arc1;
- arc1 = NULL;
- } else {
- throw std::exception();
- }
-
- } catch (std::exception const & ex) {
- printf("WARNING: Error extrapolating line join: %s\n", ex.what());
- path_builder.appendNew<Geom::LineSegment>(endPt);
- }
- } else {
- // 1 or no solutions found, default to miter
- miter_join(path_builder, outgoing, miter_limit, line_width);
+ arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true);
+ arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+ }
+ } else if (inc_ls && !out_ls) {
+ solutions = Geom::circle_line_intersection(circle2, incoming.initialPoint(), incoming.finalPoint(), points[0], points[1]);
+
+ if (solutions == 2) {
+ Geom::Point sol = pick_solution(points, tang2, endPt);
+ path_builder.setFinal(sol);
+ arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+ }
+ } else if (!inc_ls && out_ls) {
+ solutions = Geom::circle_line_intersection(circle1, outgoing.initialPoint(), outgoing.finalPoint(), points[0], points[1]);
+
+ if (solutions == 2) {
+ Geom::Point sol = pick_solution(points, tang2, endPt);
+ arc0 = circle1.arc(incoming.finalPoint(), 0.5*(sol+incoming.finalPoint()), sol, true);
}
- } else {
- // Line segments exist
- miter_join(path_builder, outgoing, miter_limit, line_width);
}
+
+ if (solutions != 2)
+ // no solutions available, fall back to miter
+ return miter_join(path_builder, outgoing, miter_limit, line_width);
+
+ if (arc0)
+ path_builder.append(*arc0);
+ if (arc1)
+ path_builder.append(*arc1);
+
+ delete arc0;
+ delete arc1;
+
+ if (!inc_ls && out_ls)
+ path_builder.appendNew<Geom::LineSegment>(outgoing.finalPoint());
+ else
+ path_builder.append(outgoing);
}
void join_inside(Geom::Path& res, Geom::Curve const& outgoing)
{
- res.appendNew<Geom::LineSegment>(outgoing.initialPoint());
+ Geom::Curve const& incoming = res.back_open();
+ Geom::Crossings cross = Geom::crossings(incoming, outgoing);
+
+ if (!cross.empty()) {
+ // yeah if we could avoid allocing that'd be great
+ Geom::Curve *d1 = incoming.portion(0., cross[0].ta);
+ res.erase_last();
+ res.append(*d1);
+ delete d1;
+
+ Geom::Curve *d2 = outgoing.portion(cross[0].tb, 1.);
+ res.setFinal(d2->initialPoint());
+ res.append(*d2);
+ delete d2;
+ } else {
+ res.appendNew<Geom::LineSegment>(outgoing.initialPoint());
+ res.append(outgoing);
+ }
}
void outline_helper(Geom::Path& res, Geom::Path const& to_add, double width, double miter, Inkscape::LineJoinType join)
{
- Geom::Point tang1 = -Geom::unitTangentAt(reverse(res.back().toSBasis()), 0.);
- //Geom::Point tang2 = to_add[0].unitTangentAt(0);
- Geom::Point discontinuity_vec = to_add.initialPoint() - res.finalPoint();
- bool on_outside = (Geom::dot(tang1, discontinuity_vec) >= 0);
+ if (res.size() == 0 || to_add.size() == 0)
+ return;
+
+ Geom::Curve const& outgoing = to_add[0];
+ if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) {
+ // if the points are /that/ close, just ignore this one
+ res.setFinal(outgoing.initialPoint());
+ res.append(outgoing);
+ return;
+ }
+
+ Geom::Point tang1 = Geom::unitTangentAt(reverse(res.back().toSBasis()), 0.);
+ Geom::Point tang2 = Geom::unitTangentAt(to_add.front().toSBasis(), 0.);
+ // Geom::Point discontinuity_vec = to_add.initialPoint() - res.finalPoint();
+ bool on_outside = (Geom::cross(tang1, tang2) < 0);
+ // std::cout << std::fixed << std::setprecision(3)
+ // << " in: " << tang1 << " out: " << tang2
+ // << " side: " << (on_outside?"inside":"outside") << std::endl;
if (on_outside) {
join_func *jf;
@@ -186,15 +355,16 @@ void outline_helper(Geom::Path& res, Geom::Path const& to_add, double width, dou
case Inkscape::JOIN_EXTRAPOLATE:
jf = &extrapolate_join;
break;
+ case Inkscape::JOIN_MITER_CLIP:
+ jf = &miter_clip_join;
+ break;
default:
jf = &miter_join;
}
- jf(res, to_add[0], miter, width);
+ jf(res, outgoing, miter, width);
} else {
- join_inside(res, to_add[0]);
+ join_inside(res, outgoing);
}
-
- res.append(to_add);
}
// Offsetting a line segment is mathematically stable and quick to do
@@ -324,9 +494,11 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi
void offset_curve(Geom::Path& res, Geom::Curve const* current, double width)
{
- double const tolerance = 0.0025;
+ double const tolerance = 0.005;
size_t levels = 8;
+ if (current->isDegenerate()) return; // don't do anything
+
// TODO: we can handle SVGEllipticalArc here as well, do that!
if (Geom::BezierCurve const *b = dynamic_cast<Geom::BezierCurve const*>(current)) {
@@ -346,7 +518,7 @@ void offset_curve(Geom::Path& res, Geom::Curve const* current, double width)
break;
}
default: {
- Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1);
+ Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), tolerance);
for (size_t i = 0; i < sbasis_path.size(); ++i)
offset_curve(res, &sbasis_path[i], width);
break;
@@ -359,8 +531,40 @@ void offset_curve(Geom::Path& res, Geom::Curve const* current, double width)
}
}
+typedef void cap_func(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width);
+
+void flat_cap(Geom::PathBuilder& res, Geom::Path const&, Geom::Path const& against_dir, double)
+{
+ res.lineTo(against_dir.initialPoint());
+}
+
+void round_cap(Geom::PathBuilder& res, Geom::Path const&, Geom::Path const& against_dir, double width)
+{
+ res.arcTo(width / 2., width / 2., 0., true, false, against_dir.initialPoint());
+}
+
+void square_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width)
+{
+ width /= 2.;
+ Geom::Point normal_1 = -Geom::unitTangentAt(Geom::reverse(with_dir.back().toSBasis()), 0.);
+ Geom::Point normal_2 = -against_dir[0].unitTangentAt(0.);
+ res.lineTo(with_dir.finalPoint() + normal_1*width);
+ res.lineTo(against_dir.initialPoint() + normal_2*width);
+ res.lineTo(against_dir.initialPoint());
+}
+
+void peak_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width)
+{
+ width /= 2.;
+ Geom::Point normal_1 = -Geom::unitTangentAt(Geom::reverse(with_dir.back().toSBasis()), 0.);
+ Geom::Point normal_2 = -against_dir[0].unitTangentAt(0.);
+ Geom::Point midpoint = ((with_dir.finalPoint() + normal_1*width) + (against_dir.initialPoint() + normal_2*width)) * 0.5;
+ res.lineTo(midpoint);
+ res.lineTo(against_dir.initialPoint());
}
+} // namespace
+
namespace Inkscape {
Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join, LineCapType butt)
@@ -374,67 +578,36 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li
res.moveTo(with_dir[0].initialPoint());
res.append(with_dir);
+ cap_func *cf;
+ switch (butt) {
+ case BUTT_ROUND:
+ cf = &round_cap;
+ break;
+ case BUTT_SQUARE:
+ cf = &square_cap;
+ break;
+ case BUTT_PEAK:
+ cf = &peak_cap;
+ break;
+ default:
+ cf = &flat_cap;
+ }
+
// glue caps
if (!input.closed()) {
- switch (butt) {
- case BUTT_ROUND:
- res.arcTo(width / 2., width / 2., 0., true, false, against_dir.initialPoint());
- break;
- case BUTT_SQUARE: {
- Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(input[input.size()-1].toSBasis()), 0.);
- double radius = 0.5 * Geom::distance(with_dir.finalPoint(), against_dir.initialPoint());
- res.lineTo(with_dir.finalPoint() + end_deriv*radius);
- res.lineTo(against_dir.initialPoint() + end_deriv*radius);
- res.lineTo(against_dir.initialPoint());
- break;
- }
- case BUTT_PEAK: {
- Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(input[input.size()-1].toSBasis()), 0.);
- double radius = 0.5 * Geom::distance(with_dir.finalPoint(), against_dir.initialPoint());
- Geom::Point midpoint = ((with_dir.finalPoint() + against_dir.initialPoint()) * 0.5) + end_deriv*radius;
- res.lineTo(midpoint);
- res.lineTo(against_dir.initialPoint());
- break;
- }
- case BUTT_FLAT:
- default:
- res.lineTo(against_dir.initialPoint());
- break;
- }
+ cf(res, with_dir, against_dir, width);
} else {
+ res.closePath();
res.moveTo(against_dir.initialPoint());
}
res.append(against_dir);
if (!input.closed()) {
- switch(butt) {
- case BUTT_ROUND:
- res.arcTo(width / 2., width / 2., 0., true, false, with_dir.initialPoint());
- break;
- case BUTT_SQUARE: {
- Geom::Point end_deriv = -input[0].unitTangentAt(0.);
- double radius = 0.5 * Geom::distance(against_dir.finalPoint(), with_dir.initialPoint());
- res.lineTo(against_dir.finalPoint() + end_deriv*radius);
- res.lineTo(with_dir.initialPoint() + end_deriv*radius);
- res.lineTo(with_dir.initialPoint());
- break;
- }
- case BUTT_PEAK: {
- Geom::Point end_deriv = -input[0].unitTangentAt(0.);
- double radius = 0.5 * Geom::distance(against_dir.finalPoint(), with_dir.initialPoint());
- Geom::Point midpoint = ((against_dir.finalPoint() + with_dir.initialPoint()) * 0.5) + end_deriv*radius;
- res.lineTo(midpoint);
- res.lineTo(with_dir.initialPoint());
- break;
- }
- case BUTT_FLAT:
- default:
- res.lineTo(with_dir.initialPoint());
- }
- res.closePath();
+ cf(res, against_dir, with_dir, width);
}
+ res.closePath();
res.flush();
return res.peek();
}
@@ -451,7 +624,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin
res.start(start);
// Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
- const size_t k = input.size_default();
+ const size_t k = (input.back_closed().isDegenerate() && input.closed())
+ ?input.size_default()-1:input.size_default();
for (size_t u = 0; u < k; u += 2) {
temp = Geom::Path();
@@ -462,6 +636,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin
res.append(temp);
} else {
outline_helper(res, temp, width, miter, join);
+ if (temp.size() > 0)
+ res.insert(res.end(), ++temp.begin(), temp.end());
}
// odd number of paths
@@ -469,15 +645,12 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin
temp = Geom::Path();
offset_curve(temp, &input[u+1], width);
outline_helper(res, temp, width, miter, join);
+ if (temp.size() > 0)
+ res.insert(res.end(), ++temp.begin(), temp.end());
}
}
if (input.closed()) {
- if (input.back_closed().isDegenerate()) {
- res.erase_last();
- res.erase_last(); // ?
- }
-
Geom::Curve const &c1 = res.back();
Geom::Curve const &c2 = res.front();
temp = Geom::Path();
@@ -485,9 +658,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin
Geom::Path temp2;
temp2.append(c2);
outline_helper(temp, temp2, width, miter, join);
- temp.erase_last(); // we already outlined c2
- temp.erase(temp.begin()); // we already outlined c1
-
+ res.erase(res.begin());
+ res.erase_last();
//
res.append(temp);
res.close();
diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h
index fe79e2777..0cfb9f817 100644
--- a/src/helper/geom-pathstroke.h
+++ b/src/helper/geom-pathstroke.h
@@ -18,6 +18,7 @@ enum LineJoinType {
JOIN_BEVEL,
JOIN_ROUND,
JOIN_MITER,
+ JOIN_MITER_CLIP,
JOIN_EXTRAPOLATE,
};
@@ -34,7 +35,7 @@ enum LineCapType {
*
* @param input
* @param width Amount to offset.
- * @param miter Miter limit. Only used with JOIN_EXTRAPOLATE and JOIN_MITER.
+ * @param miter Miter limit. Only used with JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE.
* @param join
*/
Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL);
diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp
index b924d8a23..dba501f42 100644
--- a/src/live_effects/lpe-bspline.cpp
+++ b/src/live_effects/lpe-bspline.cpp
@@ -1,48 +1,16 @@
/*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
#include <gtkmm.h>
-
-#if WITH_GLIBMM_2_32
-# include <glibmm/threads.h>
-#endif
-
-#include <glib.h>
-#include <glibmm/i18n.h>
-
-
-#include "display/curve.h"
-#include <2geom/bezier-curve.h>
-#include <2geom/point.h>
-#include "helper/geom-curves.h"
#include "live_effects/lpe-bspline.h"
-#include "live_effects/lpeobject.h"
-#include "live_effects/parameter/parameter.h"
#include "ui/widget/scalar.h"
-#include "xml/repr.h"
-#include "svg/svg.h"
+#include "display/curve.h"
+#include "helper/geom-curves.h"
#include "sp-path.h"
-#include "style.h"
-#include "document-private.h"
-#include "document.h"
-#include "document-undo.h"
-#include "verbs.h"
-#include "sp-lpe-item.h"
-#include "sp-namedview.h"
-#include "display/sp-canvas.h"
-#include <typeinfo>
-#include <vector>
-#include "util/units.h"
-// For handling un-continuous paths:
-#include "message-stack.h"
-#include "inkscape.h"
-
-using Inkscape::DocumentUndo;
+#include "svg/svg.h"
+#include "xml/repr.h"
+// TODO due to internal breakage in glibmm headers, this must be last:
+#include <glibmm/i18n.h>
namespace Inkscape {
namespace LivePathEffect {
diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h
index 642562b24..8017e39ef 100644
--- a/src/live_effects/lpe-bspline.h
+++ b/src/live_effects/lpe-bspline.h
@@ -6,9 +6,8 @@
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-
#include "live_effects/effect.h"
-#include "live_effects/parameter/bool.h"
+
#include <vector>
namespace Inkscape {
diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp
index 09869822c..0111a0f99 100644
--- a/src/live_effects/lpe-jointype.cpp
+++ b/src/live_effects/lpe-jointype.cpp
@@ -28,9 +28,10 @@ namespace Inkscape {
namespace LivePathEffect {
static const Util::EnumData<unsigned> JoinTypeData[] = {
- {JOIN_BEVEL, N_("Beveled"), "bevel"},
- {JOIN_ROUND, N_("Rounded"), "round"},
- {JOIN_MITER, N_("Miter"), "miter"},
+ {JOIN_BEVEL, N_("Beveled"), "bevel"},
+ {JOIN_ROUND, N_("Rounded"), "round"},
+ {JOIN_MITER, N_("Miter"), "miter"},
+ {JOIN_MITER_CLIP, N_("Miter Clip"), "miter-clip"},
{JOIN_EXTRAPOLATE, N_("Extrapolated arc"), "extrp_arc"},
};
@@ -63,7 +64,6 @@ LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) :
//registerParameter(&end_lean);
registerParameter(&miter_limit);
registerParameter(&attempt_force_join);
- was_initialized = false;
//start_lean.param_set_range(-1,1);
//start_lean.param_set_increments(0.1, 0.1);
//start_lean.param_set_digits(4);
@@ -109,10 +109,7 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem)
sp_desktop_apply_css_recursive(item, css, true);
sp_repr_css_attr_unref (css);
- if (!was_initialized) {
- was_initialized = true;
- line_width.param_set_value(width);
- }
+ line_width.param_set_value(width);
}
}
diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h
index 799901eb6..bca0961c9 100644
--- a/src/live_effects/lpe-jointype.h
+++ b/src/live_effects/lpe-jointype.h
@@ -37,7 +37,6 @@ private:
//ScalarParam end_lean;
ScalarParam miter_limit;
BoolParam attempt_force_join;
- bool was_initialized;
};
} //namespace LivePathEffect
diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp
index d60a13c23..b5885bdb3 100644
--- a/src/live_effects/lpe-perspective-envelope.cpp
+++ b/src/live_effects/lpe-perspective-envelope.cpp
@@ -48,11 +48,11 @@ LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject)
Down_Right_Point(_("Down Right"), _("Down Right - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "Down_Right_Point", &wr, this)
{
// register all your parameters here, so Inkscape knows which parameters this effect has:
- registerParameter( dynamic_cast<Parameter *>(&deform_type));
- registerParameter( dynamic_cast<Parameter *>(&Up_Left_Point) );
- registerParameter( dynamic_cast<Parameter *>(&Up_Right_Point) );
- registerParameter( dynamic_cast<Parameter *>(&Down_Left_Point) );
- registerParameter( dynamic_cast<Parameter *>(&Down_Right_Point) );
+ registerParameter(&deform_type);
+ registerParameter(&Up_Left_Point);
+ registerParameter(&Up_Right_Point);
+ registerParameter(&Down_Left_Point);
+ registerParameter(&Down_Right_Point);
}
LPEPerspectiveEnvelope::~LPEPerspectiveEnvelope()
@@ -340,8 +340,8 @@ LPEPerspectiveEnvelope::resetDefaults(SPItem const* item)
{
Effect::resetDefaults(item);
original_bbox(SP_LPE_ITEM(item));
- resetGrid();
setDefaults();
+ resetGrid();
}
void
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index d54b2acc0..bdd36df68 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -186,18 +186,8 @@ static Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smoo
return temp;
}
-static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in)
-{
- std::vector<Geom::Point> temp;
- Geom::sbasis_to_bezier(temp, sbasis_in, 4);
- return Geom::CubicBezier( temp );
-}
-
Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double width);
-// references to pointers
-void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second);
-
// actual effect
Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
@@ -357,85 +347,18 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
*/
Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in)
{
- size_t size = path_in[0].size();
-
- 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();
-
- Geom::Path trimmed_start = Geom::Path();
- Geom::Path trimmed_end = Geom::Path();
-
- for (size_t i = 0; i < loc; ++i) {
- trimmed_start.append(path_in[0] [i]);
- }
-
- Geom::Curve * temp;
- subdivideCurve(curve_start, attach_start - loc, temp, curve_start);
- trimmed_start.append(*temp);
- if (temp) delete temp; temp = 0;
-
- // 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 )) {
-
- // If you look into it, I don't actually think there is a working way to do this
- // with only point math. So we use nearest_point instead.
- 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.
-
- subdivideCurve(curve_start, t, curve_start, temp);
- trimmed_end.append(*temp);
- if (temp) delete temp; temp = 0;
-
- for (size_t j = (size - attach_end) + 1; j < size; ++j) {
- trimmed_end.append(path_in[0] [j]);
- }
-
- 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;
- }
-
- pathv_out.push_back(trimmed_start);
+ Geom::Coord endTime = path_in[0].size() - attach_end;
- // 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 (size_t 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;
-
- Geom::Curve * curve_end = path_in[0] [loc].duplicate();
-
- Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
-
- subdivideCurve(curve_end, t, curve_end, temp);
- trimmed_end.append(*temp);
- if (temp) delete temp; temp = 0;
-
- for (size_t 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);
-
- pathv_out.push_back(trimmed_end);
-
- if (curve_end) delete curve_end;
- if (curve_start) delete curve_start;
- return pathv_out;
+ Geom::Path p1 = path_in[0].portion(0., attach_start);
+ Geom::Path p2 = path_in[0].portion(attach_start, endTime);
+ Geom::Path p3 = path_in[0].portion(endTime, path_in[0].size());
+
+ Geom::PathVector out;
+ out.push_back(p1);
+ out.push_back(p2);
+ out.push_back(p3);
+
+ return out;
}
@@ -523,23 +446,6 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path
}
}
-void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second)
-{
- if (Geom::LineSegment* linear = dynamic_cast<Geom::LineSegment*>(curve_in)) {
- // special case for line segments
- std::pair<Geom::LineSegment, Geom::LineSegment> seg_pair = linear->subdivide(t);
- val_first = seg_pair.first.duplicate();
- val_second = seg_pair.second.duplicate();
- } else {
- // all other cases:
- Geom::CubicBezier cubic = sbasis_to_cubicbezier(curve_in->toSBasis());
- std::pair<Geom::CubicBezier, Geom::CubicBezier> cubic_pair = cubic.subdivide(t);
- val_first = cubic_pair.first.duplicate();
- val_second = cubic_pair.second.duplicate();
- }
-}
-
-
void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
{
KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this);
diff --git a/src/svg-profile.h b/src/svg-profile.h
index 9d221fc76..8945ee313 100644
--- a/src/svg-profile.h
+++ b/src/svg-profile.h
@@ -74,7 +74,7 @@ public:
SVG_PRINT_1_1, /**< */
- PROFILE_UNIQUE_CNT, /**< A marker to seperate between the entires
+ PROFILE_UNIQUE_CNT, /**< A marker to separate between the entires
that are unique, and those which are
aggregates of them. */
diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp
index 3da0e0043..077c6f5ca 100644
--- a/src/ui/dialog/filter-effects-dialog.cpp
+++ b/src/ui/dialog/filter-effects-dialog.cpp
@@ -211,7 +211,7 @@ private:
ComboBoxEnum<T>* combo;
};
-// Contains an arbitrary number of spin buttons that use seperate attributes
+// Contains an arbitrary number of spin buttons that use separate attributes
class MultiSpinButton : public Gtk::HBox
{
public:
diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp
index 49c949107..a90dfb155 100644
--- a/src/ui/tool/curve-drag-point.cpp
+++ b/src/ui/tool/curve-drag-point.cpp
@@ -54,7 +54,7 @@ bool CurveDragPoint::grabbed(GdkEventMotion */*event*/)
// delta is a vector equal 1/3 of distance from first to second
Geom::Point delta = (second->position() - first->position()) / 3.0;
// only update the nodes if the mode is bspline
- if(!_pm.isBSpline(false)){
+ if(!_pm.isBSpline()){
first->front()->move(first->front()->position() + delta);
second->back()->move(second->back()->position() - delta);
}
@@ -91,7 +91,7 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event)
Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta;
//modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed
- if(!_pm.isBSpline(false)){
+ if(!_pm.isBSpline()){
first->front()->move(first->front()->position() + offset0);
second->back()->move(second->back()->position() + offset1);
}else if(weight>=0.8){
diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h
index cdbf34e9d..1bbcdd7ec 100644
--- a/src/ui/tool/multi-path-manipulator.h
+++ b/src/ui/tool/multi-path-manipulator.h
@@ -39,7 +39,7 @@ public:
virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *event);
bool empty() { return _mmap.empty(); }
- unsigned size() { return _mmap.empty(); }
+ unsigned size() { return _mmap.size(); }
void setItems(std::set<ShapeRecord> const &);
void clear() { _mmap.clear(); }
void cleanup();
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp
index eeea47e4d..08cc6708d 100644
--- a/src/ui/tool/node.cpp
+++ b/src/ui/tool/node.cpp
@@ -192,7 +192,7 @@ void Handle::move(Geom::Point const &new_pos)
setRelativePos(new_delta);
//move the handler and its oposite the same proportion
- if(_pm().isBSpline()){
+ if(_pm().isBSpline()){
setPosition(_pm().BSplineHandleReposition(this,this));
this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this));
}
@@ -222,7 +222,6 @@ void Handle::move(Geom::Point const &new_pos)
setPosition(_pm().BSplineHandleReposition(this,this));
this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this));
}
-
}
void Handle::setPosition(Geom::Point const &p)
@@ -382,7 +381,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
std::vector<Inkscape::SnapCandidatePoint> unselected;
//if the snap adjustment is activated and it is not bspline
- if (snap && !_pm().isBSpline(false)) {
+ if (snap && !_pm().isBSpline()) {
ControlPointSelection::Set &nodes = _parent->_selection.allPoints();
for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) {
Node *n = static_cast<Node*>(*i);
@@ -486,7 +485,7 @@ Glib::ustring Handle::_getTip(unsigned state) const
char const *more;
// a trick to mark as bspline if the node has no strength, we are going to use it later
// to show the appropiate messages. We cannot do it in any different way becasue the function is constant
-
+ Handle *h = const_cast<Handle *>(this);
bool isBSpline = _pm().isBSpline();
bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate();
if (can_shift_rotate && !isBSpline) {
@@ -550,7 +549,7 @@ Glib::ustring Handle::_getTip(unsigned state) const
"<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more);
}else{
return format_tip(C_("Path handle tip",
- "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s)"), more);
+ "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s). %g power"),more,_pm().BSplineHandlePosition(h,NULL));
}
}
}
@@ -1435,6 +1434,8 @@ Node *Node::nodeAwayFrom(Handle *h)
Glib::ustring Node::_getTip(unsigned state) const
{
bool isBSpline = _pm().isBSpline();
+ Handle *h = const_cast<Handle *>(&_front);
+ Handle *h2 = const_cast<Handle *>(&_back);
if (state_held_shift(state)) {
bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate());
if (can_drag_out) {
@@ -1469,7 +1470,7 @@ Glib::ustring Node::_getTip(unsigned state) const
"<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype);
}else if(_selection.size() == 1){
return format_tip(C_("Path node tip",
- "<b>BSpline node</b>: %g weight, drag to shape the path (more: Shift, Ctrl, Alt)"),noPower/*this->bsplineWeight*/);
+ "<b>BSpline node</b>: drag to shape the path (more: Shift, Ctrl, Alt). %g power"),_pm().BSplineHandlePosition(h,h2));
}
return format_tip(C_("Path node tip",
"<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype);
@@ -1479,7 +1480,7 @@ Glib::ustring Node::_getTip(unsigned state) const
"<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype);
}else{
return format_tip(C_("Path node tip",
- "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"));
+ "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt). %g power"),_pm().BSplineHandlePosition(h,h2));
}
}
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index dbcde240a..ccf75dcde 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -11,6 +11,7 @@
*/
#include "live_effects/lpe-powerstroke.h"
+#include "live_effects/lpe-bspline.h"
#include "live_effects/lpe-fillet-chamfer.h"
#include <string>
#include <sstream>
@@ -43,7 +44,6 @@
#include "ui/tool/multi-path-manipulator.h"
#include "xml/node.h"
#include "xml/node-observer.h"
-#include "live_effects/lpe-bspline.h"
namespace Inkscape {
namespace UI {
@@ -151,7 +151,7 @@ PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path,
_createControlPointsFromGeometry();
//Define if the path is BSpline on construction
- isBSpline(true);
+ recalculateIsBSpline();
}
PathManipulator::~PathManipulator()
@@ -1238,19 +1238,24 @@ int PathManipulator::BSplineGetSteps() const {
}
// determines if the trace has bspline effect
-bool PathManipulator::isBSpline(bool recalculate){
- if(recalculate){
- _is_bspline = this->BSplineGetSteps() > 0;
+void PathManipulator::recalculateIsBSpline(){
+ if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) {
+ Inkscape::LivePathEffect::Effect const *thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE);
+ if(thisEffect){
+ _is_bspline = true;
+ return;
+ }
}
- return _is_bspline;
+ _is_bspline = false;
}
bool PathManipulator::isBSpline() const {
- return BSplineGetSteps() > 0;
+ return _is_bspline;
}
// returns the corresponding strength to the position of the handlers
-double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){
+double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2)
+{
using Geom::X;
using Geom::Y;
if(h2){
@@ -1275,7 +1280,8 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){
}
// give the location for the handler in the corresponding position
-Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){
+Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2)
+{
double pos = this->BSplineHandlePosition(h, h2);
return BSplineHandleReposition(h,pos);
}
@@ -1312,7 +1318,7 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
{
Geom::PathBuilder builder;
//Refresh if is bspline some times -think on path change selection, this value get lost
- isBSpline(true);
+ recalculateIsBSpline();
for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) {
SubpathPtr subpath = *spi;
if (subpath->empty()) {
@@ -1341,22 +1347,19 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
_spcurve->set_pathvector(pathv);
if (alert_LPE) {
/// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming?
- SPLPEItem * path = dynamic_cast<SPLPEItem *>(_path);
- if (path){
- if(path->hasPathEffect()){
- Inkscape::LivePathEffect::Effect* thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE);
- if(thisEffect){
- LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe());
- if (lpe_pwr) {
- lpe_pwr->adjustForNewPath(pathv);
- }
+ if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) {
+ Inkscape::LivePathEffect::Effect* thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE);
+ if(thisEffect){
+ LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe());
+ if (lpe_pwr) {
+ lpe_pwr->adjustForNewPath(pathv);
}
- thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER);
- if(thisEffect){
- LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe());
- if (lpe_fll) {
- lpe_fll->adjustForNewPath(pathv);
- }
+ }
+ thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER);
+ if(thisEffect){
+ LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe());
+ if (lpe_fll) {
+ lpe_fll->adjustForNewPath(pathv);
}
}
}
diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h
index 4d2bf4300..6dc1c9d09 100644
--- a/src/ui/tool/path-manipulator.h
+++ b/src/ui/tool/path-manipulator.h
@@ -107,7 +107,7 @@ private:
void _createControlPointsFromGeometry();
- bool isBSpline(bool recalculate = false);
+ void recalculateIsBSpline();
bool isBSpline() const;
double BSplineHandlePosition(Handle *h, Handle *h2 = NULL);
Geom::Point BSplineHandleReposition(Handle *h, Handle *h2 = NULL);
diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp
index 8e647ebb4..0a5697661 100644
--- a/src/ui/widget/page-sizer.cpp
+++ b/src/ui/widget/page-sizer.cpp
@@ -242,7 +242,13 @@ PageSizer::PageSizer(Registry & _wr)
_marginBottom( _("Botto_m:"), _("Bottom margin"), "fit-margin-bottom", _wr),
_lockMarginUpdate(false),
_scaleX(_("Scale _x:"), _("Scale X"), "scale-x", _wr),
+ _scaleY(_("Scale _y:"), _("Scale Y"), "scale-y", _wr),
_lockScaleUpdate(false),
+ _viewboxX(_("X:"), _("X"), "viewbox-x", _wr),
+ _viewboxY(_("Y:"), _("Y"), "viewbox-y", _wr),
+ _viewboxW(_("Width:"), _("Width"), "viewbox-width", _wr),
+ _viewboxH(_("Height:"), _("Height"), "viewbox-height", _wr),
+ _lockViewboxUpdate(false),
_widgetRegistry(&_wr)
{
// set precision of scalar entry boxes
@@ -254,7 +260,21 @@ PageSizer::PageSizer(Registry & _wr)
_marginRight.setDigits(5);
_marginBottom.setDigits(5);
_scaleX.setDigits(5);
+ _scaleY.setDigits(5);
+ _viewboxX.setDigits(2);
+ _viewboxY.setDigits(2);
+ _viewboxW.setDigits(2);
+ _viewboxH.setDigits(2);
+
_scaleX.setRange( 0.00001, 100000 );
+ _scaleY.setRange( 0.00001, 100000 );
+ _viewboxX.setRange( -100000, 100000 );
+ _viewboxY.setRange( -100000, 100000 );
+ _viewboxW.setRange( 0, 200000 );
+ _viewboxH.setRange( 0, 200000 );
+
+ _scaleY.set_sensitive (false); // We only want to display Y scale.
+
_wr.setUpdating (false);
//# Set up the Paper Size combo box
@@ -435,21 +455,62 @@ PageSizer::PageSizer(Registry & _wr)
_scaleTable.set_row_spacing(4);
_scaleTable.set_column_spacing(4);
- _dimensionWidth.set_hexpand();
- _dimensionWidth.set_vexpand();
_scaleTable.attach(_scaleX, 0, 0, 1, 1);
+ _scaleTable.attach(_scaleY, 1, 0, 1, 1);
- _dimensionUnits.set_hexpand();
- _dimensionUnits.set_vexpand();
- _scaleTable.attach(_scaleLabel, 1, 0, 1, 1);
+ _scaleTable.attach(_scaleLabel, 2, 0, 1, 1);
+ _scaleTable.attach(_scaleWarning, 0, 1, 2, 1);
+ _viewboxExpander.set_hexpand();
+ _viewboxExpander.set_vexpand();
+ _scaleTable.attach(_viewboxExpander, 0, 2, 2, 1);
#else
- _scaleTable.resize(2, 1);
+ _scaleTable.resize(3, 2);
_scaleTable.set_row_spacings(4);
_scaleTable.set_col_spacings(4);
_scaleTable.attach(_scaleX, 0,1, 0,1);
- _scaleTable.attach(_scaleLabel, 1,2, 0,1);
+ _scaleTable.attach(_scaleY, 1,2, 0,1);
+ _scaleTable.attach(_scaleLabel, 2,3, 0,1);
+ _scaleTable.attach(_scaleWarning, 0,3, 1,2, Gtk::FILL);
+ _scaleTable.attach(_viewboxExpander, 0,3, 2,3);
#endif
+ _scaleWarning.set_label(_("While SVG allows non-uniform scaling it is recommended to use only uniform scaling in Inkscape. To set a non-uniform scaling, set the 'viewBox' directly."));
+ _scaleWarning.set_line_wrap( true );
+
+ _viewboxExpander.set_use_underline();
+ _viewboxExpander.set_label(_("_Viewbox..."));
+ _viewboxExpander.add(_viewboxTable);
+
+#if WITH_GTKMM_3_0
+ _viewboxTable.set_row_spacing(2);
+ _viewboxTable.set_column_spacing(2);
+
+ _viewboxX.set_hexpand();
+ _viewboxX.set_vexpand();
+ _viewboxTable.attach(_viewboxX, 0, 0, 1, 1);
+
+ _viewboxY.set_hexpand();
+ _viewboxY.set_vexpand();
+ _viewboxTable.attach(_viewboxY, 1, 0, 1, 1);
+
+ _viewboxW.set_hexpand();
+ _viewboxW.set_vexpand();
+ _viewboxTable.attach(_viewboxW, 0, 1, 1, 1);
+
+ _viewboxH.set_hexpand();
+ _viewboxH.set_vexpand();
+ _viewboxTable.attach(_viewboxH, 1, 1, 1, 1);
+
+#else
+ _viewboxTable.set_border_width(4);
+ _viewboxTable.set_row_spacings(2);
+ _viewboxTable.set_col_spacings(2);
+ _viewboxTable.attach(_viewboxX, 0,1, 0,1);
+ _viewboxTable.attach(_viewboxY, 1,2, 0,1);
+ _viewboxTable.attach(_viewboxW, 0,1, 1,2);
+ _viewboxTable.attach(_viewboxH, 1,2, 1,2);
+#endif
+
_wr.setUpdating (true);
updateScaleUI();
_wr.setUpdating (false);
@@ -478,7 +539,11 @@ PageSizer::init ()
_changedh_connection = _dimensionHeight.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
_changedu_connection = _dimensionUnits.getUnitMenu()->signal_changed().connect (sigc::mem_fun (*this, &PageSizer::on_units_changed));
_fitPageButton.signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
- _changeds_connection = _scaleX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_scale_changed));
+ _changeds_connection = _scaleX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_scale_changed));
+ _changedvx_connection = _viewboxX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed));
+ _changedvy_connection = _viewboxY.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed));
+ _changedvw_connection = _viewboxW.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed));
+ _changedvh_connection = _viewboxH.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed));
show_all_children();
}
@@ -744,10 +809,6 @@ void
PageSizer::updateScaleUI()
{
- if (_lockScaleUpdate) {
- return;
- }
-
static bool _called = false;
if (_called) {
return;
@@ -756,28 +817,56 @@ PageSizer::updateScaleUI()
_called = true;
_changeds_connection.block();
+ _changedvx_connection.block();
+ _changedvy_connection.block();
+ _changedvw_connection.block();
+ _changedvh_connection.block();
SPDesktop *dt = SP_ACTIVE_DESKTOP;
if (dt) {
SPDocument *doc = dt->getDocument();
+
+ // Update scale
Geom::Scale scale = doc->getDocumentScale();
-
SPNamedView *nv = dt->getNamedView();
std::stringstream ss;
ss << _("User units per ") << nv->display_units->abbr << "." ;
_scaleLabel.set_text( ss.str() );
- double scaleX_inv =
- Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units );
- if( scaleX_inv > 0 ) {
- _scaleX.setValue(1.0/scaleX_inv);
- } else {
- // Should never happen
- std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleX_inv << std::endl;
- _scaleX.setValue(1.0);
+ if( !_lockScaleUpdate ) {
+
+ double scaleX_inv =
+ Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units );
+ if( scaleX_inv > 0 ) {
+ _scaleX.setValue(1.0/scaleX_inv);
+ } else {
+ // Should never happen
+ std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleX_inv << std::endl;
+ _scaleX.setValue(1.0);
+ }
+ }
+
+ { // Don't need to lock as scaleY widget not linked to callback.
+ double scaleY_inv =
+ Inkscape::Util::Quantity::convert( scale[Geom::Y], "px", nv->display_units );
+ if( scaleY_inv > 0 ) {
+ _scaleY.setValue(1.0/scaleY_inv);
+ } else {
+ // Should never happen
+ std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleY_inv << std::endl;
+ _scaleY.setValue(1.0);
+ }
}
+ if( !_lockViewboxUpdate ) {
+ Geom::Rect viewBox = doc->getViewBox();
+ _viewboxX.setValue( viewBox.min()[Geom::X] );
+ _viewboxY.setValue( viewBox.min()[Geom::Y] );
+ _viewboxW.setValue( viewBox.width() );
+ _viewboxH.setValue( viewBox.height() );
+ }
+
} else {
// Should never happen
std::cerr << "PageSizer::updateScaleUI(): No active desktop." << std::endl;
@@ -785,6 +874,10 @@ PageSizer::updateScaleUI()
}
_changeds_connection.unblock();
+ _changedvx_connection.unblock();
+ _changedvy_connection.unblock();
+ _changedvw_connection.unblock();
+ _changedvh_connection.unblock();
_called = false;
}
@@ -801,6 +894,7 @@ PageSizer::on_value_changed()
setDim (Inkscape::Util::Quantity(_dimensionWidth.getValue(""), _dimensionUnits.getUnit()),
Inkscape::Util::Quantity(_dimensionHeight.getValue(""), _dimensionUnits.getUnit()));
}
+
void
PageSizer::on_units_changed()
{
@@ -830,13 +924,44 @@ PageSizer::on_scale_changed()
double scaleX_inv = Inkscape::Util::Quantity(1.0/value, nv->display_units ).value("px");
_lockScaleUpdate = true;
- doc->setDocumentScale( 1.0/scaleX_inv );
+ doc->setDocumentScale( 1.0/scaleX_inv );
+ updateScaleUI();
_lockScaleUpdate = false;
DocumentUndo::done(doc, SP_VERB_NONE, _("Set page scale"));
}
}
}
+/**
+ * Callback for viewbox widgets
+ */
+void
+PageSizer::on_viewbox_changed()
+{
+ if (_widgetRegistry->isUpdating()) return;
+
+ double viewboxX = _viewboxX.getValue();
+ double viewboxY = _viewboxY.getValue();
+ double viewboxW = _viewboxW.getValue();
+ double viewboxH = _viewboxH.getValue();
+
+ if( viewboxW > 0 && viewboxH > 0) {
+ SPDesktop *dt = SP_ACTIVE_DESKTOP;
+ if (dt) {
+ SPDocument *doc = dt->getDocument();
+ _lockViewboxUpdate = true;
+ doc->setViewBox( Geom::Rect::from_xywh( viewboxX, viewboxY, viewboxW, viewboxH ) );
+ updateScaleUI();
+ _lockViewboxUpdate = false;
+ DocumentUndo::done(doc, SP_VERB_NONE, _("Set 'viewBox'"));
+ }
+ } else {
+ std::cerr
+ << "PageSizer::on_viewbox_changed(): width and height must both be greater than zero."
+ << std::endl;
+ }
+}
+
} // namespace Widget
} // namespace UI
} // namespace Inkscape
diff --git a/src/ui/widget/page-sizer.h b/src/ui/widget/page-sizer.h
index f9a72d9f3..0eea28e61 100644
--- a/src/ui/widget/page-sizer.h
+++ b/src/ui/widget/page-sizer.h
@@ -264,17 +264,38 @@ protected:
#endif
Gtk::Label _scaleLabel;
+ Gtk::Label _scaleWarning;
RegisteredScalar _scaleX;
+ RegisteredScalar _scaleY;
bool _lockScaleUpdate;
+ // Viewbox
+ Gtk::Expander _viewboxExpander;
+#if WITH_GTKMM_3_0
+ Gtk::Grid _viewboxTable;
+#else
+ Gtk::Table _viewboxTable;
+#endif
+
+ RegisteredScalar _viewboxX;
+ RegisteredScalar _viewboxY;
+ RegisteredScalar _viewboxW;
+ RegisteredScalar _viewboxH;
+ bool _lockViewboxUpdate;
+
//callback
void on_value_changed();
void on_units_changed();
void on_scale_changed();
+ void on_viewbox_changed();
sigc::connection _changedw_connection;
sigc::connection _changedh_connection;
sigc::connection _changedu_connection;
sigc::connection _changeds_connection;
+ sigc::connection _changedvx_connection;
+ sigc::connection _changedvy_connection;
+ sigc::connection _changedvw_connection;
+ sigc::connection _changedvh_connection;
Registry *_widgetRegistry;
diff --git a/src/uri-references.cpp b/src/uri-references.cpp
index b23bed74a..2518c173e 100644
--- a/src/uri-references.cpp
+++ b/src/uri-references.cpp
@@ -65,7 +65,7 @@ void URIReference::attach(const URI &uri) throw(BadURIException)
skip = true;
}
- // The path contains references to seperate document files to load.
+ // The path contains references to separate document files to load.
if(document && uri.getPath() && !skip ) {
std::string base = document->getBase() ? document->getBase() : "";
std::string path = uri.getFullPath(base);