summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/live_effects/lpe-powerstroke.cpp185
-rw-r--r--src/live_effects/lpe-powerstroke.h2
-rw-r--r--src/ui/dialog/inkscape-preferences.cpp10
-rw-r--r--src/ui/dialog/inkscape-preferences.h1
-rw-r--r--src/ui/tools/freehand-base.cpp4
-rw-r--r--src/ui/tools/pencil-tool.cpp313
-rw-r--r--src/ui/tools/pencil-tool.h11
7 files changed, 147 insertions, 379 deletions
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index f4e4a06ca..e7ceaa397 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -97,10 +97,13 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0
if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // try second time
dM=derivative(dM);
}
- if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // admit defeat
+ if ( dM.isZero(tol) || (are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) )) { // admit defeat
return Geom::Circle(Geom::Point(0., 0.), 0.);
}
Piecewise<D2<SBasis> > unitv = unitVector(dM,tol);
+ if (unitv.empty()) { // admit defeat
+ return Geom::Circle(Geom::Point(0., 0.), 0.);
+ }
Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv);
Piecewise<SBasis> k = cross(derivative(unitv),unitv);
k = divide(k,dMlength,tol,3);
@@ -165,7 +168,6 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),
sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true),
- interpolate_original(_("Interpolate original"), _("Interpolate original path"), "interpolate_original", &wr, this, false),
interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER),
interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2),
scale_width(_("Width scale:"), _("Width scale all points"), "scale_width", &wr, this, 1.0),
@@ -183,7 +185,6 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
registerParameter(&offset_points);
registerParameter(&sort_points);
- registerParameter(&interpolate_original);
registerParameter(&interpolator_type);
registerParameter(&interpolator_beta);
registerParameter(&start_linecap_type);
@@ -194,7 +195,6 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
scale_width.param_set_range(0.0, Geom::infinity());
scale_width.param_set_increments(0.1, 0.1);
scale_width.param_set_digits(4);
- interpolate_original_prev = !interpolate_original;
}
LPEPowerStroke::~LPEPowerStroke()
@@ -566,57 +566,16 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in)
Geom::PathVector path_out;
if (path_in.empty()) {
- return path_in;
+ return path_out;
}
Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(path_in);
- // create stroke path where points (x,y) := (t, offset)
-
- if (interpolate_original) {
- Geom::PathVector path_out;
- for(Geom::PathVector::const_iterator path_it = path_in.begin(); path_it != path_in.end(); ++path_it) {
- if (path_it->empty())
- continue;
-
- if (path_it->closed()) {
- g_warning("Interpolate points LPE currently ignores whether path is closed or not.");
- }
- std::vector<Geom::Point> pts;
- pts.push_back(path_it->initialPoint());
-
- for (Geom::Path::const_iterator it = path_it->begin(), e = path_it->end_default(); it != e; ++it) {
- pts.push_back((*it).finalPoint());
- }
- //We use this fixed interpolator to simplfy the UI and for better results
- Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM);
- Geom::Path path = interpolator->interpolateToPath(pts);
-
- path_out.push_back(path);
- }
- pathv = path_out;
- }
- Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value()));
- if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {
- johan->setBeta(interpolator_beta);
- }
- if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) {
- smooth->setBeta(interpolator_beta);
- }
- if (interpolate_original_prev != interpolate_original) {
- adjustForNewPath(pathv);
- interpolate_original_prev = interpolate_original;
- }
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = pathv[0].toPwSb();
- if (!pwd2_in.size()) {
- return path_in;
- }
Piecewise<D2<SBasis> > der = derivative(pwd2_in);
- if (!der.size()) {
- return path_in;
- }
Piecewise<D2<SBasis> > n = unitVector(der,0.0001);
- if (!n.size()) {
+ if (!n.size() || !pwd2_in.size() || !n.size()) {
return path_in;
}
+
n = rot90(n);
offset_points.set_pwd2(pwd2_in, n);
@@ -654,116 +613,18 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in)
// instead of the heavily compressed coordinate system of (segment_no offset, Y) in which the knots are stored
double pwd2_in_arclength = length(pwd2_in);
double xcoord_scaling = pwd2_in_arclength / ts.back()[Geom::X];
- if (interpolate_original) {
- size_t i = 0;
- std::vector<Geom::Point> ts_aprox;
- size_t steps = 0;
- double distance = 0;
- Geom::PathVector splits;
- Geom::Coord start = 0;
- for(std::vector<Geom::Point>::iterator point = ts.begin(); point != ts.end();) {
- point++;
- Geom::Coord end = (*point)[Geom::X];
- if (Geom::are_near(start, end,0.0001)) {
- continue;
- }
- splits.push_back(path_in[0].portion(start, end));
- start = end;
- if (end == pathv[0].size()) {
- break;
- }
- }
- size_t counter = 0;
- for(Geom::PathVector::const_iterator path_it = splits.begin(); path_it != splits.end(); ++path_it) {
- if (path_it->empty()) {
- continue;
- }
- Geom::Piecewise<Geom::D2<Geom::SBasis> > path_pwd = (*path_it).toPwSb();
- size_t size = (*path_it).size();
- double path_it_arclength = length(path_pwd);
- Geom::Point start = ts[counter];
- counter++;
- Geom::Point end = ts[counter];
- if (Geom::are_near(start[Geom::Y],end[Geom::Y],0.0001)) {
- continue;
- }
- double gap = (start[Geom::Y] - end[Geom::Y])/size;
- double width = 0;
- width = start[Geom::Y];
- for (size_t j = 1; j < size; j++){
- Geom::Path current_curve = (*path_it).portion(j-1, j);
- double path_it_arclength_sub = length(current_curve.toPwSb());
- double factor = path_it_arclength_sub * size/path_it_arclength;
- width -= gap * factor;
- ts.push_back(Geom::Point(std::floor(ts[counter-1][Geom::X]) + j, width));
- }
- }
- sort(ts.begin(), ts.end(), compare_offsets);
+ for (std::size_t i = 0, e = ts.size(); i < e; ++i) {
+ ts[i][Geom::X] *= xcoord_scaling;
}
// create stroke path where points (x,y) := (t, offset)
-
- Geom::Path fixed_path;
- Geom::Path fixed_mirrorpath;
- Geom::Path strokepath;
- if (interpolate_original && 2==1) {
- std::vector<Geom::Point> ts_normal;
- std::vector<Geom::Point> ts_mirror;
-
- bool previous_isnode = false;
- size_t counter = 0;
- for(auto point:ts) {
- Geom::Point normal_pos = pwd2_in.valueAt(point[Geom::X]) + (point[Geom::Y] * scale_width) * n.valueAt(point[Geom::X]);
- Geom::Point mirror_pos = pwd2_in.valueAt(point[Geom::X]) + (point[Geom::Y] * -1 * scale_width) * n.valueAt(point[Geom::X]);
- Geom::Point normal = Geom::Point(normal_pos[Geom::X] * xcoord_scaling, normal_pos[Geom::Y]);
- Geom::Point mirror = Geom::Point(mirror_pos [Geom::X] * xcoord_scaling, mirror_pos [Geom::Y]);
- //a bit smoothig tweak
- if (counter > 2) {
- Geom::Point granparent_normal = ts_normal[counter-2];
- Geom::Point parent_normal = ts_normal[counter-1];
- Geom::Point granparent_mirror = ts_mirror [counter-2];
- Geom::Point parent_mirror = ts_mirror [counter-1];
- bool isnode = ts[counter][Geom::X] == std::floor(ts[counter ][Geom::X]);
- bool previous_isnode = ts[counter-1][Geom::X] == std::floor(ts[counter-1][Geom::X]);
- bool ccw_toggle_normal = cross(parent_normal - granparent_normal, normal - granparent_normal) < 0;
- bool ccw_toggle_mirror = cross(parent_mirror - granparent_mirror , mirror - granparent_mirror ) < 0;
- Geom::Ray ray_normal_a(parent_normal, granparent_normal);
- Geom::Ray ray_normal_b(parent_normal , normal);
- Geom::Ray ray_mirror_a (parent_mirror, granparent_mirror);
- Geom::Ray ray_mirror_b (parent_mirror , mirror);
- double angle_normal = angle_between(ray_normal_a, ray_normal_b, ccw_toggle_normal);
- double angle_mirror = angle_between(ray_mirror_a , ray_mirror_b , ccw_toggle_mirror);
- if (point[Geom::X] > 2 &&
- previous_isnode &&
- !isnode &&
- !ccw_toggle_normal &&
- angle_normal < Geom::rad_from_deg(90))
- {
- ts_normal.pop_back();
- }
- if (point[Geom::X] > 2 &&
- previous_isnode &&
- !isnode &&
- ccw_toggle_mirror &&
- angle_mirror < Geom::rad_from_deg(90))
- {
- ts_mirror.pop_back();
- }
- }
- ts_normal.push_back(normal);
- ts_mirror.push_back(mirror);
- counter++;
- }
- fixed_path = interpolator->interpolateToPath(ts_normal);
- fixed_path *= Scale(1/xcoord_scaling, 1);
- fixed_mirrorpath = interpolator->interpolateToPath(ts_mirror);
- fixed_mirrorpath *= Scale(1/xcoord_scaling, 1);
- fixed_mirrorpath = fixed_mirrorpath.reversed();
- } else {
- for (std::size_t i = 0, e = ts.size(); i < e; ++i) {
- ts[i][Geom::X] *= xcoord_scaling;
- }
- strokepath = interpolator->interpolateToPath(ts);
+ Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value()));
+ if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {
+ johan->setBeta(interpolator_beta);
}
+ if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) {
+ smooth->setBeta(interpolator_beta);
+ }
+ Geom::Path strokepath = interpolator->interpolateToPath(ts);
delete interpolator;
// apply the inverse knot-xcoord scaling that was applied before the interpolation
@@ -781,13 +642,13 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in)
y = portion(y, rtsmin.at(0), rtsmax.at(0));
}
- if (!interpolate_original || 2>1) {
- LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value());
- 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));
- fixed_path = path_from_piecewise_fix_cusps( pwd2_out, y, jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
- fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, reverse(y), jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
- }
+ LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value());
+
+ 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_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, reverse(y), jointype, miter_limit, LPE_CONVERSION_TOLERANCE);
if (pathv[0].closed()) {
fixed_path.close(true);
path_out.push_back(fixed_path);
diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h
index 53aab1b05..19696ac57 100644
--- a/src/live_effects/lpe-powerstroke.h
+++ b/src/live_effects/lpe-powerstroke.h
@@ -38,7 +38,6 @@ public:
private:
BoolParam sort_points;
- BoolParam interpolate_original;
EnumParam<unsigned> interpolator_type;
ScalarParam interpolator_beta;
ScalarParam scale_width;
@@ -46,7 +45,6 @@ private:
EnumParam<unsigned> linejoin_type;
ScalarParam miter_limit;
EnumParam<unsigned> end_linecap_type;
- bool interpolate_original_prev;
LPEPowerStroke(const LPEPowerStroke&);
LPEPowerStroke& operator=(const LPEPowerStroke&);
};
diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp
index d75b22ddb..eb4a6567e 100644
--- a/src/ui/dialog/inkscape-preferences.cpp
+++ b/src/ui/dialog/inkscape-preferences.cpp
@@ -213,15 +213,6 @@ void InkscapePreferences::AddPencilPowerStrokePressureStep(DialogPage &p, Glib::
false );
}
-void InkscapePreferences::AddPencilPowerStrokeMinSimplify(DialogPage &p, Glib::ustring const &prefs_path, gint def_value)
-{
- PrefSpinButton* sb = Gtk::manage( new PrefSpinButton);
- sb->init ( prefs_path + "/powerstrokemintolerance", 1, 100, 1, 10, def_value, true, false);
- p.add_line( false, _("Power Stroke base min simplify:"), *sb, _("%"),
- _("Base simplify for pencil with pressure"),
- false );
-}
-
static void StyleFromSelectionToTool(Glib::ustring const &prefs_path, StyleSwatch *swatch)
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
@@ -442,7 +433,6 @@ void InkscapePreferences::initPageTools()
this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0);
_page_pencil.add_group_header( _("Pressure sensitivity settings"));
this->AddPencilPowerStrokePressureStep(_page_pencil, "/tools/freehand/pencil", 10);
- this->AddPencilPowerStrokeMinSimplify(_page_pencil, "/tools/freehand/pencil", 40);
_page_pencil.add_group_header( _("Sketch mode"));
_page_pencil.add_line( true, "", _pencil_average_all_sketches, "",
diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h
index f7c5cc971..e0bd7f257 100644
--- a/src/ui/dialog/inkscape-preferences.h
+++ b/src/ui/dialog/inkscape-preferences.h
@@ -502,7 +502,6 @@ protected:
static void AddDotSizeSpinbutton(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value);
static void AddBaseSimplifySpinbutton(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value);
static void AddPencilPowerStrokePressureStep(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, gint def_value);
- static void AddPencilPowerStrokeMinSimplify(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, gint def_value);
static void AddNewObjectsStyle(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, const gchar* banner = NULL);
void on_pagelist_selection_changed();
diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp
index b72eb84db..e294c37a2 100644
--- a/src/ui/tools/freehand-base.cpp
+++ b/src/ui/tools/freehand-base.cpp
@@ -259,10 +259,6 @@ static void spdc_apply_powerstroke_shape(std::vector<Geom::Point> points, Freeha
Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE();
lpe->getRepr()->setAttribute("sort_points", "true");
lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom");
- lpe->getRepr()->setAttribute("interpolate_original", "true");
- lpe->getRepr()->setAttribute("interpolator_beta", "0.2");
- lpe->getRepr()->setAttribute("miter_limit", "100");
- lpe->getRepr()->setAttribute("linejoin_type", "miter");
static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(pt->points);
pt->points.clear();
return;
diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp
index 6dd3126e3..7779f6687 100644
--- a/src/ui/tools/pencil-tool.cpp
+++ b/src/ui/tools/pencil-tool.cpp
@@ -45,6 +45,7 @@
#include "display/sp-canvas.h"
#include "display/curve.h"
#include "live_effects/lpe-powerstroke.h"
+#include "live_effects/lpe-powerstroke-interpolators.h"
#include "ui/tool/event-utils.h"
namespace Inkscape {
@@ -70,11 +71,8 @@ PencilTool::PencilTool()
, state(SP_PENCIL_CONTEXT_IDLE)
, req_tangent(0, 0)
, is_drawing(false)
- , points_parsed(0)
, sketch_n(0)
- , _powerpreviewtail(NULL)
, _powerpreview(NULL)
- , _preview_ok(true)
{
}
@@ -98,6 +96,7 @@ PencilTool::~PencilTool() {
void PencilTool::_extinput(GdkEvent *event) {
if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &this->pressure)) {
this->pressure = CLAMP (this->pressure, DDC_MIN_PRESSURE, DDC_MAX_PRESSURE);
+ input_has_pressure = true;
} else {
this->pressure = DDC_DEFAULT_PRESSURE;
//If no pressure device ignore pressure button
@@ -200,7 +199,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) {
/* Set first point of sequence */
SnapManager &m = desktop->namedview->snap_manager;
if (bevent.state & GDK_CONTROL_MASK) {
- m.setup(desktop, true);
+ m.setup(desktop, true, _powerpreview);
if (!(bevent.state & GDK_SHIFT_MASK)) {
m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE);
}
@@ -219,7 +218,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) {
}
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path"));
} else {
- m.setup(desktop, true);
+ m.setup(desktop, true, _powerpreview);
if (!(bevent.state & GDK_SHIFT_MASK)) {
// This is the first click of a new curve; deselect item so that
// this curve is not combined with it (unless it is drawn from its
@@ -380,7 +379,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) {
// b) release the mousebutton to finish a freehand drawing
if (!this->sp_event_context_knot_mouseover()) {
SnapManager &m = desktop->namedview->snap_manager;
- m.setup(desktop, true);
+ m.setup(desktop, true, _powerpreview);
m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE));
m.unSetup();
}
@@ -453,7 +452,7 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) {
/* Write curves to object */
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand"));
- this->_interpolate(true);
+ this->_interpolate();
//Remove here because points are recalculated once finish. This is because we want live preview.
this->points.clear();
spdc_concat_colors_and_flush(this, FALSE);
@@ -664,7 +663,7 @@ void PencilTool::_finishEndpoint() {
}
void
-PencilTool::_powerStrokePreview(Geom::Path const path, std::vector<Geom::Point> points, bool write)
+PencilTool::_powerStrokePreview(Geom::Path const path, std::vector<Geom::Point> points)
{
using namespace Inkscape::LivePathEffect;
SPDocument * document = SP_ACTIVE_DOCUMENT;
@@ -672,15 +671,14 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector<Geom::Point>
return;
}
Inkscape::XML::Document *xml_doc = document->getReprDoc();
- SPCurve * curve = new SPCurve();
Geom::PathVector const pathv(path);
- if (!_powerpreview) {
+ SPLPEItem * lpeitem = dynamic_cast<SPLPEItem *>(_powerpreview);
+ if (!lpeitem) {
Inkscape::XML::Node *body = NULL;
body = xml_doc->createElement("svg:path");
body->setAttribute("sodipodi:insensitive", "true");
sp_desktop_apply_style_tool(desktop, body, Glib::ustring("/tools/freehand/pencil").data(), false);
_powerpreview = SP_SHAPE(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(body));
- _powerpreview->setCurve(curve, true);
Inkscape::GC::release(body);
SPCSSAttr *css = sp_css_attr_from_object(_powerpreview, SP_STYLE_FLAG_ALWAYS);
const gchar * stroke = sp_repr_css_property(css, "stroke", "none");
@@ -705,57 +703,21 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector<Geom::Point>
}
Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(_powerpreview));
Effect* lpe = lpeitem->getCurrentLPE();
+ static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(points);
lpe->getRepr()->setAttribute("sort_points", "true");
lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom");
- lpe->getRepr()->setAttribute("interpolate_original", "true");
- lpe->getRepr()->setAttribute("interpolator_beta", "0.2");
- lpe->getRepr()->setAttribute("miter_limit", "100");
- lpe->getRepr()->setAttribute("linejoin_type", "miter");
- end_linecap_type = lpe->getRepr()->attribute("end_linecap_type");
- }
- SPLPEItem * lpeitem = dynamic_cast<SPLPEItem *>(_powerpreview);
- if (!lpeitem) {
- return;
- }
- Effect* lpe = lpeitem->getCurrentLPE();
- lpe->getRepr()->setAttribute("end_linecap_type", end_linecap_type);
- static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(points);
- gchar * pvector_str = sp_svg_write_path(pathv);
- _powerpreview->setAttribute("inkscape:original-d" , pvector_str);
- g_free(pvector_str);
- if (write) {
- lpe->getRepr()->setAttribute("end_linecap_type", "round");
- curve = _powerpreview->getCurve();
- _powerpreview->setAttribute("inkscape:original-d", "");
- _powerpreview->setAttribute("d", "");
- if (!_powerpreviewtail) {
- Inkscape::XML::Node *tail = NULL;
- tail = xml_doc->createElement("svg:path");
- tail->setAttribute("sodipodi:insensitive", "true");
- _powerpreviewtail = SP_SHAPE(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(tail));
- SPCSSAttr *css = sp_css_attr_from_object(_powerpreview, SP_STYLE_FLAG_ALWAYS);
- Glib::ustring css_str;
- sp_repr_css_write_string(css,css_str);
- tail->setAttribute("style", css_str.c_str());
- Inkscape::GC::release(tail);
- }
- if (!curve->is_empty()) {
- _powerpreviewtail->setCurve(curve, true);
- _preview_ok = true;
- } else {//if (!_second_chance_preview) {
- _preview_ok = false;
- }
+ } else {
+ Effect* lpe = lpeitem->getCurrentLPE();
+ static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(points);
+ gchar * pvector_str = sp_svg_write_path(pathv);
+ _powerpreview->setAttribute("inkscape:original-d" , pvector_str);
+ g_free(pvector_str);
}
- curve->unref();
}
void
PencilTool::removePowerStrokePreview()
{
- if (_powerpreviewtail) {
- _powerpreviewtail->deleteObject();
- _powerpreviewtail = NULL;
- }
if(_powerpreview) {
using namespace Inkscape::LivePathEffect;
Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE();
@@ -774,12 +736,15 @@ PencilTool::addPowerStrokePencil(SPCurve * c)
{
using namespace Inkscape::LivePathEffect;
this->points.clear();
+ this->points_pos.clear();
+ this->key_nodes.clear();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0);
- double pstol = prefs->getDoubleLimited("/tools/freehand/pencil/powerstrokemintolerance", 45.0, 1.0, 100.0);
- double gap_pressure = prefs->getIntLimited("/tools/freehand/pencil/ps-step-pressure",10, 1, 100)/100.0;
- double min = prefs->getIntLimited("/tools/freehand/pencil/minpressure", 0, 1, 100) / 100.0;
- double max = prefs->getIntLimited("/tools/freehand/pencil/maxpressure", 100, 1, 100) / 100.0;
+ double step = prefs->getIntLimited("/tools/freehand/pencil/ps-step-pressure",10, 1, 100)/100.0;
+ double min = prefs->getIntLimited("/tools/freehand/pencil/minpressure", 0, 1, 100) / 100.0;
+ double max = prefs->getIntLimited("/tools/freehand/pencil/maxpressure", 100, 1, 100) / 100.0;
+ //This is a calculated number of nodes from 2 to 7 to get a simplify simil
+ int tol = 2 + (prefs->getIntLimited("/tools/freehand/pencil/tolerance",10, 1, 100)/20);
+
if (min > max){
min = max;
}
@@ -798,18 +763,6 @@ PencilTool::addPowerStrokePencil(SPCurve * c)
}
if(!c) {
live = true;
- //Simplify a bit the base curve to avoid artifacts
- SPCurve * previous_red = red_curve->copy();
- SPCurve * previous_green = green_curve->copy();
- prefs->setDouble("/tools/freehand/pencil/tolerance", pstol);
- std::vector<Geom::Point> stroreps = this->ps;
- std::vector<double> strorewps = this->wps;
- _interpolate(true);
- this->ps = stroreps;
- this->wps = strorewps;
- stroreps.clear();
- strorewps.clear();
- prefs->setDouble("/tools/freehand/pencil/tolerance", tol);
if (sa && sa->curve) {
curve = sa_overwrited->copy();
if (!green_curve->is_unset()) {
@@ -828,13 +781,9 @@ PencilTool::addPowerStrokePencil(SPCurve * c)
curve = NULL;
}
}
- if (curve->is_empty()) {
+ if (!curve || curve->is_unset()) {
curve = NULL;
}
- red_curve = previous_red->copy();
- green_curve = previous_green->copy();
- previous_red->unref();
- previous_green->unref();
} else {
curve = c->copy();
removePowerStrokePreview();
@@ -848,8 +797,10 @@ PencilTool::addPowerStrokePencil(SPCurve * c)
}
Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine();
Geom::PathVector pathvector = curve->get_pathvector();
+ if (!pathvector.size()) {
+ return;
+ }
Geom::Path const path = pathvector[0];
- curve->unref();
double previous_pressure = 0.0;
double pressure_computed = 0.0;
double dezoomify_factor = 0.05 * 1000/SP_EVENT_CONTEXT(this)->desktop->current_zoom();//\/100 we want 100% = 1;
@@ -861,47 +812,56 @@ PencilTool::addPowerStrokePencil(SPCurve * c)
position *= transformCoordinate.inverse();
}
double pos = Geom::nearest_time(position, path);
+ size_t last = 0;
for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) {
+ position = *point;
counter++;
+
double pressure_shrunk = (*pressure * (max - min)) + min;
//We need half width for power stroke
pressure_computed = pressure_shrunk * dezoomify_factor/2.0;
//remove start pressure gap
if (start) {
start = false;
- this->points.push_back(Geom::Point(pos + 0.01, pressure_computed));
+ this->points.push_back(Geom::Point(pos, pressure_computed));
+ this->key_nodes.push_back(position);
+ this->points_pos.push_back(position);
previous_pressure = pressure_shrunk;
continue;
}
- position = *point;
if (!live) {
position *= transformCoordinate.inverse();
}
pos = Geom::nearest_time(position, path);
- if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > gap_pressure && pos < path.size() - 1) {
+ if (counter%tol == 0) {
+ this->key_nodes.push_back(position);
+ }
+ if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > step && pos < path.size() - 1) {
previous_pressure = pressure_shrunk;
this->points.push_back(Geom::Point(pos, pressure_computed));
+ this->points_pos.push_back(position);
+ if (counter%tol != 0) {
+ this->key_nodes.push_back(position);
+ }
}
- }
- if (live && this->points.size() > 0) {
- bool write = false;
- if (points_parsed != this->points.size() || !_preview_ok) {
- points_parsed = this->points.size();
- write = true;
+ if (Geom::are_near(this->key_nodes[this->key_nodes.size()-1],this->key_nodes[this->key_nodes.size()-2])) {
+ this->key_nodes.pop_back();
}
- if (write) {
- _powerStrokePreview(path * transformCoordinate.inverse(), this->points, write);
+ }
+ bool last_added = counter%tol == 0;
+ if (!last_added) {
+ this->key_nodes.pop_back();
+ this->key_nodes.push_back(position);
+ }
+ if (this->points.size() > 0) {
+ _interpolate(curve);
+ if (live && !curve->is_empty()) {
+ _powerStrokePreview(curve->get_pathvector()[0] * transformCoordinate.inverse(), this->points);
} else {
- Geom::Point start = this->points[this->points.size()-1];
- if (start[Geom::X] < path.size()) {
- std::vector<Geom::Point> pointsend;
- Geom::Path const pathtail = path.portion(start[Geom::X], path.size());
- pointsend.push_back(Geom::Point(0.01, start[Geom::Y]));
- _powerStrokePreview(pathtail * transformCoordinate.inverse(), pointsend, write);
- pointsend.clear();
- }
+ c = curve->copy();
}
}
+ curve->unref();
}
void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) {
@@ -914,8 +874,13 @@ void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) {
this->p[this->npoints++] = p;
this->_fitAndSplit();
this->ps.push_back(p);
- this->wps.push_back(this->pressure);
if (input_has_pressure) {
+ this->wps.push_back(this->pressure);
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), NULL);
+ for (auto i:this->green_bpaths) {
+ sp_canvas_item_destroy(i);
+ }
+ this->green_bpaths.clear();
this->addPowerStrokePencil(NULL);
}
}
@@ -927,7 +892,8 @@ square(double const x)
return x * x;
}
-void PencilTool::_interpolate(bool realize) {
+void
+PencilTool::_interpolate(SPCurve * curve) {
if ( this->ps.size() <= 1 ) {
return;
}
@@ -943,66 +909,11 @@ void PencilTool::_interpolate(bool realize) {
double tol2 = prefs->getDoubleLimited("/tools/freehand/pencil/base-simplify", 25.0, 1.0, 100.0) * 0.4;
tol = std::min(tol,tol2);
}
- //To avoid artifacts on pressure power
- if (input_has_pressure) {
- double pstol = prefs->getDoubleLimited("/tools/freehand/pencil/powerstrokemintolerance", 40);
- if (tol < pstol * 0.4) {
- tol = pstol * 0.4;
- }
- //we dont need a exact calulation set up a high precission
- //we remove pointa at start and end nearest to 1/20 of total length
- double distance = 0;
- Geom::Point prev = this->ps[0];
- for (auto i:this->ps) {
- if (i == prev) {
- continue;
- }
- distance += Geom::distance(i, prev);
- prev = i;
- }
- double smoothlenght = (distance/this->ps.size());
- std::cout << smoothlenght << "smoothlenght" << std::endl;
-
-// //Double check to limit on large strokes
-// double limitlenght = desktop->get_display_area().diameter()/20.0;
-// std::cout << limitlenght << "limitlenght" << std::endl;
-// //smoothlenght = std::min(smoothlenght, limitlenght);
-// //Smooth start segments
-
-
-
-
- if (realize && this->ps.size() > 3) {
- Geom::Point start_point = *this->ps.begin();
- while ( this->ps.size() > 6 && Geom::distance(*(this->ps.begin()+1), start_point) < smoothlenght) {
- this->ps.erase(this->ps.begin() + 1);
- this->wps.erase(this->wps.begin() + 1);
- }
- }
- //Smooth last segments
- if (realize && this->ps.size() > 3) {
- Geom::Point last_point = *this->ps.end();
- bool erased = false;
- while ( this->ps.size() > 6 && Geom::distance(*this->ps.end(), last_point) < smoothlenght) {
- this->ps.pop_back();
- this->wps.pop_back();
- erased = true;
- }
- if (erased) {
- this->wps.push_back(this->wps[this->wps.size()-1]);
- this->ps.push_back(last_point);
- }
- }
- }
double tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2);
g_assert(is_zero(this->req_tangent) || is_unit_vector(this->req_tangent));
- this->green_curve->reset();
- this->red_curve->reset();
- this->red_curve_is_valid = false;
-
int n_points = this->ps.size();
// worst case gives us a segment per point
@@ -1011,55 +922,69 @@ void PencilTool::_interpolate(bool realize) {
std::vector<Geom::Point> b(max_segs);
int const n_segs = Geom::bezier_fit_cubic_r(b.data(), this->ps.data(), n_points, tolerance_sq, max_segs);
-
if (n_segs > 0) {
- /* Fit and draw and reset state */
-
- this->green_curve->moveto(b[0]);
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0);
- for (int c = 0; c < n_segs; c++) {
- // if we are in BSpline we modify the trace to create adhoc nodes
- if (mode == 2) {
- Geom::Point point_at1 = b[4 * c + 0] + (1./3) * (b[4 * c + 3] - b[4 * c + 0]);
- point_at1 = Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
- Geom::Point point_at2 = b[4 * c + 3] + (1./3) * (b[4 * c + 0] - b[4 * c + 3]);
- point_at2 = Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
- this->green_curve->curveto(point_at1,point_at2,b[4*c+3]);
- } else {
- //force retracted handle at end if power stroke
- if (c == n_segs - 1 && input_has_pressure) {
- this->green_curve->curveto(b[4 * c + 1], b[4 * c + 3], b[4 * c + 3]);
- } else if (c == 0 && input_has_pressure) {
- this->green_curve->curveto(b[4 * c], b[4 * c + 2], b[4 * c + 3]);
+ if (input_has_pressure) {
+ std::unique_ptr<Geom::Interpolate::Interpolator> interpolator( Geom::Interpolate::Interpolator::create(Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM) );
+ Geom::Path path = interpolator->interpolateToPath(this->key_nodes);
+ size_t counter = 0;
+ for (auto point:this->points_pos) {
+ double pos = Geom::nearest_time(point, path);
+ this->points[counter][Geom::X] = pos;
+ counter++;
+ }
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), NULL);
+ curve->set_pathvector(path);
+ } else {
+ /* Fit and draw and reset state */
+ curve->moveto(b[0]);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0);
+ for (int c = 0; c < n_segs; c++) {
+ // if we are in BSpline we modify the trace to create adhoc nodes
+ if (mode == 2) {
+ Geom::Point point_at1 = b[4 * c + 0] + (1./3) * (b[4 * c + 3] - b[4 * c + 0]);
+ point_at1 = Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
+ Geom::Point point_at2 = b[4 * c + 3] + (1./3) * (b[4 * c + 0] - b[4 * c + 3]);
+ point_at2 = Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
+ curve->curveto(point_at1,point_at2,b[4*c+3]);
} else {
- this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]);
+ //force retracted handle at end if power stroke
+ if (c == n_segs - 1 && input_has_pressure) {
+ curve->curveto(b[4 * c + 1], b[4 * c + 3], b[4 * c + 3]);
+ } else if (c == 0 && input_has_pressure) {
+ curve->curveto(b[4 * c], b[4 * c + 2], b[4 * c + 3]);
+ } else {
+ curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]);
+ }
}
}
- }
- if (!input_has_pressure) {
- sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->green_curve);
- }
-
- /* Fit and draw and copy last point */
- g_assert(!this->green_curve->is_empty());
-
- /* Set up direction of next curve. */
- {
- Geom::Curve const * last_seg = this->green_curve->last_segment();
- g_assert( last_seg ); // Relevance: validity of (*last_seg)
- this->p[0] = last_seg->finalPoint();
- this->npoints = 1;
- Geom::Curve *last_seg_reverse = last_seg->reverse();
- Geom::Point const req_vec( -last_seg_reverse->unitTangentAt(0) );
- delete last_seg_reverse;
- this->req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) )
- ? Geom::Point(0, 0)
- : Geom::unit_vector(req_vec) );
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), curve);
+ /* Fit and draw and copy last point */
+ g_assert(!curve->is_empty());
+ /* Set up direction of next curve. */
+ {
+ Geom::Curve const * last_seg = curve->last_segment();
+ g_assert( last_seg ); // Relevance: validity of (*last_seg)
+ this->p[0] = last_seg->finalPoint();
+ this->npoints = 1;
+ Geom::Curve *last_seg_reverse = last_seg->reverse();
+ Geom::Point const req_vec( -last_seg_reverse->unitTangentAt(0) );
+ delete last_seg_reverse;
+ this->req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) )
+ ? Geom::Point(0, 0)
+ : Geom::unit_vector(req_vec) );
+ }
}
}
}
+void PencilTool::_interpolate() {
+ this->green_curve->reset();
+ this->red_curve->reset();
+ this->red_curve_is_valid = false;
+ _interpolate(this->green_curve);
+}
+
/* interpolates the sketched curve and tweaks the current sketch interpolation*/
void PencilTool::_sketchInterpolate() {
diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h
index e15289ad1..354acfbba 100644
--- a/src/ui/tools/pencil-tool.h
+++ b/src/ui/tools/pencil-tool.h
@@ -44,9 +44,10 @@ public:
Geom::Point req_tangent;
bool is_drawing;
- size_t points_parsed;
std::vector<Geom::Point> ps;
+ std::vector<Geom::Point> key_nodes;
std::vector<Geom::Point> points;
+ std::vector<Geom::Point> points_pos;
std::vector<double> wps;
void addPowerStrokePencil(SPCurve * c);
@@ -70,16 +71,14 @@ private:
bool _handleKeyPress(GdkEventKey const &event);
bool _handleKeyRelease(GdkEventKey const &event);
void _setStartpoint(Geom::Point const &p);
- void _powerStrokePreview(Geom::Path const path, std::vector<Geom::Point> points, bool write);
+ void _powerStrokePreview(Geom::Path const path, std::vector<Geom::Point> points);
SPShape *_powerpreview;
- SPShape *_powerpreviewtail;
- bool _preview_ok;
- gchar const * end_linecap_type;
void _setEndpoint(Geom::Point const &p);
void _finishEndpoint();
void _addFreehandPoint(Geom::Point const &p, guint state);
void _fitAndSplit();
- void _interpolate(bool realize = false);
+ void _interpolate();
+ void _interpolate(SPCurve * curve);
void _sketchInterpolate();
void _extinput(GdkEvent *event);
void _cancel();