diff options
| author | Marc Jeanmougin <marc@jeanmougin.fr> | 2017-12-29 10:18:01 +0000 |
|---|---|---|
| committer | Marc Jeanmougin <marc@jeanmougin.fr> | 2017-12-29 10:18:01 +0000 |
| commit | 9564781d4eef5ac13b2c4ec393171d00fbdacd1d (patch) | |
| tree | 93481b36fc6004238b6e919db03ac1680e11f096 /src | |
| parent | remove git remnants (diff) | |
| parent | Merge branch 'master' into powerpencilII (diff) | |
| download | inkscape-9564781d4eef5ac13b2c4ec393171d00fbdacd1d.tar.gz inkscape-9564781d4eef5ac13b2c4ec393171d00fbdacd1d.zip | |
Merge branch 'master' of gitlab.com:inkscape/inkscape
Diffstat (limited to 'src')
| -rw-r--r-- | src/knot.cpp | 4 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke.cpp | 103 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke.h | 1 | ||||
| -rw-r--r-- | src/live_effects/lpeobject-reference.cpp | 6 | ||||
| -rw-r--r-- | src/live_effects/parameter/powerstrokepointarray.cpp | 54 | ||||
| -rw-r--r-- | src/live_effects/parameter/powerstrokepointarray.h | 1 | ||||
| -rw-r--r-- | src/ui/dialog/inkscape-preferences.cpp | 41 | ||||
| -rw-r--r-- | src/ui/dialog/inkscape-preferences.h | 5 | ||||
| -rw-r--r-- | src/ui/tools/freehand-base.cpp | 117 | ||||
| -rw-r--r-- | src/ui/tools/freehand-base.h | 4 | ||||
| -rw-r--r-- | src/ui/tools/pen-tool.cpp | 2 | ||||
| -rw-r--r-- | src/ui/tools/pencil-tool.cpp | 631 | ||||
| -rw-r--r-- | src/ui/tools/pencil-tool.h | 34 | ||||
| -rw-r--r-- | src/widgets/pencil-toolbar.cpp | 6 |
14 files changed, 563 insertions, 446 deletions
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/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 51c8451a5..4e937c1e4 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -97,10 +97,13 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0 if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // try second time dM=derivative(dM); } - if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // admit defeat + if ( dM.isZero(tol) || (are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) )) { // admit defeat return Geom::Circle(Geom::Point(0., 0.), 0.); } Piecewise<D2<SBasis> > unitv = unitVector(dM,tol); + if (unitv.empty()) { // admit defeat + return Geom::Circle(Geom::Point(0., 0.), 0.); + } Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv); Piecewise<SBasis> k = cross(derivative(unitv),unitv); k = divide(k,dMlength,tol,3); @@ -322,7 +325,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: /* per definition, each discontinuity should be fixed with a join-ending, as defined by linejoin_type */ Geom::PathBuilder pb; - if (B.size() == 0) { + if (B.empty()) { return pb.peek().front(); } @@ -555,7 +558,6 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: return pb.peek().front(); } - Geom::PathVector LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) { @@ -567,11 +569,18 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(path_in); Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = pathv[0].toPwSb(); + if (pwd2_in.empty()) { + return path_in; + } Piecewise<D2<SBasis> > der = derivative(pwd2_in); - Piecewise<D2<SBasis> > n = unitVector(der,0.0001); - if (!n.size() || !pwd2_in.size() || !n.size()) { + if (der.empty()) { + return path_in; + } + Piecewise<D2<SBasis> > n = unitVector(der,0.00001); + if (n.empty()) { return path_in; } + n = rot90(n); offset_points.set_pwd2(pwd2_in, n); @@ -590,12 +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<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); + if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) { + johan->setBeta(interpolator_beta); + } + if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) { + smooth->setBeta(interpolator_beta); + } if (pathv[0].closed()) { - // add extra points for interpolation between first and last point - Point first_point = ts.front(); - Point last_point = ts.back(); - ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); - ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) ); + std::vector<Geom::Point> 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. @@ -612,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<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())); - if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) { - johan->setBeta(interpolator_beta); - } - if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) { - smooth->setBeta(interpolator_beta); - } + Geom::Path strokepath = interpolator->interpolateToPath(ts); delete interpolator; @@ -639,7 +662,9 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value()); - + if (x.empty() || y.empty()) { + return path_in; + } Piecewise<D2<SBasis> > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise<D2<SBasis> > mirrorpath = reverse( compose(pwd2_in,x) - y*compose(n,x)); @@ -659,31 +684,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<LineSegment>(midpoint); - fixed_path.appendNew<LineSegment>(mirrorpath.firstValue()); + fixed_path.appendNew<LineSegment>(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<LineSegment>( pwd2_out.lastValue() + radius*end_deriv ); - fixed_path.appendNew<LineSegment>( mirrorpath.firstValue() + radius*end_deriv ); - fixed_path.appendNew<LineSegment>( mirrorpath.firstValue() ); + double radius = 0.5 * distance(fixed_path.finalPoint(), fixed_mirrorpath.initialPoint()); + fixed_path.appendNew<LineSegment>( fixed_path.finalPoint() + radius*end_deriv ); + fixed_path.appendNew<LineSegment>( fixed_mirrorpath.initialPoint() + radius*end_deriv ); + fixed_path.appendNew<LineSegment>( fixed_mirrorpath.initialPoint() ); break; } case LINECAP_BUTT: { - fixed_path.appendNew<LineSegment>( mirrorpath.firstValue() ); + fixed_path.appendNew<LineSegment>( fixed_mirrorpath.initialPoint() ); break; } case LINECAP_ROUND: default: { - double radius1 = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue()); - fixed_path.appendNew<EllipticalArc>( 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<EllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, fixed_mirrorpath.initialPoint() ); break; } } @@ -696,31 +721,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<LineSegment>( midpoint ); - fixed_path.appendNew<LineSegment>( pwd2_out.firstValue() ); + fixed_path.appendNew<LineSegment>( 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<LineSegment>( mirrorpath.lastValue() - radius*start_deriv ); - fixed_path.appendNew<LineSegment>( pwd2_out.firstValue() - radius*start_deriv ); - fixed_path.appendNew<LineSegment>( pwd2_out.firstValue() ); + double radius = 0.5 * distance(fixed_path.initialPoint(), fixed_mirrorpath.finalPoint()); + fixed_path.appendNew<LineSegment>( fixed_mirrorpath.finalPoint() - radius*start_deriv ); + fixed_path.appendNew<LineSegment>( fixed_path.initialPoint() - radius*start_deriv ); + fixed_path.appendNew<LineSegment>( fixed_path.initialPoint() ); break; } case LINECAP_BUTT: { - fixed_path.appendNew<LineSegment>( pwd2_out.firstValue() ); + fixed_path.appendNew<LineSegment>( fixed_path.initialPoint() ); break; } case LINECAP_ROUND: default: { - double radius2 = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue()); - fixed_path.appendNew<EllipticalArc>( 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<EllipticalArc>( 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..19696ac57 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -45,7 +45,6 @@ private: EnumParam<unsigned> linejoin_type; ScalarParam miter_limit; EnumParam<unsigned> end_linecap_type; - LPEPowerStroke(const LPEPowerStroke&); LPEPowerStroke& operator=(const LPEPowerStroke&); }; 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/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index bc06e42ca..64c772602 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -89,6 +89,31 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi } } +/** call this method to recalculate the controlpoints when path is reversed.*/ +std::vector<Geom::Point> +PowerStrokePointArrayParam::reverse_controlpoints(bool write) +{ + std::vector<Geom::Point> controlpoints; + if (!last_pwd2.empty()) { + Geom::Piecewise<Geom::D2<Geom::SBasis> > 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; + } + if (write) { + write_to_SVG(); + _vector.clear(); + _vector = controlpoints; + controlpoints.clear(); + write_to_SVG(); + return _vector; + } + } + return controlpoints; +} + float PowerStrokePointArrayParam::median_width() { size_t size = _vector.size(); @@ -205,22 +230,21 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state) if (state & GDK_MOD1_MASK) { // delete the clicked knot std::vector<Geom::Point> & 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<KnotHolderEntity *>::iterator ent = parent_holder->entity.begin(); ent != parent_holder->entity.end(); ++ent) { - PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast<PowerStrokePointArrayParamKnotHolderEntity *>(*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<KnotHolderEntity *>::iterator ent = parent_holder->entity.begin(); ent != parent_holder->entity.end(); ++ent) { + PowerStrokePointArrayParamKnotHolderEntity *pspa_ent = dynamic_cast<PowerStrokePointArrayParamKnotHolderEntity *>(*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 diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h index 621d3b969..a1d4db3ca 100644 --- a/src/live_effects/parameter/powerstrokepointarray.h +++ b/src/live_effects/parameter/powerstrokepointarray.h @@ -47,6 +47,7 @@ public: Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2_normal() const { return last_pwd2_normal; } void recalculate_controlpoints_for_new_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); + std::vector<Geom::Point> 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 f1129513f..0ac12d15c 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -204,41 +204,15 @@ void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring false ); } -void InkscapePreferences::AddPressureSensibility(DialogPage &p, Glib::ustring const &prefs_path, double 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 + "/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 + "/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 ); } -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, _("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"), - false ); -} - -void InkscapePreferences::AddPowerStrokeGapPressureFactor(DialogPage &p, Glib::ustring const &prefs_path, double 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 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"), - 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,11 +431,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); - this->AddPowerStrokeKnotDistanceFactor(_page_pencil, "/tools/freehand/pencil", 135.0); - this->AddPowerStrokeGapPressureFactor(_page_pencil, "/tools/freehand/pencil", 1.0); + _page_pencil.add_group_header( _("Pressure sensitivity settings")); + 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/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 58e61470c..e0bd7f257 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -501,10 +501,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 AddPressureSensibility(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 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 79c9ce7f9..8a078d510 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,44 +235,52 @@ static void spdc_apply_powerstroke_shape(std::vector<Geom::Point> 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<SPShape *>(item); if (sp_shape) { SPCurve * c = sp_shape->getCurve(); if (!c) { return; } + Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + LPEPowerStroke* ps = NULL; pt->addPowerStrokePencil(c); - } - 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; + if (lpe) { + ps = static_cast<LPEPowerStroke*>(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); } - 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)); - } - 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"); + 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)); + } + Effect::createAndApply(POWERSTROKE, dc->desktop->doc(), item); + lpe = SP_LPE_ITEM(item)->getCurrentLPE(); + ps = static_cast<LPEPowerStroke*>(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); + } + return; } - static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(pt->points); - pt->points.clear(); - return; } } @@ -338,7 +347,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; @@ -404,12 +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 (dc->tablet_enabled) { std::vector<Geom::Point> points; spdc_apply_powerstroke_shape(points, dc, item); - shape = NONE; - //To allow retain color shape_applied = true; + shape = NONE; + previous_shape_type = NONE; } } #define SHAPE_LENGTH 10 @@ -615,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 @@ -766,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); } @@ -838,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(); @@ -868,9 +879,17 @@ 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)); + } + 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); @@ -883,16 +902,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<Geom::Point> 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 96bd14a46..2f68688a1 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -41,9 +41,11 @@ #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" +#include "live_effects/lpe-powerstroke-interpolators.h" #include "ui/tool/event-utils.h" namespace Inkscape { @@ -65,12 +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()) { } @@ -79,45 +84,44 @@ void PencilTool::setup() { if (prefs->getBool("/tools/freehand/pencil/selcue")) { this->enableSelectionCue(); } - + this->_curve = new SPCurve(); FreehandBase::setup(); - this->is_drawing = false; + this->_is_drawing = false; this->anchor_statusbar = false; } PencilTool::~PencilTool() { + if (this->_curve) { + this->_curve->unref(); + } } 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); + is_tablet = true; } else { this->pressure = DDC_DEFAULT_PRESSURE; - input_has_pressure = false; + //If no pressure device ignore pressure button + 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<Geom::Point> origin = this->npoints > 0 ? this->p[0] : boost::optional<Geom::Point>(); + boost::optional<Geom::Point> origin = this->_npoints > 0 ? this->p[0] : boost::optional<Geom::Point>(); spdc_endpoint_snap_free(this, p, origin, state); } } @@ -153,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; @@ -190,10 +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(); - 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; @@ -201,7 +202,6 @@ 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, _powerpreview); if (!(bevent.state & GDK_SHIFT_MASK)) { @@ -223,7 +223,13 @@ 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")); + } 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) @@ -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,33 +296,21 @@ 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) 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: + if (is_tablet) { + this->_state = SP_PENCIL_CONTEXT_FREEHAND; + return false; + } /* Set red endpoint */ if (anchor) { p = anchor->dp; @@ -323,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 */ @@ -336,28 +337,32 @@ 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; } - if (anchor && !this->anchor_statusbar) { this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Release</b> 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 { @@ -387,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)); @@ -396,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<LPEPowerStroke*>(lpe); + if (ps) { + desktop->selection->clear(); + desktop->selection->add(item); + } + } + } } break; case SP_PENCIL_CONTEXT_ADDLINE: @@ -415,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(); @@ -427,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) @@ -435,8 +455,7 @@ 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) { + if (!tablet_enabled) { this->_endpointSnap(p_end, revent.state); if (p_end != p) { // then we must have snapped! @@ -447,21 +466,25 @@ 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(); - //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(); - 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; } @@ -489,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(); @@ -524,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; } @@ -534,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; } @@ -553,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, _("<b>Sketch mode</b>: holding <b>Alt</b> interpolates between sketched paths. Release <b>Alt</b> to finalize.")); } break; @@ -571,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; @@ -595,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; } } @@ -613,28 +636,29 @@ 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; - - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + if (!tablet_enabled) { + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve); + } } } @@ -660,188 +684,155 @@ void PencilTool::_finishEndpoint() { } void -sp_powerstrole_preview(Geom::PathVector pathvector, SPItem * powerpreview, const char * item_id, std::vector<Geom::Point> points){ +PencilTool::_powerStrokePreview(Geom::Path const path) +{ 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(); - static_cast<LPEPowerStroke*>(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); - } else { - preview = xml_doc->createElement("svg:path"); - preview->setAttribute("sodipodi:insensitive", "true"); - preview->setAttribute("id", item_id); - 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"); + Geom::PathVector const pathv(path); + SPLPEItem * lpeitem = dynamic_cast<SPLPEItem *>(_powerpreview); + if (!lpeitem) { + Inkscape::XML::Node *body = NULL; + body = xml_doc->createElement("svg:path"); + body->setAttribute("sodipodi:insensitive", "true"); + sp_desktop_apply_style_tool(desktop, body, Glib::ustring("/tools/freehand/pencil").data(), false); + _powerpreview = SP_SHAPE(SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->appendChildRepr(body)); + 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"); + if (!strcmp(fill, "none")) { + fill = "#000000"; + } + if (!strcmp(stroke, "none")) { + stroke = "#000000"; + } + 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); - preview->setAttribute("style", css_str.c_str()); - gchar * pvector_str = sp_svg_write_path(pathvector); - preview->setAttribute("d" , pvector_str); + body->setAttribute("style", css_str.c_str()); + gchar * pvector_str = sp_svg_write_path(pathv); + _powerpreview->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"); - static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(points); - } -} - -void -PencilTool::addPowerStrokePencil(SPCurve * c) -{ - - this->points.clear(); - using namespace Inkscape::LivePathEffect; - bool live = false; - SPCurve * curve = new SPCurve(); - 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 && 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; - } + SPLPEItem * lpeitem = dynamic_cast<SPLPEItem *>(_powerpreview); + if (!lpeitem) { + return; } - if (curve->is_empty()) { - curve = NULL; + Effect::createAndApply(POWERSTROKE, SP_ACTIVE_DESKTOP->doc(), SP_ITEM(_powerpreview)); + Effect* lpe = lpeitem->getCurrentLPE(); + LPEPowerStroke * ps = static_cast<LPEPowerStroke*>(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"); } - red_curve = previous_red->copy(); - green_curve = previous_green->copy(); - previous_red->unref(); - previous_green->unref(); } else { - curve = c->copy(); - _powerpreview = NULL; - } - SPObject *elemref = NULL; - SPDocument * document = SP_ACTIVE_DOCUMENT; - if (!document) { - return; + Effect* lpe = lpeitem->getCurrentLPE(); + LPEPowerStroke * ps = static_cast<LPEPowerStroke*>(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); } - const char * item_id = "___pencil_preview_powerstroke___"; - if ((elemref = document->getObjectById(item_id))) { - if (!live) { - LivePathEffectObject * lpeobj = SP_LPE_ITEM(elemref)->getCurrentLPE()->getLPEObj(); +} + +void +PencilTool::removePowerStrokePreview() +{ + 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; - elemref->deleteObject(); - elemref = NULL; - } else { - _powerpreview = SP_ITEM(elemref); } + _powerpreview->deleteObject(); + _powerpreview = NULL; } - if (!curve) { - return; +} +void +PencilTool::addPowerStrokePencil(SPCurve *& c) +{ + if (this->_curve) { + c = this->_curve->copy(); } - Geom::Affine transformCoordinate = SP_ITEM(SP_ACTIVE_DESKTOP->currentLayer())->i2dt_affine(); - Geom::Coord scale = transformCoordinate.expansionX(); - Geom::PathVector pathvector = curve->get_pathvector(); - //pathvector *= transformCoordinate.inverse(); - double zoom = SP_EVENT_CONTEXT(this)->desktop->current_zoom() * 5.0; +} + +void +PencilTool::addPowerStrokePencil() +{ + using namespace Inkscape::LivePathEffect; 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; + 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(); 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 previous_pressure = 0.0; - double tol = knots_distance / zoom; - Geom::Point previous_point = Geom::Point(0,0); - bool start = true; - auto pressure = this->wps.begin(); - for (auto point = this->ps.begin(); point != this->ps.end(); ++point,++pressure) { - //Maybe the 12 POW can be moved to a preferences 12 gives a good results of sensibility on my tablet - //But maybe is a tweakable value. less number = less sensibility - //8 give an acceptable max width 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; - 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)) - { - continue; - } - previous_point = *point; - previous_pressure = pressure_computed; - start = false; - this->points.push_back(Geom::Point(pos, pressure_computed)); + if (!green_curve->is_unset()) { + this->_curve = green_curve->copy(); + if (!red_curve->is_unset()) { + this->_curve->append_continuous( red_curve, 0.0625); } } - if (live) { - sp_powerstrole_preview(pathvector * transformCoordinate.inverse(), _powerpreview, item_id, this->points); + if (!this->_curve || this->_curve->is_unset()) { + this->_curve = NULL; + } + 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 || + _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); + } + 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); - this->wps.push_back(this->pressure); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (input_has_pressure) { - this->addPowerStrokePencil(NULL); + 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(); } } } @@ -852,28 +843,121 @@ square(double const x) return x * x; } -void PencilTool::_interpolate() { - if ( this->ps.size() <= 1 ) { +void +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<Geom::Interpolate::Interpolator> 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<Geom::Point> 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<LPEPowerStroke*>(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<Geom::LineSegment>(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 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); } - 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; + 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)); int n_points = this->ps.size(); @@ -881,43 +965,37 @@ void PencilTool::_interpolate() { int max_segs = 4 * n_points; std::vector<Geom::Point> b(max_segs); - int const n_segs = Geom::bezier_fit_cubic_r(b.data(), this->ps.data(), n_points, tolerance_sq, max_segs); - if (n_segs > 0) { /* Fit and draw and reset state */ - this->green_curve->moveto(b[0]); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0); for (int c = 0; c < n_segs; c++) { // if we are in BSpline we modify the trace to create adhoc nodes - if(mode == 2){ + 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 { 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); - /* 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; + 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) ); } @@ -942,7 +1020,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; @@ -995,8 +1073,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 (!tablet_enabled) { + 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()); @@ -1005,11 +1084,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) ); } @@ -1017,24 +1096,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 */ @@ -1053,7 +1132,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 (!tablet_enabled) { + 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 */ @@ -1065,11 +1146,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 8f57cf4d0..c361978cb 100644 --- a/src/ui/tools/pencil-tool.h +++ b/src/ui/tools/pencil-tool.h @@ -6,10 +6,11 @@ */ #include "ui/tools/freehand-base.h" - +#include "sp-shape.h" #include <2geom/piecewise.h> #include <2geom/d2.h> #include <2geom/sbasis.h> +#include <2geom/pathvector.h> #define DDC_MIN_PRESSURE 0.0 @@ -36,25 +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; - std::vector<Geom::Point> ps; - std::vector<Geom::Point> points; - std::vector<double> wps; - - void addPowerStrokePencil(SPCurve * c); + void addPowerStrokePencil(SPCurve *& c); + void addPowerStrokePencil(); + void removePowerStrokePreview(); Geom::Piecewise<Geom::D2<Geom::SBasis> > 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: @@ -69,17 +60,28 @@ private: bool _handleKeyPress(GdkEventKey const &event); bool _handleKeyRelease(GdkEventKey const &event); void _setStartpoint(Geom::Point const &p); - SPItem *_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 _powerstrokeInterpolate(bool apply); void _sketchInterpolate(); void _extinput(GdkEvent *event); void _cancel(); void _endpointSnap(Geom::Point &p, guint const state); + std::vector<double> _wps; + std::vector<Geom::Point> _points_pos; + std::vector<Geom::Point> _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 88b54c24e..688eb2cf0 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 ); @@ -513,7 +515,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) ); |
