From db164d8341a6dca5cab6fdc4576234bc656ac6f7 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 12 Nov 2017 18:10:11 +0100 Subject: Base refactor --- src/live_effects/lpe-powerstroke.cpp | 14 +- src/ui/dialog/inkscape-preferences.cpp | 29 ++-- src/ui/dialog/inkscape-preferences.h | 3 +- src/ui/tools/freehand-base.cpp | 40 +++-- src/ui/tools/pencil-tool.cpp | 257 ++++++++++++++++++++------------- src/ui/tools/pencil-tool.h | 8 +- 6 files changed, 201 insertions(+), 150 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 51c8451a5..7c060a965 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -563,13 +563,19 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) Geom::PathVector path_out; if (path_in.empty()) { - return path_out; + return path_in; } Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(path_in); Geom::Piecewise > pwd2_in = pathv[0].toPwSb(); + if (!pwd2_in.size()) { + return path_in; + } Piecewise > der = derivative(pwd2_in); + if (!der.size()) { + return path_in; + } Piecewise > n = unitVector(der,0.0001); - if (!n.size() || !pwd2_in.size() || !n.size()) { + if (!n.size()) { return path_in; } n = rot90(n); @@ -639,10 +645,8 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } LineJoinType jointype = static_cast(linejoin_type.get_value()); - - Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); + Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise > 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()) { diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 79d8b7411..665b9643c 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -204,12 +204,12 @@ void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring false ); } -void InkscapePreferences::AddPressureSensibility(DialogPage &p, Glib::ustring const &prefs_path, double def_value) +void InkscapePreferences::AddPressureSensitivity(DialogPage &p, Glib::ustring const &prefs_path, double def_value) { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); - sb->init ( prefs_path + "/pressure-sensibility", 1, 100.0, 1.0, 10.0, def_value, true, false); - p.add_line( false, _("Pressure sensibility:"), *sb, _("on tablet usage"), - _("Pressure sensibility, 12 is the default value"), + sb->init ( prefs_path + "/pressure-sensitivity", 1, 100.0, 1.0, 10.0, def_value, true, false); + p.add_line( false, _("Pressure sensitivity:"), *sb, _("on tablet usage"), + _("Pressure sensitivity, 12 is the default value"), false ); } @@ -217,8 +217,8 @@ void InkscapePreferences::AddPowerStrokeKnotDistanceFactor(DialogPage &p, Glib:: { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); sb->init ( prefs_path + "/knots-distance", 0.1, 9999.0, 1.0, 10.0, def_value, false, false); - p.add_line( false, _("Pressure min knot distance factor:"), *sb, _("on tablet usage"), - _("Min distance between knots, this is a factor value computed with other parameters, 135 is the default one"), + p.add_line( false, _("Minimum knot distance factor:"), *sb, _("on tablet usage"), + _("This unitless value affects the density of LPE knots. The default value is 135. A small value allows you to draw squiggly lines, while a higher value creates smoother lines with less width differences."), false ); } @@ -226,19 +226,11 @@ void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::u { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); sb->init ( prefs_path + "/gap-pressure", 0.01, 9999.0, 1.0, 10.0, def_value, false, false); - p.add_line( false, _("Pressure inputs difference for made knots:"), *sb, _("on tablet usage"), - _("Diference between input pressure to make a powerstroke knot, this is a factor value computed with other parameters, 1 is the default value"), + p.add_line( false, _("Pressure change needed to create knot:"), *sb, _("on tablet usage"), + _("Difference in pressure that is required to create a new PowerStroke knot. This factor is one of several that are taken into account when deciding where to place a knot. The default value is 1."), false ); } -void InkscapePreferences::AddPowerStrokeUseOptimusValues(DialogPage &p, Glib::ustring const &prefs_path, bool def_value) -{ - PrefCheckButton* cb = Gtk::manage( new PrefCheckButton); - cb->init ( _("Use optimiced powerstroke values instead the default ones:"), prefs_path + "/optimus-powerstroke", def_value); - p.add_line( false, "", *cb, "", _("Use optimized powerstroke parameters values in pencil tool por pressure inputs instead the default ones")); -} - - static void StyleFromSelectionToTool(Glib::ustring const &prefs_path, StyleSwatch *swatch) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; @@ -457,9 +449,8 @@ void InkscapePreferences::initPageTools() this->AddNewObjectsStyle(_page_pencil, "/tools/freehand/pencil"); this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0); this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0); - _page_pencil.add_group_header( _("Pencil pressure")); - this->AddPowerStrokeUseOptimusValues(_page_pencil, "/tools/freehand/pencil", true); - this->AddPressureSensibility(_page_pencil, "/tools/freehand/pencil", 12.0); + _page_pencil.add_group_header( _("Pressure sensitivity settings")); + this->AddPressureSensitivity(_page_pencil, "/tools/freehand/pencil", 12.0); this->AddPowerStrokeKnotDistanceFactor(_page_pencil, "/tools/freehand/pencil", 135.0); this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 1.0); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index a197a8e65..c6a9c5af6 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -500,10 +500,9 @@ protected: static void AddFirstAndLastCheckbox(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, bool def_value); 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 AddPressureSensibility(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); + static void AddPressureSensitivity(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); static void AddPowerStrokeKnotDistanceFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); static void AddPowerStrokeGapPressureFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); - static void AddPowerStrokeUseOptimusValues(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, bool 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 064a83a5a..e6b55b6ce 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -235,39 +235,33 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (dc->input_has_pressure) { SPShape *sp_shape = dynamic_cast(item); + Geom::Path path; if (sp_shape) { SPCurve * c = sp_shape->getCurve(); if (!c) { + pt->points.clear(); return; } - pt->addPowerStrokePencil(c); + path = c->get_pathvector()[0]; } + pt->removePowerStrokePreview(); + double zoom = SP_EVENT_CONTEXT(dc)->desktop->current_zoom() * 5.0; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + 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; + if (min > max){ + min = max; + } + Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); + Geom::Coord scale = transformCoordinate.expansionX(); + double pressure_shirnked = (1.0 * (max - min)) + min; + double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; if(pt->points.empty()){ - //if use mouse give a line - double zoom = SP_EVENT_CONTEXT(dc)->desktop->current_zoom() * 5.0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - 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; - if (min > max){ - min = max; - } - Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); - Geom::Coord scale = transformCoordinate.expansionX(); - double pressure_shirnked = (1.0 * (max - min)) + min; - double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; - pt->points.push_back(Geom::Point(0,pressure_computed)); + pt->points.push_back(Geom::Point(0, pressure_computed)); } Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); - if(prefs->getBool("/tools/freehand/pencil/optimus-powerstroke",true)) { - lpe->getRepr()->setAttribute("start_linecap_type", "round"); - lpe->getRepr()->setAttribute("end_linecap_type", "round"); - lpe->getRepr()->setAttribute("sort_points", "true"); - lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); - lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("miter_limit", "4"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); - } + lpe->getRepr()->setAttribute("interpolator_type" , "CubicBezierSmooth"); static_cast(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 6f64e8e26..97cb8c9f8 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -41,6 +41,7 @@ #include "svg/svg.h" #include "display/curve.h" #include "desktop-style.h" +#include "style.h" #include "display/sp-canvas.h" #include "display/curve.h" #include "live_effects/lpe-powerstroke.h" @@ -69,7 +70,9 @@ PencilTool::PencilTool() , state(SP_PENCIL_CONTEXT_IDLE) , req_tangent(0, 0) , is_drawing(false) + , points_parsed(2) , sketch_n(0) + , _powerpreviewtail(NULL) , _powerpreview(NULL) { } @@ -203,7 +206,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { SnapManager &m = desktop->namedview->snap_manager; if (bevent.state & GDK_CONTROL_MASK) { - m.setup(desktop, true, _powerpreview); + m.setup(desktop, true, _powerpreviewtail); if (!(bevent.state & GDK_SHIFT_MASK)) { m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } @@ -217,7 +220,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { this->overwrite_curve = anchor->curve; desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); } else { - m.setup(desktop, true, _powerpreview); + m.setup(desktop, true, _powerpreviewtail); 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 @@ -355,7 +358,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, _powerpreview); + m.setup(desktop, true, _powerpreviewtail); m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); m.unSetup(); } @@ -429,10 +432,7 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { /* Write curves to object */ desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand")); - 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); this->sa = NULL; this->ea = NULL; @@ -640,110 +640,130 @@ void PencilTool::_finishEndpoint() { } void -sp_powerstrole_preview(Geom::PathVector pathvector, SPItem * powerpreview, const char * item_id, std::vector points){ +PencilTool::_powerStrokePreview(Geom::Path path, std::vector points, bool write) +{ using namespace Inkscape::LivePathEffect; SPDocument * document = SP_ACTIVE_DOCUMENT; if (!document) { return; } Inkscape::XML::Document *xml_doc = document->getReprDoc(); - Inkscape::XML::Node *preview = NULL; - if (powerpreview) { - Effect* lpe = SP_LPE_ITEM(powerpreview)->getCurrentLPE(); + Inkscape::XML::Node *previewend = NULL; + if (_powerpreview) { + Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); + if (write) { + lpe->getRepr()->setAttribute("end_linecap_type", "butt"); + } + lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); + lpe->getRepr()->setAttribute("start_linecap_type", "round"); + lpe->getRepr()->setAttribute("linejoin_type", "round"); + + gchar * path_str = sp_svg_write_path(path); + _powerpreview->setAttribute("inkscape:original-d" , path_str); + g_free(path_str); static_cast(lpe)->offset_points.param_set_and_write_new_value(points); - gchar * pvector_str = sp_svg_write_path(pathvector); - powerpreview->setAttribute("inkscape:original-d" , pvector_str); - g_free(pvector_str); + if (write) { + SPCurve * curve = SP_SHAPE(_powerpreview)->getCurve(); + if (_powerpreviewtail) { + SPCurve * previous_curve = SP_SHAPE(_powerpreviewtail)->getCurve(); + curve->append(previous_curve, false); + previous_curve->unref(); + } else { + Inkscape::XML::Node *preview = NULL; + preview = xml_doc->createElement("svg:path"); + preview->setAttribute("sodipodi:insensitive", "true"); + sp_desktop_apply_style_tool(desktop, preview, "/tools/freehand/pencil", false); + _powerpreviewtail = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(preview)); + SPCSSAttr *css = sp_css_attr_from_object(_powerpreviewtail, SP_STYLE_FLAG_ALWAYS); + std::cout << sp_repr_css_property(css, "stroke", "none") << "sp_repr_css_property(css," << std::endl; + sp_repr_css_set_property (css, "fill", sp_repr_css_property(css, "stroke", "none")); + sp_repr_css_set_property (css, "stroke","none"); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + preview->setAttribute("style", css_str.c_str()); + Inkscape::GC::release(preview); + } + SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); + curve->unref(); + } } else { - preview = xml_doc->createElement("svg:path"); - preview->setAttribute("sodipodi:insensitive", "true"); - preview->setAttribute("id", item_id); + previewend = xml_doc->createElement("svg:path"); + previewend->setAttribute("sodipodi:insensitive", "true"); SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property (css, "fill","none"); - sp_repr_css_set_property (css, "stroke","#DEDEDE"); - sp_repr_css_set_property (css, "opacity","0.85"); + sp_repr_css_set_property (css, "stroke","#ff0000"); + sp_repr_css_set_property (css, "opacity","1"); Glib::ustring css_str; sp_repr_css_write_string(css,css_str); - preview->setAttribute("style", css_str.c_str()); - gchar * pvector_str = sp_svg_write_path(pathvector); - preview->setAttribute("d" , pvector_str); - g_free(pvector_str); - SPObject *elemref = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(preview); - Inkscape::GC::release(preview); - Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(elemref)); - Effect* lpe = SP_LPE_ITEM(elemref)->getCurrentLPE(); - lpe->getRepr()->setAttribute("start_linecap_type", "round"); - lpe->getRepr()->setAttribute("end_linecap_type", "round"); - lpe->getRepr()->setAttribute("sort_points", "true"); - lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); - lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("miter_limit", "4"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); + previewend->setAttribute("style", css_str.c_str()); + gchar * path_str = sp_svg_write_path(path); + previewend->setAttribute("d" , path_str); + g_free(path_str); + _powerpreview = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(previewend)); + Inkscape::GC::release(previewend); + Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), _powerpreview); + Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); static_cast(lpe)->offset_points.param_set_and_write_new_value(points); } } +void +PencilTool::removePowerStrokePreview() +{ + if (_powerpreviewtail) { + _powerpreviewtail->deleteObject(); + _powerpreviewtail = NULL; + } + if(_powerpreview) { + using namespace Inkscape::LivePathEffect; + Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); + SP_LPE_ITEM(_powerpreview)->removeCurrentPathEffect(true); + LivePathEffectObject * lpeobj = lpe->getLPEObj(); + if (lpeobj) { + SP_OBJECT(lpeobj)->deleteObject(); + lpeobj = NULL; + } + _powerpreview->deleteObject(); + _powerpreview = NULL; + } +} void -PencilTool::addPowerStrokePencil(SPCurve * c) +PencilTool::addPowerStrokePencil() { - - this->points.clear(); using namespace Inkscape::LivePathEffect; - bool live = false; SPCurve * curve; - if(!c) { - SPCurve * previous_red = red_curve->copy(); - SPCurve * previous_green = green_curve->copy(); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - //Simplify a bit the base curve to avoid artifacts - double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); - prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); - _interpolate(); - prefs->setDouble("/tools/freehand/pencil/tolerance", tol); - live = true; - if (sa) { - curve = sa->curve; - if (!green_curve->is_empty()) { - curve->append_continuous( green_curve, 0.0625); - if (!red_curve->is_empty()) { - curve->append_continuous( red_curve, 0.0625); - } - } - } else { - if (!green_curve->is_empty()) { - curve = green_curve->copy(); - if (!red_curve->is_empty()) { - curve->append_continuous( red_curve, 0.0625); - } - } else { - curve = NULL; + SPCurve * previous_red = red_curve->copy(); + SPCurve * previous_green = green_curve->copy(); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + _interpolate(); + if (sa) { + curve = sa->curve; + if (!green_curve->is_empty()) { + curve->append_continuous( green_curve, 0.0625); + if (!red_curve->is_empty()) { + curve->append_continuous( red_curve, 0.0625); } } - red_curve = previous_red->copy(); - green_curve = previous_green->copy(); - previous_red->unref(); - previous_green->unref(); } else { - curve = c->copy(); - _powerpreview = NULL; + if (!green_curve->is_empty()) { + curve = green_curve->copy(); + if (!red_curve->is_empty()) { + curve->append_continuous( red_curve, 0.0625); + } + } else { + curve = NULL; + } } + red_curve = previous_red->copy(); + green_curve = previous_green->copy(); + previous_red->unref(); + previous_green->unref(); SPObject *elemref = NULL; SPDocument * document = SP_ACTIVE_DOCUMENT; if (!document) { return; } - const char * item_id = "___pencil_preview_powerstroke___"; - if ((elemref = document->getObjectById(item_id))) { - if (!live) { - LivePathEffectObject * lpeobj = SP_LPE_ITEM(elemref)->getCurrentLPE()->getLPEObj(); - SP_OBJECT(lpeobj)->deleteObject(); - lpeobj = NULL; - elemref->deleteObject(); - elemref = NULL; - } else { - _powerpreview = SP_ITEM(elemref); - } - } + if (!curve) { return; } @@ -752,7 +772,6 @@ PencilTool::addPowerStrokePencil(SPCurve * c) Geom::PathVector pathvector = curve->get_pathvector(); //pathvector *= transformCoordinate.inverse(); double zoom = SP_EVENT_CONTEXT(this)->desktop->current_zoom() * 5.0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); 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; if (min > max){ @@ -768,40 +787,77 @@ PencilTool::addPowerStrokePencil(SPCurve * c) Geom::Point previous_point = Geom::Point(0,0); bool start = true; auto pressure = this->wps.begin(); + std::vector tmp_points; for (auto point = this->ps.begin(); point != this->ps.end(); ++point,++pressure) { - //Maybe the 12 POW can be moved to a preferences 12 give a good results of sensibility on my tablet - //But maybe is a tweakable value. less number = less sensibility - //8 give an aceptable max witht to powerstoke double pressure_base = pow(*pressure, pressure_sensibility); double pressure_shirnked = (pressure_base * (max - min)) + min; double pressure_factor = pressure_base/pressure_shirnked; double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; bool buttonrelease = false; if (pressure_computed < 0.05 && previous_pressure != 0.0 ) { - pressure_computed = previous_pressure; + pressure_base = pow(--(*pressure), pressure_sensibility); + pressure_shirnked = (pressure_base * (max - min)) + min; + pressure_factor = pressure_base/pressure_shirnked; + pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; buttonrelease = true; } if (start || std::abs(previous_pressure - pressure_computed) > gap_pressure / (zoom * pressure_factor)) { Geom::Point position = *point; - if (!live) { - position *= transformCoordinate.inverse(); - } - double pos = Geom::nearest_time(position, paths_to_pw(pathvector)); - if (pos > 1e6 || - buttonrelease || - (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) + double pos = Geom::nearest_time(position, pathvector[0]); + if (!start && + (pos > 1e6 || + (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) ) { continue; } previous_point = *point; previous_pressure = pressure_computed; start = false; - this->points.push_back(Geom::Point(pos, pressure_computed)); + tmp_points.push_back(Geom::Point(pos, pressure_computed)); } } - if (live) { - sp_powerstrole_preview(pathvector * transformCoordinate.inverse(), _powerpreview, item_id, this->points); + this->ps.clear(); + this->wps.clear(); + if (tmp_points.size() > 0) { + Geom::Point start = Geom::Point(0,0); + double start_pos = 0.0; + std::vector pointsend; + if (tmp_points.size() > 1) { + start = tmp_points[tmp_points.size() - 2]; + } else { + start = tmp_points[tmp_points.size() - 1]; + } + Geom::Point middle = tmp_points[tmp_points.size()-1]; + Geom::Path out = pathvector[0]; + if (tmp_points.size() == 1) { + pointsend.push_back(tmp_points[0]); + this->points.push_back(tmp_points[0]); + } else if (tmp_points.size() == 2) { + pointsend.push_back(tmp_points[0]); + pointsend.push_back(tmp_points[1]); + this->points.push_back(tmp_points[1]); + } else if (points_parsed != tmp_points.size()-2) { + out = pathvector[0].portion(start[Geom::X], pathvector[0].size()); + this->points.push_back(Geom::Point(start[Geom::X], start[Geom::Y])); + pointsend.push_back(Geom::Point(0, start[Geom::Y])); + pointsend.push_back(Geom::Point(out.size(), middle[Geom::Y])); + } else { + double pressure_base = pow(this->pressure, pressure_sensibility); + double pressure_shirnked = (pressure_base * (max - min)) + min; + double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; + out = pathvector[0].portion(middle[Geom::X], pathvector[0].size()); + pointsend.push_back(Geom::Point(0, middle[Geom::Y])); + pointsend.push_back(Geom::Point(out.size(), pressure_computed)); + } + bool write = false; + if (tmp_points.size() > 1 && points_parsed != tmp_points.size()-2) { + points_parsed = tmp_points.size()-2; + write = true; + } + _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); + pointsend.clear(); } + tmp_points.clear(); } void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { @@ -811,14 +867,13 @@ void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { if ( ( p != this->p[ this->npoints - 1 ] ) && in_svg_plane(p) ) { - this->p[this->npoints++] = p; this->_fitAndSplit(); this->ps.push_back(p); this->wps.push_back(this->pressure); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (input_has_pressure) { - this->addPowerStrokePencil(NULL); + this->addPowerStrokePencil(); } } } @@ -838,12 +893,18 @@ void PencilTool::_interpolate() { using Geom::Y; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + //Remove powerstroke arifacts with input_has_pressure min clamp double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4; bool simplify = prefs->getInt("/tools/freehand/pencil/simplify", 0); if(simplify){ 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 stroke + if (input_has_pressure) { + tol = std::min(18.0,tol); + } + std::cout << tol << "tol" << std::endl; 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)); diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 8f57cf4d0..798a5fa49 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -43,13 +43,13 @@ public: Geom::Point req_tangent; bool is_drawing; - + size_t points_parsed; std::vector ps; - std::vector points; std::vector wps; - void addPowerStrokePencil(SPCurve * c); + void addPowerStrokePencil(); + void removePowerStrokePreview(); Geom::Piecewise > sketch_interpolation; // the current proposal from the sketched paths unsigned sketch_n; // number of sketches done @@ -69,7 +69,9 @@ private: bool _handleKeyPress(GdkEventKey const &event); bool _handleKeyRelease(GdkEventKey const &event); void _setStartpoint(Geom::Point const &p); + void _powerStrokePreview(Geom::Path path, std::vector points, bool write); SPItem *_powerpreview; + SPItem *_powerpreviewtail; void _setEndpoint(Geom::Point const &p); void _finishEndpoint(); -- cgit v1.2.3 From b363482ca06fc72ffedd0fd05f5663b128352f89 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Mon, 13 Nov 2017 00:24:23 +0100 Subject: Added speed improbements --- src/ui/tools/freehand-base.cpp | 20 +++++++-- src/ui/tools/pencil-tool.cpp | 93 +++++++++++++++++++++--------------------- src/ui/tools/pencil-tool.h | 3 ++ 3 files changed, 67 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index e6b55b6ce..52f578d13 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -235,14 +235,18 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (dc->input_has_pressure) { SPShape *sp_shape = dynamic_cast(item); - Geom::Path path; + Geom::PathVector pathv; if (sp_shape) { SPCurve * c = sp_shape->getCurve(); if (!c) { pt->points.clear(); return; } - path = c->get_pathvector()[0]; + pathv = c->get_pathvector(); + SPCurve * tmp_curve_to_adjust = new SPCurve(); + tmp_curve_to_adjust->set_pathvector(pt->pressure_pv); + sp_shape->setCurve(tmp_curve_to_adjust, true); + tmp_curve_to_adjust->unref(); } pt->removePowerStrokePreview(); double zoom = SP_EVENT_CONTEXT(dc)->desktop->current_zoom() * 5.0; @@ -262,7 +266,17 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); lpe->getRepr()->setAttribute("interpolator_type" , "CubicBezierSmooth"); - static_cast(lpe)->offset_points.param_set_and_write_new_value(pt->points); + LPEPowerStroke* pslpe = static_cast(lpe); + if (pslpe) { + pslpe->offset_points.param_set_and_write_new_value(pt->points); + if (sp_shape) { + SPCurve * tmp_curve_to_adjust = new SPCurve(); + tmp_curve_to_adjust->set_pathvector(pathv); + sp_shape->setCurve(tmp_curve_to_adjust, true); + tmp_curve_to_adjust->unref(); + pslpe->adjustForNewPath(pt->pressure_pv); + } + } pt->points.clear(); return; } diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 97cb8c9f8..115018913 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -70,7 +70,7 @@ PencilTool::PencilTool() , state(SP_PENCIL_CONTEXT_IDLE) , req_tangent(0, 0) , is_drawing(false) - , points_parsed(2) + , points_parsed(0) , sketch_n(0) , _powerpreviewtail(NULL) , _powerpreview(NULL) @@ -651,13 +651,8 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points Inkscape::XML::Node *previewend = NULL; if (_powerpreview) { Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - if (write) { - lpe->getRepr()->setAttribute("end_linecap_type", "butt"); - } - lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); - lpe->getRepr()->setAttribute("start_linecap_type", "round"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); - + //lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); + //lpe->getRepr()->setAttribute("linejoin_type", "round"); gchar * path_str = sp_svg_write_path(path); _powerpreview->setAttribute("inkscape:original-d" , path_str); g_free(path_str); @@ -672,12 +667,8 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points Inkscape::XML::Node *preview = NULL; preview = xml_doc->createElement("svg:path"); preview->setAttribute("sodipodi:insensitive", "true"); - sp_desktop_apply_style_tool(desktop, preview, "/tools/freehand/pencil", false); _powerpreviewtail = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(preview)); - SPCSSAttr *css = sp_css_attr_from_object(_powerpreviewtail, SP_STYLE_FLAG_ALWAYS); - std::cout << sp_repr_css_property(css, "stroke", "none") << "sp_repr_css_property(css," << std::endl; - sp_repr_css_set_property (css, "fill", sp_repr_css_property(css, "stroke", "none")); - sp_repr_css_set_property (css, "stroke","none"); + SPCSSAttr *css = sp_css_attr_from_object(_powerpreview, SP_STYLE_FLAG_ALWAYS); Glib::ustring css_str; sp_repr_css_write_string(css,css_str); preview->setAttribute("style", css_str.c_str()); @@ -689,18 +680,20 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points } else { previewend = xml_doc->createElement("svg:path"); previewend->setAttribute("sodipodi:insensitive", "true"); - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property (css, "fill","none"); - sp_repr_css_set_property (css, "stroke","#ff0000"); - sp_repr_css_set_property (css, "opacity","1"); - Glib::ustring css_str; - sp_repr_css_write_string(css,css_str); - previewend->setAttribute("style", css_str.c_str()); + sp_desktop_apply_style_tool(desktop, previewend, Glib::ustring("/tools/freehand/pencil").data(), false); + _powerpreview = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(previewend)); gchar * path_str = sp_svg_write_path(path); previewend->setAttribute("d" , path_str); g_free(path_str); - _powerpreview = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(previewend)); Inkscape::GC::release(previewend); + SPCSSAttr *css = sp_css_attr_from_object(_powerpreview, SP_STYLE_FLAG_ALWAYS); + const gchar * stroke = sp_repr_css_property(css, "stroke", "none"); + const gchar * fill = sp_repr_css_property(css, "fill", "none"); + sp_repr_css_set_property (css, "fill", stroke); + sp_repr_css_set_property (css, "stroke", fill); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + previewend->setAttribute("style", css_str.c_str()); Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), _powerpreview); Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); static_cast(lpe)->offset_points.param_set_and_write_new_value(points); @@ -735,7 +728,11 @@ PencilTool::addPowerStrokePencil() SPCurve * previous_red = red_curve->copy(); SPCurve * previous_green = green_curve->copy(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + //Simplify a bit the base curve to avoid artifacts + double tolerance = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); + prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); _interpolate(); + prefs->setDouble("/tools/freehand/pencil/tolerance", tolerance); if (sa) { curve = sa->curve; if (!green_curve->is_empty()) { @@ -769,7 +766,7 @@ PencilTool::addPowerStrokePencil() } Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); Geom::Coord scale = transformCoordinate.expansionX(); - Geom::PathVector pathvector = curve->get_pathvector(); + pressure_pv = curve->get_pathvector(); //pathvector *= transformCoordinate.inverse(); double zoom = SP_EVENT_CONTEXT(this)->desktop->current_zoom() * 5.0; double min = prefs->getIntLimited("/tools/freehand/pencil/minpressure", 0, 1, 100) / 100.0; @@ -803,7 +800,7 @@ PencilTool::addPowerStrokePencil() } if (start || std::abs(previous_pressure - pressure_computed) > gap_pressure / (zoom * pressure_factor)) { Geom::Point position = *point; - double pos = Geom::nearest_time(position, pathvector[0]); + double pos = Geom::nearest_time(position, pressure_pv[0]); if (!start && (pos > 1e6 || (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) ) @@ -816,19 +813,23 @@ PencilTool::addPowerStrokePencil() tmp_points.push_back(Geom::Point(pos, pressure_computed)); } } - this->ps.clear(); - this->wps.clear(); if (tmp_points.size() > 0) { - Geom::Point start = Geom::Point(0,0); - double start_pos = 0.0; + bool write = false; + if (points_parsed != tmp_points.size()) { + points_parsed = tmp_points.size(); + write = true; + } std::vector pointsend; + Geom::Point prev = Geom::Point(0,0); + if (tmp_points.size() > 2) { + prev = tmp_points[tmp_points.size() - 3]; + } + Geom::Point start = Geom::Point(0,0); if (tmp_points.size() > 1) { start = tmp_points[tmp_points.size() - 2]; - } else { - start = tmp_points[tmp_points.size() - 1]; } - Geom::Point middle = tmp_points[tmp_points.size()-1]; - Geom::Path out = pathvector[0]; + Geom::Point end = tmp_points[tmp_points.size()-1]; + Geom::Path out = pressure_pv[0]; if (tmp_points.size() == 1) { pointsend.push_back(tmp_points[0]); this->points.push_back(tmp_points[0]); @@ -836,24 +837,24 @@ PencilTool::addPowerStrokePencil() pointsend.push_back(tmp_points[0]); pointsend.push_back(tmp_points[1]); this->points.push_back(tmp_points[1]); - } else if (points_parsed != tmp_points.size()-2) { - out = pathvector[0].portion(start[Geom::X], pathvector[0].size()); + } else if (write) { + Geom::Point pos = pressure_pv[0].pointAt(start[Geom::X]); + out = pressure_pv[0].portion(prev[Geom::X], pressure_pv[0].size()); + pointsend.push_back(Geom::Point(0, prev[Geom::Y])); + pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), start[Geom::Y])); + pointsend.push_back(Geom::Point(out.size(), end[Geom::Y])); this->points.push_back(Geom::Point(start[Geom::X], start[Geom::Y])); - pointsend.push_back(Geom::Point(0, start[Geom::Y])); - pointsend.push_back(Geom::Point(out.size(), middle[Geom::Y])); } else { double pressure_base = pow(this->pressure, pressure_sensibility); double pressure_shirnked = (pressure_base * (max - min)) + min; double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; - out = pathvector[0].portion(middle[Geom::X], pathvector[0].size()); - pointsend.push_back(Geom::Point(0, middle[Geom::Y])); + Geom::Point pos = pressure_pv[0].pointAt(end[Geom::X]); + out = pressure_pv[0].portion(start[Geom::X], pressure_pv[0].size()); + pointsend.push_back(Geom::Point(0, start[Geom::Y])); + pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), end[Geom::Y])); pointsend.push_back(Geom::Point(out.size(), pressure_computed)); } - bool write = false; - if (tmp_points.size() > 1 && points_parsed != tmp_points.size()-2) { - points_parsed = tmp_points.size()-2; - write = true; - } + _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); pointsend.clear(); } @@ -900,11 +901,11 @@ void PencilTool::_interpolate() { 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 stroke - if (input_has_pressure) { - tol = std::min(18.0,tol); + //To avoid artifacts on pressure power + if (input_has_pressure && tol < 18.0 * 0.4) { + tol = 18.0 * 0.4; } - std::cout << tol << "tol" << std::endl; + 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)); diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 798a5fa49..c3b9fb48c 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -10,6 +10,7 @@ #include <2geom/piecewise.h> #include <2geom/d2.h> #include <2geom/sbasis.h> +#include <2geom/pathvector.h> #define DDC_MIN_PRESSURE 0.0 @@ -47,6 +48,8 @@ public: std::vector ps; std::vector points; std::vector wps; + + Geom::PathVector pressure_pv; void addPowerStrokePencil(); void removePowerStrokePreview(); -- cgit v1.2.3 From 3ff3539433be9d91f2d77eb5b45f9b1faeb577ec Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Mon, 13 Nov 2017 23:06:14 +0100 Subject: Working refactor --- src/ui/tools/freehand-base.cpp | 22 +---- src/ui/tools/pencil-tool.cpp | 180 +++++++++++++++++++++++------------------ src/ui/tools/pencil-tool.h | 4 +- 3 files changed, 105 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 52f578d13..bff24346a 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -235,20 +235,13 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (dc->input_has_pressure) { SPShape *sp_shape = dynamic_cast(item); - Geom::PathVector pathv; if (sp_shape) { SPCurve * c = sp_shape->getCurve(); if (!c) { - pt->points.clear(); return; } - pathv = c->get_pathvector(); - SPCurve * tmp_curve_to_adjust = new SPCurve(); - tmp_curve_to_adjust->set_pathvector(pt->pressure_pv); - sp_shape->setCurve(tmp_curve_to_adjust, true); - tmp_curve_to_adjust->unref(); + pt->addPowerStrokePencil(c); } - pt->removePowerStrokePreview(); double zoom = SP_EVENT_CONTEXT(dc)->desktop->current_zoom() * 5.0; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double min = prefs->getIntLimited("/tools/freehand/pencil/minpressure", 0, 1, 100) / 100.0; @@ -265,18 +258,7 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha } Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); - lpe->getRepr()->setAttribute("interpolator_type" , "CubicBezierSmooth"); - LPEPowerStroke* pslpe = static_cast(lpe); - if (pslpe) { - pslpe->offset_points.param_set_and_write_new_value(pt->points); - if (sp_shape) { - SPCurve * tmp_curve_to_adjust = new SPCurve(); - tmp_curve_to_adjust->set_pathvector(pathv); - sp_shape->setCurve(tmp_curve_to_adjust, true); - tmp_curve_to_adjust->unref(); - pslpe->adjustForNewPath(pt->pressure_pv); - } - } + static_cast(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 115018913..fe02258c7 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -613,8 +613,9 @@ void PencilTool::_setEndpoint(Geom::Point const &p) { this->red_curve->moveto(this->p[0]); this->red_curve->lineto(this->p[1]); this->red_curve_is_valid = true; - - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + if (!input_has_pressure) { + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + } } } @@ -651,12 +652,12 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points Inkscape::XML::Node *previewend = NULL; if (_powerpreview) { Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - //lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); - //lpe->getRepr()->setAttribute("linejoin_type", "round"); + lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); + lpe->getRepr()->setAttribute("linejoin_type", "round"); + static_cast(lpe)->offset_points.param_set_and_write_new_value(points); gchar * path_str = sp_svg_write_path(path); _powerpreview->setAttribute("inkscape:original-d" , path_str); g_free(path_str); - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); if (write) { SPCurve * curve = SP_SHAPE(_powerpreview)->getCurve(); if (_powerpreviewtail) { @@ -689,6 +690,10 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points SPCSSAttr *css = sp_css_attr_from_object(_powerpreview, SP_STYLE_FLAG_ALWAYS); const gchar * stroke = sp_repr_css_property(css, "stroke", "none"); const gchar * fill = sp_repr_css_property(css, "fill", "none"); + if (!strcmp(fill, "none")) { + fill = "#000000"; + stroke = "#000000"; + } sp_repr_css_set_property (css, "fill", stroke); sp_repr_css_set_property (css, "stroke", fill); Glib::ustring css_str; @@ -721,54 +726,74 @@ PencilTool::removePowerStrokePreview() } } void -PencilTool::addPowerStrokePencil() +PencilTool::addPowerStrokePencil(SPCurve * c) { + this->points.clear(); using namespace Inkscape::LivePathEffect; + bool live = false; SPCurve * curve; - SPCurve * previous_red = red_curve->copy(); - SPCurve * previous_green = green_curve->copy(); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - //Simplify a bit the base curve to avoid artifacts - double tolerance = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); - prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); - _interpolate(); - prefs->setDouble("/tools/freehand/pencil/tolerance", tolerance); - if (sa) { - curve = sa->curve; - if (!green_curve->is_empty()) { - curve->append_continuous( green_curve, 0.0625); - if (!red_curve->is_empty()) { - curve->append_continuous( red_curve, 0.0625); - } - } - } else { - if (!green_curve->is_empty()) { - curve = green_curve->copy(); - if (!red_curve->is_empty()) { - curve->append_continuous( red_curve, 0.0625); + if(!c) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPCurve * previous_red = red_curve->copy(); + SPCurve * previous_green = green_curve->copy(); + //Simplify a bit the base curve to avoid artifacts + double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); + prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); + _interpolate(); + prefs->setDouble("/tools/freehand/pencil/tolerance", tol); + live = true; + if (sa) { + curve = sa->curve; + if (!green_curve->is_empty()) { + curve->append_continuous( green_curve, 0.0625); + if (!red_curve->is_empty()) { + curve->append_continuous( red_curve, 0.0625); + } } } else { - curve = NULL; + if (!green_curve->is_empty()) { + curve = green_curve->copy(); + if (!red_curve->is_empty()) { + curve->append_continuous( red_curve, 0.0625); + } + } else { + curve = NULL; + } } + red_curve = previous_red->copy(); + green_curve = previous_green->copy(); + previous_red->unref(); + previous_green->unref(); + } else { + curve = c->copy(); + removePowerStrokePreview(); } - red_curve = previous_red->copy(); - green_curve = previous_green->copy(); - previous_red->unref(); - previous_green->unref(); SPObject *elemref = NULL; SPDocument * document = SP_ACTIVE_DOCUMENT; if (!document) { return; } - + const char * item_id = "___pencil_preview_powerstroke___"; + if ((elemref = document->getObjectById(item_id))) { + if (!live) { + LivePathEffectObject * lpeobj = SP_LPE_ITEM(elemref)->getCurrentLPE()->getLPEObj(); + SP_OBJECT(lpeobj)->deleteObject(); + lpeobj = NULL; + elemref->deleteObject(); + elemref = NULL; + } else { + _powerpreview = SP_ITEM(elemref); + } + } if (!curve) { return; } Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); Geom::Coord scale = transformCoordinate.expansionX(); - pressure_pv = curve->get_pathvector(); + Geom::PathVector pathvector = curve->get_pathvector(); //pathvector *= transformCoordinate.inverse(); double zoom = SP_EVENT_CONTEXT(this)->desktop->current_zoom() * 5.0; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); 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; if (min > max){ @@ -784,81 +809,77 @@ PencilTool::addPowerStrokePencil() Geom::Point previous_point = Geom::Point(0,0); bool start = true; auto pressure = this->wps.begin(); - std::vector tmp_points; + bool buttonrelease = false; for (auto point = this->ps.begin(); point != this->ps.end(); ++point,++pressure) { + //Maybe the 12 POW can be moved to a preferences 12 give a good results of sensibility on my tablet + //But maybe is a tweakable value. less number = less sensibility + //8 give an aceptable max witht to powerstoke double pressure_base = pow(*pressure, pressure_sensibility); double pressure_shirnked = (pressure_base * (max - min)) + min; double pressure_factor = pressure_base/pressure_shirnked; double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; - bool buttonrelease = false; if (pressure_computed < 0.05 && previous_pressure != 0.0 ) { - pressure_base = pow(--(*pressure), pressure_sensibility); - pressure_shirnked = (pressure_base * (max - min)) + min; - pressure_factor = pressure_base/pressure_shirnked; - pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; + pressure_computed = previous_pressure; buttonrelease = true; } if (start || std::abs(previous_pressure - pressure_computed) > gap_pressure / (zoom * pressure_factor)) { Geom::Point position = *point; - double pos = Geom::nearest_time(position, pressure_pv[0]); - if (!start && - (pos > 1e6 || - (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) ) + if (!live) { + position *= transformCoordinate.inverse(); + } + double pos = Geom::nearest_time(position, pathvector[0]); + if (pos > 1e6 || + buttonrelease || + (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) { continue; } previous_point = *point; previous_pressure = pressure_computed; start = false; - tmp_points.push_back(Geom::Point(pos, pressure_computed)); + this->points.push_back(Geom::Point(pos, pressure_computed)); } } - if (tmp_points.size() > 0) { + if (live && this->points.size() > 0) { bool write = false; - if (points_parsed != tmp_points.size()) { - points_parsed = tmp_points.size(); + if (points_parsed != this->points.size()) { + points_parsed = this->points.size(); write = true; } std::vector pointsend; Geom::Point prev = Geom::Point(0,0); - if (tmp_points.size() > 2) { - prev = tmp_points[tmp_points.size() - 3]; + if (this->points.size() > 2) { + prev = this->points[this->points.size() - 3]; } Geom::Point start = Geom::Point(0,0); - if (tmp_points.size() > 1) { - start = tmp_points[tmp_points.size() - 2]; + if (this->points.size() > 1) { + start = this->points[this->points.size() - 2]; } - Geom::Point end = tmp_points[tmp_points.size()-1]; - Geom::Path out = pressure_pv[0]; - if (tmp_points.size() == 1) { - pointsend.push_back(tmp_points[0]); - this->points.push_back(tmp_points[0]); - } else if (tmp_points.size() == 2) { - pointsend.push_back(tmp_points[0]); - pointsend.push_back(tmp_points[1]); - this->points.push_back(tmp_points[1]); + Geom::Point end = this->points[this->points.size()-1]; + Geom::Path out = pathvector[0]; + if (this->points.size() == 1) { + pointsend.push_back(this->points[0]); + } else if (this->points.size() == 2) { + pointsend.push_back(this->points[0]); + pointsend.push_back(this->points[1]); } else if (write) { - Geom::Point pos = pressure_pv[0].pointAt(start[Geom::X]); - out = pressure_pv[0].portion(prev[Geom::X], pressure_pv[0].size()); + Geom::Point pos = out.pointAt(start[Geom::X]); + out = out.portion(prev[Geom::X], out.size()); pointsend.push_back(Geom::Point(0, prev[Geom::Y])); pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), start[Geom::Y])); - pointsend.push_back(Geom::Point(out.size(), end[Geom::Y])); - this->points.push_back(Geom::Point(start[Geom::X], start[Geom::Y])); } else { double pressure_base = pow(this->pressure, pressure_sensibility); double pressure_shirnked = (pressure_base * (max - min)) + min; double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; - Geom::Point pos = pressure_pv[0].pointAt(end[Geom::X]); - out = pressure_pv[0].portion(start[Geom::X], pressure_pv[0].size()); - pointsend.push_back(Geom::Point(0, start[Geom::Y])); - pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), end[Geom::Y])); + Geom::Point pos = out.pointAt(start[Geom::X]); + out = out.portion(prev[Geom::X], out.size()); + pointsend.push_back(Geom::Point(0, prev[Geom::Y])); + pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), start[Geom::Y])); pointsend.push_back(Geom::Point(out.size(), pressure_computed)); } - _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); pointsend.clear(); } - tmp_points.clear(); } void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { @@ -874,7 +895,7 @@ void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { this->wps.push_back(this->pressure); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (input_has_pressure) { - this->addPowerStrokePencil(); + this->addPowerStrokePencil(NULL); } } } @@ -941,9 +962,9 @@ void PencilTool::_interpolate() { this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); } } - - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->green_curve); - + 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()); @@ -1034,8 +1055,9 @@ void PencilTool::_sketchInterpolate() { this->green_curve->reset(); this->green_curve->set_pathvector(Geom::path_from_piecewise(this->sketch_interpolation, 0.01)); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->green_curve); - + 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()); @@ -1092,7 +1114,9 @@ void PencilTool::_fitAndSplit() { }else{ this->red_curve->curveto(b[1], b[2], b[3]); } - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + if (!input_has_pressure) { + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + } this->red_curve_is_valid = true; } else { /* Fit and draw and copy last point */ diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index c3b9fb48c..6c98d303c 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -48,10 +48,8 @@ public: std::vector ps; std::vector points; std::vector wps; - - Geom::PathVector pressure_pv; - void addPowerStrokePencil(); + void addPowerStrokePencil(SPCurve * c); void removePowerStrokePreview(); Geom::Piecewise > sketch_interpolation; // the current proposal from the sketched paths unsigned sketch_n; // number of sketches done -- cgit v1.2.3 From 28827f1a9cca56af08fafb205bd3f2d739f28f05 Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Tue, 14 Nov 2017 04:07:28 +0100 Subject: Some improvements to redraw --- src/ui/tools/pencil-tool.cpp | 75 +++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index fe02258c7..d31e76986 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -652,7 +652,7 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points Inkscape::XML::Node *previewend = NULL; if (_powerpreview) { Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - lpe->getRepr()->setAttribute("interpolator_type" , "SpiroInterpolator"); + lpe->getRepr()->setAttribute("interpolator_type" , "CubicBezierSmooth"); lpe->getRepr()->setAttribute("linejoin_type", "round"); static_cast(lpe)->offset_points.param_set_and_write_new_value(points); gchar * path_str = sp_svg_write_path(path); @@ -660,11 +660,7 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points g_free(path_str); if (write) { SPCurve * curve = SP_SHAPE(_powerpreview)->getCurve(); - if (_powerpreviewtail) { - SPCurve * previous_curve = SP_SHAPE(_powerpreviewtail)->getCurve(); - curve->append(previous_curve, false); - previous_curve->unref(); - } else { + if (!_powerpreviewtail) { Inkscape::XML::Node *preview = NULL; preview = xml_doc->createElement("svg:path"); preview->setAttribute("sodipodi:insensitive", "true"); @@ -675,7 +671,9 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points preview->setAttribute("style", css_str.c_str()); Inkscape::GC::release(preview); } - SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); + if (!curve->is_empty()) { + SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); + } curve->unref(); } } else { @@ -692,6 +690,8 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points const gchar * fill = sp_repr_css_property(css, "fill", "none"); if (!strcmp(fill, "none")) { fill = "#000000"; + } + if (!strcmp(stroke, "none")) { stroke = "#000000"; } sp_repr_css_set_property (css, "fill", stroke); @@ -701,7 +701,10 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points previewend->setAttribute("style", css_str.c_str()); Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), _powerpreview); Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); + LPEPowerStroke* ps = static_cast(lpe); + if (ps) { + ps->offset_points.param_set_and_write_new_value(points); + } } } @@ -734,13 +737,13 @@ PencilTool::addPowerStrokePencil(SPCurve * c) SPCurve * curve; if(!c) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - SPCurve * previous_red = red_curve->copy(); - SPCurve * previous_green = green_curve->copy(); //Simplify a bit the base curve to avoid artifacts double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); _interpolate(); prefs->setDouble("/tools/freehand/pencil/tolerance", tol); + SPCurve * previous_red = red_curve->copy(); + SPCurve * previous_green = green_curve->copy(); live = true; if (sa) { curve = sa->curve; @@ -810,16 +813,16 @@ PencilTool::addPowerStrokePencil(SPCurve * c) bool start = true; auto pressure = this->wps.begin(); bool buttonrelease = false; + bool lastsegmentpressure = 0.0; for (auto point = this->ps.begin(); point != this->ps.end(); ++point,++pressure) { //Maybe the 12 POW can be moved to a preferences 12 give a good results of sensibility on my tablet //But maybe is a tweakable value. less number = less sensibility //8 give an aceptable max witht to powerstoke double pressure_base = pow(*pressure, pressure_sensibility); - double pressure_shirnked = (pressure_base * (max - min)) + min; - double pressure_factor = pressure_base/pressure_shirnked; - double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; - if (pressure_computed < 0.05 && previous_pressure != 0.0 ) { - pressure_computed = previous_pressure; + double pressure_shrunk = (pressure_base * (max - min)) + min; + double pressure_factor = pressure_base/pressure_shrunk; + double pressure_computed = (pressure_shrunk * 8.0 * scale) / zoom; + if (this->wps[this->wps.size()-1] < 0.5) { buttonrelease = true; } if (start || std::abs(previous_pressure - pressure_computed) > gap_pressure / (zoom * pressure_factor)) { @@ -835,6 +838,10 @@ PencilTool::addPowerStrokePencil(SPCurve * c) continue; } previous_point = *point; + if (pathvector[0].size()-1 < pos) { + this->points.push_back(Geom::Point(pos, previous_pressure)); + break; + } previous_pressure = pressure_computed; start = false; this->points.push_back(Geom::Point(pos, pressure_computed)); @@ -863,18 +870,14 @@ PencilTool::addPowerStrokePencil(SPCurve * c) pointsend.push_back(this->points[0]); pointsend.push_back(this->points[1]); } else if (write) { - Geom::Point pos = out.pointAt(start[Geom::X]); - out = out.portion(prev[Geom::X], out.size()); - pointsend.push_back(Geom::Point(0, prev[Geom::Y])); - pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), start[Geom::Y])); + pointsend = this->points; } else { double pressure_base = pow(this->pressure, pressure_sensibility); - double pressure_shirnked = (pressure_base * (max - min)) + min; - double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; + double pressure_shrunk = (pressure_base * (max - min)) + min; + double pressure_computed = (pressure_shrunk * 8.0 * scale) / zoom; Geom::Point pos = out.pointAt(start[Geom::X]); - out = out.portion(prev[Geom::X], out.size()); - pointsend.push_back(Geom::Point(0, prev[Geom::Y])); - pointsend.push_back(Geom::Point(Geom::nearest_time(pos, out), start[Geom::Y])); + out = out.portion(start[Geom::X], out.size()); + pointsend.push_back(Geom::Point(0, start[Geom::Y])); pointsend.push_back(Geom::Point(out.size(), pressure_computed)); } _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); @@ -910,7 +913,7 @@ void PencilTool::_interpolate() { if ( this->ps.size() <= 1 ) { return; } - + using Geom::X; using Geom::Y; @@ -923,8 +926,19 @@ void PencilTool::_interpolate() { tol = std::min(tol,tol2); } //To avoid artifacts on pressure power - if (input_has_pressure && tol < 18.0 * 0.4) { - tol = 18.0 * 0.4; + if (input_has_pressure) { + if (tol < 18.0 * 0.4) { + tol = 18.0 * 0.4; + } + size_t i = this->ps.size(); + while (i > 3 && + (this->wps[this->wps.size()-1] < 0.5 || + Geom::distance(this->ps[this->ps.size()-1], this->ps[this->ps.size()-2]) < Geom::distance(this->ps[this->ps.size()-2], this->ps[this->ps.size()-3])/2.0) ) + { + this->ps.pop_back(); + this->wps.pop_back(); + i--; + } } double tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2); @@ -959,12 +973,17 @@ void PencilTool::_interpolate() { 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{ - this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); + 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 { + this->green_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()); -- cgit v1.2.3 From 6abbceb6ea91e40535edc0cdabbb406f1bc31e6d Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Tue, 14 Nov 2017 19:40:06 +0100 Subject: Some improvements to redraw --- src/ui/tools/pencil-tool.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index d31e76986..003119fa8 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -673,6 +673,8 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points } if (!curve->is_empty()) { SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); + } else { + addPowerStrokePencil(NULL); } curve->unref(); } @@ -932,8 +934,8 @@ void PencilTool::_interpolate() { } size_t i = this->ps.size(); while (i > 3 && - (this->wps[this->wps.size()-1] < 0.5 || - Geom::distance(this->ps[this->ps.size()-1], this->ps[this->ps.size()-2]) < Geom::distance(this->ps[this->ps.size()-2], this->ps[this->ps.size()-3])/2.0) ) + (this->wps[i-1] < 0.5 || + Geom::distance(this->ps[i-1], this->ps[i-2]) < Geom::distance(this->ps[i-2], this->ps[i-3])/2.0) ) { this->ps.pop_back(); this->wps.pop_back(); -- cgit v1.2.3 From 4c790c63b7918e2745420c082bfa13e69cec74bd Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 19 Nov 2017 02:47:56 +0100 Subject: Working on fix smooth draw --- .../parameter/powerstrokepointarray.cpp | 15 ++ src/live_effects/parameter/powerstrokepointarray.h | 1 + src/ui/dialog/inkscape-preferences.cpp | 21 +- src/ui/dialog/inkscape-preferences.h | 3 +- src/ui/tools/freehand-base.cpp | 3 + src/ui/tools/pencil-tool.cpp | 225 +++++++++++---------- src/ui/tools/pencil-tool.h | 3 +- 7 files changed, 146 insertions(+), 125 deletions(-) (limited to 'src') diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index bc06e42ca..7e2da4cfd 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -89,6 +89,21 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi } } +/** call this method to recalculate the controlpoints when path is reversed.*/ +void +PowerStrokePointArrayParam::reverse_controlpoints() +{ + if (!last_pwd2.empty()) { + Geom::Piecewise > const & pwd2_in_reverse = reverse(last_pwd2); + for (unsigned int i = 0; i < _vector.size(); ++i) { + Geom::Point control_pos = last_pwd2.valueAt(_vector[i][Geom::X]); + double new_pos = Geom::nearest_time(control_pos, pwd2_in_reverse); + _vector[i][Geom::X] = new_pos; + } + write_to_SVG(); + } +} + float PowerStrokePointArrayParam::median_width() { size_t size = _vector.size(); diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h index 621d3b969..b8e63e5f7 100644 --- a/src/live_effects/parameter/powerstrokepointarray.h +++ b/src/live_effects/parameter/powerstrokepointarray.h @@ -47,6 +47,7 @@ public: Geom::Piecewise > const & get_pwd2_normal() const { return last_pwd2_normal; } void recalculate_controlpoints_for_new_pwd2(Geom::Piecewise > const & pwd2_in); + void reverse_controlpoints(); void set_scale_width(double scale_width){_scale_width = scale_width;}; double _scale_width; friend class PowerStrokePointArrayParamKnotHolderEntity; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 665b9643c..93168a727 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -204,30 +204,22 @@ void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring false ); } -void InkscapePreferences::AddPressureSensitivity(DialogPage &p, Glib::ustring const &prefs_path, double def_value) -{ - PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); - sb->init ( prefs_path + "/pressure-sensitivity", 1, 100.0, 1.0, 10.0, def_value, true, false); - p.add_line( false, _("Pressure sensitivity:"), *sb, _("on tablet usage"), - _("Pressure sensitivity, 12 is the default value"), - false ); -} void InkscapePreferences::AddPowerStrokeKnotDistanceFactor(DialogPage &p, Glib::ustring const &prefs_path, double def_value) { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); sb->init ( prefs_path + "/knots-distance", 0.1, 9999.0, 1.0, 10.0, def_value, false, false); p.add_line( false, _("Minimum knot distance factor:"), *sb, _("on tablet usage"), - _("This unitless value affects the density of LPE knots. The default value is 135. A small value allows you to draw squiggly lines, while a higher value creates smoother lines with less width differences."), + _("This unitless value affects the density of LPE knots. A small value allows you to draw squiggly lines, while a higher value creates smoother lines with less width differences."), false ); } -void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::ustring const &prefs_path, double def_value) +void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::ustring const &prefs_path, gint def_value) { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); - sb->init ( prefs_path + "/gap-pressure", 0.01, 9999.0, 1.0, 10.0, def_value, false, false); - p.add_line( false, _("Pressure change needed to create knot:"), *sb, _("on tablet usage"), - _("Difference in pressure that is required to create a new PowerStroke knot. This factor is one of several that are taken into account when deciding where to place a knot. The default value is 1."), + sb->init ( prefs_path + "/gap-pressure", 1, 100, 1, 10, def_value, true, false); + p.add_line( false, _("Minimun pressure % diference needed to create knot:"), *sb, _("on tablet usage"), + _("Minimun percent difference in pressure that is required to create a new PowerStroke knot."), false ); } @@ -450,9 +442,8 @@ void InkscapePreferences::initPageTools() this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0); this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0); _page_pencil.add_group_header( _("Pressure sensitivity settings")); - this->AddPressureSensitivity(_page_pencil, "/tools/freehand/pencil", 12.0); this->AddPowerStrokeKnotDistanceFactor(_page_pencil, "/tools/freehand/pencil", 135.0); - this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 1.0); + this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 10); _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 c6a9c5af6..bd9168913 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -500,9 +500,8 @@ protected: static void AddFirstAndLastCheckbox(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, bool def_value); 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 AddPressureSensitivity(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); static void AddPowerStrokeKnotDistanceFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); - static void AddPowerStrokeGapPressureFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); + static void AddPowerStrokeGapPressureFactor(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 bff24346a..ae91f1f8b 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -258,6 +258,9 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha } Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + lpe->getRepr()->setAttribute("sort_points", "true"); + lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + lpe->getRepr()->setAttribute("linejoin_type", "round"); static_cast(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 003119fa8..c2c3d571f 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -74,6 +74,7 @@ PencilTool::PencilTool() , sketch_n(0) , _powerpreviewtail(NULL) , _powerpreview(NULL) + , _second_chance_preview(false) { } @@ -156,9 +157,8 @@ bool PencilTool::root_handler(GdkEvent* event) { default: break; } - if (!ret) { - ret = FreehandBase::root_handler(event); + ret = FreehandBase::root_handler(event); } return ret; @@ -192,7 +192,6 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { pencil_drag_origin_w = Geom::Point(bevent.x,bevent.y); pencil_within_tolerance = true; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (input_has_pressure) { this->state = SP_PENCIL_CONTEXT_FREEHAND; } @@ -204,9 +203,8 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { default: /* Set first point of sequence */ SnapManager &m = desktop->namedview->snap_manager; - if (bevent.state & GDK_CONTROL_MASK) { - m.setup(desktop, true, _powerpreviewtail); + m.setup(desktop, true); if (!(bevent.state & GDK_SHIFT_MASK)) { m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } @@ -220,7 +218,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { this->overwrite_curve = anchor->curve; desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); } else { - m.setup(desktop, true, _powerpreviewtail); + m.setup(desktop, true); 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 @@ -319,7 +317,6 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { if (anchor) { p = anchor->dp; } - if ( this->npoints != 0) { // buttonpress may have happened before we entered draw context! if (this->ps.empty()) { // Only in freehand mode we have to add the first point also to this->ps (apparently) @@ -332,7 +329,6 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { this->_addFreehandPoint(p, mevent.state); ret = true; } - if (anchor && !this->anchor_statusbar) { this->message_context->set(Inkscape::NORMAL_MESSAGE, _("Release here to close and finish the path.")); this->anchor_statusbar = true; @@ -356,9 +352,9 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { // Show the pre-snap indicator to communicate to the user where we would snap to if he/she were to // a) press the mousebutton to start a freehand drawing, or // b) release the mousebutton to finish a freehand drawing - if (!this->sp_event_context_knot_mouseover()) { + if (!this->sp_event_context_knot_mouseover() && !input_has_pressure) { SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop, true, _powerpreviewtail); + m.setup(desktop, true); m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); m.unSetup(); } @@ -418,7 +414,6 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { p = anchor->dp; } else { Geom::Point p_end = p; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (!input_has_pressure) { this->_endpointSnap(p_end, revent.state); if (p_end != p) { @@ -649,44 +644,63 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points return; } Inkscape::XML::Document *xml_doc = document->getReprDoc(); - Inkscape::XML::Node *previewend = NULL; if (_powerpreview) { - Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - lpe->getRepr()->setAttribute("interpolator_type" , "CubicBezierSmooth"); + SPLPEItem * lpeitem = dynamic_cast(_powerpreview); + if (!lpeitem) { + return; + } + Effect* lpe = lpeitem->getCurrentLPE(); + lpeitem->path_effects_enabled = 0; + lpe->getRepr()->setAttribute("interpolator_type" , "CentripetalCatmullRom"); lpe->getRepr()->setAttribute("linejoin_type", "round"); - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); + lpe->getRepr()->setAttribute("end_linecap_type", end_linecap_type); gchar * path_str = sp_svg_write_path(path); _powerpreview->setAttribute("inkscape:original-d" , path_str); g_free(path_str); + lpeitem->path_effects_enabled = 1; + LPEPowerStroke * ps = static_cast(lpe); + if (ps) { + ps->offset_points.param_set_and_write_new_value(points); + } if (write) { + lpe->getRepr()->setAttribute("end_linecap_type", "round"); SPCurve * curve = SP_SHAPE(_powerpreview)->getCurve(); + //SPCurve * curve_before = SP_SHAPE(_powerpreview)->getCurveBeforeLPE(); if (!_powerpreviewtail) { - Inkscape::XML::Node *preview = NULL; - preview = xml_doc->createElement("svg:path"); - preview->setAttribute("sodipodi:insensitive", "true"); - _powerpreviewtail = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(preview)); + Inkscape::XML::Node *tail = NULL; + tail = xml_doc->createElement("svg:path"); + tail->setAttribute("sodipodi:insensitive", "true"); + _powerpreviewtail = SP_ITEM(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); - preview->setAttribute("style", css_str.c_str()); - Inkscape::GC::release(preview); + tail->setAttribute("style", css_str.c_str()); + Inkscape::GC::release(tail); } - if (!curve->is_empty()) { + // if (curve != curve_before) { SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); - } else { - addPowerStrokePencil(NULL); - } + //second_chance_preview = false; +// } else if (!_second_chance_preview) { +// Try to reload if the process go bad +// _second_chance_preview = true; +// points_parsed--; +// addPowerStrokePencil(NULL); +// } else { +// _second_chance_preview = false; + //} curve->unref(); + //curve_before->unref(); } } else { - previewend = xml_doc->createElement("svg:path"); - previewend->setAttribute("sodipodi:insensitive", "true"); - sp_desktop_apply_style_tool(desktop, previewend, Glib::ustring("/tools/freehand/pencil").data(), false); - _powerpreview = SP_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(previewend)); + 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_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(body)); gchar * path_str = sp_svg_write_path(path); - previewend->setAttribute("d" , path_str); + body->setAttribute("d" , path_str); g_free(path_str); - Inkscape::GC::release(previewend); + 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"); const gchar * fill = sp_repr_css_property(css, "fill", "none"); @@ -700,13 +714,14 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points sp_repr_css_set_property (css, "stroke", fill); Glib::ustring css_str; sp_repr_css_write_string(css,css_str); - previewend->setAttribute("style", css_str.c_str()); + body->setAttribute("style", css_str.c_str()); Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), _powerpreview); Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); LPEPowerStroke* ps = static_cast(lpe); if (ps) { ps->offset_points.param_set_and_write_new_value(points); } + end_linecap_type = body->attribute("end_linecap_type"); } } @@ -733,22 +748,34 @@ PencilTool::removePowerStrokePreview() void PencilTool::addPowerStrokePencil(SPCurve * c) { - this->points.clear(); using namespace Inkscape::LivePathEffect; + this->points.clear(); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool live = false; SPCurve * curve; if(!c) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + live = true; //Simplify a bit the base curve to avoid artifacts + SPCurve * previous_red = red_curve->copy(); + SPCurve * previous_green = green_curve->copy(); double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); _interpolate(); prefs->setDouble("/tools/freehand/pencil/tolerance", tol); - SPCurve * previous_red = red_curve->copy(); - SPCurve * previous_green = green_curve->copy(); - live = true; if (sa) { curve = sa->curve; + if (sa->start) { + curve = curve->create_reverse(); + } + Effect* lpe = SP_LPE_ITEM(white_item)->getCurrentLPE(); + LPEPowerStroke* ps = static_cast(lpe); + if (ps) { + if (sa->start) { + ps->offset_points.reverse_controlpoints(); + } + this->points = ps->offset_points.data(); + } + if (!green_curve->is_empty()) { curve->append_continuous( green_curve, 0.0625); if (!red_curve->is_empty()) { @@ -773,79 +800,79 @@ PencilTool::addPowerStrokePencil(SPCurve * c) curve = c->copy(); removePowerStrokePreview(); } - SPObject *elemref = NULL; SPDocument * document = SP_ACTIVE_DOCUMENT; if (!document) { return; } - const char * item_id = "___pencil_preview_powerstroke___"; - if ((elemref = document->getObjectById(item_id))) { - if (!live) { - LivePathEffectObject * lpeobj = SP_LPE_ITEM(elemref)->getCurrentLPE()->getLPEObj(); - SP_OBJECT(lpeobj)->deleteObject(); - lpeobj = NULL; - elemref->deleteObject(); - elemref = NULL; - } else { - _powerpreview = SP_ITEM(elemref); - } - } if (!curve) { return; } Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); Geom::Coord scale = transformCoordinate.expansionX(); Geom::PathVector pathvector = curve->get_pathvector(); + curve->unref(); //pathvector *= transformCoordinate.inverse(); - double zoom = SP_EVENT_CONTEXT(this)->desktop->current_zoom() * 5.0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); 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; if (min > max){ min = max; } - double gap_pressure = prefs->getDoubleLimited("/tools/freehand/pencil/gap-pressure",1.0, 0.1, 9999.0); - double knots_distance = prefs->getDoubleLimited("/tools/freehand/pencil/knots-distance",1.0, 0.1, 9999.0); - gint pressure_sensibility = prefs->getIntLimited("/tools/freehand/pencil/pressure-sensibility",12, 1, 100); - - + double gap_pressure = prefs->getIntLimited("/tools/freehand/pencil/gap-pressure",10, 1, 100)/100.0; + //TODO: shrunk pressure sensitive over 0.5, less means release pen input. Need to check in more hardware + //gap_pressure = (gap_pressure * 0.5) + 0.5; + double knots_distance = prefs->getDoubleLimited("/tools/freehand/pencil/knots-distance",135.0, 0.1, 9999.0); double previous_pressure = 0.0; - double tol = knots_distance / zoom; + double previous_pressure_computed = 0.0; + double dezoomify_factor = 0.05 * 1000/SP_EVENT_CONTEXT(this)->desktop->current_zoom();//\/100 we want 100% = 1; + double tol = knots_distance * dezoomify_factor/100.0; Geom::Point previous_point = Geom::Point(0,0); bool start = true; auto pressure = this->wps.begin(); bool buttonrelease = false; bool lastsegmentpressure = 0.0; - for (auto point = this->ps.begin(); point != this->ps.end(); ++point,++pressure) { - //Maybe the 12 POW can be moved to a preferences 12 give a good results of sensibility on my tablet - //But maybe is a tweakable value. less number = less sensibility - //8 give an aceptable max witht to powerstoke - double pressure_base = pow(*pressure, pressure_sensibility); - double pressure_shrunk = (pressure_base * (max - min)) + min; - double pressure_factor = pressure_base/pressure_shrunk; - double pressure_computed = (pressure_shrunk * 8.0 * scale) / zoom; - if (this->wps[this->wps.size()-1] < 0.5) { - buttonrelease = true; + double pressure_computed = 0.0; + size_t counter = 0; + for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) { +// //TODO: shrunk pressure sensitive over 0.5, less means release pen input. Need to check in more hardware +// if (this->wps[this->wps.size()-1] < 0.01) { +// // break; +// } + counter++; + //remove start/end pressure gap + if (counter < 4 || this->ps.size()-4 < counter) { + continue; } - if (start || std::abs(previous_pressure - pressure_computed) > gap_pressure / (zoom * pressure_factor)) { + + double pressure_shrunk = (*pressure * (max - min)) + min; + //We need half width for power stroke + pressure_computed = pressure_shrunk * dezoomify_factor/2.0; + std::cout << *pressure << "pressure" << std::endl; + std::cout << pressure_computed << "pressure_computed" << std::endl; + if (start) { + start = false; + previous_point = *point; + previous_pressure = *pressure; + previous_pressure_computed = pressure_computed; + this->points.push_back(Geom::Point(0.0, pressure_computed)); + } else if (std::abs(previous_pressure - *pressure) > gap_pressure/100.0 ) { Geom::Point position = *point; if (!live) { position *= transformCoordinate.inverse(); } double pos = Geom::nearest_time(position, pathvector[0]); - if (pos > 1e6 || - buttonrelease || - (previous_point != Geom::Point(0,0) && Geom::distance(*point, previous_point) < tol)) + std::cout << tol << "tol" << std::endl; + if (pos > 1e6 || Geom::distance(*point, previous_point) < tol) { + std::cout << Geom::distance(*point, previous_point) << "Geom::distance(*point, previous_point)" << std::endl; continue; } +// if (pathvector[0].size()-1 < pos) { +// this->points.push_back(Geom::Point(pos, previous_pressure_computed)); +// break; +// } previous_point = *point; - if (pathvector[0].size()-1 < pos) { - this->points.push_back(Geom::Point(pos, previous_pressure)); - break; - } - previous_pressure = pressure_computed; - start = false; + previous_pressure = *pressure; + previous_pressure_computed = pressure_computed; this->points.push_back(Geom::Point(pos, pressure_computed)); } } @@ -856,33 +883,19 @@ PencilTool::addPowerStrokePencil(SPCurve * c) write = true; } std::vector pointsend; - Geom::Point prev = Geom::Point(0,0); - if (this->points.size() > 2) { - prev = this->points[this->points.size() - 3]; - } - Geom::Point start = Geom::Point(0,0); - if (this->points.size() > 1) { - start = this->points[this->points.size() - 2]; - } - Geom::Point end = this->points[this->points.size()-1]; Geom::Path out = pathvector[0]; if (this->points.size() == 1) { - pointsend.push_back(this->points[0]); - } else if (this->points.size() == 2) { - pointsend.push_back(this->points[0]); - pointsend.push_back(this->points[1]); + _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, false); } else if (write) { - pointsend = this->points; + _powerStrokePreview(out * transformCoordinate.inverse(), this->points, write); } else { - double pressure_base = pow(this->pressure, pressure_sensibility); - double pressure_shrunk = (pressure_base * (max - min)) + min; - double pressure_computed = (pressure_shrunk * 8.0 * scale) / zoom; - Geom::Point pos = out.pointAt(start[Geom::X]); + Geom::Point start = this->points[this->points.size()-2]; + Geom::Point end = this->points[this->points.size()-1]; out = out.portion(start[Geom::X], out.size()); pointsend.push_back(Geom::Point(0, start[Geom::Y])); - pointsend.push_back(Geom::Point(out.size(), pressure_computed)); + pointsend.push_back(Geom::Point(out.size(), end[Geom::Y])); + _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); } - _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); pointsend.clear(); } } @@ -898,7 +911,6 @@ void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { this->_fitAndSplit(); this->ps.push_back(p); this->wps.push_back(this->pressure); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (input_has_pressure) { this->addPowerStrokePencil(NULL); } @@ -933,14 +945,13 @@ void PencilTool::_interpolate() { tol = 18.0 * 0.4; } size_t i = this->ps.size(); - while (i > 3 && - (this->wps[i-1] < 0.5 || - Geom::distance(this->ps[i-1], this->ps[i-2]) < Geom::distance(this->ps[i-2], this->ps[i-3])/2.0) ) - { - this->ps.pop_back(); - this->wps.pop_back(); - i--; - } +// while (i > 3 && +// (this->wps[i-1] < 0.5 || Geom::distance(this->ps[i-1], this->ps[i-2]) < Geom::distance(this->ps[i-2], this->ps[i-3])/2.0)) +// { +// this->ps.pop_back(); +// this->wps.pop_back(); +// i--; +// } } double tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2); diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 6c98d303c..3e1332501 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -73,7 +73,8 @@ private: void _powerStrokePreview(Geom::Path path, std::vector points, bool write); SPItem *_powerpreview; SPItem *_powerpreviewtail; - + bool _second_chance_preview; + gchar const * end_linecap_type; void _setEndpoint(Geom::Point const &p); void _finishEndpoint(); void _addFreehandPoint(Geom::Point const &p, guint state); -- cgit v1.2.3 From d91112f7116f50489bd93047f6e7bc0985512c68 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 19 Nov 2017 23:54:21 +0100 Subject: Working on coontinuing paths --- src/live_effects/lpe-powerstroke.cpp | 2 +- .../parameter/powerstrokepointarray.cpp | 16 +- src/live_effects/parameter/powerstrokepointarray.h | 2 +- src/ui/dialog/inkscape-preferences.cpp | 15 +- src/ui/dialog/inkscape-preferences.h | 1 - src/ui/tools/freehand-base.cpp | 24 +- src/ui/tools/pencil-tool.cpp | 294 ++++++++++----------- src/ui/tools/pencil-tool.h | 10 +- 8 files changed, 173 insertions(+), 191 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 7c060a965..e9943af4c 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -645,7 +645,7 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } LineJoinType jointype = static_cast(linejoin_type.get_value()); - Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); + Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise > 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); diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index 7e2da4cfd..289498498 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -90,18 +90,28 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi } /** call this method to recalculate the controlpoints when path is reversed.*/ -void -PowerStrokePointArrayParam::reverse_controlpoints() +std::vector +PowerStrokePointArrayParam::reverse_controlpoints(bool write) { + std::vector controlpoints; if (!last_pwd2.empty()) { Geom::Piecewise > const & pwd2_in_reverse = reverse(last_pwd2); for (unsigned int i = 0; i < _vector.size(); ++i) { Geom::Point control_pos = last_pwd2.valueAt(_vector[i][Geom::X]); double new_pos = Geom::nearest_time(control_pos, pwd2_in_reverse); + controlpoints.push_back(Geom::Point(new_pos,_vector[i][Geom::Y])); _vector[i][Geom::X] = new_pos; } - write_to_SVG(); + if (write) { + write_to_SVG(); + _vector.clear(); + _vector = controlpoints; + controlpoints.clear(); + write_to_SVG(); + return _vector; + } } + return controlpoints; } float PowerStrokePointArrayParam::median_width() diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h index b8e63e5f7..a1d4db3ca 100644 --- a/src/live_effects/parameter/powerstrokepointarray.h +++ b/src/live_effects/parameter/powerstrokepointarray.h @@ -47,7 +47,7 @@ public: Geom::Piecewise > const & get_pwd2_normal() const { return last_pwd2_normal; } void recalculate_controlpoints_for_new_pwd2(Geom::Piecewise > const & pwd2_in); - void reverse_controlpoints(); + std::vector reverse_controlpoints(bool write); void set_scale_width(double scale_width){_scale_width = scale_width;}; double _scale_width; friend class PowerStrokePointArrayParamKnotHolderEntity; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 93168a727..ef69fdade 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -204,22 +204,12 @@ void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring false ); } - -void InkscapePreferences::AddPowerStrokeKnotDistanceFactor(DialogPage &p, Glib::ustring const &prefs_path, double def_value) -{ - PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); - sb->init ( prefs_path + "/knots-distance", 0.1, 9999.0, 1.0, 10.0, def_value, false, false); - p.add_line( false, _("Minimum knot distance factor:"), *sb, _("on tablet usage"), - _("This unitless value affects the density of LPE knots. A small value allows you to draw squiggly lines, while a higher value creates smoother lines with less width differences."), - false ); -} - void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::ustring const &prefs_path, gint def_value) { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); sb->init ( prefs_path + "/gap-pressure", 1, 100, 1, 10, def_value, true, false); - p.add_line( false, _("Minimun pressure % diference needed to create knot:"), *sb, _("on tablet usage"), - _("Minimun percent difference in pressure that is required to create a new PowerStroke knot."), + p.add_line( false, _("Pressure diference on knots:"), *sb, _("%"), + _("Pressure diference percent that is required to create a new PowerStroke knot."), false ); } @@ -442,7 +432,6 @@ void InkscapePreferences::initPageTools() this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0); this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0); _page_pencil.add_group_header( _("Pressure sensitivity settings")); - this->AddPowerStrokeKnotDistanceFactor(_page_pencil, "/tools/freehand/pencil", 135.0); this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 10); _page_pencil.add_group_header( _("Sketch mode")); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index bd9168913..0f78a49a4 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -500,7 +500,6 @@ protected: static void AddFirstAndLastCheckbox(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, bool def_value); 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 AddPowerStrokeKnotDistanceFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, double def_value); static void AddPowerStrokeGapPressureFactor(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); diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index ae91f1f8b..0d539b0f3 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -242,24 +242,24 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha } pt->addPowerStrokePencil(c); } - double zoom = SP_EVENT_CONTEXT(dc)->desktop->current_zoom() * 5.0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - 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; - if (min > max){ - min = max; - } - Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); - Geom::Coord scale = transformCoordinate.expansionX(); - double pressure_shirnked = (1.0 * (max - min)) + min; - double pressure_computed = (pressure_shirnked * 8.0 * scale) / zoom; if(pt->points.empty()){ - pt->points.push_back(Geom::Point(0, pressure_computed)); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); + const char *stroke_width = sp_repr_css_property(css_item, "stroke-width", "0"); + double swidth; + sp_svg_number_read_d(stroke_width, &swidth); + swidth = prefs->getDouble("/live_effect/power_stroke/width", swidth/2); + if (!swidth) { + swidth = swidth/2; + } + pt->points.push_back(Geom::Point(0, swidth)); } Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); lpe->getRepr()->setAttribute("sort_points", "true"); lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); + lpe->getRepr()->setAttribute("miter_limit", "4"); lpe->getRepr()->setAttribute("linejoin_type", "round"); static_cast(lpe)->offset_points.param_set_and_write_new_value(pt->points); pt->points.clear(); diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index c2c3d571f..478ba61f6 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -74,7 +74,7 @@ PencilTool::PencilTool() , sketch_n(0) , _powerpreviewtail(NULL) , _powerpreview(NULL) - , _second_chance_preview(false) + , _previewok(true) { } @@ -97,16 +97,10 @@ PencilTool::~PencilTool() { void PencilTool::_extinput(GdkEvent *event) { if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &this->pressure)) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (prefs->getBool("/tools/freehand/pencil/pressure", true)) { - this->pressure = CLAMP (this->pressure, DDC_MIN_PRESSURE, DDC_MAX_PRESSURE); - input_has_pressure = true; - } else { - this->pressure = DDC_DEFAULT_PRESSURE; - input_has_pressure = false; - } + this->pressure = CLAMP (this->pressure, DDC_MIN_PRESSURE, DDC_MAX_PRESSURE); } else { this->pressure = DDC_DEFAULT_PRESSURE; + //If no pressure device ignore pressure button input_has_pressure = false; } } @@ -192,6 +186,8 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { pencil_drag_origin_w = Geom::Point(bevent.x,bevent.y); pencil_within_tolerance = true; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + input_has_pressure = prefs->getBool("/tools/freehand/pencil/pressure", true); if (input_has_pressure) { this->state = SP_PENCIL_CONTEXT_FREEHAND; } @@ -225,7 +221,9 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { // anchor, which is handled by the sibling branch above) selection->clear(); desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new path")); - m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); + if (!input_has_pressure) { + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); + } } else if (selection->singleItem() && SP_IS_PATH(selection->singleItem())) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Appending to selected path")); m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); @@ -271,7 +269,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { Geom::Point p = desktop->w2d(Geom::Point(mevent.x, mevent.y)); /* Test whether we hit any anchor. */ - SPDrawAnchor *anchor = spdc_test_inside(this, Geom::Point(mevent.x, mevent.y)); + SPDrawAnchor *anchor = spdc_test_inside(this, Geom::Point(mevent.x,mevent.y)); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (pencil_within_tolerance) { gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); @@ -428,6 +426,8 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand")); 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); this->sa = NULL; this->ea = NULL; @@ -636,7 +636,7 @@ void PencilTool::_finishEndpoint() { } void -PencilTool::_powerStrokePreview(Geom::Path path, std::vector points, bool write) +PencilTool::_powerStrokePreview(Geom::Path const path, std::vector points, bool write) { using namespace Inkscape::LivePathEffect; SPDocument * document = SP_ACTIVE_DOCUMENT; @@ -644,62 +644,15 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points return; } Inkscape::XML::Document *xml_doc = document->getReprDoc(); - if (_powerpreview) { - SPLPEItem * lpeitem = dynamic_cast(_powerpreview); - if (!lpeitem) { - return; - } - Effect* lpe = lpeitem->getCurrentLPE(); - lpeitem->path_effects_enabled = 0; - lpe->getRepr()->setAttribute("interpolator_type" , "CentripetalCatmullRom"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); - lpe->getRepr()->setAttribute("end_linecap_type", end_linecap_type); - gchar * path_str = sp_svg_write_path(path); - _powerpreview->setAttribute("inkscape:original-d" , path_str); - g_free(path_str); - lpeitem->path_effects_enabled = 1; - LPEPowerStroke * ps = static_cast(lpe); - if (ps) { - ps->offset_points.param_set_and_write_new_value(points); - } - if (write) { - lpe->getRepr()->setAttribute("end_linecap_type", "round"); - SPCurve * curve = SP_SHAPE(_powerpreview)->getCurve(); - //SPCurve * curve_before = SP_SHAPE(_powerpreview)->getCurveBeforeLPE(); - if (!_powerpreviewtail) { - Inkscape::XML::Node *tail = NULL; - tail = xml_doc->createElement("svg:path"); - tail->setAttribute("sodipodi:insensitive", "true"); - _powerpreviewtail = SP_ITEM(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 != curve_before) { - SP_SHAPE(_powerpreviewtail)->setCurve(curve, true); - //second_chance_preview = false; -// } else if (!_second_chance_preview) { -// Try to reload if the process go bad -// _second_chance_preview = true; -// points_parsed--; -// addPowerStrokePencil(NULL); -// } else { -// _second_chance_preview = false; - //} - curve->unref(); - //curve_before->unref(); - } - } else { + SPCurve * curve = new SPCurve(); + Geom::PathVector const pathv(path); + if (!_powerpreview) { 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_ITEM(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(body)); - gchar * path_str = sp_svg_write_path(path); - body->setAttribute("d" , path_str); - g_free(path_str); + _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"); @@ -715,14 +668,56 @@ PencilTool::_powerStrokePreview(Geom::Path path, std::vector points Glib::ustring css_str; sp_repr_css_write_string(css,css_str); body->setAttribute("style", css_str.c_str()); - Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), _powerpreview); - Effect* lpe = SP_LPE_ITEM(_powerpreview)->getCurrentLPE(); - LPEPowerStroke* ps = static_cast(lpe); - if (ps) { - ps->offset_points.param_set_and_write_new_value(points); + gchar * pvector_str = sp_svg_write_path(pathv); + _powerpreview->setAttribute("d" , pvector_str); + g_free(pvector_str); + SPLPEItem * lpeitem = dynamic_cast(_powerpreview); + if (!lpeitem) { + return; } - end_linecap_type = body->attribute("end_linecap_type"); - } + Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(_powerpreview)); + Effect* lpe = lpeitem->getCurrentLPE(); + lpe->getRepr()->setAttribute("sort_points", "true"); + lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); + lpe->getRepr()->setAttribute("miter_limit", "4"); + lpe->getRepr()->setAttribute("linejoin_type", "round"); + end_linecap_type = lpe->getRepr()->attribute("end_linecap_type"); + } + SPLPEItem * lpeitem = dynamic_cast(_powerpreview); + if (!lpeitem) { + return; + } + Effect* lpe = lpeitem->getCurrentLPE(); + lpe->getRepr()->setAttribute("end_linecap_type", end_linecap_type); + static_cast(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); + _previewok = true; + } else {//if (!_second_chance_preview) { + _previewok = false; + } + } + curve->unref(); } void @@ -751,6 +746,13 @@ PencilTool::addPowerStrokePencil(SPCurve * c) using namespace Inkscape::LivePathEffect; this->points.clear(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); + double gap_pressure = prefs->getIntLimited("/tools/freehand/pencil/gap-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; + if (min > max){ + min = max; + } bool live = false; SPCurve * curve; if(!c) { @@ -758,26 +760,38 @@ PencilTool::addPowerStrokePencil(SPCurve * c) //Simplify a bit the base curve to avoid artifacts SPCurve * previous_red = red_curve->copy(); SPCurve * previous_green = green_curve->copy(); - double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); prefs->setDouble("/tools/freehand/pencil/tolerance", 18.0); _interpolate(); prefs->setDouble("/tools/freehand/pencil/tolerance", tol); if (sa) { curve = sa->curve; + if(prefs->getInt("/tools/freehand/pencil/freehand-mode", 0) == 1 || + prefs->getInt("/tools/freehand/pencil/freehand-mode", 0) == 2) + { + curve = overwrite_curve; + } if (sa->start) { - curve = curve->create_reverse(); + SPCurve *ret = curve->create_reverse(); + curve->unref(); + curve = ret->copy(); + ret->unref(); } Effect* lpe = SP_LPE_ITEM(white_item)->getCurrentLPE(); LPEPowerStroke* ps = static_cast(lpe); if (ps) { if (sa->start) { - ps->offset_points.reverse_controlpoints(); + this->points = ps->offset_points.reverse_controlpoints(false); + } else { + this->points = ps->offset_points.data(); } - this->points = ps->offset_points.data(); } - if (!green_curve->is_empty()) { - curve->append_continuous( green_curve, 0.0625); + if (curve->is_empty()) { + curve = green_curve->copy(); + } else { + green_curve->move_endpoints(curve->first_path()->finalPoint(), *green_curve->last_point()); + curve->append_continuous( green_curve, 0.0625); + } if (!red_curve->is_empty()) { curve->append_continuous( red_curve, 0.0625); } @@ -808,95 +822,63 @@ PencilTool::addPowerStrokePencil(SPCurve * c) return; } Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); - Geom::Coord scale = transformCoordinate.expansionX(); Geom::PathVector pathvector = curve->get_pathvector(); + Geom::Path const path = pathvector[0]; curve->unref(); - //pathvector *= transformCoordinate.inverse(); - 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; - if (min > max){ - min = max; - } - double gap_pressure = prefs->getIntLimited("/tools/freehand/pencil/gap-pressure",10, 1, 100)/100.0; - //TODO: shrunk pressure sensitive over 0.5, less means release pen input. Need to check in more hardware - //gap_pressure = (gap_pressure * 0.5) + 0.5; - double knots_distance = prefs->getDoubleLimited("/tools/freehand/pencil/knots-distance",135.0, 0.1, 9999.0); double previous_pressure = 0.0; - double previous_pressure_computed = 0.0; - double dezoomify_factor = 0.05 * 1000/SP_EVENT_CONTEXT(this)->desktop->current_zoom();//\/100 we want 100% = 1; - double tol = knots_distance * dezoomify_factor/100.0; - Geom::Point previous_point = Geom::Point(0,0); - bool start = true; - auto pressure = this->wps.begin(); - bool buttonrelease = false; - bool lastsegmentpressure = 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; + bool start = true; + auto pressure = this->wps.begin(); size_t counter = 0; for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) { -// //TODO: shrunk pressure sensitive over 0.5, less means release pen input. Need to check in more hardware -// if (this->wps[this->wps.size()-1] < 0.01) { -// // break; -// } counter++; - //remove start/end pressure gap - if (counter < 4 || this->ps.size()-4 < counter) { - continue; + //remove end pressure gap + if (this->ps.size() > 2 && this->ps.size()-2 < counter) { + break; } - double pressure_shrunk = (*pressure * (max - min)) + min; - //We need half width for power stroke + //We need half width for power stroke pressure_computed = pressure_shrunk * dezoomify_factor/2.0; - std::cout << *pressure << "pressure" << std::endl; - std::cout << pressure_computed << "pressure_computed" << std::endl; - if (start) { - start = false; - previous_point = *point; - previous_pressure = *pressure; - previous_pressure_computed = pressure_computed; - this->points.push_back(Geom::Point(0.0, pressure_computed)); - } else if (std::abs(previous_pressure - *pressure) > gap_pressure/100.0 ) { - Geom::Point position = *point; - if (!live) { - position *= transformCoordinate.inverse(); - } - double pos = Geom::nearest_time(position, pathvector[0]); - std::cout << tol << "tol" << std::endl; - if (pos > 1e6 || Geom::distance(*point, previous_point) < tol) - { - std::cout << Geom::distance(*point, previous_point) << "Geom::distance(*point, previous_point)" << std::endl; - continue; + //remove start pressure gap + if (counter < 3) { + if (start) { + start = false; + this->points.push_back(Geom::Point(0, pressure_computed)); + } else { + this->points[this->points.size()-1] = Geom::Point(0, pressure_computed); } -// if (pathvector[0].size()-1 < pos) { -// this->points.push_back(Geom::Point(pos, previous_pressure_computed)); -// break; -// } - previous_point = *point; - previous_pressure = *pressure; - previous_pressure_computed = pressure_computed; + previous_pressure = pressure_shrunk; + continue; + } + Geom::Point position = *point; + if (!live) { + position *= transformCoordinate.inverse(); + } + double pos = Geom::nearest_time(position, path); + if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > gap_pressure) { + previous_pressure = pressure_shrunk; this->points.push_back(Geom::Point(pos, pressure_computed)); } } if (live && this->points.size() > 0) { bool write = false; - if (points_parsed != this->points.size()) { + if (points_parsed != this->points.size() || !_previewok) { points_parsed = this->points.size(); write = true; } - std::vector pointsend; - Geom::Path out = pathvector[0]; - if (this->points.size() == 1) { - _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, false); - } else if (write) { - _powerStrokePreview(out * transformCoordinate.inverse(), this->points, write); + if (write) { + _powerStrokePreview(path * transformCoordinate.inverse(), this->points, write); } else { - Geom::Point start = this->points[this->points.size()-2]; - Geom::Point end = this->points[this->points.size()-1]; - out = out.portion(start[Geom::X], out.size()); - pointsend.push_back(Geom::Point(0, start[Geom::Y])); - pointsend.push_back(Geom::Point(out.size(), end[Geom::Y])); - _powerStrokePreview(out * transformCoordinate.inverse(), pointsend, write); + Geom::Point start = this->points[this->points.size()-1]; + if (start[Geom::X] < path.size()) { + std::vector pointsend; + Geom::Path const pathtail = path.portion(start[Geom::X], path.size()); + pointsend.push_back(Geom::Point(0, start[Geom::Y])); + _powerStrokePreview(pathtail * transformCoordinate.inverse(), pointsend, write); + pointsend.clear(); + } } - pointsend.clear(); } } @@ -944,14 +926,15 @@ void PencilTool::_interpolate() { if (tol < 18.0 * 0.4) { tol = 18.0 * 0.4; } - size_t i = this->ps.size(); -// while (i > 3 && -// (this->wps[i-1] < 0.5 || Geom::distance(this->ps[i-1], this->ps[i-2]) < Geom::distance(this->ps[i-2], this->ps[i-3])/2.0)) -// { -// this->ps.pop_back(); -// this->wps.pop_back(); -// i--; -// } + //Smooth last 2 segments + if (this->ps.size() > 2) { + Geom::Point last_point = this->ps[this->ps.size()-1]; + Geom::Point prev_point = middle_point(last_point,this->ps[this->ps.size()-3]); + this->ps.pop_back(); + this->ps.pop_back(); + this->ps.push_back(prev_point); + this->ps.push_back(last_point); + } } double tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2); @@ -979,13 +962,14 @@ void PencilTool::_interpolate() { 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){ + 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{ + } 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 { diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 3e1332501..ce38f5deb 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -6,7 +6,7 @@ */ #include "ui/tools/freehand-base.h" - +#include "sp-shape.h" #include <2geom/piecewise.h> #include <2geom/d2.h> #include <2geom/sbasis.h> @@ -70,10 +70,10 @@ private: bool _handleKeyPress(GdkEventKey const &event); bool _handleKeyRelease(GdkEventKey const &event); void _setStartpoint(Geom::Point const &p); - void _powerStrokePreview(Geom::Path path, std::vector points, bool write); - SPItem *_powerpreview; - SPItem *_powerpreviewtail; - bool _second_chance_preview; + void _powerStrokePreview(Geom::Path const path, std::vector points, bool write); + SPShape *_powerpreview; + SPShape *_powerpreviewtail; + bool _previewok; gchar const * end_linecap_type; void _setEndpoint(Geom::Point const &p); void _finishEndpoint(); -- cgit v1.2.3 From b861f310ab1c2c71ec3aae2dd231baffbc8080be Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Mon, 20 Nov 2017 22:29:38 +0100 Subject: Smoothing start and end --- src/ui/tools/freehand-base.cpp | 4 +- src/ui/tools/pencil-tool.cpp | 103 +++++++++++++++++++++++++++-------------- src/ui/tools/pencil-tool.h | 2 +- 3 files changed, 70 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 0d539b0f3..1259d411f 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -259,8 +259,8 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha lpe->getRepr()->setAttribute("sort_points", "true"); lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("miter_limit", "4"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); + lpe->getRepr()->setAttribute("miter_limit", "100"); + lpe->getRepr()->setAttribute("linejoin_type", "miter"); static_cast(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 478ba61f6..ae6fd6c17 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -425,7 +425,7 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { /* Write curves to object */ desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand")); - this->_interpolate(); + this->_interpolate(true); //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); @@ -680,8 +680,8 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector lpe->getRepr()->setAttribute("sort_points", "true"); lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("miter_limit", "4"); - lpe->getRepr()->setAttribute("linejoin_type", "round"); + 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(_powerpreview); @@ -755,13 +755,30 @@ PencilTool::addPowerStrokePencil(SPCurve * c) } bool live = false; SPCurve * curve; + if (sa) { + Effect* lpe = SP_LPE_ITEM(white_item)->getCurrentLPE(); + LPEPowerStroke* ps = static_cast(lpe); + if (ps) { + if (sa->start) { + this->points = ps->offset_points.reverse_controlpoints(false); + } else { + this->points = ps->offset_points.data(); + } + } + } 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", 18.0); - _interpolate(); + std::vector stroreps = this->ps; + std::vector strorewps = this->wps; + _interpolate(true); + this->ps = stroreps; + this->wps = strorewps; + stroreps.clear(); + strorewps.clear(); prefs->setDouble("/tools/freehand/pencil/tolerance", tol); if (sa) { curve = sa->curve; @@ -776,15 +793,6 @@ PencilTool::addPowerStrokePencil(SPCurve * c) curve = ret->copy(); ret->unref(); } - Effect* lpe = SP_LPE_ITEM(white_item)->getCurrentLPE(); - LPEPowerStroke* ps = static_cast(lpe); - if (ps) { - if (sa->start) { - this->points = ps->offset_points.reverse_controlpoints(false); - } else { - this->points = ps->offset_points.data(); - } - } if (!green_curve->is_empty()) { if (curve->is_empty()) { curve = green_curve->copy(); @@ -831,31 +839,36 @@ PencilTool::addPowerStrokePencil(SPCurve * c) bool start = true; auto pressure = this->wps.begin(); size_t counter = 0; + Geom::Point position = *this->ps.begin(); + if (!live) { + position *= transformCoordinate.inverse(); + } + double pos = Geom::nearest_time(position, path); for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) { counter++; - //remove end pressure gap - if (this->ps.size() > 2 && this->ps.size()-2 < counter) { - break; - } +// //remove end pressure gap +// if (this->ps.size() > 4 && this->ps.size()-4 < counter) { +// break; +// } 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 (counter < 3) { + if (counter < 2) { if (start) { start = false; - this->points.push_back(Geom::Point(0, pressure_computed)); + this->points.push_back(Geom::Point(pos + 0.01, pressure_computed)); } else { - this->points[this->points.size()-1] = Geom::Point(0, pressure_computed); + this->points[this->points.size()-1] = Geom::Point(pos + 0.01, pressure_computed); } previous_pressure = pressure_shrunk; continue; } - Geom::Point position = *point; + position = *point; if (!live) { position *= transformCoordinate.inverse(); } - double pos = Geom::nearest_time(position, path); + pos = Geom::nearest_time(position, path); if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > gap_pressure) { previous_pressure = pressure_shrunk; this->points.push_back(Geom::Point(pos, pressure_computed)); @@ -874,7 +887,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) if (start[Geom::X] < path.size()) { std::vector pointsend; Geom::Path const pathtail = path.portion(start[Geom::X], path.size()); - pointsend.push_back(Geom::Point(0, start[Geom::Y])); + pointsend.push_back(Geom::Point(0.01, start[Geom::Y])); _powerStrokePreview(pathtail * transformCoordinate.inverse(), pointsend, write); pointsend.clear(); } @@ -905,7 +918,7 @@ square(double const x) return x * x; } -void PencilTool::_interpolate() { +void PencilTool::_interpolate(bool realize) { if ( this->ps.size() <= 1 ) { return; } @@ -926,14 +939,30 @@ void PencilTool::_interpolate() { if (tol < 18.0 * 0.4) { tol = 18.0 * 0.4; } - //Smooth last 2 segments - if (this->ps.size() > 2) { - Geom::Point last_point = this->ps[this->ps.size()-1]; - Geom::Point prev_point = middle_point(last_point,this->ps[this->ps.size()-3]); - this->ps.pop_back(); - this->ps.pop_back(); - this->ps.push_back(prev_point); - this->ps.push_back(last_point); + //Smooth start segments + if (realize && this->ps.size() > 3) { + this->ps.erase(this->ps.begin() + 1, this->ps.begin() + 2); + this->wps.erase(this->wps.begin() + 1, this->wps.begin() + 2); + } + //Smooth last segments + if (realize && this->ps.size() > 3) { + double start_distance = Geom::distance(this->ps[0], this->ps[1]); + Geom::Point last_point = *this->ps.end(); + std::cout << start_distance << "start_distance" << std::endl; + std::cout << Geom::distance(*this->ps.end(), last_point) << "distance" << std::endl; + std::cout << "::::::::::::::::::::::::::::::::::::::distance" << std::endl; + bool erased = false; + while ( this->ps.size() > 9 && Geom::distance(*this->ps.end(), last_point) < start_distance) { + std::cout << Geom::distance(*this->ps.end(), last_point) << "distance" << std::endl; + std::cout << start_distance << "start_distance" << std::endl; + 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); + } } } @@ -970,11 +999,13 @@ void PencilTool::_interpolate() { 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 == 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]); +// } else { this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); - } + //} } } if (!input_has_pressure) { diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index ce38f5deb..56604eaca 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -79,7 +79,7 @@ private: void _finishEndpoint(); void _addFreehandPoint(Geom::Point const &p, guint state); void _fitAndSplit(); - void _interpolate(); + void _interpolate(bool realize = false); void _sketchInterpolate(); void _extinput(GdkEvent *event); void _cancel(); -- cgit v1.2.3 From 157d892c95684c195bfe9ad050e8c881cc3d4555 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Wed, 22 Nov 2017 23:58:26 +0100 Subject: Working on continuous paths --- src/ui/dialog/inkscape-preferences.cpp | 10 ++-- src/ui/dialog/inkscape-preferences.h | 2 +- src/ui/tools/freehand-base.cpp | 18 ++++--- src/ui/tools/pencil-tool.cpp | 90 ++++++++++++++++++++-------------- src/ui/tools/pencil-tool.h | 2 +- 5 files changed, 71 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index ef69fdade..99312f8c8 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -204,12 +204,12 @@ void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring false ); } -void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::ustring const &prefs_path, gint def_value) +void InkscapePreferences::AddPencilPowerStrokePressureStep(DialogPage &p, Glib::ustring const &prefs_path, gint def_value) { PrefSpinButton* sb = Gtk::manage( new PrefSpinButton); - sb->init ( prefs_path + "/gap-pressure", 1, 100, 1, 10, def_value, true, false); - p.add_line( false, _("Pressure diference on knots:"), *sb, _("%"), - _("Pressure diference percent that is required to create a new PowerStroke knot."), + sb->init ( prefs_path + "/ps-step-pressure", 1, 100, 1, 10, def_value, true, false); + p.add_line( false, _("Pressure change for new knot:"), *sb, _("%"), + _("Percentage increase / decrease of stylus pressure that is required to create a new PowerStroke knot."), false ); } @@ -432,7 +432,7 @@ void InkscapePreferences::initPageTools() this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0); this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0); _page_pencil.add_group_header( _("Pressure sensitivity settings")); - this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 10); + this->AddPencilPowerStrokePressureStep(_page_pencil, "/tools/freehand/pencil", 10); _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 0f78a49a4..639c3af68 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -500,7 +500,7 @@ protected: static void AddFirstAndLastCheckbox(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, bool def_value); 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 AddPowerStrokeGapPressureFactor(UI::Widget::DialogPage& p, Glib::ustring const &prefs_path, gint def_value); + static void AddPencilPowerStrokePressureStep(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 1259d411f..15027b6f3 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -330,7 +330,6 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, { using namespace Inkscape::LivePathEffect; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (item && SP_IS_LPE_ITEM(item)) { //Store the clipboard path to apply in the future without the use of clipboard static Geom::PathVector previous_shape_pathv; @@ -397,11 +396,16 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, } if (SP_IS_PENCIL_CONTEXT(dc)) { if (dc->input_has_pressure) { - std::vector points; - spdc_apply_powerstroke_shape(points, dc, item); - shape = NONE; - //To allow retain color - shape_applied = true; + if (shape == NONE) { + std::vector points; + spdc_apply_powerstroke_shape(points, dc, item); + //To allow retain color + shape_applied = true; + } else { + PencilTool *pt = SP_PENCIL_CONTEXT(dc); + pt->removePowerStrokePreview(); + shape == NONE; + } } } #define SHAPE_LENGTH 10 @@ -865,7 +869,7 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); //Bend needs the transforms applied after, Other effects best before - spdc_check_for_and_apply_waiting_LPE(dc, item, c, true); + (dc, item, c, true); Inkscape::GC::release(repr); item->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); item->updateRepr(); diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index ae6fd6c17..1e1650d44 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -74,7 +74,7 @@ PencilTool::PencilTool() , sketch_n(0) , _powerpreviewtail(NULL) , _powerpreview(NULL) - , _previewok(true) + , _preview_ok(true) { } @@ -281,6 +281,10 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process the // motion notify coordinates as given (no snapping back to origin) + if (input_has_pressure && pencil_within_tolerance) { + p = desktop->w2d(pencil_drag_origin_w); + anchor = spdc_test_inside(this, pencil_drag_origin_w); + } pencil_within_tolerance = false; switch (this->state) { @@ -288,8 +292,8 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { /* Set red endpoint */ if (input_has_pressure) { this->state = SP_PENCIL_CONTEXT_FREEHAND; - return true; - } + return false; + } if (anchor) { p = anchor->dp; } else { @@ -313,6 +317,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { this->green_anchor = sp_draw_anchor_new(this, this->green_curve, TRUE, this->p[0]); } if (anchor) { + std::cout << "aaaaaaaaaaaaaaaaaaaaaaaaaa" << std::endl; p = anchor->dp; } if ( this->npoints != 0) { // buttonpress may have happened before we entered draw context! @@ -350,7 +355,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { // Show the pre-snap indicator to communicate to the user where we would snap to if he/she were to // a) press the mousebutton to start a freehand drawing, or // b) release the mousebutton to finish a freehand drawing - if (!this->sp_event_context_knot_mouseover() && !input_has_pressure) { + if (!this->sp_event_context_knot_mouseover()) { SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true); m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); @@ -712,9 +717,9 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector } if (!curve->is_empty()) { _powerpreviewtail->setCurve(curve, true); - _previewok = true; + _preview_ok = true; } else {//if (!_second_chance_preview) { - _previewok = false; + _preview_ok = false; } } curve->unref(); @@ -747,7 +752,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) this->points.clear(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0); - double gap_pressure = prefs->getIntLimited("/tools/freehand/pencil/gap-pressure",10, 1, 100)/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; if (min > max){ @@ -797,7 +802,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) if (curve->is_empty()) { curve = green_curve->copy(); } else { - green_curve->move_endpoints(curve->first_path()->finalPoint(), *green_curve->last_point()); + green_curve->move_endpoints(curve->first_path()->finalPoint(), green_curve->first_path()->finalPoint()); curve->append_continuous( green_curve, 0.0625); } if (!red_curve->is_empty()) { @@ -846,21 +851,13 @@ PencilTool::addPowerStrokePencil(SPCurve * c) double pos = Geom::nearest_time(position, path); for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) { counter++; -// //remove end pressure gap -// if (this->ps.size() > 4 && this->ps.size()-4 < counter) { -// break; -// } 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 (counter < 2) { - if (start) { - start = false; - this->points.push_back(Geom::Point(pos + 0.01, pressure_computed)); - } else { - this->points[this->points.size()-1] = Geom::Point(pos + 0.01, pressure_computed); - } + if (start) { + start = false; + this->points.push_back(Geom::Point(pos + 0.01, pressure_computed)); previous_pressure = pressure_shrunk; continue; } @@ -869,14 +866,14 @@ PencilTool::addPowerStrokePencil(SPCurve * c) position *= transformCoordinate.inverse(); } pos = Geom::nearest_time(position, path); - if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > gap_pressure) { + if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > gap_pressure && pos < path.size() - 1) { previous_pressure = pressure_shrunk; this->points.push_back(Geom::Point(pos, pressure_computed)); } } if (live && this->points.size() > 0) { bool write = false; - if (points_parsed != this->points.size() || !_previewok) { + if (points_parsed != this->points.size() || !_preview_ok) { points_parsed = this->points.size(); write = true; } @@ -939,22 +936,41 @@ void PencilTool::_interpolate(bool realize) { if (tol < 18.0 * 0.4) { tol = 18.0 * 0.4; } - //Smooth start segments + //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) { - this->ps.erase(this->ps.begin() + 1, this->ps.begin() + 2); - this->wps.erase(this->wps.begin() + 1, this->wps.begin() + 2); + 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) { - double start_distance = Geom::distance(this->ps[0], this->ps[1]); Geom::Point last_point = *this->ps.end(); - std::cout << start_distance << "start_distance" << std::endl; - std::cout << Geom::distance(*this->ps.end(), last_point) << "distance" << std::endl; - std::cout << "::::::::::::::::::::::::::::::::::::::distance" << std::endl; bool erased = false; - while ( this->ps.size() > 9 && Geom::distance(*this->ps.end(), last_point) < start_distance) { - std::cout << Geom::distance(*this->ps.end(), last_point) << "distance" << std::endl; - std::cout << start_distance << "start_distance" << std::endl; + while ( this->ps.size() > 6 && Geom::distance(*this->ps.end(), last_point) < smoothlenght) { this->ps.pop_back(); this->wps.pop_back(); erased = true; @@ -999,13 +1015,13 @@ void PencilTool::_interpolate(bool realize) { 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]); -// } else { + 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]); + } else { this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); - //} + } } } if (!input_has_pressure) { diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 56604eaca..e15289ad1 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -73,7 +73,7 @@ private: void _powerStrokePreview(Geom::Path const path, std::vector points, bool write); SPShape *_powerpreview; SPShape *_powerpreviewtail; - bool _previewok; + bool _preview_ok; gchar const * end_linecap_type; void _setEndpoint(Geom::Point const &p); void _finishEndpoint(); -- cgit v1.2.3 From 2930567e3fed1875a3c1d903634b865b135ea6ef Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Wed, 29 Nov 2017 15:26:56 +0100 Subject: Add base interpolator parameter --- src/live_effects/lpe-powerstroke.cpp | 209 +++++++++++++++++++++++++++++------ src/live_effects/lpe-powerstroke.h | 3 +- 2 files changed, 175 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 51c8451a5..96f487211 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -164,6 +164,7 @@ static const Util::EnumDataConverter LineJoinTypeConverter(LineJoinTyp LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : Effect(lpeobject), offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this), + interpolate_original(_("Interpolate original"), _("Interpolate original path"), "interpolate_original", &wr, this, false), sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true), 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), @@ -182,6 +183,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : registerParameter(&offset_points); registerParameter(&sort_points); + registerParameter(&interpolate_original); registerParameter(&interpolator_type); registerParameter(&interpolator_beta); registerParameter(&start_linecap_type); @@ -192,6 +194,7 @@ 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,6 +569,42 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & 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 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(interpolator_type.get_value())); + if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { + johan->setBeta(interpolator_beta); + } + if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(interpolator)) { + smooth->setBeta(interpolator_beta); + } + if (interpolate_original_prev != interpolate_original) { + adjustForNewPath(pathv); + interpolate_original_prev = interpolate_original; + } Geom::Piecewise > pwd2_in = pathv[0].toPwSb(); Piecewise > der = derivative(pwd2_in); Piecewise > n = unitVector(der,0.0001); @@ -609,18 +648,116 @@ 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]; - for (std::size_t i = 0, e = ts.size(); i < e; ++i) { - ts[i][Geom::X] *= xcoord_scaling; + if (interpolate_original) { + size_t i = 0; + std::vector ts_aprox; + size_t steps = 0; + double distance = 0; + Geom::PathVector splits; + Geom::Coord start = 0; + for(std::vector::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 > 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); } // create stroke path where points (x,y) := (t, offset) - Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast(interpolator_type.get_value())); - if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { - johan->setBeta(interpolator_beta); - } - if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(interpolator)) { - smooth->setBeta(interpolator_beta); + + Geom::Path fixed_path; + Geom::Path fixed_mirrorpath; + Geom::Path strokepath; + if (interpolate_original) { + std::vector ts_normal; + std::vector 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::Path strokepath = interpolator->interpolateToPath(ts); delete interpolator; // apply the inverse knot-xcoord scaling that was applied before the interpolation @@ -638,13 +775,13 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) y = portion(y, rtsmin.at(0), rtsmax.at(0)); } - LineJoinType jointype = static_cast(linejoin_type.get_value()); - - Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); - Piecewise > 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 (!interpolate_original) { + LineJoinType jointype = static_cast(linejoin_type.get_value()); + Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); + Piecewise > 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); + } if (pathv[0].closed()) { fixed_path.close(true); path_out.push_back(fixed_path); @@ -659,31 +796,31 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) case LINECAP_PEAK: { Geom::Point end_deriv = -unitTangentAt( reverse(pwd2_in.segs.back()), 0.); - double radius = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue()); - Geom::Point midpoint = 0.5*(pwd2_out.lastValue() + mirrorpath.firstValue()) + radius*end_deriv; + double radius = 0.5 * distance(fixed_path.finalPoint(), fixed_mirrorpath.initialPoint()); + Geom::Point midpoint = 0.5*(fixed_path.finalPoint() + fixed_mirrorpath.initialPoint()) + radius*end_deriv; fixed_path.appendNew(midpoint); - fixed_path.appendNew(mirrorpath.firstValue()); + fixed_path.appendNew(fixed_mirrorpath.initialPoint()); break; } case LINECAP_SQUARE: { Geom::Point end_deriv = -unitTangentAt( reverse(pwd2_in.segs.back()), 0.); - double radius = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue()); - fixed_path.appendNew( pwd2_out.lastValue() + radius*end_deriv ); - fixed_path.appendNew( mirrorpath.firstValue() + radius*end_deriv ); - fixed_path.appendNew( mirrorpath.firstValue() ); + double radius = 0.5 * distance(fixed_path.finalPoint(), fixed_mirrorpath.initialPoint()); + fixed_path.appendNew( fixed_path.finalPoint() + radius*end_deriv ); + fixed_path.appendNew( fixed_mirrorpath.initialPoint() + radius*end_deriv ); + fixed_path.appendNew( fixed_mirrorpath.initialPoint() ); break; } case LINECAP_BUTT: { - fixed_path.appendNew( mirrorpath.firstValue() ); + fixed_path.appendNew( fixed_mirrorpath.initialPoint() ); break; } case LINECAP_ROUND: default: { - double radius1 = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue()); - fixed_path.appendNew( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() ); + double radius1 = 0.5 * distance(fixed_path.finalPoint(), fixed_mirrorpath.initialPoint()); + fixed_path.appendNew( radius1, radius1, M_PI/2., false, y.lastValue() < 0, fixed_mirrorpath.initialPoint() ); break; } } @@ -696,31 +833,31 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) case LINECAP_PEAK: { Geom::Point start_deriv = unitTangentAt( pwd2_in.segs.front(), 0.); - double radius = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue()); - Geom::Point midpoint = 0.5*(mirrorpath.lastValue() + pwd2_out.firstValue()) - radius*start_deriv; + double radius = 0.5 * distance(fixed_path.initialPoint(), fixed_mirrorpath.finalPoint()); + Geom::Point midpoint = 0.5*(fixed_mirrorpath.finalPoint() + fixed_path.initialPoint()) - radius*start_deriv; fixed_path.appendNew( midpoint ); - fixed_path.appendNew( pwd2_out.firstValue() ); + fixed_path.appendNew( fixed_path.initialPoint() ); break; } case LINECAP_SQUARE: { Geom::Point start_deriv = unitTangentAt( pwd2_in.segs.front(), 0.); - double radius = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue()); - fixed_path.appendNew( mirrorpath.lastValue() - radius*start_deriv ); - fixed_path.appendNew( pwd2_out.firstValue() - radius*start_deriv ); - fixed_path.appendNew( pwd2_out.firstValue() ); + double radius = 0.5 * distance(fixed_path.initialPoint(), fixed_mirrorpath.finalPoint()); + fixed_path.appendNew( fixed_mirrorpath.finalPoint() - radius*start_deriv ); + fixed_path.appendNew( fixed_path.initialPoint() - radius*start_deriv ); + fixed_path.appendNew( fixed_path.initialPoint() ); break; } case LINECAP_BUTT: { - fixed_path.appendNew( pwd2_out.firstValue() ); + fixed_path.appendNew( fixed_path.initialPoint() ); break; } case LINECAP_ROUND: default: { - double radius2 = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue()); - fixed_path.appendNew( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() ); + double radius2 = 0.5 * distance(fixed_path.initialPoint(), fixed_mirrorpath.finalPoint()); + fixed_path.appendNew( radius2, radius2, M_PI/2., false, y.firstValue() < 0, fixed_path.initialPoint() ); break; } } diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index 4a0eda75a..53aab1b05 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -38,6 +38,7 @@ public: private: BoolParam sort_points; + BoolParam interpolate_original; EnumParam interpolator_type; ScalarParam interpolator_beta; ScalarParam scale_width; @@ -45,7 +46,7 @@ private: EnumParam linejoin_type; ScalarParam miter_limit; EnumParam end_linecap_type; - + bool interpolate_original_prev; LPEPowerStroke(const LPEPowerStroke&); LPEPowerStroke& operator=(const LPEPowerStroke&); }; -- cgit v1.2.3 From cd56d55f26a8c9029de91924d0ab827ad877713d Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Wed, 29 Nov 2017 15:34:49 +0100 Subject: Add BSPLine interpolator --- src/live_effects/lpe-interpolate_points.cpp | 3 +- src/live_effects/lpe-powerstroke-interpolators.h | 37 +++++++++++++++++++++++- src/live_effects/lpe-powerstroke.cpp | 3 +- 3 files changed, 40 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index 7d4c88dc1..c745921c2 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -25,7 +25,8 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, + {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index e3ab37e27..651eba057 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -15,7 +15,7 @@ #include <2geom/path.h> #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> - +#include #include "live_effects/spiro.h" @@ -27,6 +27,7 @@ enum InterpolatorType { INTERP_LINEAR, INTERP_CUBICBEZIER, INTERP_CUBICBEZIER_JOHAN, + INTERP_BSPLINE, INTERP_SPIRO, INTERP_CUBICBEZIER_SMOOTH, INTERP_CENTRIPETAL_CATMULLROM @@ -65,6 +66,38 @@ private: Linear& operator=(const Linear&); }; +class BSpline : public Interpolator { +public: + BSpline() {}; + virtual ~BSpline() {}; + + virtual Path interpolateToPath(std::vector const &points) const { + Path path; + path.start( points.at(0) ); + for (unsigned int i = 1 ; i < points.size(); ++i) { + Geom::Point pointA = points.at(i-1); + Geom::Point pointB = points.at(i); + Geom::Ray ray(pointA, pointB); + double angle = ray.angle(); + if (angle == 0) { + continue; + } + std::cout << angle << "angle" << std::endl; + double k1 = (Geom::distance(pointA, pointB)*std::sin(angle))/2.0; + std::cout << k1 << "k1" << std::endl; + std::cout << std::sin(angle) << "std::sin(angle)" << std::endl; + Geom::Point handle_1 = Geom::Point::polar(angle, k1) + pointA; + Geom::Point handle_2 = Geom::Point::polar(angle, k1 * 2) + pointA; + path.appendNew(handle_1, handle_2, pointB); + } + return path; + }; + +private: + BSpline(const BSpline&); + BSpline& operator=(const BSpline&); +}; + // this class is terrible class CubicBezierFit : public Interpolator { public: @@ -302,6 +335,8 @@ Interpolator::create(InterpolatorType type) { return new Geom::Interpolate::CubicBezierSmooth(); case INTERP_CENTRIPETAL_CATMULLROM: return new Geom::Interpolate::CentripetalCatmullRomInterpolator(); + case INTERP_BSPLINE: + return new Geom::Interpolate::BSpline(); default: return new Geom::Interpolate::Linear(); } diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 96f487211..b2b55a08c 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -123,7 +123,8 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, + {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); -- cgit v1.2.3 From 9599c53b48cbd737cf4c95120f0eb654dfce7b74 Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Wed, 29 Nov 2017 22:40:27 +0100 Subject: Working with BSPline interpolator --- src/live_effects/lpe-powerstroke-interpolators.h | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index 651eba057..baa341fcb 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -74,21 +74,17 @@ public: virtual Path interpolateToPath(std::vector const &points) const { Path path; path.start( points.at(0) ); - for (unsigned int i = 1 ; i < points.size(); ++i) { - Geom::Point pointA = points.at(i-1); - Geom::Point pointB = points.at(i); - Geom::Ray ray(pointA, pointB); - double angle = ray.angle(); - if (angle == 0) { - continue; - } - std::cout << angle << "angle" << std::endl; - double k1 = (Geom::distance(pointA, pointB)*std::sin(angle))/2.0; - std::cout << k1 << "k1" << std::endl; - std::cout << std::sin(angle) << "std::sin(angle)" << std::endl; - Geom::Point handle_1 = Geom::Point::polar(angle, k1) + pointA; - Geom::Point handle_2 = Geom::Point::polar(angle, k1 * 2) + pointA; - path.appendNew(handle_1, handle_2, pointB); + Geom::Point handle_prev = points.at(0); + for (unsigned int i = 1 ; i < points.size() - 2; ++i) { + Geom::Line line_a(points.at(i), points.at(i+1)); + Geom::Line line_b(points.at(i+1), points.at(i+2)); + Geom::Point handle_next_tmp = line_a.pointAt(line_a.nearestTime(points.at(i-1))); + Geom::Point handle_next = line_b.pointAt(line_b.nearestTime(handle_next_tmp)); + Geom::Line line_handles(handle_next, points.at(i-1)); + double angle = line_handles.angle(); + Geom::Point handle_2 = Geom::Point::polar(angle, Geom::distance(handle_next,points.at(i))) + handle_next; + path.appendNew(handle_prev, handle_2, points.at(i)); + handle_prev = handle_next; } return path; }; -- cgit v1.2.3 From 524ed2d90dbea0b3be40c0786b4200bf3f6a146c Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Thu, 30 Nov 2017 02:26:02 +0100 Subject: Working bspline, to fine tune --- src/live_effects/lpe-powerstroke-interpolators.h | 41 ++++++++++++++++++------ 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index baa341fcb..21d25a797 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -74,18 +74,41 @@ public: virtual Path interpolateToPath(std::vector const &points) const { Path path; path.start( points.at(0) ); + Geom::Point handle_1(0,0); Geom::Point handle_prev = points.at(0); - for (unsigned int i = 1 ; i < points.size() - 2; ++i) { - Geom::Line line_a(points.at(i), points.at(i+1)); - Geom::Line line_b(points.at(i+1), points.at(i+2)); - Geom::Point handle_next_tmp = line_a.pointAt(line_a.nearestTime(points.at(i-1))); - Geom::Point handle_next = line_b.pointAt(line_b.nearestTime(handle_next_tmp)); - Geom::Line line_handles(handle_next, points.at(i-1)); - double angle = line_handles.angle(); - Geom::Point handle_2 = Geom::Point::polar(angle, Geom::distance(handle_next,points.at(i))) + handle_next; + for (unsigned int i = 1 ; i < points.size() - 1; ++i) { + Geom::Line line_a(points.at(i - 1), points.at(i)); + double angle_a = line_a.angle(); + Geom::Line line_b(points.at(i),points.at(i + 1)); + double angle_b = line_b.angle(); + Geom::Line line_handles(line_a.pointAt(0.66667),line_b.pointAt(0.33334)); + line_handles *= Geom::Translate(points.at(i) - line_handles.pointAt(0.5)); + Geom::Line line_perp_a(line_a.pointAt(0.5), Geom::Point::polar(angle_a + Geom::rad_from_deg(90),1) +line_a.pointAt(0.5)); + Geom::Line line_perp_b(line_b.pointAt(0.5), Geom::Point::polar(angle_b + Geom::rad_from_deg(90),1) +line_b.pointAt(0.5)); + std::vector result = line_perp_a.intersect(line_perp_b); + Geom::Point handle_2(0,0); + if(result.size() > 0) { + Geom::Line line_to_handle_a(result[0],line_a.pointAt(0.66667)); + Geom::Line line_to_handle_b(result[0],line_b.pointAt(0.33334)); + std::vector handle_a_res = line_to_handle_a.intersect(line_handles); + std::vector handle_b_res = line_to_handle_b.intersect(line_handles); + if(handle_a_res.size() > 0) { + handle_2 = handle_a_res[0]; + } + if(handle_a_res.size() > 0) { + handle_1 = handle_b_res[0]; + } + } + if (i == 1) { + Geom::Line start_segment(handle_prev,handle_2); + handle_prev = start_segment.pointAt(0.5); + } path.appendNew(handle_prev, handle_2, points.at(i)); - handle_prev = handle_next; + handle_prev = handle_1; } + Geom::Point last = points.at(points.size()-1); + Geom::Line last_segment(last,handle_1); + path.appendNew(handle_1, last_segment.pointAt(0.5), last); return path; }; -- cgit v1.2.3 From 67f654a049a12eb86b0e2f137212a50407b8e82e Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Fri, 1 Dec 2017 20:21:25 +0100 Subject: Add show handles --- src/live_effects/lpe-show_handles.cpp | 23 +++++++++++++++++++++-- src/live_effects/lpe-show_handles.h | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-show_handles.cpp b/src/live_effects/lpe-show_handles.cpp index 7c298d0e7..2da570537 100644 --- a/src/live_effects/lpe-show_handles.cpp +++ b/src/live_effects/lpe-show_handles.cpp @@ -12,6 +12,7 @@ #include <2geom/svg-path-parser.h> #include "helper/geom.h" #include "desktop-style.h" +#include "display/curve.h" #include "style.h" #include "svg/svg.h" @@ -27,12 +28,14 @@ LPEShowHandles::LPEShowHandles(LivePathEffectObject *lpeobject) handles(_("Show handles"), _("Show handles"), "handles", &wr, this, true), original_path(_("Show path"), _("Show path"), "original_path", &wr, this, true), show_center_node(_("Show center of node"), _("Show center of node"), "show_center_node", &wr, this, false), + original_d(_("Show original"), _("Show original"), "original_d", &wr, this, false), scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10) { registerParameter(&nodes); registerParameter(&handles); registerParameter(&original_path); registerParameter(&show_center_node); + registerParameter(&original_d); registerParameter(&scale_nodes_and_handles); scale_nodes_and_handles.param_set_range(0, 500.); scale_nodes_and_handles.param_set_increments(1, 1); @@ -80,7 +83,7 @@ void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem) Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in) { Geom::PathVector path_out; - Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); + Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(path_in); if(original_path) { for (unsigned int i=0; i < path_in.size(); i++) { path_out.push_back(path_in[i]); @@ -89,10 +92,26 @@ Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in if(!outline_path.empty()) { outline_path.clear(); } - generateHelperPath(original_pathv); + if (original_d) { + SPCurve * shape_curve = sp_shape->getCurveBeforeLPE(); + if (shape_curve) { + Geom::PathVector original_curve = shape_curve->get_pathvector(); + if(original_path) { + for (unsigned int i=0; i < original_curve.size(); i++) { + path_out.push_back(original_curve[i]); + } + } + original_pathv.insert(original_pathv.end(), original_curve.begin(), original_curve.end()); + } + generateHelperPath(original_pathv); + shape_curve->unref(); + } else { + generateHelperPath(original_pathv); + } for (unsigned int i=0; i < outline_path.size(); i++) { path_out.push_back(outline_path[i]); } + return path_out; } diff --git a/src/live_effects/lpe-show_handles.h b/src/live_effects/lpe-show_handles.h index c46abd2c2..583be9e61 100644 --- a/src/live_effects/lpe-show_handles.h +++ b/src/live_effects/lpe-show_handles.h @@ -43,6 +43,7 @@ private: BoolParam nodes; BoolParam handles; BoolParam original_path; + BoolParam original_d; BoolParam show_center_node; ScalarParam scale_nodes_and_handles; double stroke_width; -- cgit v1.2.3 From cbdd1dfb6d9e228c82905cc5032eef81164887d6 Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Fri, 1 Dec 2017 20:54:29 +0100 Subject: Remobe BSPline interpolator, a non success code and remove showhandles code --- src/live_effects/lpe-interpolate_points.cpp | 3 +- src/live_effects/lpe-powerstroke-interpolators.h | 56 +----------------------- src/live_effects/lpe-powerstroke.cpp | 3 +- src/live_effects/lpe-show_handles.cpp | 23 +--------- src/live_effects/lpe-show_handles.h | 1 - 5 files changed, 5 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index c745921c2..7d4c88dc1 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -25,8 +25,7 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, - {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index 21d25a797..e3ab37e27 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -15,7 +15,7 @@ #include <2geom/path.h> #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> -#include + #include "live_effects/spiro.h" @@ -27,7 +27,6 @@ enum InterpolatorType { INTERP_LINEAR, INTERP_CUBICBEZIER, INTERP_CUBICBEZIER_JOHAN, - INTERP_BSPLINE, INTERP_SPIRO, INTERP_CUBICBEZIER_SMOOTH, INTERP_CENTRIPETAL_CATMULLROM @@ -66,57 +65,6 @@ private: Linear& operator=(const Linear&); }; -class BSpline : public Interpolator { -public: - BSpline() {}; - virtual ~BSpline() {}; - - virtual Path interpolateToPath(std::vector const &points) const { - Path path; - path.start( points.at(0) ); - Geom::Point handle_1(0,0); - Geom::Point handle_prev = points.at(0); - for (unsigned int i = 1 ; i < points.size() - 1; ++i) { - Geom::Line line_a(points.at(i - 1), points.at(i)); - double angle_a = line_a.angle(); - Geom::Line line_b(points.at(i),points.at(i + 1)); - double angle_b = line_b.angle(); - Geom::Line line_handles(line_a.pointAt(0.66667),line_b.pointAt(0.33334)); - line_handles *= Geom::Translate(points.at(i) - line_handles.pointAt(0.5)); - Geom::Line line_perp_a(line_a.pointAt(0.5), Geom::Point::polar(angle_a + Geom::rad_from_deg(90),1) +line_a.pointAt(0.5)); - Geom::Line line_perp_b(line_b.pointAt(0.5), Geom::Point::polar(angle_b + Geom::rad_from_deg(90),1) +line_b.pointAt(0.5)); - std::vector result = line_perp_a.intersect(line_perp_b); - Geom::Point handle_2(0,0); - if(result.size() > 0) { - Geom::Line line_to_handle_a(result[0],line_a.pointAt(0.66667)); - Geom::Line line_to_handle_b(result[0],line_b.pointAt(0.33334)); - std::vector handle_a_res = line_to_handle_a.intersect(line_handles); - std::vector handle_b_res = line_to_handle_b.intersect(line_handles); - if(handle_a_res.size() > 0) { - handle_2 = handle_a_res[0]; - } - if(handle_a_res.size() > 0) { - handle_1 = handle_b_res[0]; - } - } - if (i == 1) { - Geom::Line start_segment(handle_prev,handle_2); - handle_prev = start_segment.pointAt(0.5); - } - path.appendNew(handle_prev, handle_2, points.at(i)); - handle_prev = handle_1; - } - Geom::Point last = points.at(points.size()-1); - Geom::Line last_segment(last,handle_1); - path.appendNew(handle_1, last_segment.pointAt(0.5), last); - return path; - }; - -private: - BSpline(const BSpline&); - BSpline& operator=(const BSpline&); -}; - // this class is terrible class CubicBezierFit : public Interpolator { public: @@ -354,8 +302,6 @@ Interpolator::create(InterpolatorType type) { return new Geom::Interpolate::CubicBezierSmooth(); case INTERP_CENTRIPETAL_CATMULLROM: return new Geom::Interpolate::CentripetalCatmullRomInterpolator(); - case INTERP_BSPLINE: - return new Geom::Interpolate::BSpline(); default: return new Geom::Interpolate::Linear(); } diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index b2b55a08c..96f487211 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -123,8 +123,7 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, - {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); diff --git a/src/live_effects/lpe-show_handles.cpp b/src/live_effects/lpe-show_handles.cpp index 2da570537..7c298d0e7 100644 --- a/src/live_effects/lpe-show_handles.cpp +++ b/src/live_effects/lpe-show_handles.cpp @@ -12,7 +12,6 @@ #include <2geom/svg-path-parser.h> #include "helper/geom.h" #include "desktop-style.h" -#include "display/curve.h" #include "style.h" #include "svg/svg.h" @@ -28,14 +27,12 @@ LPEShowHandles::LPEShowHandles(LivePathEffectObject *lpeobject) handles(_("Show handles"), _("Show handles"), "handles", &wr, this, true), original_path(_("Show path"), _("Show path"), "original_path", &wr, this, true), show_center_node(_("Show center of node"), _("Show center of node"), "show_center_node", &wr, this, false), - original_d(_("Show original"), _("Show original"), "original_d", &wr, this, false), scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10) { registerParameter(&nodes); registerParameter(&handles); registerParameter(&original_path); registerParameter(&show_center_node); - registerParameter(&original_d); registerParameter(&scale_nodes_and_handles); scale_nodes_and_handles.param_set_range(0, 500.); scale_nodes_and_handles.param_set_increments(1, 1); @@ -83,7 +80,7 @@ void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem) Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in) { Geom::PathVector path_out; - Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(path_in); + Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); if(original_path) { for (unsigned int i=0; i < path_in.size(); i++) { path_out.push_back(path_in[i]); @@ -92,26 +89,10 @@ Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in if(!outline_path.empty()) { outline_path.clear(); } - if (original_d) { - SPCurve * shape_curve = sp_shape->getCurveBeforeLPE(); - if (shape_curve) { - Geom::PathVector original_curve = shape_curve->get_pathvector(); - if(original_path) { - for (unsigned int i=0; i < original_curve.size(); i++) { - path_out.push_back(original_curve[i]); - } - } - original_pathv.insert(original_pathv.end(), original_curve.begin(), original_curve.end()); - } - generateHelperPath(original_pathv); - shape_curve->unref(); - } else { - generateHelperPath(original_pathv); - } + generateHelperPath(original_pathv); for (unsigned int i=0; i < outline_path.size(); i++) { path_out.push_back(outline_path[i]); } - return path_out; } diff --git a/src/live_effects/lpe-show_handles.h b/src/live_effects/lpe-show_handles.h index 583be9e61..c46abd2c2 100644 --- a/src/live_effects/lpe-show_handles.h +++ b/src/live_effects/lpe-show_handles.h @@ -43,7 +43,6 @@ private: BoolParam nodes; BoolParam handles; BoolParam original_path; - BoolParam original_d; BoolParam show_center_node; ScalarParam scale_nodes_and_handles; double stroke_width; -- cgit v1.2.3 From 5eb7d43e7f43c68191ef9ec616b9bd48c9b6c251 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Fri, 1 Dec 2017 23:13:57 +0100 Subject: Working on BSpline interpolator --- src/live_effects/lpe-interpolate_points.cpp | 3 +- src/live_effects/lpe-powerstroke-interpolators.h | 78 +++++++++++++++++++++++- src/live_effects/lpe-powerstroke.cpp | 3 +- 3 files changed, 80 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index 7d4c88dc1..c745921c2 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -25,7 +25,8 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, + {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index e3ab37e27..0c1b60754 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -15,7 +15,7 @@ #include <2geom/path.h> #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> - +#include #include "live_effects/spiro.h" @@ -29,7 +29,8 @@ enum InterpolatorType { INTERP_CUBICBEZIER_JOHAN, INTERP_SPIRO, INTERP_CUBICBEZIER_SMOOTH, - INTERP_CENTRIPETAL_CATMULLROM + INTERP_CENTRIPETAL_CATMULLROM, + INTERP_BSPLINE }; class Interpolator { @@ -65,6 +66,77 @@ private: Linear& operator=(const Linear&); }; +class BSpline : public Interpolator { +public: + BSpline() {}; + virtual ~BSpline() {}; + + virtual Path interpolateToPath(std::vector const &points) const { + Path path; + path.start( points.at(0) ); + + Geom::Point point_a = points.at(0); + Geom::Point point_b = points.at(1); + Geom::Point point_c = points.at(2); + Geom::Point point_d = points.at(2); + if (points.size() > 3) { + point_d = points.at(3); + } + Geom::Point handle_1 = point_a; + Geom::Point handle_2 = point_b; + Geom::Point node_1 = point_a; + Geom::Point node_2 = point_b; //if the line is straight with next + Geom::Line line_ab(point_a, point_b); + Geom::Line line_bc(point_b, point_c); + Geom::Line line_cd(point_c, point_d); + Geom::Point handle_next(line_bc.pointAt(0.33334)); //if the line is straight with next + Geom::Point center_ab = line_ab.pointAt(0.5); + Geom::Point center_bc = line_bc.pointAt(0.5); + Geom::Point center_cd = line_cd.pointAt(0.5); + Geom::Line cross_line_hlp(line_ab.pointAt(0.66667), center_cd); + Geom::Point cross_center_hlp = cross_line_hlp.pointAt(0.5); + double angle_ab = line_ab.angle(); + double angle_bc = line_bc.angle(); + double angle_cd = line_cd.angle(); + double cross_angle_hlp = cross_line_hlp.angle(); + Geom::Line perpend_ab(center_ab, center_ab + Geom::Point::polar(angle_ab + Geom::rad_from_deg(90),1)); + Geom::Line perpend_bc(center_bc, center_bc + Geom::Point::polar(angle_bc + Geom::rad_from_deg(90),1)); + Geom::Line cross_1 (cross_center_hlp, cross_center_hlp + Geom::Point::polar(cross_angle_hlp + Geom::rad_from_deg(90),1)); + std::vector cross_hlp = perpend_ab.intersect(perpend_bc); + if(cross_hlp.size() > 0) { + cross_line_hlp.setPoints(cross_hlp[0], line_ab.pointAt(0.66667)); + cross_angle_hlp = cross_line_hlp.angle(); + Geom::Line cross_2(line_ab.pointAt(0.66667), line_ab.pointAt(0.66667) + Geom::Point::polar(cross_angle_hlp + Geom::rad_from_deg(90),1)); + std::vector cross = cross_1.intersect(cross_2); + if(cross.size() > 0) { + Geom::Line handle_line(cross[0],point_b); + handle_2 = handle_line.pointAt(2.0); + Geom::Line rootline(points.at(0),handle_2); + handle_1 = rootline.pointAt(0.5); + node_2 = rootline.pointAt(1.5); + handle_next = cross[0]; + } + } + path.appendNew(handle_1, handle_2, points.at(1)); + for (unsigned int i = 2 ; i < points.size(); ++i) { + Geom::Line rootline(node_2, handle_next); + handle_2 = rootline.pointAt(2.0); + node_2 = rootline.pointAt(3.0); + path.appendNew(handle_next, handle_2, points.at(i)); + Geom::Line handleline(handle_2, points.at(i)); + handle_next = handleline.pointAt(2.0); + } + Geom::Point last = points.at(points.size()-1); + Geom::LineSegment last_segment(handle_next,last); + path.appendNew(last_segment.initialPoint(), last_segment.pointAt(0.5), last); + return path; + }; + +private: + BSpline(const BSpline&); + BSpline& operator=(const BSpline&); +}; + // this class is terrible class CubicBezierFit : public Interpolator { public: @@ -302,6 +374,8 @@ Interpolator::create(InterpolatorType type) { return new Geom::Interpolate::CubicBezierSmooth(); case INTERP_CENTRIPETAL_CATMULLROM: return new Geom::Interpolate::CentripetalCatmullRomInterpolator(); + case INTERP_BSPLINE: + return new Geom::Interpolate::BSpline(); default: return new Geom::Interpolate::Linear(); } diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 170995b90..47543107b 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -123,7 +123,8 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, + {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); -- cgit v1.2.3 From b95f5438b75deffac0e8780857733257e21b1f68 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sat, 2 Dec 2017 11:51:39 +0100 Subject: Remobing bspline interpolator --- src/live_effects/lpe-interpolate_points.cpp | 3 +- src/live_effects/lpe-powerstroke-interpolators.h | 78 +----------------------- src/live_effects/lpe-powerstroke.cpp | 9 ++- src/ui/tools/freehand-base.cpp | 1 + src/ui/tools/pencil-tool.cpp | 1 + 5 files changed, 9 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index c745921c2..7d4c88dc1 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -25,8 +25,7 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, - {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index 0c1b60754..e3ab37e27 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -15,7 +15,7 @@ #include <2geom/path.h> #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> -#include + #include "live_effects/spiro.h" @@ -29,8 +29,7 @@ enum InterpolatorType { INTERP_CUBICBEZIER_JOHAN, INTERP_SPIRO, INTERP_CUBICBEZIER_SMOOTH, - INTERP_CENTRIPETAL_CATMULLROM, - INTERP_BSPLINE + INTERP_CENTRIPETAL_CATMULLROM }; class Interpolator { @@ -66,77 +65,6 @@ private: Linear& operator=(const Linear&); }; -class BSpline : public Interpolator { -public: - BSpline() {}; - virtual ~BSpline() {}; - - virtual Path interpolateToPath(std::vector const &points) const { - Path path; - path.start( points.at(0) ); - - Geom::Point point_a = points.at(0); - Geom::Point point_b = points.at(1); - Geom::Point point_c = points.at(2); - Geom::Point point_d = points.at(2); - if (points.size() > 3) { - point_d = points.at(3); - } - Geom::Point handle_1 = point_a; - Geom::Point handle_2 = point_b; - Geom::Point node_1 = point_a; - Geom::Point node_2 = point_b; //if the line is straight with next - Geom::Line line_ab(point_a, point_b); - Geom::Line line_bc(point_b, point_c); - Geom::Line line_cd(point_c, point_d); - Geom::Point handle_next(line_bc.pointAt(0.33334)); //if the line is straight with next - Geom::Point center_ab = line_ab.pointAt(0.5); - Geom::Point center_bc = line_bc.pointAt(0.5); - Geom::Point center_cd = line_cd.pointAt(0.5); - Geom::Line cross_line_hlp(line_ab.pointAt(0.66667), center_cd); - Geom::Point cross_center_hlp = cross_line_hlp.pointAt(0.5); - double angle_ab = line_ab.angle(); - double angle_bc = line_bc.angle(); - double angle_cd = line_cd.angle(); - double cross_angle_hlp = cross_line_hlp.angle(); - Geom::Line perpend_ab(center_ab, center_ab + Geom::Point::polar(angle_ab + Geom::rad_from_deg(90),1)); - Geom::Line perpend_bc(center_bc, center_bc + Geom::Point::polar(angle_bc + Geom::rad_from_deg(90),1)); - Geom::Line cross_1 (cross_center_hlp, cross_center_hlp + Geom::Point::polar(cross_angle_hlp + Geom::rad_from_deg(90),1)); - std::vector cross_hlp = perpend_ab.intersect(perpend_bc); - if(cross_hlp.size() > 0) { - cross_line_hlp.setPoints(cross_hlp[0], line_ab.pointAt(0.66667)); - cross_angle_hlp = cross_line_hlp.angle(); - Geom::Line cross_2(line_ab.pointAt(0.66667), line_ab.pointAt(0.66667) + Geom::Point::polar(cross_angle_hlp + Geom::rad_from_deg(90),1)); - std::vector cross = cross_1.intersect(cross_2); - if(cross.size() > 0) { - Geom::Line handle_line(cross[0],point_b); - handle_2 = handle_line.pointAt(2.0); - Geom::Line rootline(points.at(0),handle_2); - handle_1 = rootline.pointAt(0.5); - node_2 = rootline.pointAt(1.5); - handle_next = cross[0]; - } - } - path.appendNew(handle_1, handle_2, points.at(1)); - for (unsigned int i = 2 ; i < points.size(); ++i) { - Geom::Line rootline(node_2, handle_next); - handle_2 = rootline.pointAt(2.0); - node_2 = rootline.pointAt(3.0); - path.appendNew(handle_next, handle_2, points.at(i)); - Geom::Line handleline(handle_2, points.at(i)); - handle_next = handleline.pointAt(2.0); - } - Geom::Point last = points.at(points.size()-1); - Geom::LineSegment last_segment(handle_next,last); - path.appendNew(last_segment.initialPoint(), last_segment.pointAt(0.5), last); - return path; - }; - -private: - BSpline(const BSpline&); - BSpline& operator=(const BSpline&); -}; - // this class is terrible class CubicBezierFit : public Interpolator { public: @@ -374,8 +302,6 @@ Interpolator::create(InterpolatorType type) { return new Geom::Interpolate::CubicBezierSmooth(); case INTERP_CENTRIPETAL_CATMULLROM: return new Geom::Interpolate::CentripetalCatmullRomInterpolator(); - case INTERP_BSPLINE: - return new Geom::Interpolate::BSpline(); default: return new Geom::Interpolate::Linear(); } diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 47543107b..f4e4a06ca 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -123,8 +123,7 @@ static const Util::EnumData InterpolatorTypeData[] = { {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, {Geom::Interpolate::INTERP_SPIRO , N_("SpiroInterpolator"), "SpiroInterpolator"}, - {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"}, - {Geom::Interpolate::INTERP_BSPLINE , N_("BSpline"), "BSpline"} + {Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM, N_("Centripetal Catmull-Rom"), "CentripetalCatmullRom"} }; static const Util::EnumDataConverter InterpolatorTypeConverter(InterpolatorTypeData, sizeof(InterpolatorTypeData)/sizeof(*InterpolatorTypeData)); @@ -165,8 +164,8 @@ static const Util::EnumDataConverter LineJoinTypeConverter(LineJoinTyp LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : Effect(lpeobject), offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this), - interpolate_original(_("Interpolate original"), _("Interpolate original path"), "interpolate_original", &wr, this, false), 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), @@ -706,7 +705,7 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) Geom::Path fixed_path; Geom::Path fixed_mirrorpath; Geom::Path strokepath; - if (interpolate_original) { + if (interpolate_original && 2==1) { std::vector ts_normal; std::vector ts_mirror; @@ -782,7 +781,7 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) y = portion(y, rtsmin.at(0), rtsmax.at(0)); } - if (!interpolate_original) { + if (!interpolate_original || 2>1) { LineJoinType jointype = static_cast(linejoin_type.get_value()); Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise > mirrorpath = reverse( compose(pwd2_in,x) - y*compose(n,x)); diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 15027b6f3..89d9074ba 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -258,6 +258,7 @@ static void spdc_apply_powerstroke_shape(std::vector 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"); diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 1e1650d44..108a89af8 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -684,6 +684,7 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector Effect* lpe = lpeitem->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"); -- cgit v1.2.3 From eb2502c7271cdee9b7b31bd641fa729bad00c38b Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Fri, 8 Dec 2017 20:40:18 +0100 Subject: Moving tolerance --- src/ui/dialog/inkscape-preferences.cpp | 10 ++++++++++ src/ui/dialog/inkscape-preferences.h | 1 + src/ui/tools/pencil-tool.cpp | 9 +++++---- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index adeee35fd..74774f8f4 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -213,6 +213,15 @@ 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; @@ -433,6 +442,7 @@ 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 639c3af68..7cfe3695c 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -501,6 +501,7 @@ 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/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index db1b975ef..6dd3126e3 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -341,7 +341,6 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { this->green_anchor = sp_draw_anchor_new(this, this->green_curve, TRUE, this->p[0]); } if (anchor) { - std::cout << "aaaaaaaaaaaaaaaaaaaaaaaaaa" << std::endl; p = anchor->dp; } if ( this->npoints != 0) { // buttonpress may have happened before we entered draw context! @@ -777,6 +776,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) this->points.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; @@ -801,7 +801,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) //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", 18.0); + prefs->setDouble("/tools/freehand/pencil/tolerance", pstol); std::vector stroreps = this->ps; std::vector strorewps = this->wps; _interpolate(true); @@ -945,8 +945,9 @@ void PencilTool::_interpolate(bool realize) { } //To avoid artifacts on pressure power if (input_has_pressure) { - if (tol < 18.0 * 0.4) { - tol = 18.0 * 0.4; + 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 -- cgit v1.2.3 From 3d059a10fd17411f73e88ccde2cd853642ee7dc3 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Mon, 18 Dec 2017 01:11:36 +0100 Subject: Working on powerstroke fixes to pentool --- src/live_effects/lpe-powerstroke.cpp | 185 +++---------------- src/live_effects/lpe-powerstroke.h | 2 - src/ui/dialog/inkscape-preferences.cpp | 10 -- src/ui/dialog/inkscape-preferences.h | 1 - src/ui/tools/freehand-base.cpp | 4 - src/ui/tools/pencil-tool.cpp | 313 +++++++++++++-------------------- src/ui/tools/pencil-tool.h | 11 +- 7 files changed, 147 insertions(+), 379 deletions(-) (limited to 'src') 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 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 > unitv = unitVector(dM,tol); + if (unitv.empty()) { // admit defeat + return Geom::Circle(Geom::Point(0., 0.), 0.); + } Piecewise dMlength = dot(Piecewise >(dM),unitv); Piecewise 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 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(interpolator_type.get_value())); - if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { - johan->setBeta(interpolator_beta); - } - if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(interpolator)) { - smooth->setBeta(interpolator_beta); - } - if (interpolate_original_prev != interpolate_original) { - adjustForNewPath(pathv); - interpolate_original_prev = interpolate_original; - } Geom::Piecewise > pwd2_in = pathv[0].toPwSb(); - if (!pwd2_in.size()) { - return path_in; - } Piecewise > der = derivative(pwd2_in); - if (!der.size()) { - return path_in; - } Piecewise > 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 ts_aprox; - size_t steps = 0; - double distance = 0; - Geom::PathVector splits; - Geom::Coord start = 0; - for(std::vector::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 > 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 ts_normal; - std::vector 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(interpolator_type.get_value())); + if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { + johan->setBeta(interpolator_beta); } + if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(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(linejoin_type.get_value()); - Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); - Piecewise > 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(linejoin_type.get_value()); + + Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); + Piecewise > 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 interpolator_type; ScalarParam interpolator_beta; ScalarParam scale_width; @@ -46,7 +45,6 @@ private: EnumParam linejoin_type; ScalarParam miter_limit; EnumParam 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 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(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 points, bool write) +PencilTool::_powerStrokePreview(Geom::Path const path, std::vector points) { using namespace Inkscape::LivePathEffect; SPDocument * document = SP_ACTIVE_DOCUMENT; @@ -672,15 +671,14 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector return; } Inkscape::XML::Document *xml_doc = document->getReprDoc(); - SPCurve * curve = new SPCurve(); Geom::PathVector const pathv(path); - if (!_powerpreview) { + SPLPEItem * lpeitem = dynamic_cast(_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 } Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(_powerpreview)); Effect* lpe = lpeitem->getCurrentLPE(); + static_cast(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(_powerpreview); - if (!lpeitem) { - return; - } - Effect* lpe = lpeitem->getCurrentLPE(); - lpe->getRepr()->setAttribute("end_linecap_type", end_linecap_type); - static_cast(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(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 stroreps = this->ps; - std::vector 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 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 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 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 ps; + std::vector key_nodes; std::vector points; + std::vector points_pos; std::vector 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 points, bool write); + void _powerStrokePreview(Geom::Path const path, std::vector 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(); -- cgit v1.2.3 From daae1f8c25dcd6e35a469f488c24c751132dbffc Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Mon, 18 Dec 2017 01:20:41 +0100 Subject: Tiny fix to improbe release pentool --- src/ui/tools/freehand-base.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index e294c37a2..8d701d057 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -242,6 +242,7 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha return; } pt->addPowerStrokePencil(c); + sp_shape->setCurve(c, true); } if(pt->points.empty()){ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -- cgit v1.2.3 From e94c84e82f183e118b3f65c52d814c0155276c3f Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Thu, 21 Dec 2017 01:25:47 +0100 Subject: Refactoring pencil powerstroke --- src/ui/tools/freehand-base.cpp | 49 +++++++++++++++-- src/ui/tools/pencil-tool.cpp | 119 ++++++++++++++++++----------------------- src/ui/tools/pencil-tool.h | 5 +- 3 files changed, 103 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 8d701d057..e09219a23 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -241,8 +241,51 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha if (!c) { return; } - pt->addPowerStrokePencil(c); - sp_shape->setCurve(c, true); + if(dc->sa) { + c = dc->sa_overwrited->copy(); + Effect* lpe = SP_LPE_ITEM(dc->white_item)->getCurrentLPE(); + LPEPowerStroke* ps = static_cast(lpe); + std::vector points; + if (ps) { + if (dc->sa->start) { + points = ps->offset_points.reverse_controlpoints(false); + } else { + points = ps->offset_points.data(); + } + } + size_t sa_curve_size = dc->sa->curve->get_segment_count(); + for (auto ptp:pt->points) { + ptp[Geom::X] = ptp[Geom::X] + sa_curve_size; + points.push_back(ptp); + } + pt->addPowerStrokePencil(c); + if (lpe) { + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("inkscape:original-d" , pvector_str); + g_free(pvector_str); + } else { + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("d" , pvector_str); + g_free(pvector_str); + } + if (ps) { + ps->offset_points.param_set_and_write_new_value(points); + points.clear(); + return; + } + } else { + Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + pt->addPowerStrokePencil(c); + if (lpe) { + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("inkscape:original-d" , pvector_str); + g_free(pvector_str); + } else { + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("d" , pvector_str); + g_free(pvector_str); + } + } } if(pt->points.empty()){ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -868,7 +911,7 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); //Bend needs the transforms applied after, Other effects best before - (dc, item, c, true); + spdc_check_for_and_apply_waiting_LPE(dc, item, c, true); Inkscape::GC::release(repr); item->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); item->updateRepr(); diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 7779f6687..bf2edadba 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -73,6 +73,7 @@ PencilTool::PencilTool() , is_drawing(false) , sketch_n(0) , _powerpreview(NULL) + , _curve(NULL) { } @@ -81,7 +82,7 @@ void PencilTool::setup() { if (prefs->getBool("/tools/freehand/pencil/selcue")) { this->enableSelectionCue(); } - + this->_curve = new SPCurve(); FreehandBase::setup(); this->is_drawing = false; @@ -91,6 +92,7 @@ void PencilTool::setup() { PencilTool::~PencilTool() { + this->_curve->unref(); } void PencilTool::_extinput(GdkEvent *event) { @@ -453,9 +455,8 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand")); 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); + this->points.clear(); this->sa = NULL; this->ea = NULL; this->ps.clear(); @@ -732,7 +733,18 @@ PencilTool::removePowerStrokePreview() } } void -PencilTool::addPowerStrokePencil(SPCurve * c) +PencilTool::addPowerStrokePencil(SPCurve *& c) +{ + if(sa) { + c->append_continuous( this->_curve->copy(), 0.0625); + } else { + c = this->_curve->copy(); + } + return; +} + +void +PencilTool::addPowerStrokePencil() { using namespace Inkscape::LivePathEffect; this->points.clear(); @@ -744,59 +756,31 @@ PencilTool::addPowerStrokePencil(SPCurve * c) 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); - + Geom::Affine transform_coordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); if (min > max){ min = max; } - bool live = false; - SPCurve * curve = new SPCurve(); - if (sa) { - Effect* lpe = SP_LPE_ITEM(white_item)->getCurrentLPE(); - LPEPowerStroke* ps = static_cast(lpe); - if (ps) { - if (sa->start) { - this->points = ps->offset_points.reverse_controlpoints(false); - } else { - this->points = ps->offset_points.data(); - } - } - } - if(!c) { - live = true; - if (sa && sa->curve) { - curve = sa_overwrited->copy(); - if (!green_curve->is_unset()) { - curve->append_continuous( green_curve, 0.0625); - if (!red_curve->is_unset()) { - curve->append_continuous( red_curve, 0.0625); - } - } - } else { - if (!green_curve->is_unset()) { - curve = green_curve->copy(); - if (!red_curve->is_unset()) { - curve->append_continuous( red_curve, 0.0625); - } - } else { - curve = NULL; - } - } - if (!curve || curve->is_unset()) { - curve = NULL; + + if (!green_curve->is_unset()) { + this->_curve = green_curve->copy(); + if (!red_curve->is_unset()) { + this->_curve->append_continuous( red_curve, 0.0625); } } else { - curve = c->copy(); - removePowerStrokePreview(); + this->_curve = NULL; + } + if (!this->_curve || this->_curve->is_unset()) { + this->_curve = NULL; } SPDocument * document = SP_ACTIVE_DOCUMENT; if (!document) { return; } - if (!curve) { + if (!this->_curve) { return; } - Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); - Geom::PathVector pathvector = curve->get_pathvector(); + + Geom::PathVector pathvector = this->_curve->get_pathvector(); if (!pathvector.size()) { return; } @@ -807,11 +791,7 @@ PencilTool::addPowerStrokePencil(SPCurve * c) bool start = true; auto pressure = this->wps.begin(); size_t counter = 0; - Geom::Point position = *this->ps.begin(); - if (!live) { - position *= transformCoordinate.inverse(); - } - double pos = Geom::nearest_time(position, path); + Geom::Point position = Geom::Point(); size_t last = 0; for (auto point = this->ps.begin(); point != this->ps.end(); ++point, ++pressure) { position = *point; @@ -821,6 +801,9 @@ PencilTool::addPowerStrokePencil(SPCurve * c) //We need half width for power stroke pressure_computed = pressure_shrunk * dezoomify_factor/2.0; //remove start pressure gap + position *= transform_coordinate.inverse(); + + double pos = Geom::nearest_time(position, path); if (start) { start = false; this->points.push_back(Geom::Point(pos, pressure_computed)); @@ -829,14 +812,11 @@ PencilTool::addPowerStrokePencil(SPCurve * c) previous_pressure = pressure_shrunk; continue; } - if (!live) { - position *= transformCoordinate.inverse(); - } - pos = Geom::nearest_time(position, path); + if (counter%tol == 0) { this->key_nodes.push_back(position); } - if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > step && pos < path.size() - 1) { + if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > step ) { previous_pressure = pressure_shrunk; this->points.push_back(Geom::Point(pos, pressure_computed)); this->points_pos.push_back(position); @@ -848,20 +828,23 @@ PencilTool::addPowerStrokePencil(SPCurve * c) this->key_nodes.pop_back(); } } - bool last_added = counter%tol == 0; - if (!last_added) { + std::cout << *pressure << "pressure" << std::endl; + std::cout << this->pressure << "pressure222" << std::endl; + //Smooth end + if (counter%tol != 0) { + this->key_nodes.pop_back(); + } else { + this->key_nodes.pop_back(); this->key_nodes.pop_back(); - this->key_nodes.push_back(position); } + 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 { - c = curve->copy(); + _interpolate(this->_curve); + if (!this->_curve->is_empty()) { + _powerStrokePreview(this->_curve->get_pathvector()[0], this->points); } } - curve->unref(); } void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { @@ -881,7 +864,7 @@ void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { sp_canvas_item_destroy(i); } this->green_bpaths.clear(); - this->addPowerStrokePencil(NULL); + this->addPowerStrokePencil(); } } } @@ -982,7 +965,11 @@ void PencilTool::_interpolate() { this->green_curve->reset(); this->red_curve->reset(); this->red_curve_is_valid = false; - _interpolate(this->green_curve); + if (input_has_pressure && _curve) { + this->green_curve = _curve->copy(); + } else { + _interpolate(this->green_curve); + } } diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 354acfbba..7d009e035 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -44,13 +44,15 @@ public: Geom::Point req_tangent; bool is_drawing; + double previous_pressure; std::vector ps; std::vector key_nodes; std::vector points; std::vector points_pos; std::vector wps; - void addPowerStrokePencil(SPCurve * c); + void addPowerStrokePencil(SPCurve *& c); + void addPowerStrokePencil(); void removePowerStrokePreview(); Geom::Piecewise > sketch_interpolation; // the current proposal from the sketched paths unsigned sketch_n; // number of sketches done @@ -65,6 +67,7 @@ protected: virtual bool root_handler(GdkEvent* event); private: + SPCurve * _curve; bool _handleButtonPress(GdkEventButton const &bevent); bool _handleMotionNotify(GdkEventMotion const &mevent); bool _handleButtonRelease(GdkEventButton const &revent); -- cgit v1.2.3 From d9211f4fcd295d5a63bb713463487049311a24a2 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Tue, 26 Dec 2017 00:45:37 +0100 Subject: Fix start anchors --- src/live_effects/lpe-powerstroke.cpp | 27 +- src/live_effects/lpeobject-reference.cpp | 6 +- src/ui/tools/freehand-base.cpp | 158 ++++----- src/ui/tools/freehand-base.h | 4 +- src/ui/tools/pen-tool.cpp | 2 +- src/ui/tools/pencil-tool.cpp | 553 ++++++++++++++++--------------- src/ui/tools/pencil-tool.h | 30 +- src/widgets/pencil-toolbar.cpp | 6 +- 8 files changed, 400 insertions(+), 386 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index e7ceaa397..995f0be35 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -325,7 +325,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise > pwd2_in = pathv[0].toPwSb(); + if (pwd2_in.empty()) { + return path_in; + } Piecewise > der = derivative(pwd2_in); - Piecewise > n = unitVector(der,0.0001); - if (!n.size() || !pwd2_in.size() || !n.size()) { + if (der.empty()) { + return path_in; + } + Piecewise > n = unitVector(der,0.00001); + if (n.empty()) { return path_in; } @@ -598,8 +603,16 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) // add extra points for interpolation between first and last point Point first_point = ts.front(); Point last_point = ts.back(); + //TODO: this is wrong we need to give a calulated Y value ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); - ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) ); +// double startpercentwidth = ts.front()[Geom::X]/pwd2_in.domain().max(); +// double endpercentwidth = (pwd2_in.domain().max() - ts.back()[Geom::X])/pwd2_in.domain().max(); +// double totalwidth = endpercentwidth + startpercentwidth ; +// double factor = endpercentwidth/totalwidth; +// std::cout << factor << "factor" << std::endl; +// double gap = ts.front()[Geom::Y] - ts.back()[Geom::Y]; +// ts.insert(ts.begin(), Point( pwd2_in.domain().min(), ts.back()[Geom::Y] - (gap / factor) ) ); +// ts.push_back( Point( pwd2_in.domain().max(), ts.back()[Geom::Y] - (gap / factor) ) ); } else { // add width data for first and last point on the path // depending on cap type, these first and last points have width zero or take the width from the closest width point. @@ -643,7 +656,9 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } LineJoinType jointype = static_cast(linejoin_type.get_value()); - + if (x.empty() || y.empty()) { + return path_in; + } Piecewise > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise > mirrorpath = reverse( compose(pwd2_in,x) - y*compose(n,x)); diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp index 83cd6623c..89df843e8 100644 --- a/src/live_effects/lpeobject-reference.cpp +++ b/src/live_effects/lpeobject-reference.cpp @@ -76,8 +76,10 @@ LPEObjectReference::link(const char *to) void LPEObjectReference::unlink(void) { - g_free(lpeobject_href); - lpeobject_href = NULL; + if (lpeobject_href) { + g_free(lpeobject_href); + lpeobject_href = NULL; + } detach(); } diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index e09219a23..d2ef1e00d 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -23,6 +23,7 @@ #include "live_effects/lpe-bendpath.h" #include "live_effects/lpe-patternalongpath.h" #include "live_effects/lpe-simplify.h" +#include "live_effects/lpe-powerstroke.h" #include "display/canvas-bpath.h" #include "svg/svg.h" #include "display/curve.h" @@ -37,7 +38,6 @@ #include "selection-chemistry.h" #include "sp-item-group.h" #include "sp-rect.h" -#include "live_effects/lpe-powerstroke.h" #include "style.h" #include "ui/control-manager.h" // clipboard support @@ -92,7 +92,8 @@ FreehandBase::FreehandBase(gchar const *const *cursor_shape) , waiting_LPE_type(Inkscape::LivePathEffect::INVALID_LPE) , red_curve_is_valid(false) , anchor_statusbar(false) - , input_has_pressure(false) + , tablet_enabled(false) + , is_tablet(false) , pressure(DEFAULT_PRESSURE) { } @@ -234,78 +235,52 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha if (SP_IS_PENCIL_CONTEXT(dc)) { PencilTool *pt = SP_PENCIL_CONTEXT(dc); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (dc->input_has_pressure) { + if (dc->tablet_enabled) { SPShape *sp_shape = dynamic_cast(item); if (sp_shape) { SPCurve * c = sp_shape->getCurve(); if (!c) { return; } - if(dc->sa) { - c = dc->sa_overwrited->copy(); - Effect* lpe = SP_LPE_ITEM(dc->white_item)->getCurrentLPE(); - LPEPowerStroke* ps = static_cast(lpe); - std::vector points; - if (ps) { - if (dc->sa->start) { - points = ps->offset_points.reverse_controlpoints(false); - } else { - points = ps->offset_points.data(); - } - } - size_t sa_curve_size = dc->sa->curve->get_segment_count(); - for (auto ptp:pt->points) { - ptp[Geom::X] = ptp[Geom::X] + sa_curve_size; - points.push_back(ptp); - } - pt->addPowerStrokePencil(c); - if (lpe) { - gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); - item->setAttribute("inkscape:original-d" , pvector_str); - g_free(pvector_str); - } else { - gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); - item->setAttribute("d" , pvector_str); - g_free(pvector_str); - } - if (ps) { - ps->offset_points.param_set_and_write_new_value(points); - points.clear(); - return; - } + Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + LPEPowerStroke* ps = NULL; + pt->addPowerStrokePencil(c); + if (lpe) { + ps = static_cast(lpe); + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("inkscape:original-d" , pvector_str); + g_free(pvector_str); } else { - Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); - pt->addPowerStrokePencil(c); - if (lpe) { - gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); - item->setAttribute("inkscape:original-d" , pvector_str); - g_free(pvector_str); - } else { - gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); - item->setAttribute("d" , pvector_str); - g_free(pvector_str); + gchar * pvector_str = sp_svg_write_path(c->get_pathvector()); + item->setAttribute("d" , pvector_str); + g_free(pvector_str); + } + if (ps && dc->sa) { + ps->offset_points.param_set_and_write_new_value(pt->points); + return; + } + if(pt->points.empty()){ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); + const char *stroke_width = sp_repr_css_property(css_item, "stroke-width", "0"); + double swidth; + sp_svg_number_read_d(stroke_width, &swidth); + swidth = prefs->getDouble("/live_effect/power_stroke/width", swidth/2); + if (!swidth) { + swidth = swidth/2; } + points.push_back(Geom::Point(0, swidth)); } - } - if(pt->points.empty()){ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); - const char *stroke_width = sp_repr_css_property(css_item, "stroke-width", "0"); - double swidth; - sp_svg_number_read_d(stroke_width, &swidth); - swidth = prefs->getDouble("/live_effect/power_stroke/width", swidth/2); - if (!swidth) { - swidth = swidth/2; + Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); + lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + ps = static_cast(lpe); + if (ps) { + ps->getRepr()->setAttribute("sort_points", "true"); + ps->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + ps->offset_points.param_set_and_write_new_value(pt->points); } - pt->points.push_back(Geom::Point(0, swidth)); + return; } - Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); - Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); - lpe->getRepr()->setAttribute("sort_points", "true"); - lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); - static_cast(lpe)->offset_points.param_set_and_write_new_value(pt->points); - pt->points.clear(); - return; } } @@ -437,17 +412,12 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, swidth = swidth/2; } if (SP_IS_PENCIL_CONTEXT(dc)) { - if (dc->input_has_pressure) { - if (shape == NONE) { - std::vector points; - spdc_apply_powerstroke_shape(points, dc, item); - //To allow retain color - shape_applied = true; - } else { - PencilTool *pt = SP_PENCIL_CONTEXT(dc); - pt->removePowerStrokePreview(); - shape == NONE; - } + if (dc->tablet_enabled) { + std::vector points; + spdc_apply_powerstroke_shape(points, dc, item); + shape_applied = true; + shape = NONE; + previous_shape_type = NONE; } } #define SHAPE_LENGTH 10 @@ -653,7 +623,7 @@ static void spdc_selection_modified(Inkscape::Selection *sel, guint /*flags*/, F static void spdc_attach_selection(FreehandBase *dc, Inkscape::Selection */*sel*/) { - if (SP_IS_PENCIL_CONTEXT(dc) && dc->sa && dc->input_has_pressure) { + if (SP_IS_PENCIL_CONTEXT(dc) && dc->sa) { return; } // We reset white and forget white/start/end anchors @@ -804,24 +774,27 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed) dc->sa_overwrited->append_continuous(c, 0.0625); c->unref(); dc->sa_overwrited->closepath_current(); - if(dc->sa){ + if (!dc->white_curves.empty()) { dc->white_curves.erase(std::find(dc->white_curves.begin(),dc->white_curves.end(), dc->sa->curve)); - dc->white_curves.push_back(dc->sa_overwrited); } - + dc->white_curves.push_back(dc->sa_overwrited); spdc_flush_white(dc, NULL); return; } // Step C - test start if (dc->sa) { - dc->white_curves.erase(std::find(dc->white_curves.begin(),dc->white_curves.end(), dc->sa->curve)); + if (!dc->white_curves.empty()) { + dc->white_curves.erase(std::find(dc->white_curves.begin(),dc->white_curves.end(), dc->sa->curve)); + } SPCurve *s = dc->sa_overwrited; s->append_continuous(c, 0.0625); c->unref(); c = s; } else /* Step D - test end */ if (dc->ea) { SPCurve *e = dc->ea->curve; - dc->white_curves.erase(std::find(dc->white_curves.begin(),dc->white_curves.end(), e)); + if (!dc->white_curves.empty()) { + dc->white_curves.erase(std::find(dc->white_curves.begin(),dc->white_curves.end(), e)); + } if (!dc->ea->start) { e = reverse_then_unref(e); } @@ -876,8 +849,8 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) // Now we have to go back to item coordinates at last c->transform( dc->white_item - ? (dc->white_item)->dt2i_affine() - : dc->desktop->dt2doc() ); + ? (dc->white_item)->dt2i_affine() + : dc->desktop->dt2doc() ); SPDesktop *desktop = dc->desktop; SPDocument *doc = desktop->getDocument(); @@ -906,9 +879,18 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) repr->setAttribute("d", str); g_free(str); + if (SP_IS_PENCIL_CONTEXT(dc)) { + if (dc->tablet_enabled) { + if (!dc->white_item) { + dc->white_item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); + } + std::cout << "lololololo----------------------lololo" << std::endl; + spdc_check_for_and_apply_waiting_LPE(dc, dc->white_item, c, false); + dc->selection->set(dc->white_item); + } + } if (!dc->white_item) { // Attach repr - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); //Bend needs the transforms applied after, Other effects best before spdc_check_for_and_apply_waiting_LPE(dc, item, c, true); @@ -921,16 +903,6 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) if(previous_shape_type == BEND_CLIPBOARD){ repr->parent()->removeChild(repr); } - } else if (SP_IS_PENCIL_CONTEXT(dc)) { - if (dc->input_has_pressure) { - spdc_check_for_and_apply_waiting_LPE(dc, dc->white_item, c, false); -// Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -// shapeType shape = (shapeType)prefs->getInt(tool_name(dc) + "/shape", 0); -// if (shape == NONE) { -// std::vector points; -// spdc_apply_powerstroke_shape(points, dc, dc->white_item); -// } - } } DocumentUndo::done(doc, SP_IS_PEN_CONTEXT(dc)? SP_VERB_CONTEXT_PEN : SP_VERB_CONTEXT_PENCIL, _("Draw path")); diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h index 4a14cf8d3..09c57f6f7 100644 --- a/src/ui/tools/freehand-base.h +++ b/src/ui/tools/freehand-base.h @@ -96,7 +96,9 @@ public: bool anchor_statusbar; - bool input_has_pressure; + bool tablet_enabled; + + bool is_tablet; gdouble pressure; virtual void set(const Inkscape::Preferences::Entry& val); diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index a42a3a07a..0616ba013 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -99,7 +99,7 @@ PenTool::PenTool() , cl1(NULL) , events_disabled(false) { - input_has_pressure = false; + tablet_enabled = false; } PenTool::PenTool(gchar const *const *cursor_shape) diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index bf2edadba..8d9981ae2 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -67,13 +67,15 @@ const std::string PencilTool::prefsPath = "/tools/freehand/pencil"; PencilTool::PencilTool() : FreehandBase(cursor_pencil_xpm) , p() - , npoints(0) - , state(SP_PENCIL_CONTEXT_IDLE) - , req_tangent(0, 0) - , is_drawing(false) + , _npoints(0) + , _state(SP_PENCIL_CONTEXT_IDLE) + , _req_tangent(0, 0) + , _is_drawing(false) , sketch_n(0) , _powerpreview(NULL) , _curve(NULL) + , _previous_pressure(0.0) + , _last_point(Geom::Point()) { } @@ -85,38 +87,41 @@ void PencilTool::setup() { this->_curve = new SPCurve(); FreehandBase::setup(); - this->is_drawing = false; + this->_is_drawing = false; this->anchor_statusbar = false; } PencilTool::~PencilTool() { - this->_curve->unref(); + if (this->_curve) { + this->_curve->unref(); + } } 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; + is_tablet = true; } else { this->pressure = DDC_DEFAULT_PRESSURE; //If no pressure device ignore pressure button - input_has_pressure = false; + tablet_enabled = false; + is_tablet = false; } } /** Snaps new node relative to the previous node. */ void PencilTool::_endpointSnap(Geom::Point &p, guint const state) { if ((state & GDK_CONTROL_MASK)) { //CTRL enables constrained snapping - if (this->npoints > 0) { + if (this->_npoints > 0) { spdc_endpoint_snap_rotation(this, p, this->p[0], state); } } else { if (!(state & GDK_SHIFT_MASK)) { //SHIFT disables all snapping, except the angular snapping above //After all, the user explicitly asked for angular snapping by //pressing CTRL - boost::optional origin = this->npoints > 0 ? this->p[0] : boost::optional(); + boost::optional origin = this->_npoints > 0 ? this->p[0] : boost::optional(); spdc_endpoint_snap_free(this, p, origin, state); } } @@ -188,11 +193,8 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { pencil_drag_origin_w = Geom::Point(bevent.x,bevent.y); pencil_within_tolerance = true; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - input_has_pressure = prefs->getBool("/tools/freehand/pencil/pressure", true); - if (input_has_pressure) { - this->state = SP_PENCIL_CONTEXT_FREEHAND; - } - switch (this->state) { + tablet_enabled = prefs->getBool("/tools/freehand/pencil/pressure", false); + switch (this->_state) { case SP_PENCIL_CONTEXT_ADDLINE: /* Current segment will be finished with release */ ret = true; @@ -221,15 +223,19 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); } else { m.setup(desktop, true, _powerpreview); - if (!(bevent.state & GDK_SHIFT_MASK)) { + if (tablet_enabled && this->pressure) { // 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 // anchor, which is handled by the sibling branch above) selection->clear(); desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new path")); - if (!input_has_pressure) { - m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); - } + } else 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 + // anchor, which is handled by the sibling branch above) + selection->clear(); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new path")); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } else if (selection->singleItem() && SP_IS_PATH(selection->singleItem())) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Appending to selected path")); m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); @@ -242,7 +248,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) { break; } - this->is_drawing = true; + this->_is_drawing = true; } return ret; } @@ -251,18 +257,25 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { if ((mevent.state & GDK_CONTROL_MASK) && (mevent.state & GDK_BUTTON1_MASK)) { // mouse was accidentally moved during Ctrl+click; // ignore the motion and create a single point - this->is_drawing = false; + this->_is_drawing = false; return true; } bool ret = false; - + if (this->space_panning || (mevent.state & GDK_BUTTON2_MASK) || (mevent.state & GDK_BUTTON3_MASK)) { // allow scrolling - return false; + return ret; } - if ( ( mevent.state & GDK_BUTTON1_MASK ) && !this->grab && this->is_drawing) { + /* Test whether we hit any anchor. */ + SPDrawAnchor *anchor = spdc_test_inside(this, pencil_drag_origin_w); + if (this->pressure == 0.0 && tablet_enabled && !anchor) { + // tablet event was accidentally fired without press; + return ret; + } + + if ( ( mevent.state & GDK_BUTTON1_MASK ) && !this->grab && this->_is_drawing) { /* Grab mouse, so release will not pass unnoticed */ this->grab = SP_CANVAS_ITEM(desktop->acetate); sp_canvas_item_grab(this->grab, ( GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | @@ -273,9 +286,9 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { /* Find desktop coordinates */ Geom::Point p = desktop->w2d(Geom::Point(mevent.x, mevent.y)); - - /* Test whether we hit any anchor. */ - SPDrawAnchor *anchor = spdc_test_inside(this, Geom::Point(mevent.x,mevent.y)); + + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (pencil_within_tolerance) { gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); @@ -283,42 +296,22 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { return false; // Do not drag if we're within tolerance from origin. } } - // motion notify coordinates as given (no snapping back to origin) - if (input_has_pressure && pencil_within_tolerance) { - anchor = spdc_test_inside(this, pencil_drag_origin_w); - if (anchor) { - this->sa = anchor; - //Put the start overwrite curve always on the same direction - if (anchor->start) { - this->sa_overwrited = this->sa->curve->create_reverse(); - } else { - this->sa_overwrited = this->sa->curve->copy(); - } - p = anchor->dp; - this->_setStartpoint(p); - desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path")); - } - } - if (input_has_pressure) { - this->state = SP_PENCIL_CONTEXT_FREEHAND; - } // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process the // motion notify coordinates as given (no snapping back to origin) - if (input_has_pressure && pencil_within_tolerance) { - p = desktop->w2d(pencil_drag_origin_w); - anchor = spdc_test_inside(this, pencil_drag_origin_w); - } pencil_within_tolerance = false; - switch (this->state) { + anchor = spdc_test_inside(this, Geom::Point(mevent.x,mevent.y)); + + + switch (this->_state) { case SP_PENCIL_CONTEXT_ADDLINE: - /* Set red endpoint */ - if (input_has_pressure) { - this->state = SP_PENCIL_CONTEXT_FREEHAND; + if (is_tablet) { + this->_state = SP_PENCIL_CONTEXT_FREEHAND; return false; - } + } + /* Set red endpoint */ if (anchor) { p = anchor->dp; } else { @@ -331,11 +324,11 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { break; default: /* We may be idle or already freehand */ - if ( (mevent.state & GDK_BUTTON1_MASK) && this->is_drawing ) { - if (this->state == SP_PENCIL_CONTEXT_IDLE) { + if ( (mevent.state & GDK_BUTTON1_MASK) && this->_is_drawing ) { + if (this->_state == SP_PENCIL_CONTEXT_IDLE) { sp_event_context_discard_delayed_snap_event(this); } - this->state = SP_PENCIL_CONTEXT_FREEHAND; + this->_state = SP_PENCIL_CONTEXT_FREEHAND; if ( !this->sa && !this->green_anchor ) { /* Create green anchor */ @@ -344,14 +337,17 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { if (anchor) { p = anchor->dp; } - if ( this->npoints != 0) { // buttonpress may have happened before we entered draw context! + if ( this->_npoints != 0) { // buttonpress may have happened before we entered draw context! if (this->ps.empty()) { // Only in freehand mode we have to add the first point also to this->ps (apparently) // - We cannot add this point in spdc_set_startpoint, because we only need it for freehand // - We cannot do this in the button press handler because at that point we don't know yet // whether we're going into freehand mode or not this->ps.push_back(this->p[0]); - this->wps.push_back(this->pressure); + if (tablet_enabled) { + this->_wps.push_back(this->pressure); + this->addPowerStrokePencil(); + } } this->_addFreehandPoint(p, mevent.state); ret = true; @@ -359,11 +355,14 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { if (anchor && !this->anchor_statusbar) { this->message_context->set(Inkscape::NORMAL_MESSAGE, _("Release here to close and finish the path.")); this->anchor_statusbar = true; + this->ea = anchor; } else if (!anchor && this->anchor_statusbar) { this->message_context->clear(); this->anchor_statusbar = false; + this->ea = NULL; } else if (!anchor) { this->message_context->set(Inkscape::NORMAL_MESSAGE, _("Drawing a freehand path")); + this->ea = NULL; } } else { @@ -393,8 +392,8 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) { bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { bool ret = false; - if ( revent.button == 1 && this->is_drawing && !this->space_panning) { - this->is_drawing = false; + if ( revent.button == 1 && this->_is_drawing && !this->space_panning) { + this->_is_drawing = false; /* Find desktop coordinates */ Geom::Point p = desktop->w2d(Geom::Point(revent.x, revent.y)); @@ -402,13 +401,28 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { /* Test whether we hit any anchor. */ SPDrawAnchor *anchor = spdc_test_inside(this, Geom::Point(revent.x, revent.y)); - switch (this->state) { + switch (this->_state) { case SP_PENCIL_CONTEXT_IDLE: /* Releasing button in idle mode means single click */ /* We have already set up start point/anchor in button_press */ - if (!(revent.state & GDK_CONTROL_MASK)) { + if (!(revent.state & GDK_CONTROL_MASK) && !is_tablet) { // Ctrl+click creates a single point so only set context in ADDLINE mode when Ctrl isn't pressed - this->state = SP_PENCIL_CONTEXT_ADDLINE; + this->_state = SP_PENCIL_CONTEXT_ADDLINE; + } + /*Or select the down item if we are in tablet mode*/ + if (is_tablet) { + using namespace Inkscape::LivePathEffect; + SPItem * item = sp_event_context_find_item (desktop, Geom::Point(revent.x, revent.y), FALSE, FALSE); + if (item && (!this->white_item || item != white_item)) { + Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + if (lpe) { + LPEPowerStroke* ps = static_cast(lpe); + if (ps) { + desktop->selection->clear(); + desktop->selection->add(item); + } + } + } } break; case SP_PENCIL_CONTEXT_ADDLINE: @@ -421,11 +435,11 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { this->ea = anchor; this->_setEndpoint(p); this->_finishEndpoint(); - this->state = SP_PENCIL_CONTEXT_IDLE; + this->_state = SP_PENCIL_CONTEXT_IDLE; sp_event_context_discard_delayed_snap_event(this); break; case SP_PENCIL_CONTEXT_FREEHAND: - if (revent.state & GDK_MOD1_MASK) { + if (revent.state & GDK_MOD1_MASK && !tablet_enabled) { /* sketch mode: interpolate the sketched path and improve the current output path with the new interpolation. don't finish sketch */ this->_sketchInterpolate(); @@ -433,7 +447,7 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { this->green_anchor = sp_draw_anchor_destroy(this->green_anchor); } - this->state = SP_PENCIL_CONTEXT_SKETCH; + this->_state = SP_PENCIL_CONTEXT_SKETCH; } else { /* Finish segment now */ /// \todo fixme: Clean up what follows (Lauris) @@ -441,7 +455,7 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { p = anchor->dp; } else { Geom::Point p_end = p; - if (!input_has_pressure) { + if (!tablet_enabled) { this->_endpointSnap(p_end, revent.state); if (p_end != p) { // then we must have snapped! @@ -452,7 +466,9 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { this->ea = anchor; /* Write curves to object */ - + if (tablet_enabled) { + _powerstrokeInterpolate(true); + } desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand")); this->_interpolate(); spdc_concat_colors_and_flush(this, FALSE); @@ -460,11 +476,15 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { this->sa = NULL; this->ea = NULL; this->ps.clear(); - this->wps.clear(); + this->_wps.clear(); + this->_key_nodes.clear(); + this->_points_pos.clear(); + this->_last_point = Geom::Point(); + this->_previous_pressure = 0.0; if (this->green_anchor) { this->green_anchor = sp_draw_anchor_destroy(this->green_anchor); } - this->state = SP_PENCIL_CONTEXT_IDLE; + this->_state = SP_PENCIL_CONTEXT_IDLE; // reset sketch mode too this->sketch_n = 0; } @@ -492,8 +512,8 @@ void PencilTool::_cancel() { this->grab = NULL; } - this->is_drawing = false; - this->state = SP_PENCIL_CONTEXT_IDLE; + this->_is_drawing = false; + this->_state = SP_PENCIL_CONTEXT_IDLE; sp_event_context_discard_delayed_snap_event(this); this->red_curve->reset(); @@ -527,9 +547,9 @@ bool PencilTool::_handleKeyPress(GdkEventKey const &event) { } break; case GDK_KEY_Escape: - if (this->npoints != 0) { + if (this->_npoints != 0) { // if drawing, cancel, otherwise pass it up for deselecting - if (this->state != SP_PENCIL_CONTEXT_IDLE) { + if (this->_state != SP_PENCIL_CONTEXT_IDLE) { this->_cancel(); ret = true; } @@ -537,9 +557,9 @@ bool PencilTool::_handleKeyPress(GdkEventKey const &event) { break; case GDK_KEY_z: case GDK_KEY_Z: - if (Inkscape::UI::held_only_control(event) && this->npoints != 0) { + if (Inkscape::UI::held_only_control(event) && this->_npoints != 0) { // if drawing, cancel, otherwise pass it up for undo - if (this->state != SP_PENCIL_CONTEXT_IDLE) { + if (this->_state != SP_PENCIL_CONTEXT_IDLE) { this->_cancel(); ret = true; } @@ -556,7 +576,7 @@ bool PencilTool::_handleKeyPress(GdkEventKey const &event) { case GDK_KEY_Alt_R: case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: - if (this->state == SP_PENCIL_CONTEXT_IDLE) { + if (this->_state == SP_PENCIL_CONTEXT_IDLE) { this->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Sketch mode: holding Alt interpolates between sketched paths. Release Alt to finalize.")); } break; @@ -574,15 +594,15 @@ bool PencilTool::_handleKeyRelease(GdkEventKey const &event) { case GDK_KEY_Alt_R: case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: - if (this->state == SP_PENCIL_CONTEXT_SKETCH) { - spdc_concat_colors_and_flush(this, false); + if (this->_state == SP_PENCIL_CONTEXT_SKETCH) { + spdc_concat_colors_and_flush(this, FALSE); this->sketch_n = 0; this->sa = NULL; this->ea = NULL; if (this->green_anchor) { this->green_anchor = sp_draw_anchor_destroy(this->green_anchor); } - this->state = SP_PENCIL_CONTEXT_IDLE; + this->_state = SP_PENCIL_CONTEXT_IDLE; sp_event_context_discard_delayed_snap_event(this); this->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing freehand sketch")); ret = true; @@ -598,10 +618,10 @@ bool PencilTool::_handleKeyRelease(GdkEventKey const &event) { * Reset points and set new starting point. */ void PencilTool::_setStartpoint(Geom::Point const &p) { - this->npoints = 0; + this->_npoints = 0; this->red_curve_is_valid = false; if (in_svg_plane(p)) { - this->p[this->npoints++] = p; + this->p[this->_npoints++] = p; } } @@ -616,27 +636,27 @@ void PencilTool::_setStartpoint(Geom::Point const &p) { * We change RED curve. */ void PencilTool::_setEndpoint(Geom::Point const &p) { - if (this->npoints == 0) { + if (this->_npoints == 0) { return; /* May occur if first point wasn't in SVG plane (e.g. weird w2d transform, perhaps from bad * zoom setting). */ } - g_return_if_fail( this->npoints > 0 ); + g_return_if_fail( this->_npoints > 0 ); this->red_curve->reset(); if ( ( p == this->p[0] ) || !in_svg_plane(p) ) { - this->npoints = 1; + this->_npoints = 1; } else { this->p[1] = p; - this->npoints = 2; + this->_npoints = 2; this->red_curve->moveto(this->p[0]); this->red_curve->lineto(this->p[1]); this->red_curve_is_valid = true; - if (!input_has_pressure) { + if (!tablet_enabled) { sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); } } @@ -664,7 +684,7 @@ void PencilTool::_finishEndpoint() { } void -PencilTool::_powerStrokePreview(Geom::Path const path, std::vector points) +PencilTool::_powerStrokePreview(Geom::Path const path) { using namespace Inkscape::LivePathEffect; SPDocument * document = SP_ACTIVE_DOCUMENT; @@ -704,12 +724,18 @@ PencilTool::_powerStrokePreview(Geom::Path const path, std::vector } Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(_powerpreview)); Effect* lpe = lpeitem->getCurrentLPE(); - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); - lpe->getRepr()->setAttribute("sort_points", "true"); - lpe->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + LPEPowerStroke * ps = static_cast(lpe); + if (ps) { + ps->offset_points.param_set_and_write_new_value(this->points); + ps->getRepr()->setAttribute("sort_points", "true"); + ps->getRepr()->setAttribute("interpolator_type", "CentripetalCatmullRom"); + } } else { Effect* lpe = lpeitem->getCurrentLPE(); - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); + LPEPowerStroke * ps = static_cast(lpe); + if (ps) { + ps->offset_points.param_set_and_write_new_value(this->points); + } gchar * pvector_str = sp_svg_write_path(pathv); _powerpreview->setAttribute("inkscape:original-d" , pvector_str); g_free(pvector_str); @@ -735,136 +761,73 @@ PencilTool::removePowerStrokePreview() void PencilTool::addPowerStrokePencil(SPCurve *& c) { - if(sa) { - c->append_continuous( this->_curve->copy(), 0.0625); - } else { + if (this->_curve) { c = this->_curve->copy(); } - return; } void PencilTool::addPowerStrokePencil() { using namespace Inkscape::LivePathEffect; - this->points.clear(); - this->points_pos.clear(); - this->key_nodes.clear(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); 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); Geom::Affine transform_coordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); if (min > max){ min = max; } - if (!green_curve->is_unset()) { this->_curve = green_curve->copy(); if (!red_curve->is_unset()) { this->_curve->append_continuous( red_curve, 0.0625); } - } else { - this->_curve = NULL; } if (!this->_curve || this->_curve->is_unset()) { this->_curve = NULL; } - SPDocument * document = SP_ACTIVE_DOCUMENT; - if (!document) { - return; - } - if (!this->_curve) { - return; - } - - Geom::PathVector pathvector = this->_curve->get_pathvector(); - if (!pathvector.size()) { - return; + double dezoomify_factor = 0.05 * 1000/SP_EVENT_CONTEXT(this)->desktop->current_zoom();//\/100 we want 100% = 1; + double last_pressure = this->_wps.back(); + double pressure_shrunk = (last_pressure * (max - min)) + min; + //We need half width for power stroke + double pressure_computed = pressure_shrunk * dezoomify_factor/2.0; + this->_last_point = this->ps.back(); + this->_last_point *= transform_coordinate.inverse(); + if (this->ps.size() == 1 || std::abs(_previous_pressure - pressure_shrunk) > step ) { + _previous_pressure = pressure_shrunk; + this->points.push_back(Geom::Point(0, pressure_computed)); + this->_points_pos.push_back(this->_last_point); } - Geom::Path const path = pathvector[0]; - 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; - bool start = true; - auto pressure = this->wps.begin(); - size_t counter = 0; - Geom::Point position = Geom::Point(); - 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 - position *= transform_coordinate.inverse(); - - double pos = Geom::nearest_time(position, path); - if (start) { - start = false; - 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; - } - - if (counter%tol == 0) { - this->key_nodes.push_back(position); - } - if (pos < 1e6 && std::abs(previous_pressure - pressure_shrunk) > step ) { - 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 (Geom::are_near(this->key_nodes[this->key_nodes.size()-1],this->key_nodes[this->key_nodes.size()-2])) { - this->key_nodes.pop_back(); - } - } - std::cout << *pressure << "pressure" << std::endl; - std::cout << this->pressure << "pressure222" << std::endl; - //Smooth end - if (counter%tol != 0) { - this->key_nodes.pop_back(); - } else { - this->key_nodes.pop_back(); - this->key_nodes.pop_back(); - } - this->key_nodes.push_back(position); - - if (this->points.size() > 0) { - _interpolate(this->_curve); - if (!this->_curve->is_empty()) { - _powerStrokePreview(this->_curve->get_pathvector()[0], this->points); + if (this->_curve && this->ps.size() > 1) { + _powerstrokeInterpolate(false); + Geom::PathVector cpv = this->_curve->get_pathvector(); + if (cpv.size()) { + _powerStrokePreview(cpv[0]); } } } void PencilTool::_addFreehandPoint(Geom::Point const &p, guint /*state*/) { - g_assert( this->npoints > 0 ); - g_return_if_fail(unsigned(this->npoints) < G_N_ELEMENTS(this->p)); + g_assert( this->_npoints > 0 ); + g_return_if_fail(unsigned(this->_npoints) < G_N_ELEMENTS(this->p)); - if ( ( p != this->p[ this->npoints - 1 ] ) + if ( ( p != this->p[ this->_npoints - 1 ] ) && in_svg_plane(p) ) { - this->p[this->npoints++] = p; + this->p[this->_npoints++] = p; this->_fitAndSplit(); this->ps.push_back(p); - if (input_has_pressure) { - this->wps.push_back(this->pressure); + if (tablet_enabled) { + if (this->pressure != 0) { + this->_wps.push_back(this->pressure); + this->addPowerStrokePencil(); + } 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(); } } } @@ -876,26 +839,120 @@ square(double const x) } void -PencilTool::_interpolate(SPCurve * curve) { - if ( this->ps.size() <= 1 ) { +PencilTool::_powerstrokeInterpolate(bool apply) { + size_t ps_size = this->ps.size(); + if ( ps_size <= 1 ) { return; } using Geom::X; using Geom::Y; + if (apply) { + removePowerStrokePreview(); + } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + //This is a calculated number of nodes from 2 to 12 to get a simplify simil + int tol = 2 + (prefs->getIntLimited("/tools/freehand/pencil/tolerance",10, 1, 100)/10); + std::unique_ptr interpolator( Geom::Interpolate::Interpolator::create(Geom::Interpolate::INTERP_CENTRIPETAL_CATMULLROM) ); + Geom::Affine transform_coordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); + this->_key_nodes.clear(); + std::vector sa_points; + SPItem *item = selection ? selection->singleItem() : NULL; + if(sa && apply && item) { + using namespace Inkscape::LivePathEffect; + SPCurve * c = sa_overwrited->copy(); + Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + LPEPowerStroke* ps = static_cast(lpe); + if (ps) { + if (sa->start) { + sa_points = ps->offset_points.reverse_controlpoints(false); + } else { + sa_points = ps->offset_points.data(); + } + } + Geom::PathVector cpv = c->get_pathvector(); + c->reset(); + cpv *= item->dt2i_affine(); + this->_key_nodes = cpv.nodes(); + this->_key_nodes.pop_back(); + } + size_t count = 0; + for (auto current:this->ps) { + current *= transform_coordinate.inverse(); + Geom::Point prev = Geom::Point(0,0); + if (count == ps_size - 1 || (apply && count%tol == 0 ) || (!apply && count%2 == 0)) { + size_t keys_size = this->_key_nodes.size(); + if (count == ps_size - 1 && apply && keys_size > 1) { + this->_key_nodes.pop_back(); + keys_size--; + } + this->_key_nodes.push_back(current); + if (keys_size > 1) { + prev = this->_key_nodes[keys_size-1]; + current = this->_key_nodes[keys_size]; + if (Geom::are_near(current, prev)) { + this->_key_nodes.pop_back(); + } + } + } + count++; + } + Geom::Path path(this->_key_nodes.front()); + if (this->_key_nodes.size() == 2) { + path.appendNew(this->_key_nodes.back()); + } else { + path = interpolator->interpolateToPath(this->_key_nodes); + } + count = 0; + size_t points_size = this->_points_pos.size(); + for (auto point:this->_points_pos) { + double pos = Geom::nearest_time(point, path); + this->points[count][Geom::X] = pos; + if (count > 1 && (this->points[count-1][Geom::X] == pos || (apply && pos > path.size() - 1 ))) { + this->points.pop_back(); + } + count++; + } + sa_points.insert(sa_points.end(),this->points.begin(),this->points.end()); + this->points = sa_points; + sa_points.clear(); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), NULL); + if (!path.empty()){ + this->_curve->set_pathvector(path); + if( apply && + sa && + ea + && sa->curve == ea->curve + && ( ( sa != ea ) + || sa->curve->is_closed() )) + { + this->_curve->closepath_current(); + } + } +} +void PencilTool::_interpolate() { + size_t ps_size = this->ps.size(); + if ( ps_size <= 1 ) { + return; + } + + using Geom::X; + using Geom::Y; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - //Remove powerstroke arifacts with input_has_pressure min clamp + //Remove powerstroke arifacts with tablet_enabled min clamp double tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4; bool simplify = prefs->getInt("/tools/freehand/pencil/simplify", 0); if(simplify){ double tol2 = prefs->getDoubleLimited("/tools/freehand/pencil/base-simplify", 25.0, 1.0, 100.0) * 0.4; tol = std::min(tol,tol2); } - + this->green_curve->reset(); + this->red_curve->reset(); + this->red_curve_is_valid = false; 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)); + g_assert(is_zero(this->_req_tangent) || is_unit_vector(this->_req_tangent)); int n_points = this->ps.size(); @@ -903,72 +960,40 @@ PencilTool::_interpolate(SPCurve * curve) { int max_segs = 4 * n_points; std::vector 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) { - if (input_has_pressure) { - std::unique_ptr 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 { - //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]); - } - } - } - 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) ); + /* 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 { + this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); } } - } -} - -void PencilTool::_interpolate() { - this->green_curve->reset(); - this->red_curve->reset(); - this->red_curve_is_valid = false; - if (input_has_pressure && _curve) { - this->green_curve = _curve->copy(); - } else { - _interpolate(this->green_curve); + 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) ); + } } } @@ -990,7 +1015,7 @@ void PencilTool::_sketchInterpolate() { bool average_all_sketches = prefs->getBool("/tools/freehand/pencil/average_all_sketches", true); - g_assert(is_zero(this->req_tangent) || is_unit_vector(this->req_tangent)); + g_assert(is_zero(this->_req_tangent) || is_unit_vector(this->_req_tangent)); this->red_curve->reset(); this->red_curve_is_valid = false; @@ -1043,7 +1068,7 @@ void PencilTool::_sketchInterpolate() { this->green_curve->reset(); this->green_curve->set_pathvector(Geom::path_from_piecewise(this->sketch_interpolation, 0.01)); - if (!input_has_pressure) { + if (!tablet_enabled) { sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->green_curve); } /* Fit and draw and copy last point */ @@ -1054,11 +1079,11 @@ void PencilTool::_sketchInterpolate() { 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; + 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) ) + this->_req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) ) ? Geom::Point(0, 0) : Geom::unit_vector(req_vec) ); } @@ -1066,24 +1091,24 @@ void PencilTool::_sketchInterpolate() { this->ps.clear(); this->points.clear(); - this->wps.clear(); + this->_wps.clear(); } void PencilTool::_fitAndSplit() { - g_assert( this->npoints > 1 ); + g_assert( this->_npoints > 1 ); double const tolerance_sq = 0; Geom::Point b[4]; - g_assert(is_zero(this->req_tangent) - || is_unit_vector(this->req_tangent)); + g_assert(is_zero(this->_req_tangent) + || is_unit_vector(this->_req_tangent)); Geom::Point const tHatEnd(0, 0); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int const n_segs = Geom::bezier_fit_cubic_full(b, NULL, this->p, this->npoints, - this->req_tangent, tHatEnd, + int const n_segs = Geom::bezier_fit_cubic_full(b, NULL, this->p, this->_npoints, + this->_req_tangent, tHatEnd, tolerance_sq, 1); if ( n_segs > 0 - && unsigned(this->npoints) < G_N_ELEMENTS(this->p) ) + && unsigned(this->_npoints) < G_N_ELEMENTS(this->p) ) { /* Fit and draw and reset state */ @@ -1102,7 +1127,7 @@ void PencilTool::_fitAndSplit() { }else{ this->red_curve->curveto(b[1], b[2], b[3]); } - if (!input_has_pressure) { + if (!tablet_enabled) { sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); } this->red_curve_is_valid = true; @@ -1116,11 +1141,11 @@ void PencilTool::_fitAndSplit() { Geom::Curve const * last_seg = this->red_curve->last_segment(); g_assert( last_seg ); // Relevance: validity of (*last_seg) this->p[0] = last_seg->finalPoint(); - this->npoints = 1; + 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) ) + this->_req_tangent = ( ( Geom::is_zero(req_vec) || !in_svg_plane(req_vec) ) ? Geom::Point(0, 0) : Geom::unit_vector(req_vec) ); } diff --git a/src/ui/tools/pencil-tool.h b/src/ui/tools/pencil-tool.h index 7d009e035..c361978cb 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -37,28 +37,15 @@ class PencilTool : public FreehandBase { public: PencilTool(); virtual ~PencilTool(); - Geom::Point p[16]; - gint npoints; - PencilState state; - Geom::Point req_tangent; - - bool is_drawing; - double previous_pressure; std::vector ps; - std::vector key_nodes; std::vector points; - std::vector points_pos; - std::vector wps; - void addPowerStrokePencil(SPCurve *& c); void addPowerStrokePencil(); void removePowerStrokePreview(); Geom::Piecewise > sketch_interpolation; // the current proposal from the sketched paths unsigned sketch_n; // number of sketches done - static const std::string prefsPath; - virtual const std::string& getPrefsPath(); protected: @@ -67,25 +54,34 @@ protected: virtual bool root_handler(GdkEvent* event); private: - SPCurve * _curve; bool _handleButtonPress(GdkEventButton const &bevent); bool _handleMotionNotify(GdkEventMotion const &mevent); bool _handleButtonRelease(GdkEventButton const &revent); bool _handleKeyPress(GdkEventKey const &event); bool _handleKeyRelease(GdkEventKey const &event); void _setStartpoint(Geom::Point const &p); - void _powerStrokePreview(Geom::Path const path, std::vector points); - SPShape *_powerpreview; + void _powerStrokePreview(Geom::Path const path); void _setEndpoint(Geom::Point const &p); void _finishEndpoint(); void _addFreehandPoint(Geom::Point const &p, guint state); void _fitAndSplit(); void _interpolate(); - void _interpolate(SPCurve * curve); + void _powerstrokeInterpolate(bool apply); void _sketchInterpolate(); void _extinput(GdkEvent *event); void _cancel(); void _endpointSnap(Geom::Point &p, guint const state); + std::vector _wps; + std::vector _points_pos; + std::vector _key_nodes; + Geom::Point _last_point; + double _previous_pressure; + SPCurve * _curve; + SPShape *_powerpreview; + Geom::Point _req_tangent; + bool _is_drawing; + PencilState _state; + gint _npoints; }; } diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp index ea7411285..bc5112a89 100644 --- a/src/widgets/pencil-toolbar.cpp +++ b/src/widgets/pencil-toolbar.cpp @@ -99,9 +99,11 @@ static void use_pencil_pressure(InkToggleAction* itact, GObject *dataKludge) { if (pressure) { gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "minpressure") ), true ); gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "maxpressure") ), true ); + gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "shape_action") ), false ); } else { gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "minpressure") ), false ); gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "maxpressure") ), false ); + gtk_action_set_visible( GTK_ACTION( g_object_get_data(dataKludge, "shape_action") ), true ); } } @@ -237,7 +239,7 @@ static void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GOb g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder ); gtk_action_group_add_action( mainActions, GTK_ACTION(act1) ); g_object_set_data( holder, "shape_action", act1 ); - if (prefs->getInt("/tools/freehand/pencil/freehand-mode", 0) == 3) { + if (prefs->getInt("/tools/freehand/pencil/freehand-mode", 0) == 3 || (tool_is_pencil && prefs->getBool("/tools/freehand/pencil/pressure", false))) { gtk_action_set_visible( GTK_ACTION(act1), false ); } else { gtk_action_set_visible( GTK_ACTION(act1), true ); @@ -510,7 +512,7 @@ void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb _("Use pressure input"), INKSCAPE_ICON("draw-use-pressure"), GTK_ICON_SIZE_SMALL_TOOLBAR ); - bool pressure = prefs->getBool(freehand_tool_name(holder) + "/pressure", true); + bool pressure = prefs->getBool(freehand_tool_name(holder) + "/pressure", false); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(itact), pressure ); g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(use_pencil_pressure), holder) ; gtk_action_group_add_action( mainActions, GTK_ACTION(itact) ); -- cgit v1.2.3 From d25de3679d94cc03fdba8aba4de20fab723b4b27 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Tue, 26 Dec 2017 17:14:27 +0100 Subject: Fix power stroke closed path and minor tweacks for pencil powerstroke --- src/live_effects/lpe-powerstroke.cpp | 48 +++++++++++++++++++--------------- src/ui/dialog/inkscape-preferences.cpp | 2 +- src/ui/tools/freehand-base.cpp | 1 - src/ui/tools/pencil-tool.cpp | 9 +++++-- 4 files changed, 35 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 995f0be35..4e937c1e4 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -599,20 +599,33 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) if (sort_points) { sort(ts.begin(), ts.end(), compare_offsets); } + // create stroke path where points (x,y) := (t, offset) + Geom::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast(interpolator_type.get_value())); + if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { + johan->setBeta(interpolator_beta); + } + if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(interpolator)) { + smooth->setBeta(interpolator_beta); + } if (pathv[0].closed()) { - // add extra points for interpolation between first and last point - Point first_point = ts.front(); - Point last_point = ts.back(); - //TODO: this is wrong we need to give a calulated Y value - ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); -// double startpercentwidth = ts.front()[Geom::X]/pwd2_in.domain().max(); -// double endpercentwidth = (pwd2_in.domain().max() - ts.back()[Geom::X])/pwd2_in.domain().max(); -// double totalwidth = endpercentwidth + startpercentwidth ; -// double factor = endpercentwidth/totalwidth; -// std::cout << factor << "factor" << std::endl; -// double gap = ts.front()[Geom::Y] - ts.back()[Geom::Y]; -// ts.insert(ts.begin(), Point( pwd2_in.domain().min(), ts.back()[Geom::Y] - (gap / factor) ) ); -// ts.push_back( Point( pwd2_in.domain().max(), ts.back()[Geom::Y] - (gap / factor) ) ); + std::vector ts_close; + //we have only one knot or overwrite before + Geom::Point start = Geom::Point( pwd2_in.domain().min(), ts.front()[Geom::Y]); + Geom::Point end = Geom::Point( pwd2_in.domain().max(), ts.front()[Geom::Y]); + if (ts.size() > 1) { + ts_close.push_back(ts[ts.size()-2]); + ts_close.push_back(ts.back()); + ts_close.push_back(ts.front()); + ts_close.push_back(ts[1]); + Geom::Path closepath = interpolator->interpolateToPath(ts_close); + start = closepath.pointAt(Geom::nearest_time(Geom::Point( pwd2_in.domain().min(),0), closepath)); + start[Geom::X] = pwd2_in.domain().min(); + end = start; + end[Geom::X] = pwd2_in.domain().max(); + } + ts.insert(ts.begin(), start ); + ts.push_back( end ); + ts_close.clear(); } else { // add width data for first and last point on the path // depending on cap type, these first and last points have width zero or take the width from the closest width point. @@ -629,14 +642,7 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) 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::Interpolate::Interpolator *interpolator = Geom::Interpolate::Interpolator::create(static_cast(interpolator_type.get_value())); - if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast(interpolator)) { - johan->setBeta(interpolator_beta); - } - if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast(interpolator)) { - smooth->setBeta(interpolator_beta); - } + Geom::Path strokepath = interpolator->interpolateToPath(ts); delete interpolator; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index eb4a6567e..0ac12d15c 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -432,7 +432,7 @@ void InkscapePreferences::initPageTools() this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0); 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->AddPencilPowerStrokePressureStep(_page_pencil, "/tools/freehand/pencil", 5); _page_pencil.add_group_header( _("Sketch mode")); _page_pencil.add_line( true, "", _pencil_average_all_sketches, "", diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index d2ef1e00d..8a078d510 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -884,7 +884,6 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) if (!dc->white_item) { dc->white_item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); } - std::cout << "lololololo----------------------lololo" << std::endl; spdc_check_for_and_apply_waiting_LPE(dc, dc->white_item, c, false); dc->selection->set(dc->white_item); } diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 8d9981ae2..2f68688a1 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -771,7 +771,7 @@ PencilTool::addPowerStrokePencil() { using namespace Inkscape::LivePathEffect; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - double step = prefs->getIntLimited("/tools/freehand/pencil/ps-step-pressure",10, 1, 100)/100.0; + double step = prefs->getIntLimited("/tools/freehand/pencil/ps-step-pressure",5, 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; Geom::Affine transform_coordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); @@ -790,11 +790,16 @@ PencilTool::addPowerStrokePencil() double dezoomify_factor = 0.05 * 1000/SP_EVENT_CONTEXT(this)->desktop->current_zoom();//\/100 we want 100% = 1; double last_pressure = this->_wps.back(); double pressure_shrunk = (last_pressure * (max - min)) + min; + step = (step * (max - min)) + min; //We need half width for power stroke double pressure_computed = pressure_shrunk * dezoomify_factor/2.0; this->_last_point = this->ps.back(); this->_last_point *= transform_coordinate.inverse(); - if (this->ps.size() == 1 || std::abs(_previous_pressure - pressure_shrunk) > step ) { + if (this->ps.size() == 1 || + std::abs(_previous_pressure - pressure_shrunk) > step || + _previous_pressure == 0.0 || + (_previous_pressure > step && pressure_shrunk < step)) + { _previous_pressure = pressure_shrunk; this->points.push_back(Geom::Point(0, pressure_computed)); this->_points_pos.push_back(this->_last_point); -- cgit v1.2.3 From 421d3605d3c6dd8a54a36236cbeb478dff06f194 Mon Sep 17 00:00:00 2001 From: Jabiertxo Arraiza Cenoz Date: Wed, 27 Dec 2017 19:38:11 +0100 Subject: Allow erase knots in powerstroke --- src/knot.cpp | 4 ++- .../parameter/powerstrokepointarray.cpp | 29 +++++++++++----------- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/knot.cpp b/src/knot.cpp index 8296891f4..b2f6dbe62 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -471,7 +471,9 @@ void SPKnot::updateCtrl() { void SPKnot::_setCtrlState() { int state = SP_KNOT_STATE_NORMAL; - if (this->flags & SP_KNOT_DRAGGING) { + if (this->flags | SP_KNOT_VISIBLE) { + return; + } else if (this->flags & SP_KNOT_DRAGGING) { state = SP_KNOT_STATE_DRAGGING; } else if (this->flags & SP_KNOT_MOUSEOVER) { state = SP_KNOT_STATE_MOUSEOVER; diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index 289498498..64c772602 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -230,22 +230,21 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state) if (state & GDK_MOD1_MASK) { // delete the clicked knot std::vector & vec = _pparam->_vector; - vec.erase(vec.begin() + _index); - _pparam->param_set_and_write_new_value(vec); - - // remove knot from knotholder - parent_holder->entity.remove(this); - // shift knots down one index - for(std::list::iterator ent = parent_holder->entity.begin(); ent != parent_holder->entity.end(); ++ent) { - PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast(*ent); - if ( pspa_ent && pspa_ent->_pparam == this->_pparam ) { // check if the knotentity belongs to this powerstrokepointarray parameter - if (pspa_ent->_index > this->_index) { - --pspa_ent->_index; + if (vec.size() > 1) { //Force dont remove last knot + vec.erase(vec.begin() + _index); + _pparam->param_set_and_write_new_value(vec); + // shift knots down one index + for(std::list::iterator ent = parent_holder->entity.begin(); ent != parent_holder->entity.end(); ++ent) { + PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast(*ent); + if ( pspa_ent && pspa_ent->_pparam == this->_pparam ) { // check if the knotentity belongs to this powerstrokepointarray parameter + if (pspa_ent->_index > this->_index) { + --pspa_ent->_index; + } } - } - }; - // delete self and return - delete this; + }; + // temporary hide, when knotholder were recreated it finaly drop + this->knot->hide(); + } return; } else { // add a knot to XML -- cgit v1.2.3