summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Jeanmougin <marc@jeanmougin.fr>2017-12-29 10:18:01 +0000
committerMarc Jeanmougin <marc@jeanmougin.fr>2017-12-29 10:18:01 +0000
commit9564781d4eef5ac13b2c4ec393171d00fbdacd1d (patch)
tree93481b36fc6004238b6e919db03ac1680e11f096
parentremove git remnants (diff)
parentMerge branch 'master' into powerpencilII (diff)
downloadinkscape-9564781d4eef5ac13b2c4ec393171d00fbdacd1d.tar.gz
inkscape-9564781d4eef5ac13b2c4ec393171d00fbdacd1d.zip
Merge branch 'master' of gitlab.com:inkscape/inkscape
-rw-r--r--src/knot.cpp4
-rw-r--r--src/live_effects/lpe-powerstroke.cpp103
-rw-r--r--src/live_effects/lpe-powerstroke.h1
-rw-r--r--src/live_effects/lpeobject-reference.cpp6
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp54
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.h1
-rw-r--r--src/ui/dialog/inkscape-preferences.cpp41
-rw-r--r--src/ui/dialog/inkscape-preferences.h5
-rw-r--r--src/ui/tools/freehand-base.cpp117
-rw-r--r--src/ui/tools/freehand-base.h4
-rw-r--r--src/ui/tools/pen-tool.cpp2
-rw-r--r--src/ui/tools/pencil-tool.cpp631
-rw-r--r--src/ui/tools/pencil-tool.h34
-rw-r--r--src/widgets/pencil-toolbar.cpp6
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) );