summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>2014-05-08 00:59:39 +0000
committerVinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>2014-05-08 00:59:39 +0000
commit267adec8dfa7797915cff84b463fef5f9570a6e4 (patch)
tree2580dea1021dff4f4cf56f750c978fe251de0868 /src
parentDocumentation. Advanced tutorial update (not fully translated). (diff)
parentadding const _pathmanipulatos staff to handles (handles also have tips) (diff)
downloadinkscape-267adec8dfa7797915cff84b463fef5f9570a6e4.tar.gz
inkscape-267adec8dfa7797915cff84b463fef5f9570a6e4.zip
Merging Jabier's spirolive+bspline branch
(bzr r13341.2.1)
Diffstat (limited to 'src')
-rw-r--r--src/live_effects/CMakeLists.txt2
-rw-r--r--src/live_effects/Makefile_insert2
-rw-r--r--src/live_effects/effect-enum.h1
-rw-r--r--src/live_effects/effect.cpp5
-rw-r--r--src/live_effects/effect.h1
-rw-r--r--src/live_effects/lpe-bspline.cpp719
-rw-r--r--src/live_effects/lpe-bspline.h58
-rw-r--r--src/sp-lpe-item.cpp16
-rw-r--r--src/sp-lpe-item.h1
-rw-r--r--src/ui/tool/curve-drag-point.cpp17
-rw-r--r--src/ui/tool/multi-path-manipulator.cpp13
-rw-r--r--src/ui/tool/node.cpp201
-rw-r--r--src/ui/tool/node.h12
-rw-r--r--src/ui/tool/path-manipulator.cpp101
-rw-r--r--src/ui/tool/path-manipulator.h9
-rw-r--r--src/ui/tools/freehand-base.cpp74
-rw-r--r--src/ui/tools/freehand-base.h9
-rw-r--r--src/ui/tools/node-tool.h3
-rw-r--r--src/ui/tools/pen-tool.cpp996
-rw-r--r--src/ui/tools/pen-tool.h31
-rw-r--r--src/ui/tools/pencil-tool.cpp37
-rw-r--r--src/widgets/icon.cpp1
-rw-r--r--src/widgets/mappings.xml1
-rw-r--r--src/widgets/pencil-toolbar.cpp7
24 files changed, 2234 insertions, 83 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index a5f50a69d..7aeb911b0 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -34,6 +34,7 @@ set(live_effects_SRC
lpe-spiro.cpp
lpe-tangent_to_curve.cpp
lpe-test-doEffect-stack.cpp
+ lpe-bspline.cpp
lpe-text_label.cpp
lpe-vonkoch.cpp
lpegroupbbox.cpp
@@ -94,6 +95,7 @@ set(live_effects_SRC
lpe-spiro.h
lpe-tangent_to_curve.h
lpe-test-doEffect-stack.h
+ lpe-bspline.h
lpe-text_label.h
lpe-vonkoch.h
lpegroupbbox.h
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index 9c3c171f2..248030e8c 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -38,6 +38,8 @@ ink_common_sources += \
live_effects/lpe-interpolate.h \
live_effects/lpe-test-doEffect-stack.cpp \
live_effects/lpe-test-doEffect-stack.h \
+ live_effects/lpe-bspline.cpp \
+ live_effects/lpe-bspline.h \
live_effects/lpe-lattice.cpp \
live_effects/lpe-lattice.h \
live_effects/lpe-envelope.cpp \
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 43af33b53..342a2c849 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -45,6 +45,7 @@ enum EffectType {
PATH_LENGTH,
LINE_SEGMENT,
DOEFFECTSTACK_TEST,
+ BSPLINE,
DYNASTROKE,
RECURSIVE_SKELETON,
EXTRUDE,
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 4c5e21194..9006e5359 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -19,6 +19,7 @@
#include "live_effects/lpe-rough-hatches.h"
#include "live_effects/lpe-dynastroke.h"
#include "live_effects/lpe-test-doEffect-stack.h"
+#include "live_effects/lpe-bspline.h"
#include "live_effects/lpe-gears.h"
#include "live_effects/lpe-curvestitch.h"
#include "live_effects/lpe-circle_with_radius.h"
@@ -120,6 +121,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
/* 0.49 */
{POWERSTROKE, N_("Power stroke"), "powerstroke"},
{CLONE_ORIGINAL, N_("Clone original path"), "clone_original"},
+ {BSPLINE, N_("BSpline"), "bspline"},
};
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
@@ -228,6 +230,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case DOEFFECTSTACK_TEST:
neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
break;
+ case BSPLINE:
+ neweffect = static_cast<Effect*> ( new LPEBSpline(lpeobj) );
+ break;
case DYNASTROKE:
neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
break;
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index 1da9b4cc9..940770616 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -101,6 +101,7 @@ public:
Inkscape::XML::Node * getRepr();
SPDocument * getSPDoc();
LivePathEffectObject * getLPEObj() {return lpeobj;};
+ LivePathEffectObject const * getLPEObj() const {return lpeobj;};
Parameter * getParameter(const char * key);
void readallParameters(Inkscape::XML::Node const* repr);
diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp
new file mode 100644
index 000000000..edf19d1e5
--- /dev/null
+++ b/src/live_effects/lpe-bspline.cpp
@@ -0,0 +1,719 @@
+#define INKSCAPE_LPE_BSPLINE_C
+/*
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#include <gtkmm/box.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/box.h>
+#include <gtkmm/button.h>
+#include <gtkmm/checkbutton.h>
+#include <glib.h>
+#include <glibmm/i18n.h>
+#include "display/curve.h"
+#include <2geom/bezier-curve.h>
+#include <2geom/point.h>
+#include "helper/geom-curves.h"
+#include "live_effects/lpe-bspline.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/parameter/parameter.h"
+#include "ui/widget/scalar.h"
+#include "ui/tool/node.h"
+#include "ui/tools/node-tool.h"
+#include "ui/tool/control-point-selection.h"
+#include "ui/tool/selectable-control-point.h"
+#include "selection.h"
+#include "xml/repr.h"
+#include "svg/svg.h"
+#include "sp-path.h"
+#include "style.h"
+#include "document-private.h"
+#include "document.h"
+#include "document-undo.h"
+#include "desktop-handles.h"
+#include "verbs.h"
+#include "sp-lpe-item.h"
+#include "display/sp-canvas.h"
+#include <typeinfo>
+#include <vector>
+// For handling un-continuous paths:
+#include "message-stack.h"
+#include "inkscape.h"
+#include "desktop.h"
+
+using Inkscape::DocumentUndo;
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject)
+ : Effect(lpeobject),
+ // initialise your parameters here:
+ //testpointA(_("Test Point A"), _("Test A"), "ptA", &wr, this,
+ //Geom::Point(100,100)),
+ steps(_("Steps whith CTRL:"),
+ _("Change number of steps whith CTRL pressed"), "steps", &wr, this,
+ 2),
+ ignoreCusp(_("Ignore cusp nodes:"), _("Change ignoring cusp nodes"),
+ "ignoreCusp", &wr, this, true),
+ onlySelected(_("Change only selected nodes:"),
+ _("Change only selected nodes"), "onlySelected", &wr, this,
+ false),
+ weight(_("Change weight:"), _("Change weight of the effect"), "weight",
+ &wr, this, 0.3334) {
+ registerParameter(dynamic_cast<Parameter *>(&weight));
+ registerParameter(dynamic_cast<Parameter *>(&steps));
+ registerParameter(dynamic_cast<Parameter *>(&ignoreCusp));
+ registerParameter(dynamic_cast<Parameter *>(&onlySelected));
+ weight.param_set_range(0.0000, 1);
+ weight.param_set_increments(0.1, 0.1);
+ weight.param_set_digits(4);
+ steps.param_set_range(1, 10);
+ steps.param_set_increments(1, 1);
+ steps.param_set_digits(0);
+}
+
+LPEBSpline::~LPEBSpline() {}
+
+void LPEBSpline::createAndApply(const char *name, SPDocument *doc,
+ SPItem *item) {
+ if (!SP_IS_SHAPE(item)) {
+ g_warning("LPE BSpline can only be applied to shapes (not groups).");
+ } else {
+ // Path effect definition
+ Inkscape::XML::Document *xml_doc = doc->getReprDoc();
+ Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
+ repr->setAttribute("effect", name);
+
+ doc->getDefs()->getRepr()
+ ->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
+ const gchar *repr_id = repr->attribute("id");
+ Inkscape::GC::release(repr);
+
+ gchar *href = g_strdup_printf("#%s", repr_id);
+ SP_LPE_ITEM(item)->addPathEffect(href, true);
+ g_free(href);
+ }
+}
+
+void LPEBSpline::doEffect(SPCurve *curve) {
+ if (curve->get_segment_count() < 2)
+ return;
+ // Make copy of old path as it is changed during processing
+ Geom::PathVector const original_pathv = curve->get_pathvector();
+ curve->reset();
+
+ //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el
+ //penúltimo
+ for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
+ path_it != original_pathv.end(); ++path_it) {
+ //Si está vacío...
+ if (path_it->empty())
+ continue;
+ //Itreadores
+
+ Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::const_iterator curve_it2 =
+ ++(path_it->begin()); // outgoing curve
+ Geom::Path::const_iterator curve_endit =
+ path_it->end_default(); // this determines when the loop has to stop
+ //Creamos las lineas rectas que unen todos los puntos del trazado y donde se
+ //calcularán
+ //los puntos clave para los manejadores.
+ //Esto hace que la curva BSpline no pierda su condición aunque se trasladen
+ //dichos manejadores
+ SPCurve *nCurve = new SPCurve();
+ Geom::Point previousNode(0, 0);
+ Geom::Point node(0, 0);
+ Geom::Point pointAt1(0, 0);
+ Geom::Point pointAt2(0, 0);
+ Geom::Point nextPointAt1(0, 0);
+ Geom::Point nextPointAt2(0, 0);
+ Geom::Point nextPointAt3(0, 0);
+ Geom::D2<Geom::SBasis> SBasisIn;
+ Geom::D2<Geom::SBasis> SBasisOut;
+ Geom::D2<Geom::SBasis> SBasisHelper;
+ Geom::CubicBezier const *cubic = NULL;
+ if (path_it->closed()) {
+ // if the path is closed, maybe we have to stop a bit earlier because the
+ // closing line segment has zerolength.
+ const Geom::Curve &closingline =
+ path_it->back_closed(); // the closing line segment is always of type
+ // Geom::LineSegment.
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ // closingline.isDegenerate() did not work, because it only checks for
+ // *exact* zero length, which goes wrong for relative coordinates and
+ // rounding errors...
+ // the closing line segment has zero-length. So stop before that one!
+ curve_endit = path_it->end_open();
+ }
+ }
+ //Si la curva está cerrada calculamos el punto donde
+ //deveria estar el nodo BSpline de cierre/inicio de la curva
+ //en posible caso de que se cierre con una linea recta creando un nodo
+ //BSPline
+ nCurve->moveto(curve_it1->initialPoint());
+ //Recorremos todos los segmentos menos el último
+ while (curve_it2 != curve_endit) {
+ //previousPointAt3 = pointAt3;
+ //Calculamos los puntos que dividirían en tres segmentos iguales el path
+ //recto de entrada y de salida
+ SPCurve *in = new SPCurve();
+ in->moveto(curve_it1->initialPoint());
+ in->lineto(curve_it1->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ SBasisIn = in->first_segment()->toSBasis();
+ pointAt1 = SBasisIn.valueAt(
+ Geom::nearest_point((*cubic)[1], *in->first_segment()));
+ pointAt2 = SBasisIn.valueAt(
+ Geom::nearest_point((*cubic)[2], *in->first_segment()));
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ in->reset();
+ delete in;
+ //Y hacemos lo propio con el path de salida
+ //nextPointAt0 = curveOut.valueAt(0);
+ SPCurve *out = new SPCurve();
+ out->moveto(curve_it2->initialPoint());
+ out->lineto(curve_it2->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
+ if (cubic) {
+ SBasisOut = out->first_segment()->toSBasis();
+ nextPointAt1 = SBasisOut.valueAt(
+ Geom::nearest_point((*cubic)[1], *out->first_segment()));
+ nextPointAt2 = SBasisOut.valueAt(
+ Geom::nearest_point((*cubic)[2], *out->first_segment()));
+ ;
+ nextPointAt3 = out->first_segment()->finalPoint();
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ nextPointAt2 = out->first_segment()->finalPoint();
+ nextPointAt3 = out->first_segment()->finalPoint();
+ }
+ out->reset();
+ delete out;
+ //La curva BSpline se forma calculando el centro del segmanto de unión
+ //de el punto situado en las 2/3 partes de el segmento de entrada
+ //con el punto situado en la posición 1/3 del segmento de salida
+ //Estos dos puntos ademas estan posicionados en el lugas correspondiente
+ //de
+ //los manejadores de la curva
+ SPCurve *lineHelper = new SPCurve();
+ lineHelper->moveto(pointAt2);
+ lineHelper->lineto(nextPointAt1);
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de
+ //principio de curva
+ previousNode = node;
+ //Y este hará de final de curva
+ node = SBasisHelper.valueAt(0.5);
+ nCurve->curveto(pointAt1, pointAt2, node);
+ //aumentamos los valores para el siguiente paso en el bucle
+ ++curve_it1;
+ ++curve_it2;
+ }
+ //Si está cerrada la curva, la cerramos sobre el valor guardado
+ //previamente
+ //Si no finalizamos en el punto final
+ Geom::Point startNode(0, 0);
+ if (path_it->closed()) {
+ SPCurve *start = new SPCurve();
+ start->moveto(path_it->begin()->initialPoint());
+ start->lineto(path_it->begin()->finalPoint());
+ Geom::D2<Geom::SBasis> SBasisStart = start->first_segment()->toSBasis();
+ SPCurve *lineHelper = new SPCurve();
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin());
+ if (cubic) {
+ lineHelper->moveto(SBasisStart.valueAt(
+ Geom::nearest_point((*cubic)[1], *start->first_segment())));
+ } else {
+ lineHelper->moveto(start->first_segment()->initialPoint());
+ }
+ start->reset();
+ delete start;
+
+ SPCurve *end = new SPCurve();
+ end->moveto(curve_it1->initialPoint());
+ end->lineto(curve_it1->finalPoint());
+ Geom::D2<Geom::SBasis> SBasisEnd = end->first_segment()->toSBasis();
+ //Geom::BezierCurve const *bezier = dynamic_cast<Geom::BezierCurve
+ //const*>(&*curve_endit);
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ lineHelper->lineto(SBasisEnd.valueAt(
+ Geom::nearest_point((*cubic)[2], *end->first_segment())));
+ } else {
+ lineHelper->lineto(end->first_segment()->finalPoint());
+ }
+ end->reset();
+ delete end;
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ startNode = SBasisHelper.valueAt(0.5);
+ nCurve->curveto(nextPointAt1, nextPointAt2, startNode);
+ nCurve->move_endpoints(startNode, startNode);
+ } else {
+ SPCurve *start = new SPCurve();
+ start->moveto(path_it->begin()->initialPoint());
+ start->lineto(path_it->begin()->finalPoint());
+ startNode = start->first_segment()->initialPoint();
+ start->reset();
+ delete start;
+ nCurve->curveto(nextPointAt1, nextPointAt2, nextPointAt3);
+ nCurve->move_endpoints(startNode, nextPointAt3);
+ }
+ //y cerramos la curva
+ if (path_it->closed()) {
+ nCurve->closepath_current();
+ }
+ curve->append(nCurve, false);
+ nCurve->reset();
+ delete nCurve;
+ }
+}
+
+Gtk::Widget *LPEBSpline::newWidget() {
+ // use manage here, because after deletion of Effect object, others might
+ // still be pointing to this widget.
+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
+
+ vbox->set_border_width(5);
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter *param = *it;
+ Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
+ if (param->param_key == "weight"){
+ Gtk::HBox * buttons = Gtk::manage(new Gtk::HBox(true,0));
+ Gtk::Button *defaultWeight =
+ Gtk::manage(new Gtk::Button(Glib::ustring(_("Default weight"))));
+ defaultWeight->signal_clicked()
+ .connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toDefaultWeight), widg));
+ buttons->pack_start(*defaultWeight, true, true, 2);
+ Gtk::Button *makeCusp =
+ Gtk::manage(new Gtk::Button(Glib::ustring(_("Make cusp"))));
+ makeCusp->signal_clicked()
+ .connect(sigc::bind<Gtk::Widget *>(sigc::mem_fun(*this, &LPEBSpline::toMakeCusp), widg));
+ buttons->pack_start(*makeCusp, true, true, 2);
+ vbox->pack_start(*buttons, true, true, 2);
+ }
+ if (param->param_key == "weight" || param->param_key == "steps") {
+ Inkscape::UI::Widget::Scalar *widgRegistered =
+ Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
+ widgRegistered->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &LPEBSpline::toWeight));
+ widg = dynamic_cast<Gtk::Widget *>(widgRegistered);
+ if (widg){
+ Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widg);
+ std::vector< Gtk::Widget* > childList = scalarParameter->get_children();
+ Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
+ entryWidg->set_width_chars(6);
+ }
+ }
+ if (param->param_key == "onlySelected") {
+ Gtk::CheckButton *widgRegistered =
+ Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
+ widg = dynamic_cast<Gtk::Widget *>(widgRegistered);
+ }
+ if (param->param_key == "ignoreCusp") {
+ Gtk::CheckButton *widgRegistered =
+ Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
+ widg = dynamic_cast<Gtk::Widget *>(widgRegistered);
+ }
+ Glib::ustring *tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+
+ ++it;
+ }
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+void LPEBSpline::toDefaultWeight(Gtk::Widget *widgWeight) {
+ weight.param_set_value(0.3334);
+ changeWeight(0.3334);
+ Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widgWeight);
+ std::vector< Gtk::Widget* > childList = scalarParameter->get_children();
+ Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
+ entryWidg->set_text("0.3334");
+}
+
+void LPEBSpline::toMakeCusp(Gtk::Widget *widgWeight) {
+ weight.param_set_value(0.0000);
+ changeWeight(0.0000);
+ Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widgWeight);
+ std::vector< Gtk::Widget* > childList = scalarParameter->get_children();
+ Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
+ entryWidg->set_text("0.0000");
+}
+
+void LPEBSpline::toWeight() { changeWeight(weight); }
+
+void LPEBSpline::changeWeight(double weightValue) {
+ SPDesktop *desktop = inkscape_active_desktop();
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+ GSList *items = (GSList *)selection->itemList();
+ SPItem *item = (SPItem *)g_slist_nth(items, 0)->data;
+ SPPath *path = SP_PATH(item);
+ SPCurve *curve = path->get_curve_for_edit();
+ LPEBSpline::doBSplineFromWidget(curve, weightValue);
+ gchar *str = sp_svg_write_path(curve->get_pathvector());
+ path->getRepr()->setAttribute("inkscape:original-d", str);
+ if (INK_IS_NODE_TOOL(desktop->event_context)) {
+ Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(desktop->event_context);
+ nt->desktop->updateNow();
+ }
+ g_free(str);
+ curve->unref();
+ desktop->clearWaitingCursor();
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_LPE,
+ _("Modified the weight of the BSpline"));
+}
+
+bool LPEBSpline::nodeIsSelected(Geom::Point nodePoint) {
+ using Geom::X;
+ using Geom::Y;
+
+ if (points.size() > 0) {
+ for (std::vector<Geom::Point>::iterator i = points.begin();
+ i != points.end(); ++i) {
+ Geom::Point p = *i;
+ if (Geom::are_near(p, nodePoint, 0.0001)) {
+ return true;
+ } else {
+ }
+ }
+ }
+ return false;
+}
+
+void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue) {
+ using Geom::X;
+ using Geom::Y;
+ SPDesktop *desktop = inkscape_active_desktop();
+ if (INK_IS_NODE_TOOL(desktop->event_context)) {
+ Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(desktop->event_context);
+ Inkscape::UI::ControlPointSelection::Set &selection =
+ nt->_selected_nodes->allPoints();
+ points.clear();
+ std::vector<Geom::Point>::iterator pbegin;
+ for (Inkscape::UI::ControlPointSelection::Set::iterator i =
+ selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->selected()) {
+ Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i);
+ pbegin = points.begin();
+ points.insert(pbegin, desktop->doc2dt(n->position()));
+ }
+ }
+ }
+ //bool hasNodesSelected = LPEBspline::hasNodesSelected();
+ if (curve->get_segment_count() < 2)
+ return;
+ // Make copy of old path as it is changed during processing
+ Geom::PathVector const original_pathv = curve->get_pathvector();
+ curve->reset();
+
+ //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el
+ //penúltimo
+ for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
+ path_it != original_pathv.end(); ++path_it) {
+ //Si está vacío...
+ if (path_it->empty())
+ continue;
+ //Itreadores
+
+ Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::const_iterator curve_it2 =
+ ++(path_it->begin()); // outgoing curve
+ Geom::Path::const_iterator curve_endit =
+ path_it->end_default(); // this determines when the loop has to stop
+ //Creamos las lineas rectas que unen todos los puntos del trazado y donde se
+ //calcularán
+ //los puntos clave para los manejadores.
+ //Esto hace que la curva BSpline no pierda su condición aunque se trasladen
+ //dichos manejadores
+ SPCurve *nCurve = new SPCurve();
+ Geom::Point pointAt0(0, 0);
+ Geom::Point pointAt1(0, 0);
+ Geom::Point pointAt2(0, 0);
+ Geom::Point pointAt3(0, 0);
+ Geom::Point nextPointAt0(0, 0);
+ Geom::Point nextPointAt1(0, 0);
+ Geom::Point nextPointAt2(0, 0);
+ Geom::Point nextPointAt3(0, 0);
+ Geom::D2<Geom::SBasis> SBasisIn;
+ Geom::D2<Geom::SBasis> SBasisOut;
+ Geom::CubicBezier const *cubic = NULL;
+ if (path_it->closed()) {
+ // if the path is closed, maybe we have to stop a bit earlier because the
+ // closing line segment has zerolength.
+ const Geom::Curve &closingline =
+ path_it->back_closed(); // the closing line segment is always of type
+ // Geom::LineSegment.
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ // closingline.isDegenerate() did not work, because it only checks for
+ // *exact* zero length, which goes wrong for relative coordinates and
+ // rounding errors...
+ // the closing line segment has zero-length. So stop before that one!
+ curve_endit = path_it->end_open();
+ }
+ }
+ //Si la curva está cerrada calculamos el punto donde
+ //deveria estar el nodo BSpline de cierre/inicio de la curva
+ //en posible caso de que se cierre con una linea recta creando un nodo
+ //BSPline
+ nCurve->moveto(curve_it1->initialPoint());
+ //Recorremos todos los segmentos menos el último
+ while (curve_it2 != curve_endit) {
+ //previousPointAt3 = pointAt3;
+ //Calculamos los puntos que dividirían en tres segmentos iguales el path
+ //recto de entrada y de salida
+ SPCurve *in = new SPCurve();
+ in->moveto(curve_it1->initialPoint());
+ in->lineto(curve_it1->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ pointAt0 = in->first_segment()->initialPoint();
+ pointAt3 = in->first_segment()->finalPoint();
+ SBasisIn = in->first_segment()->toSBasis();
+ if (!onlySelected) {
+ if (cubic) {
+ if (!ignoreCusp || !Geom::are_near((*cubic)[1], pointAt0)) {
+ pointAt1 = SBasisIn.valueAt(weightValue);
+ if (weightValue != 0.0000) {
+ pointAt1 =
+ Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ }
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ }
+ if (!ignoreCusp || !Geom::are_near((*cubic)[2], pointAt3)) {
+ pointAt2 = SBasisIn.valueAt(1 - weightValue);
+ if (weightValue != 0.0000) {
+ pointAt2 =
+ Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ }
+ } else {
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ } else {
+ if (!ignoreCusp && weightValue != 0.0000) {
+ pointAt1 = SBasisIn.valueAt(weightValue);
+ if (weightValue != 0.0000) {
+ pointAt1 =
+ Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ }
+ pointAt2 = SBasisIn.valueAt(1 - weightValue);
+ if (weightValue != 0.0000) {
+ pointAt2 =
+ Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ }
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ }
+ } else {
+ if (cubic) {
+ if (!ignoreCusp || !Geom::are_near((*cubic)[1], pointAt0)) {
+ if (nodeIsSelected(pointAt0)) {
+ pointAt1 = SBasisIn.valueAt(weightValue);
+ if (weightValue != 0.0000) {
+ pointAt1 =
+ Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ }
+ } else {
+ pointAt1 = (*cubic)[1];
+ }
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ }
+ if (!ignoreCusp || !Geom::are_near((*cubic)[2], pointAt3)) {
+ if (nodeIsSelected(pointAt3)) {
+ pointAt2 = SBasisIn.valueAt(1 - weightValue);
+ if (weightValue != 0.0000) {
+ pointAt2 =
+ Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ }
+ } else {
+ pointAt2 = (*cubic)[2];
+ }
+ } else {
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ } else {
+ if (!ignoreCusp && weightValue != 0.000) {
+ if (nodeIsSelected(pointAt0)) {
+ pointAt1 = SBasisIn.valueAt(weightValue);
+ pointAt1 =
+ Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ }
+ if (nodeIsSelected(pointAt3)) {
+ pointAt2 = SBasisIn.valueAt(weightValue);
+ pointAt2 =
+ Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ } else {
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ } else {
+ pointAt1 = in->first_segment()->initialPoint();
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ }
+ }
+ in->reset();
+ delete in;
+ //La curva BSpline se forma calculando el centro del segmanto de unión
+ //de el punto situado en las 2/3 partes de el segmento de entrada
+ //con el punto situado en la posición 1/3 del segmento de salida
+ //Estos dos puntos ademas estan posicionados en el lugas correspondiente
+ //de
+ //los manejadores de la curva
+ nCurve->curveto(pointAt1, pointAt2, pointAt3);
+ //aumentamos los valores para el siguiente paso en el bucle
+ ++curve_it1;
+ ++curve_it2;
+ }
+ SPCurve *out = new SPCurve();
+ out->moveto(curve_it1->initialPoint());
+ out->lineto(curve_it1->finalPoint());
+ SBasisOut = out->first_segment()->toSBasis();
+ nextPointAt0 = out->first_segment()->initialPoint();
+ nextPointAt3 = out->first_segment()->finalPoint();
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (!onlySelected) {
+ if (cubic) {
+ if (!ignoreCusp || !Geom::are_near((*cubic)[1], nextPointAt0)) {
+ nextPointAt1 = SBasisOut.valueAt(weightValue);
+ if (weightValue != 0.0000) {
+ nextPointAt1 =
+ Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
+ }
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ }
+ if (!ignoreCusp || !Geom::are_near((*cubic)[2], nextPointAt3)) {
+ nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
+ if (weightValue != 0.0000) {
+ nextPointAt2 =
+ Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
+ }
+ } else {
+ nextPointAt2 = out->first_segment()->finalPoint();
+ }
+ } else {
+ if (!ignoreCusp && weightValue != 0.0000) {
+ nextPointAt1 = SBasisOut.valueAt(weightValue);
+ nextPointAt1 =
+ Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
+ nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
+ nextPointAt2 =
+ Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ nextPointAt2 = out->first_segment()->finalPoint();
+ }
+ }
+ } else {
+ if (cubic) {
+ if (!ignoreCusp || !Geom::are_near((*cubic)[1], nextPointAt0)) {
+ if (nodeIsSelected(nextPointAt0)) {
+ nextPointAt1 = SBasisOut.valueAt(weightValue);
+ if (weightValue != 0.0000) {
+ nextPointAt1 = Geom::Point(nextPointAt1[X] + 0.0001,
+ nextPointAt1[Y] + 0.0001);
+ }
+ } else {
+ nextPointAt1 = (*cubic)[1];
+ }
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ }
+ if (!ignoreCusp || !Geom::are_near((*cubic)[2], nextPointAt3)) {
+ if (nodeIsSelected(nextPointAt3)) {
+ nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
+ if (weightValue != 0.0000) {
+ nextPointAt2 = Geom::Point(nextPointAt2[X] + 0.0001,
+ nextPointAt2[Y] + 0.0001);
+ }
+ } else {
+ nextPointAt2 = (*cubic)[2];
+ }
+ } else {
+ nextPointAt2 = out->first_segment()->finalPoint();
+ }
+ } else {
+ if (!ignoreCusp && weightValue != 0.0000) {
+ if (nodeIsSelected(nextPointAt0)) {
+ nextPointAt1 = SBasisOut.valueAt(weightValue);
+ nextPointAt1 =
+ Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ }
+ if (nodeIsSelected(nextPointAt3)) {
+ nextPointAt2 = SBasisOut.valueAt(weightValue);
+ nextPointAt2 =
+ Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
+ } else {
+ nextPointAt2 = out->first_segment()->finalPoint();
+ }
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ nextPointAt2 = out->first_segment()->finalPoint();
+ }
+ }
+ }
+ out->reset();
+ delete out;
+ //Aberiguamos la ultima parte de la curva correspondiente al último
+ //segmento
+ //Y hacemos lo propio con el path de salida
+ //nextPointAt0 = curveOut.valueAt(0);
+ if (path_it->closed()) {
+ nCurve->curveto(nextPointAt1, nextPointAt2,
+ path_it->begin()->initialPoint());
+ nCurve->move_endpoints(path_it->begin()->initialPoint(),
+ path_it->begin()->initialPoint());
+ } else {
+ nCurve->curveto(nextPointAt1, nextPointAt2, nextPointAt3);
+ nCurve->move_endpoints(path_it->begin()->initialPoint(), nextPointAt3);
+ }
+ //y cerramos la curva
+ if (path_it->closed()) {
+ nCurve->closepath_current();
+ }
+ curve->append(nCurve, false);
+ nCurve->reset();
+ delete nCurve;
+ }
+}
+
+}; //namespace LivePathEffect
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h
new file mode 100644
index 000000000..aff4ce812
--- /dev/null
+++ b/src/live_effects/lpe-bspline.h
@@ -0,0 +1,58 @@
+#ifndef INKSCAPE_LPE_BSPLINE_H
+#define INKSCAPE_LPE_BSPLINE_H
+
+/*
+ * Inkscape::LPEBSpline
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/bool.h"
+#include <vector>
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEBSpline : public Effect {
+
+public:
+ LPEBSpline(LivePathEffectObject *lpeobject);
+ virtual ~LPEBSpline();
+
+ virtual void createAndApply(const char *name, SPDocument *doc, SPItem *item);
+
+ virtual LPEPathFlashType pathFlashType() const { return SUPPRESS_FLASH; }
+
+ virtual void doEffect(SPCurve *curve);
+
+ virtual void doBSplineFromWidget(SPCurve *curve, double value);
+
+ virtual bool nodeIsSelected(Geom::Point nodePoint);
+
+ virtual Gtk::Widget *newWidget();
+
+ virtual void changeWeight(double weightValue);
+
+ virtual void toDefaultWeight(Gtk::Widget *widgWeight);
+
+ virtual void toMakeCusp(Gtk::Widget *widgWeight);
+
+ virtual void toWeight();
+
+ ScalarParam steps;
+
+private:
+ std::vector<Geom::Point> points;
+ BoolParam ignoreCusp;
+ BoolParam onlySelected;
+ ScalarParam weight;
+
+ LPEBSpline(const LPEBSpline &);
+ LPEBSpline &operator=(const LPEBSpline &);
+
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+#endif
diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp
index b5dd74fc6..762dc3191 100644
--- a/src/sp-lpe-item.cpp
+++ b/src/sp-lpe-item.cpp
@@ -613,6 +613,22 @@ SPLPEItem::getPathEffectOfType(int type)
return NULL;
}
+Inkscape::LivePathEffect::Effect const*
+SPLPEItem::getPathEffectOfType(int type) const
+{
+ std::list<Inkscape::LivePathEffect::LPEObjectReference *>::const_iterator i;
+ for (i = path_effect_list->begin(); i != path_effect_list->end(); ++i) {
+ LivePathEffectObject const *lpeobj = (*i)->lpeobject;
+ if (lpeobj) {
+ Inkscape::LivePathEffect::Effect const *lpe = lpeobj->get_lpe();
+ if (lpe && (lpe->effectType() == type)) {
+ return lpe;
+ }
+ }
+ }
+ return NULL;
+}
+
void SPLPEItem::editNextParamOncanvas(SPDesktop *dt)
{
Inkscape::LivePathEffect::LPEObjectReference *lperef = this->getCurrentLPEReference();
diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h
index cd72ac55b..85878a95b 100644
--- a/src/sp-lpe-item.h
+++ b/src/sp-lpe-item.h
@@ -77,6 +77,7 @@ public:
bool hasPathEffectOfType(int const type) const;
bool hasPathEffectRecursive() const;
Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type);
+ Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const;
bool hasBrokenPathEffect() const;
PathEffectList getEffectList();
diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp
index 4ca736f80..013553410 100644
--- a/src/ui/tool/curve-drag-point.cpp
+++ b/src/ui/tool/curve-drag-point.cpp
@@ -53,9 +53,11 @@ bool CurveDragPoint::grabbed(GdkEventMotion */*event*/)
// delta is a vector equal 1/3 of distance from first to second
Geom::Point delta = (second->position() - first->position()) / 3.0;
- first->front()->move(first->front()->position() + delta);
- second->back()->move(second->back()->position() - delta);
-
+ // only update the nodes if the mode is bspline
+ if(!_pm.isBSpline(false)){
+ first->front()->move(first->front()->position() + delta);
+ second->back()->move(second->back()->position() - delta);
+ }
_pm.update();
} else {
_segment_was_degenerate = false;
@@ -88,9 +90,12 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event)
Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta;
Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta;
- first->front()->move(first->front()->position() + offset0);
- second->back()->move(second->back()->position() + offset1);
-
+ //modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed
+ if(!_pm.isBSpline(false)){
+ first->front()->move(first->front()->position() + offset0);
+ second->back()->move(second->back()->position() + offset1);
+ }else if(weight>=0.8 && held_shift(*event))second->back()->move(new_pos);
+ else if(weight<=0.2 && held_shift(*event))first->front()->move(new_pos);
_pm.update();
}
diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp
index 65987ad52..68aaa77a5 100644
--- a/src/ui/tool/multi-path-manipulator.cpp
+++ b/src/ui/tool/multi-path-manipulator.cpp
@@ -670,7 +670,18 @@ bool MultiPathManipulator::event(Inkscape::UI::Tools::ToolBase *event_context, G
// a) del preserves shape, and control is not pressed
// b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed
// Hence xor
- deleteNodes(del_preserves_shape ^ held_control(event->key));
+ guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0);
+
+ //if the trace is bspline ( mode 2)
+ if(mode==2){
+ // is this correct ?
+ if(del_preserves_shape ^ held_control(event->key))
+ deleteNodes(false);
+ else
+ deleteNodes(true);
+ }
+ else
+ deleteNodes(del_preserves_shape ^ held_control(event->key));
// Delete any selected gradient nodes as well
event_context->deleteSelectedDrag(held_control(event->key));
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp
index fbbc4be64..1434a5c5b 100644
--- a/src/ui/tool/node.cpp
+++ b/src/ui/tool/node.cpp
@@ -13,7 +13,6 @@
#include <glib/gi18n.h>
#include <2geom/bezier-utils.h>
#include <2geom/transforms.h>
-
#include "display/sp-ctrlline.h"
#include "display/sp-canvas.h"
#include "display/sp-canvas-util.h"
@@ -29,6 +28,8 @@
#include "ui/tool/node.h"
#include "ui/tool/path-manipulator.h"
#include <gdk/gdkkeysyms.h>
+#include <cmath>
+
namespace {
@@ -166,6 +167,12 @@ void Handle::move(Geom::Point const &new_pos)
}
}
setPosition(new_pos);
+
+ //move the handler and its oposite the same proportion
+ if(_pm().isBSpline()){
+ setPosition(_pm().BSplineHandleReposition(this,this));
+ this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this));
+ }
return;
}
@@ -177,6 +184,13 @@ void Handle::move(Geom::Point const &new_pos)
Geom::Point new_delta = (Geom::dot(delta, direction)
/ Geom::L2sq(direction)) * direction;
setRelativePos(new_delta);
+
+ //move the handler and its oposite the same proportion
+ if(_pm().isBSpline()){
+ setPosition(_pm().BSplineHandleReposition(this,this));
+ this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this));
+ }
+
return;
}
@@ -195,8 +209,14 @@ void Handle::move(Geom::Point const &new_pos)
break;
default: break;
}
-
setPosition(new_pos);
+
+ // moves the handler and its oposite the same proportion
+ if(_pm().isBSpline()){
+ setPosition(_pm().BSplineHandleReposition(this,this));
+ this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this));
+ }
+
}
void Handle::setPosition(Geom::Point const &p)
@@ -273,12 +293,26 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven
break;
default: break;
}
+ // new double click event to set the handlers of a node to the default proportion, 0.3334%
+ case GDK_2BUTTON_PRESS:
+ handle_2button_press();
+ break;
+
default: break;
}
return ControlPoint::_eventHandler(event_context, event);
}
+//this function moves the handler and its oposite to the default proportion of 0.3334
+void Handle::handle_2button_press(){
+ if(_pm().isBSpline()){
+ setPosition(_pm().BSplineHandleReposition(this,0.3334));
+ this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),0.3334));
+ _pm().update();
+ }
+}
+
bool Handle::grabbed(GdkEventMotion *)
{
_saved_other_pos = other()->position();
@@ -326,10 +360,18 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - perp_pos);
}
new_pos = result;
+ // moves the handler and its oposite in X fixed positions depending on parameter "steps with control"
+ // by default in live BSpline
+ if(_pm().isBSpline()){
+ setPosition(new_pos);
+ int steps = _pm().BSplineGetSteps();
+ new_pos=_pm().BSplineHandleReposition(this,ceilf(_pm().BSplineHandlePosition(this,this)*steps)/steps);
+ }
}
std::vector<Inkscape::SnapCandidatePoint> unselected;
- if (snap) {
+ //if the snap adjustment is activated and it is not bspline
+ if (snap && !_pm().isBSpline()) {
ControlPointSelection::Set &nodes = _parent->_selection.allPoints();
for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) {
Node *n = static_cast<Node*>(*i);
@@ -369,6 +411,10 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
other()->setPosition(_saved_other_pos);
}
}
+ //if it is bspline but SHIFT or CONTROL are not pressed it fixes it in the original position
+ if(_pm().isBSpline() && !held_shift(*event) && !held_control(*event)){
+ new_pos=_last_drag_origin();
+ }
move(new_pos); // needed for correct update, even though it's redundant
_pm().update();
}
@@ -427,13 +473,19 @@ static double snap_increment_degrees() {
Glib::ustring Handle::_getTip(unsigned state) const
{
char const *more;
+ // a trick to mark as bspline if the node has no strength, we are going to use it later
+ // to show the appropiate messages. We cannot do it in any different way becasue the function is constant
+
+ bool isBSpline = _pm().isBSpline();
bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate();
- if (can_shift_rotate) {
+ if (can_shift_rotate && !isBSpline) {
more = C_("Path handle tip", "more: Shift, Ctrl, Alt");
- } else {
+ } else if(isBSpline){
+ more = C_("Path handle tip", "more: Ctrl");
+ }else {
more = C_("Path handle tip", "more: Ctrl, Alt");
}
- if (state_held_alt(state)) {
+ if (state_held_alt(state) && !isBSpline) {
if (state_held_control(state)) {
if (state_held_shift(state) && can_shift_rotate) {
return format_tip(C_("Path handle tip",
@@ -456,18 +508,24 @@ Glib::ustring Handle::_getTip(unsigned state) const
}
} else {
if (state_held_control(state)) {
- if (state_held_shift(state) && can_shift_rotate) {
+ if (state_held_shift(state) && can_shift_rotate && !isBSpline) {
return format_tip(C_("Path handle tip",
"<b>Shift+Ctrl</b>: snap rotation angle to %g° increments and rotate both handles"),
snap_increment_degrees());
- } else {
+ } else if(isBSpline){
+ return format_tip(C_("Path handle tip",
+ "<b>Ctrl</b>: Move handle by his actual steps in BSpline Live Effect"));
+ }else{
return format_tip(C_("Path handle tip",
"<b>Ctrl</b>: snap rotation angle to %g° increments, click to retract"),
snap_increment_degrees());
}
- } else if (state_held_shift(state) && can_shift_rotate) {
+ } else if (state_held_shift(state) && can_shift_rotate && !isBSpline) {
return C_("Path hande tip",
"<b>Shift</b>: rotate both handles by the same angle");
+ } else if(state_held_shift(state) && isBSpline){
+ return C_("Path hande tip",
+ "<b>Shift</b>: move handle");
}
}
@@ -476,9 +534,13 @@ Glib::ustring Handle::_getTip(unsigned state) const
return format_tip(C_("Path handle tip",
"<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more);
default:
- return format_tip(C_("Path handle tip",
- "<b>%s</b>: drag to shape the segment (%s)"),
- handle_type_to_localized_string(_parent->type()), more);
+ if(!isBSpline){
+ return format_tip(C_("Path handle tip",
+ "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more);
+ }else{
+ return format_tip(C_("Path handle tip",
+ "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s)"), more);
+ }
}
}
@@ -553,18 +615,80 @@ void Node::move(Geom::Point const &new_pos)
// move handles when the node moves.
Geom::Point old_pos = position();
Geom::Point delta = new_pos - position();
+
+ // save the previous nodes strength to apply it again once the node is moved
+ double nodeWeight = 0.0000;
+ double nextNodeWeight = 0.0000;
+ double prevNodeWeight = 0.0000;
+ Node *n = this;
+ Node * nextNode = n->nodeToward(n->front());
+ Node * prevNode = n->nodeToward(n->back());
+ nodeWeight = _pm().BSplineHandlePosition(n->front());
+ if(prevNode){
+ if(prevNode->isEndNode()){
+ prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front());
+ }
+ }
+ if(nextNode){
+ if(nextNode->isEndNode()){
+ nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back());
+ }
+ }
+
setPosition(new_pos);
+
_front.setPosition(_front.position() + delta);
_back.setPosition(_back.position() + delta);
// if the node has a smooth handle after a line segment, it should be kept colinear
// with the segment
_fixNeighbors(old_pos, new_pos);
+
+ // move the affected handlers. First the node ones, later the adjoining ones.
+ if(_pm().isBSpline()){
+ _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight));
+ _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight));
+ if(prevNode){
+ if(prevNode->isEndNode()){
+ prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight));
+ }else{
+ prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back()));
+ }
+ }
+ if(nextNode){
+ if(nextNode->isEndNode()){
+ nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight));
+ }else{
+ nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->back()));
+ }
+ }
+ }
}
void Node::transform(Geom::Affine const &m)
{
+
Geom::Point old_pos = position();
+
+ // save the previous nodes strength to apply it again once the node is moved
+ double nodeWeight = 0.0000;
+ double nextNodeWeight = 0.0000;
+ double prevNodeWeight = 0.0000;
+ Node *n = this;
+ Node * nextNode = n->nodeToward(n->front());
+ Node * prevNode = n->nodeToward(n->back());
+ nodeWeight = _pm().BSplineHandlePosition(n->front());
+ if(prevNode){
+ if(prevNode->isEndNode()){
+ prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front());
+ }
+ }
+ if(nextNode){
+ if(nextNode->isEndNode()){
+ nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back());
+ }
+ }
+
setPosition(position() * m);
_front.setPosition(_front.position() * m);
_back.setPosition(_back.position() * m);
@@ -572,6 +696,26 @@ void Node::transform(Geom::Affine const &m)
/* Affine transforms keep handle invariants for smooth and symmetric nodes,
* but smooth nodes at ends of linear segments and auto nodes need special treatment */
_fixNeighbors(old_pos, position());
+
+ // move the involved handlers, first the node ones, later the adjoining ones
+ if(_pm().isBSpline()){
+ _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight));
+ _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight));
+ if(prevNode){
+ if(prevNode->isEndNode()){
+ prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight));
+ }else{
+ prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back()));
+ }
+ }
+ if(nextNode){
+ if(nextNode->isEndNode()){
+ nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight));
+ }else{
+ nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->front()));
+ }
+ }
+ }
}
Geom::Rect Node::bounds() const
@@ -657,6 +801,7 @@ void Node::showHandles(bool v)
if (!_back.isDegenerate()) {
_back.setVisible(v);
}
+
}
void Node::updateHandles()
@@ -758,6 +903,16 @@ void Node::setType(NodeType type, bool update_handles)
break;
default: break;
}
+ /* in node type changes, about bspline traces, we can mantain them with 0.0000 power in border mode,
+ or we give them the default power in curve mode */
+ if(_pm().isBSpline()){
+ double weight = 0.0000;
+ if(_pm().BSplineHandlePosition(this->front()) != 0.0000 ){
+ weight = 0.3334;
+ }
+ _front.setPosition(_pm().BSplineHandleReposition(this->front(),weight));
+ _back.setPosition(_pm().BSplineHandleReposition(this->back(),weight));
+ }
}
_type = type;
_setControlType(nodeTypeToCtrlType(_type));
@@ -870,6 +1025,7 @@ bool Node::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent
_selection.spatialGrow(this, dir);
}
return true;
+
default:
break;
}
@@ -1004,6 +1160,11 @@ void Node::_setState(State state)
case STATE_CLICKED:
mgr.setActive(_canvas_item, true);
mgr.setPrelight(_canvas_item, false);
+ //this shows the handlers when selecting the nodes
+ if(_pm().isBSpline()){
+ this->front()->setPosition(_pm().BSplineHandleReposition(this->front()));
+ this->back()->setPosition(_pm().BSplineHandleReposition(this->back()));
+ }
break;
}
SelectableControlPoint::_setState(state);
@@ -1258,6 +1419,7 @@ Node *Node::nodeAwayFrom(Handle *h)
Glib::ustring Node::_getTip(unsigned state) const
{
+ bool isBSpline = _pm().isBSpline();
if (state_held_shift(state)) {
bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate());
if (can_drag_out) {
@@ -1287,15 +1449,24 @@ Glib::ustring Node::_getTip(unsigned state) const
// No modifiers: assemble tip from node type
char const *nodetype = node_type_to_localized_string(_type);
if (_selection.transformHandlesEnabled() && selected()) {
- if (_selection.size() == 1) {
+ if (_selection.size() == 1 && !isBSpline) {
return format_tip(C_("Path node tip",
"<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype);
+ }else if(_selection.size() == 1){
+ return format_tip(C_("Path node tip",
+ "<b>BSpline node</b>: %g weight, drag to shape the path (more: Shift, Ctrl, Alt)"),0.0000/*this->bsplineWeight*/);
}
return format_tip(C_("Path node tip",
"<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype);
}
- return format_tip(C_("Path node tip",
- "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype);
+ if (!isBSpline) {
+ return format_tip(C_("Path node tip",
+ "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype);
+ }else{
+ return format_tip(C_("Path node tip",
+ "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"));
+
+ }
}
Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index 4582d998a..5971956e1 100644
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
@@ -117,7 +117,7 @@ public:
protected:
Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent);
-
+ virtual void handle_2button_press();
virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event);
virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
virtual bool grabbed(GdkEventMotion *event);
@@ -131,6 +131,7 @@ protected:
private:
inline PathManipulator &_pm();
+ inline PathManipulator &_pm() const;
Node *_parent; // the handle's lifetime does not extend beyond that of the parent node,
// so a naked pointer is OK and allows setting it during Node's construction
SPCtrlLine *_handle_line;
@@ -217,6 +218,7 @@ public:
Node *nodeAwayFrom(Handle *h);
NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); }
+ NodeList &nodeList() const { return *(static_cast<ListNode const*>(this)->ln_list); }
/**
* Move the node to the bottom of its canvas group.
@@ -263,6 +265,7 @@ private:
Inkscape::SnapSourceType _snapSourceType() const;
Inkscape::SnapTargetType _snapTargetType() const;
inline PathManipulator &_pm();
+ inline PathManipulator &_pm() const;
/** Determine whether two nodes are joined by a linear segment. */
static bool _is_line_segment(Node *first, Node *second);
@@ -490,10 +493,17 @@ inline double Handle::length() const {
inline PathManipulator &Handle::_pm() {
return _parent->_pm();
}
+inline PathManipulator &Handle::_pm() const {
+ return _parent->_pm();
+}
inline PathManipulator &Node::_pm() {
return nodeList().subpathList().pm();
}
+inline PathManipulator &Node::_pm() const {
+ return nodeList().subpathList().pm();
+}
+
// definitions for node iterator
template <typename N>
NodeIterator<N>::operator bool() const {
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index 338499672..3beeed049 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -42,6 +42,7 @@
#include "ui/tool/multi-path-manipulator.h"
#include "xml/node.h"
#include "xml/node-observer.h"
+#include "live_effects/lpe-bspline.h"
namespace Inkscape {
namespace UI {
@@ -102,7 +103,6 @@ private:
};
void build_segment(Geom::PathBuilder &, Node *, Node *);
-
PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path,
Geom::Affine const &et, guint32 outline_color, Glib::ustring lpe_key)
: PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection)
@@ -145,6 +145,8 @@ PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path,
sigc::hide( sigc::mem_fun(*this, &PathManipulator::_updateOutlineOnZoomChange)));
_createControlPointsFromGeometry();
+ //Define if the path is BSpline on construction
+ isBSpline(true);
}
PathManipulator::~PathManipulator()
@@ -662,6 +664,15 @@ unsigned PathManipulator::_deleteStretch(NodeList::iterator start, NodeList::ite
nl.erase(start);
start = next;
}
+ // if we are removing, we readjust the handlers
+ if(isBSpline()){
+ if(start.prev()){
+ start.prev()->front()->setPosition(BSplineHandleReposition(start.prev()->front(),start.prev()->back()));
+ }
+ if(end){
+ end->back()->setPosition(BSplineHandleReposition(end->back(),end->front()));
+ }
+ }
return del_len;
}
@@ -816,7 +827,6 @@ void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel)
}
h->setRelativePos(relpos);
update();
-
gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right";
_commit(_("Scale handle"), key);
}
@@ -1104,7 +1114,6 @@ void PathManipulator::_createControlPointsFromGeometry()
Geom::Curve const &cseg = pit->back_closed();
bool fuse_ends = pit->closed()
&& Geom::are_near(cseg.initialPoint(), cseg.finalPoint());
-
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
Geom::Point pos = cit->finalPoint();
Node *current_node;
@@ -1171,6 +1180,89 @@ void PathManipulator::_createControlPointsFromGeometry()
}
}
+//determines if the trace has a bspline effect and the number of steps that it takes
+int PathManipulator::BSplineGetSteps() const {
+
+ LivePathEffect::LPEBSpline const *lpe_bsp = NULL;
+
+ if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){
+ Inkscape::LivePathEffect::Effect const *thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE);
+ if(thisEffect){
+ lpe_bsp = dynamic_cast<LivePathEffect::LPEBSpline const*>(thisEffect->getLPEObj()->get_lpe());
+ }
+ }
+ int steps = 0;
+ if(lpe_bsp){
+ steps = lpe_bsp->steps+1;
+ }
+ return steps;
+}
+
+// determines if the trace has bspline effect
+bool PathManipulator::isBSpline(bool recalculate){
+ if(recalculate){
+ _is_bspline = this->BSplineGetSteps() > 0;
+ }
+ return _is_bspline;
+}
+
+bool PathManipulator::isBSpline() const {
+ return BSplineGetSteps() > 0;
+}
+
+// returns the corresponding strength to the position of the handlers
+double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){
+ using Geom::X;
+ using Geom::Y;
+ if(h2){
+ h = h2;
+ }
+ double pos = 0.0000;
+ Node *n = h->parent();
+ Node * nextNode = NULL;
+ nextNode = n->nodeToward(h);
+ if(nextNode){
+ SPCurve *lineInsideNodes = new SPCurve();
+ lineInsideNodes->moveto(n->position());
+ lineInsideNodes->lineto(nextNode->position());
+ pos = Geom::nearest_point(h->position(),*lineInsideNodes->first_segment());
+ }
+ if (pos == 0.0000 && !h2){
+ return BSplineHandlePosition(h, h->other());
+ }
+ return pos;
+}
+
+// give the location for the handler in the corresponding position
+Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){
+ double pos = this->BSplineHandlePosition(h, h2);
+ return BSplineHandleReposition(h,pos);
+}
+
+// give the location for the handler to the specified position
+Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){
+ using Geom::X;
+ using Geom::Y;
+ Geom::Point ret = h->position();
+ Node *n = h->parent();
+ Geom::D2< Geom::SBasis > SBasisInsideNodes;
+ SPCurve *lineInsideNodes = new SPCurve();
+ Node * nextNode = NULL;
+ nextNode = n->nodeToward(h);
+ if(nextNode && pos != 0.0000){
+ lineInsideNodes->moveto(n->position());
+ lineInsideNodes->lineto(nextNode->position());
+ SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis();
+ ret = SBasisInsideNodes.valueAt(pos);
+ ret = Geom::Point(ret[X] + 0.005,ret[Y] + 0.005);
+ }else{
+ if(pos == 0.0000){
+ ret = n->position();
+ }
+ }
+ return ret;
+}
+
/** Construct the geometric representation of nodes and handles, update the outline
* and display
* \param alert_LPE if true, first the LPE is warned what the new path is going to be before updating it
@@ -1178,6 +1270,8 @@ void PathManipulator::_createControlPointsFromGeometry()
void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
{
Geom::PathBuilder builder;
+ //Refresh if is bspline some times -think on path change selection, this value get lost
+ isBSpline(true);
for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) {
SubpathPtr subpath = *spi;
if (subpath->empty()) {
@@ -1186,7 +1280,6 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
}
NodeList::iterator prev = subpath->begin();
builder.moveTo(prev->position());
-
for (NodeList::iterator i = ++subpath->begin(); i != subpath->end(); ++i) {
build_segment(builder, prev.ptr(), i.ptr());
prev = i;
diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h
index 7a13ce723..151805c83 100644
--- a/src/ui/tool/path-manipulator.h
+++ b/src/ui/tool/path-manipulator.h
@@ -19,6 +19,7 @@
#include <boost/weak_ptr.hpp>
#include "ui/tool/node.h"
#include "ui/tool/manipulator.h"
+#include "live_effects/lpe-bspline.h"
struct SPCanvasItem;
class SPCurve;
@@ -95,6 +96,7 @@ public:
NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected,
bool search_unselected, bool closest);
+ int BSplineGetSteps() const;
// this is necessary for Tab-selection in MultiPathManipulator
SubpathList &subpathList() { return _subpaths; }
@@ -104,6 +106,12 @@ private:
typedef boost::shared_ptr<NodeList> SubpathPtr;
void _createControlPointsFromGeometry();
+
+ bool isBSpline(bool recalculate = false);
+ bool isBSpline() const;
+ double BSplineHandlePosition(Handle *h, Handle *h2 = NULL);
+ Geom::Point BSplineHandleReposition(Handle *h, Handle *h2 = NULL);
+ Geom::Point BSplineHandleReposition(Handle *h, double pos);
void _createGeometryFromControlPoints(bool alert_LPE = false);
unsigned _deleteStretch(NodeList::iterator first, NodeList::iterator last, bool keep_shape);
std::string _createTypeString();
@@ -145,6 +153,7 @@ private:
bool _show_path_direction;
bool _live_outline;
bool _live_objects;
+ bool _is_bspline;
Glib::ustring _lpe_key;
friend class PathManipulatorObserver;
diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp
index 288a200f4..1c5b7b8e3 100644
--- a/src/ui/tools/freehand-base.cpp
+++ b/src/ui/tools/freehand-base.cpp
@@ -82,6 +82,8 @@ FreehandBase::FreehandBase(gchar const *const *cursor_shape, gint hot_x, gint ho
, red_curve(NULL)
, blue_bpath(NULL)
, blue_curve(NULL)
+ , blue2_bpath(NULL)
+ , blue2_curve(NULL)
, green_bpaths(NULL)
, green_curve(NULL)
, green_anchor(NULL)
@@ -89,6 +91,7 @@ FreehandBase::FreehandBase(gchar const *const *cursor_shape, gint hot_x, gint ho
, white_item(NULL)
, white_curves(NULL)
, white_anchors(NULL)
+ , overwriteCurve(NULL)
, sa(NULL)
, ea(NULL)
, waiting_LPE_type(Inkscape::LivePathEffect::INVALID_LPE)
@@ -137,6 +140,13 @@ void FreehandBase::setup() {
// Create blue curve
this->blue_curve = new SPCurve();
+ // Create blue2 bpath
+ this->blue2_bpath = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), NULL);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->blue2_bpath), this->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+
+ // Create blue2 curve
+ this->blue2_curve = new SPCurve();
+
// Create green curve
this->green_curve = new SPCurve();
@@ -144,6 +154,9 @@ void FreehandBase::setup() {
this->green_anchor = NULL;
this->green_closed = FALSE;
+ // Create start anchor alternative curve
+ this->overwriteCurve = new SPCurve();
+
this->attach = TRUE;
spdc_attach_selection(this, this->selection);
}
@@ -243,6 +256,10 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item,
if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1) {
Effect::createAndApply(SPIRO, dc->desktop->doc(), item);
}
+ //add the bspline node in the waiting effects
+ if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2) {
+ Effect::createAndApply(BSPLINE, dc->desktop->doc(), item);
+ }
int shape = prefs->getInt(tool_name(dc) + "/shape", 0);
bool shape_applied = false;
@@ -459,7 +476,7 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed)
{
// Concat RBG
SPCurve *c = dc->green_curve;
-
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
// Green
dc->green_curve = new SPCurve();
while (dc->green_bpaths) {
@@ -472,6 +489,10 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed)
dc->blue_curve->reset();
sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->blue_bpath), NULL);
+ // Blue2
+ dc->blue2_curve->reset();
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->blue2_bpath), NULL);
+
// Red
if (dc->red_curve_is_valid) {
c->append_continuous(dc->red_curve, 0.0625);
@@ -506,9 +527,20 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed)
if (dc->sa->start && !(dc->sa->curve->is_closed()) ) {
c = reverse_then_unref(c);
}
- dc->sa->curve->append_continuous(c, 0.0625);
- c->unref();
- dc->sa->curve->closepath_current();
+ if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 ||
+ prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){
+ dc->overwriteCurve->append_continuous(c, 0.0625);
+ c->unref();
+ dc->overwriteCurve->closepath_current();
+ if(dc->sa){
+ dc->white_curves = g_slist_remove(dc->white_curves, dc->sa->curve);
+ dc->white_curves = g_slist_append(dc->white_curves, dc->overwriteCurve);
+ }
+ }else{
+ dc->sa->curve->append_continuous(c, 0.0625);
+ c->unref();
+ dc->sa->curve->closepath_current();
+ }
spdc_flush_white(dc, NULL);
return;
}
@@ -517,6 +549,10 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed)
if (dc->sa) {
SPCurve *s = dc->sa->curve;
dc->white_curves = g_slist_remove(dc->white_curves, s);
+ if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 ||
+ prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){
+ s = dc->overwriteCurve;
+ }
if (dc->sa->start) {
s = reverse_then_unref(s);
}
@@ -529,6 +565,25 @@ void spdc_concat_colors_and_flush(FreehandBase *dc, gboolean forceclosed)
if (!dc->ea->start) {
e = reverse_then_unref(e);
}
+ if(prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1 ||
+ prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2){
+ e = reverse_then_unref(e);
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*e->last_segment());
+ SPCurve *lastSeg = new SPCurve();
+ if(cubic){
+ lastSeg->moveto((*cubic)[0]);
+ lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]);
+ if( e->get_segment_count() == 1){
+ e = lastSeg;
+ }else{
+ //we eliminate the last segment
+ e->backspace();
+ //and we add it again with the recreation
+ e->append_continuous(lastSeg, 0.0625);
+ }
+ }
+ e = reverse_then_unref(e);
+ }
c->append_continuous(e, 0.0625);
e->unref();
}
@@ -572,6 +627,7 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc)
bool has_lpe = false;
Inkscape::XML::Node *repr;
+
if (dc->white_item) {
repr = dc->white_item->getRepr();
has_lpe = SP_LPE_ITEM(dc->white_item)->hasPathEffectRecursive();
@@ -635,7 +691,6 @@ SPDrawAnchor *spdc_test_inside(FreehandBase *dc, Geom::Point p)
active = na;
}
}
-
return active;
}
@@ -675,6 +730,15 @@ static void spdc_free_colors(FreehandBase *dc)
dc->blue_curve = dc->blue_curve->unref();
}
+ // Blue2
+ if (dc->blue2_bpath) {
+ sp_canvas_item_destroy(SP_CANVAS_ITEM(dc->blue2_bpath));
+ dc->blue2_bpath = NULL;
+ }
+ if (dc->blue2_curve) {
+ dc->blue2_curve = dc->blue2_curve->unref();
+ }
+
// Green
while (dc->green_bpaths) {
sp_canvas_item_destroy(SP_CANVAS_ITEM(dc->green_bpaths->data));
diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h
index 5a4b91800..6e04e03b7 100644
--- a/src/ui/tools/freehand-base.h
+++ b/src/ui/tools/freehand-base.h
@@ -64,6 +64,10 @@ public:
SPCanvasItem *blue_bpath;
SPCurve *blue_curve;
+ // Blue2
+ SPCanvasItem *blue2_bpath;
+ SPCurve *blue2_curve;
+
// Green
GSList *green_bpaths;
SPCurve *green_curve;
@@ -75,12 +79,17 @@ public:
GSList *white_curves;
GSList *white_anchors;
+ //ALternative curve to use on continuing exisiting curve in case of bspline or spirolive
+ //because usigh anchor curves give memory and random bugs, - and obscure code- in some plataform reported by su_v in mac
+ SPCurve *overwriteCurve;
+
// Start anchor
SPDrawAnchor *sa;
// End anchor
SPDrawAnchor *ea;
+
/* type of the LPE that is to be applied automatically to a finished path (if any) */
Inkscape::LivePathEffect::EffectType waiting_LPE_type;
diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h
index 4d15ab70e..42f89cd1c 100644
--- a/src/ui/tools/node-tool.h
+++ b/src/ui/tools/node-tool.h
@@ -15,6 +15,9 @@
#include <glib.h>
#include "ui/tools/tool-base.h"
+// we need it to call it from Live Effect
+#include "selection.h"
+
namespace Inkscape {
namespace Display {
class TemporaryItem;
diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp
index 09c0a2a4f..9e4df5031 100644
--- a/src/ui/tools/pen-tool.cpp
+++ b/src/ui/tools/pen-tool.cpp
@@ -42,8 +42,39 @@
#include "context-fns.h"
#include "tools-switch.h"
#include "ui/control-manager.h"
+// we include the necessary files for BSpline & Spiro
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/parameter/path.h"
+#define INKSCAPE_LPE_SPIRO_C
+#include "live_effects/lpe-spiro.h"
+
+
+#include <typeinfo>
+#include <2geom/pathvector.h>
+#include <2geom/affine.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/hvlinesegment.h>
+#include "helper/geom-nodetype.h"
+#include "helper/geom-curves.h"
+
+// For handling un-continuous paths:
+#include "message-stack.h"
+#include "inkscape.h"
+#include "desktop.h"
+
+#include "live_effects/spiro.h"
+
+#define INKSCAPE_LPE_BSPLINE_C
+#include "live_effects/lpe-bspline.h"
+#include <2geom/nearest-point.h>
+
#include "tool-factory.h"
+#include "live_effects/effect.h"
+
+
using Inkscape::ControlManager;
namespace Inkscape {
@@ -135,8 +166,21 @@ PenTool::~PenTool() {
void PenTool::setPolylineMode() {
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0);
- this->polylines_only = (mode == 2 || mode == 3);
- this->polylines_paraxial = (mode == 3);
+ // change the nodes to make space for bspline mode
+ this->polylines_only = (mode == 3 || mode == 4);
+ this->polylines_paraxial = (mode == 4);
+ //we call the function which defines the Spiro modes and the BSpline
+ //todo: merge to one function only
+ this->_pen_context_set_mode(mode);
+}
+
+/*
+*.Set the mode of draw spiro, and bsplines
+*/
+void PenTool::_pen_context_set_mode(guint mode) {
+ // define the nodes
+ this->spiro = (mode == 1);
+ this->bspline = (mode == 2);
}
/**
@@ -332,9 +376,20 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
Geom::Point const event_w(bevent.x, bevent.y);
Geom::Point event_dt(desktop->w2d(event_w));
-
+ //Test whether we hit any anchor.
+ SPDrawAnchor * const anchor = spdc_test_inside(this, event_w);
+
+ //with this we avoid creating a new point over the existing one
+ if(bevent.button != 3 && (this->spiro || this->bspline) && this->npoints > 0 && this->p[0] == this->p[3]){
+ if( anchor && anchor == this->sa && this->green_curve->is_empty()){
+ //remove the following line to avoid having one node on top of another
+ _finishSegment(event_dt, bevent.state);
+ _finish( false);
+ return true;
+ }
+ return false;
+ }
bool ret = false;
-
if (bevent.button == 1 && !this->space_panning
// make sure this is not the last click for a waiting LPE (otherwise we want to finish the path)
&& this->expecting_clicks_for_LPE != 1) {
@@ -355,10 +410,8 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
pen_drag_origin_w = event_w;
pen_within_tolerance = true;
- // Test whether we hit any anchor.
- SPDrawAnchor * const anchor = spdc_test_inside(this, event_w);
-
switch (this->mode) {
+
case PenTool::MODE_CLICK:
// In click mode we add point on release
switch (this->state) {
@@ -399,8 +452,12 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
// distinction so that the case of a waiting LPE is treated separately
// Set start anchor
+
this->sa = anchor;
- if (anchor && !this->hasWaitingLPE()) {
+ if(anchor){
+ this->_bspline_spiro_start_anchor(bevent.state & GDK_SHIFT_MASK);
+ }
+ if (anchor && (!this->hasWaitingLPE()|| this->bspline || this->spiro)) {
// Adjust point to anchor if needed; if we have a waiting LPE, we need
// a fresh path to be created so don't continue an existing one
p = anchor->dp;
@@ -422,7 +479,7 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
// Create green anchor
p = event_dt;
this->_endpointSnap(p, bevent.state);
- this->green_anchor = sp_draw_anchor_new(this, this->green_curve, TRUE, p);
+ this->green_anchor = sp_draw_anchor_new(this, this->green_curve, true, p);
}
this->_setInitialPoint(p);
} else {
@@ -439,7 +496,7 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
if (this->green_anchor && this->green_anchor->active) {
// we clicked on the current curve start, so close it even if
// we drag a handle away from it
- this->green_closed = TRUE;
+ this->green_closed = true;
}
ret = true;
break;
@@ -450,8 +507,8 @@ bool PenTool::_handleButtonPress(GdkEventButton const &bevent) {
this->_setSubsequentPoint(p, true);
}
}
-
- this->state = this->polylines_only ? PenTool::POINT : PenTool::CONTROL;
+ // avoid the creation of a control point so a node is created in the release event
+ this->state = (this->spiro || this->bspline || this->polylines_only) ? PenTool::POINT : PenTool::CONTROL;
ret = true;
break;
case PenTool::CONTROL:
@@ -510,9 +567,11 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
Geom::Point const event_w(mevent.x, mevent.y);
+ //we take out the function the const "tolerance" because we need it later
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
+
if (pen_within_tolerance) {
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- gint const tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
if ( Geom::LInfty( event_w - pen_drag_origin_w ) < tolerance ) {
return false; // Do not drag if we're within tolerance from origin.
}
@@ -536,7 +595,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
// Only set point, if we are already appending
this->_endpointSnap(p, mevent.state);
this->_setSubsequentPoint(p, true);
- ret = TRUE;
+ ret = true;
} else if (!this->sp_event_context_knot_mouseover()) {
SnapManager &m = desktop->namedview->snap_manager;
m.setup(desktop);
@@ -572,7 +631,11 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
}
if (anchor && !this->anchor_statusbar) {
- this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path."));
+ if(!this->spiro && !this->bspline){
+ this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path."));
+ }else{
+ this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path. Shift+Click make a cusp node"));
+ }
this->anchor_statusbar = true;
} else if (!anchor && this->anchor_statusbar) {
this->message_context->clear();
@@ -582,11 +645,16 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
ret = true;
} else {
if (anchor && !this->anchor_statusbar) {
- this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point."));
+ if(!this->spiro && !this->bspline){
+ this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point."));
+ }else{
+ this->message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to continue the path from this point. Shift+Click make a cusp node"));
+ }
this->anchor_statusbar = true;
} else if (!anchor && this->anchor_statusbar) {
this->message_context->clear();
this->anchor_statusbar = false;
+
}
if (!this->sp_event_context_knot_mouseover()) {
SnapManager &m = desktop->namedview->snap_manager;
@@ -601,6 +669,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
// Placing controls is last operation in CLOSE state
// snap the handle
+
this->_endpointSnapHandle(p, mevent.state);
if (!this->polylines_only) {
@@ -608,6 +677,7 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
} else {
this->_setCtrl(this->p[1], mevent.state);
}
+
gobble_motion_events(GDK_BUTTON1_MASK);
ret = true;
break;
@@ -627,6 +697,18 @@ bool PenTool::_handleMotionNotify(GdkEventMotion const &mevent) {
default:
break;
}
+ // calls the function "bspline_spiro_motion" when the mouse starts or stops moving
+ if(this->bspline){
+ this->_bspline_spiro_color();
+ this->_bspline_spiro_motion(mevent.state & GDK_SHIFT_MASK);
+ }else{
+ if ( Geom::LInfty( event_w - pen_drag_origin_w ) > (tolerance/2) || mevent.time == 0) {
+ this->_bspline_spiro_color();
+ this->_bspline_spiro_motion(mevent.state & GDK_SHIFT_MASK);
+ pen_drag_origin_w = event_w;
+ }
+ }
+
return ret;
}
@@ -648,7 +730,12 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
Geom::Point p = this->desktop->w2d(event_w);
// Test whether we hit any anchor.
+
SPDrawAnchor *anchor = spdc_test_inside(this, event_w);
+ // if we try to create a node in the same place as another node, we skip
+ if((!anchor || anchor == this->sa) && (this->spiro || this->bspline) && this->npoints > 0 && this->p[0] == this->p[3]){
+ return true;
+ }
switch (this->mode) {
case PenTool::MODE_CLICK:
@@ -660,6 +747,12 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
p = anchor->dp;
}
this->sa = anchor;
+ // continue the existing curve
+ if (anchor) {
+ if(this->bspline || this->spiro){
+ this->_bspline_spiro_start_anchor(revent.state & GDK_SHIFT_MASK);;
+ }
+ }
this->_setInitialPoint(p);
} else {
// Set end anchor here
@@ -684,6 +777,10 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
this->_endpointSnap(p, revent.state);
}
this->_finishSegment(p, revent.state);
+ // hude the guide of the penultimate node when closing the curve
+ if(this->spiro){
+ sp_canvas_item_hide(this->c1);
+ }
this->_finish(true);
this->state = PenTool::POINT;
ret = true;
@@ -707,6 +804,10 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
case PenTool::CLOSE:
this->_endpointSnap(p, revent.state);
this->_finishSegment(p, revent.state);
+ // hide the penultimate node guide when closing the curve
+ if(this->spiro){
+ sp_canvas_item_hide(this->c1);
+ }
if (this->green_closed) {
// finishing at the start anchor, close curve
this->_finish(true);
@@ -727,7 +828,6 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
default:
break;
}
-
if (this->grab) {
// Release grab now
sp_canvas_item_ungrab(this->grab, revent.time);
@@ -736,7 +836,7 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) {
ret = true;
- this->green_closed = FALSE;
+ this->green_closed = false;
}
// TODO: can we be sure that the path was created correctly?
@@ -764,7 +864,7 @@ bool PenTool::_handle2ButtonPress(GdkEventButton const &bevent) {
bool ret = false;
// only end on LMB double click. Otherwise horizontal scrolling causes ending of the path
if (this->npoints != 0 && bevent.button == 1) {
- this->_finish(FALSE);
+ this->_finish(false);
ret = true;
}
return ret;
@@ -785,7 +885,6 @@ void PenTool::_redrawAll() {
this->green_bpaths = g_slist_prepend(this->green_bpaths, cshape);
}
-
if (this->green_anchor)
SP_CTRL(this->green_anchor->ctrl)->moveto(this->green_anchor->dp);
@@ -795,7 +894,8 @@ void PenTool::_redrawAll() {
sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve);
// handles
- if (this->p[0] != this->p[1]) {
+ // hide the handlers in bspline and spiro modes
+ if (this->p[0] != this->p[1] && !this->spiro && !this->bspline) {
SP_CTRL(this->c1)->moveto(this->p[1]);
this->cl1->setCoords(this->p[0], this->p[1]);
sp_canvas_item_show(this->c1);
@@ -808,8 +908,9 @@ void PenTool::_redrawAll() {
Geom::Curve const * last_seg = this->green_curve->last_segment();
if (last_seg) {
Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( last_seg );
+ // hide the handlers in bspline and spiro modes
if ( cubic &&
- (*cubic)[2] != this->p[0] )
+ (*cubic)[2] != this->p[0] && !this->spiro && !this->bspline )
{
Geom::Point p2 = (*cubic)[2];
SP_CTRL(this->c0)->moveto(p2);
@@ -821,6 +922,10 @@ void PenTool::_redrawAll() {
sp_canvas_item_hide(this->cl0);
}
}
+
+ // simply redraw the spiro. because its a redrawing, we don't call the global function,
+ // but we call the redrawing at the ending.
+ this->_bspline_spiro_build();
}
void PenTool::_lastpointMove(gdouble x, gdouble y) {
@@ -838,6 +943,7 @@ void PenTool::_lastpointMove(gdouble x, gdouble y) {
}
// red
+
this->p[0] += Geom::Point(x, y);
this->p[1] += Geom::Point(x, y);
this->_redrawAll();
@@ -848,25 +954,103 @@ void PenTool::_lastpointMoveScreen(gdouble x, gdouble y) {
}
void PenTool::_lastpointToCurve() {
- if (this->npoints != 5)
+ // avoid that if the "red_curve" contains only two points ( rect ), it doesn't stop here.
+ if (this->npoints != 5 && !this->spiro && !this->bspline)
return;
-
- Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() );
- if ( cubic ) {
- this->p[1] = this->p[0] + (Geom::Point)( (*cubic)[3] - (*cubic)[2] );
- } else {
- this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]);
+ Geom::CubicBezier const * cubic;
+ this->p[1] = this->red_curve->last_segment()->initialPoint() + (1./3)* (Geom::Point)(this->red_curve->last_segment()->finalPoint() - this->red_curve->last_segment()->initialPoint());
+ //modificate the last segment of the green curve so it creates the type of node we need
+ if(this->spiro||this->bspline){
+ if(!this->green_curve->is_empty()){
+ Geom::Point A(0,0);
+ Geom::Point B(0,0);
+ Geom::Point C(0,0);
+ Geom::Point D(0,0);
+ SPCurve * previous = new SPCurve();
+ cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() );
+ //We obtain the last segment 4 points in the previous curve
+ if ( cubic ){
+ A = (*cubic)[0];
+ B = (*cubic)[1];
+ if(this->spiro){
+ C = this->p[0] + (Geom::Point)(this->p[0] - this->p[1]);
+ }else
+ C = this->green_curve->last_segment()->finalPoint() + (1./3)* (Geom::Point)(this->green_curve->last_segment()->initialPoint() - this->green_curve->last_segment()->finalPoint());
+ D = (*cubic)[3];
+ }else{
+ A = this->green_curve->last_segment()->initialPoint();
+ B = this->green_curve->last_segment()->initialPoint();
+ if(this->spiro)
+ C = this->p[0] + (Geom::Point)(this->p[0] - this->p[1]);
+ else
+ C = this->green_curve->last_segment()->finalPoint() + (1./3)* (Geom::Point)(this->green_curve->last_segment()->initialPoint() - this->green_curve->last_segment()->finalPoint());
+ D = this->green_curve->last_segment()->finalPoint();
+ }
+ previous->moveto(A);
+ previous->curveto(B, C, D);
+ if( this->green_curve->get_segment_count() == 1){
+ this->green_curve = previous;
+ }else{
+ //we eliminate the last segment
+ this->green_curve->backspace();
+ //and we add it again with the recreation
+ this->green_curve->append_continuous(previous, 0.0625);
+ }
+ }
+ //if the last node is an union with another curve
+ if(this->green_curve->is_empty() && this->sa && !this->sa->curve->is_empty()){
+ this->_bspline_spiro_start_anchor(false);
+ }
}
this->_redrawAll();
}
+
void PenTool::_lastpointToLine() {
- if (this->npoints != 5)
+ // avoid that if the "red_curve" contains only two points ( rect) it doesn't stop here.
+ if (this->npoints != 5 && !this->bspline)
return;
+ // modify the last segment of the green curve so the type of node we want is created.
+ if(this->spiro || this->bspline){
+ if(!this->green_curve->is_empty()){
+ Geom::Point A(0,0);
+ Geom::Point B(0,0);
+ Geom::Point C(0,0);
+ Geom::Point D(0,0);
+ SPCurve * previous = new SPCurve();
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>( this->green_curve->last_segment() );
+ if ( cubic ) {
+ A = this->green_curve->last_segment()->initialPoint();
+ B = (*cubic)[1];
+ C = this->green_curve->last_segment()->finalPoint();
+ D = C;
+ } else {
+ //We obtain the last segment 4 points in the previous curve
+ A = this->green_curve->last_segment()->initialPoint();
+ B = A;
+ C = this->green_curve->last_segment()->finalPoint();
+ D = C;
+ }
+ previous->moveto(A);
+ previous->curveto(B, C, D);
+ if( this->green_curve->get_segment_count() == 1){
+ this->green_curve = previous;
+ }else{
+ //we eliminate the last segment
+ this->green_curve->backspace();
+ //and we add it again with the recreation
+ this->green_curve->append_continuous(previous, 0.0625);
+ }
+ }
+ // if the last node is an union with another curve
+ if(this->green_curve->is_empty() && this->sa && !this->sa->curve->is_empty()){
+ this->_bspline_spiro_start_anchor(true);
+ }
+ }
+
this->p[1] = this->p[0];
-
this->_redrawAll();
}
@@ -971,7 +1155,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
case GDK_KEY_p:
if (MOD__SHIFT_ONLY(event)) {
sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::PARALLEL, 2);
- ret = TRUE;
+ ret = true;
}
break;
@@ -979,7 +1163,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
case GDK_KEY_c:
if (MOD__SHIFT_ONLY(event)) {
sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::CIRCLE_3PTS, 3);
- ret = TRUE;
+ ret = true;
}
break;
@@ -987,7 +1171,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
case GDK_KEY_b:
if (MOD__SHIFT_ONLY(event)) {
sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::PERP_BISECTOR, 2);
- ret = TRUE;
+ ret = true;
}
break;
@@ -995,7 +1179,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
case GDK_KEY_a:
if (MOD__SHIFT_ONLY(event)) {
sp_pen_context_wait_for_LPE_mouse_clicks(pc, Inkscape::LivePathEffect::ANGLE_BISECTOR, 3);
- ret = TRUE;
+ ret = true;
}
break;
*/
@@ -1056,6 +1240,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
}
} else {
// Reset red curve
+ Geom::CubicBezier const * cubic = NULL;
this->red_curve->reset();
// Destroy topmost green bpath
if (this->green_bpaths) {
@@ -1068,26 +1253,56 @@ bool PenTool::_handleKeyPress(GdkEvent *event) {
g_warning("pen_handle_key_press, case GDK_KP_Delete: Green curve is empty");
break;
}
- // The code below assumes that pc->green_curve has only ONE path !
+ // The code below assumes that this->green_curve has only ONE path !
Geom::Curve const * crv = this->green_curve->last_segment();
this->p[0] = crv->initialPoint();
if ( Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const *>(crv)) {
this->p[1] = (*cubic)[1];
+
} else {
this->p[1] = this->p[0];
}
- Geom::Point const pt(( this->npoints < 4
+
+ // asign the value in a third of the distance of the last segment.
+ if(this->bspline){
+ this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]);
+ }
+
+ Geom::Point const pt((this->npoints < 4
? (Geom::Point)(crv->finalPoint())
- : this->p[3] ));
+ : this->p[3]));
+
this->npoints = 2;
- this->green_curve->backspace();
- sp_canvas_item_hide(this->c0);
- sp_canvas_item_hide(this->c1);
+ // delete the last segment of the green curve
+ if( this->green_curve->get_segment_count() == 1){
+ this->npoints = 5;
+ if (this->green_bpaths) {
+ if (this->green_bpaths->data)
+ sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data));
+ this->green_bpaths = g_slist_remove(this->green_bpaths, this->green_bpaths->data);
+ }
+ this->green_curve->reset();
+ }else{
+ this->green_curve->backspace();
+ }
+ // assign the value of this->p[1] to the oposite of the green line last segment
+ if(this->spiro){
+ cubic = dynamic_cast<Geom::CubicBezier const *>(this->green_curve->last_segment());
+ if ( cubic ) {
+ this->p[1] = (*cubic)[3] + (Geom::Point)((*cubic)[3] - (*cubic)[2]);
+ SP_CTRL(this->c1)->moveto(this->p[0]);
+ } else {
+ this->p[1] = this->p[0];
+ }
+ }
sp_canvas_item_hide(this->cl0);
sp_canvas_item_hide(this->cl1);
this->state = PenTool::POINT;
this->_setSubsequentPoint(pt, true);
pen_last_paraxial_dir = !pen_last_paraxial_dir;
+
+ //redraw
+ this->_bspline_spiro_build();
ret = true;
}
break;
@@ -1104,6 +1319,9 @@ void PenTool::_resetColors() {
// Blue
this->blue_curve->reset();
sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue_bpath), NULL);
+ // Blue2
+ this->blue2_curve->reset();
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue2_bpath), NULL);
// Green
while (this->green_bpaths) {
sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data));
@@ -1154,11 +1372,683 @@ void PenTool::_setAngleDistanceStatusMessage(Geom::Point const p, int pc_point_t
}
this->message_context->setF(Inkscape::IMMEDIATE_MESSAGE, message, angle, dist->str);
- g_string_free(dist, FALSE);
+ g_string_free(dist, false);
+}
+
+// this function changes the colors red, green and blue making them transparent or not, depending on if spiro is being used.
+void PenTool::_bspline_spiro_color()
+{
+ bool remake_green_bpaths = false;
+ if(this->spiro){
+ //If the colour is not defined as trasparent, por example when changing
+ //from drawing to spiro mode or when selecting the pen tool
+ if(this->green_color != 0x00ff000){
+ //We change the green and red colours to transparent, so this lines are not necessary
+ //to the drawing with spiro
+ this->red_color = 0xff00000;
+ this->green_color = 0x00ff000;
+ remake_green_bpaths = true;
+ }
+ }else if(this->bspline){
+ //If we come from working with the spiro curve and change the mode the "green_curve" colour is transparent
+ if(this->green_color != 0xff00007f){
+ //since we are not im spiro mode, we assign the original colours
+ //to the red and the green curve, removing their transparency
+ this->red_color = 0xff00007f;
+ //Damos color rojo a la linea verde
+ this->green_color = 0xff00007f;
+ remake_green_bpaths = true;
+ }
+ }else{
+ //If we come from working with the spiro curve and change the mode the "green_curve" colour is transparent
+ if(this->green_color != 0x00ff007f){
+ //since we are not im spiro mode, we assign the original colours
+ //to the red and the green curve, removing their transparency
+ this->red_color = 0xff00007f;
+ this->green_color = 0x00ff007f;
+ remake_green_bpaths = true;
+ }
+ //we hide the spiro/bspline rests
+ sp_canvas_item_hide(this->blue2_bpath);
+ }
+ //We erase all the "green_bpaths" to recreate them after with the colour
+ //transparency recently modified
+ if (this->green_bpaths && remake_green_bpaths) {
+ // remove old piecewise green canvasitems
+ while (this->green_bpaths) {
+ sp_canvas_item_destroy(SP_CANVAS_ITEM(this->green_bpaths->data));
+ this->green_bpaths = g_slist_remove(this->green_bpaths, this->green_bpaths->data);
+ }
+ // one canvas bpath for all of green_curve
+ SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), this->green_curve);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), this->green_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+ sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cshape), 0, SP_WIND_RULE_NONZERO);
+ this->green_bpaths = g_slist_prepend(this->green_bpaths, cshape);
+ }
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->red_bpath), this->red_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+}
+
+
+void PenTool::_bspline_spiro(bool shift)
+{
+ if(!this->spiro && !this->bspline)
+ return;
+
+ shift?this->_bspline_spiro_off():this->_bspline_spiro_on();
+ this->_bspline_spiro_build();
+}
+
+void PenTool::_bspline_spiro_on()
+{
+ if(!this->red_curve->is_empty()){
+ using Geom::X;
+ using Geom::Y;
+ this->npoints = 5;
+ this->p[0] = this->red_curve->first_segment()->initialPoint();
+ this->p[3] = this->red_curve->first_segment()->finalPoint();
+ this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]);
+ this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005);
+ }
+}
+
+void PenTool::_bspline_spiro_off()
+{
+ if(!this->red_curve->is_empty()){
+ this->npoints = 5;
+ this->p[0] = this->red_curve->first_segment()->initialPoint();
+ this->p[3] = this->red_curve->first_segment()->finalPoint();
+ this->p[2] = this->p[3];
+ }
+}
+
+void PenTool::_bspline_spiro_start_anchor(bool shift)
+{
+ if(this->sa->curve->is_empty()){
+ return;
+ }
+
+ LivePathEffect::LPEBSpline *lpe_bsp = NULL;
+
+ if (SP_IS_LPE_ITEM(this->white_item) && SP_LPE_ITEM(this->white_item)->hasPathEffect()){
+ Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(this->white_item)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE);
+ if(thisEffect){
+ lpe_bsp = dynamic_cast<LivePathEffect::LPEBSpline*>(thisEffect->getLPEObj()->get_lpe());
+ }
+ }
+ if(lpe_bsp){
+ this->bspline = true;
+ }else{
+ this->bspline = false;
+ }
+ LivePathEffect::LPESpiro *lpe_spi = NULL;
+
+ if (SP_IS_LPE_ITEM(this->white_item) && SP_LPE_ITEM(this->white_item)->hasPathEffect()){
+ Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(this->white_item)->getPathEffectOfType(Inkscape::LivePathEffect::SPIRO);
+ if(thisEffect){
+ lpe_spi = dynamic_cast<LivePathEffect::LPESpiro*>(thisEffect->getLPEObj()->get_lpe());
+ }
+ }
+ if(lpe_spi){
+ this->spiro = true;
+ }else{
+ this->spiro = false;
+ }
+ if(!this->spiro && !this->bspline)
+ return;
+
+ if(shift)
+ this->_bspline_spiro_start_anchor_off();
+ else
+ this->_bspline_spiro_start_anchor_on();
+}
+
+void PenTool::_bspline_spiro_start_anchor_on()
+{
+ using Geom::X;
+ using Geom::Y;
+ SPCurve *tmpCurve = new SPCurve();
+ tmpCurve = this->sa->curve->copy();
+ if(this->sa->start)
+ tmpCurve = tmpCurve->create_reverse();
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment());
+ SPCurve *lastSeg = new SPCurve();
+ Geom::Point A = tmpCurve->last_segment()->initialPoint();
+ Geom::Point D = tmpCurve->last_segment()->finalPoint();
+ Geom::Point C = D + (1./3)*(A - D);
+ C = Geom::Point(C[X] + 0.005,C[Y] + 0.005);
+ if(cubic){
+ lastSeg->moveto(A);
+ lastSeg->curveto((*cubic)[1],C,D);
+ }else{
+ lastSeg->moveto(A);
+ lastSeg->curveto(A,C,D);
+ }
+ if( tmpCurve->get_segment_count() == 1){
+ tmpCurve = lastSeg;
+ }else{
+ //we eliminate the last segment
+ tmpCurve->backspace();
+ //and we add it again with the recreation
+ tmpCurve->append_continuous(lastSeg, 0.0625);
+ }
+ if (this->sa->start) {
+ tmpCurve = tmpCurve->create_reverse();
+ }
+ this->overwriteCurve = tmpCurve;
+}
+
+void PenTool::_bspline_spiro_start_anchor_off()
+{
+ SPCurve *tmpCurve = new SPCurve();
+ tmpCurve = this->sa->curve->copy();
+ if(this->sa->start)
+ tmpCurve = tmpCurve->create_reverse();
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment());
+ if(cubic){
+ SPCurve *lastSeg = new SPCurve();
+ lastSeg->moveto((*cubic)[0]);
+ lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]);
+ if( tmpCurve->get_segment_count() == 1){
+ tmpCurve = lastSeg;
+ }else{
+ //we eliminate the last segment
+ tmpCurve->backspace();
+ //and we add it again with the recreation
+ tmpCurve->append_continuous(lastSeg, 0.0625);
+ }
+ if (this->sa->start) {
+ tmpCurve = tmpCurve->create_reverse();
+ }
+ this->overwriteCurve = tmpCurve;
+ }
+
+}
+
+void PenTool::_bspline_spiro_motion(bool shift){
+ if(!this->spiro && !this->bspline)
+ return;
+
+ using Geom::X;
+ using Geom::Y;
+ if(this->red_curve->is_empty()) return;
+ this->npoints = 5;
+ SPCurve *tmpCurve = new SPCurve();
+ this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]);
+ this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005);
+ if(this->green_curve->is_empty() && !this->sa){
+ this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]);
+ this->p[1] = Geom::Point(this->p[1][X] + 0.005,this->p[1][Y] + 0.005);
+ }else if(!this->green_curve->is_empty()){
+ tmpCurve = this->green_curve->copy();
+ }else{
+ tmpCurve = this->overwriteCurve->copy();
+ if(this->sa->start)
+ tmpCurve = tmpCurve->create_reverse();
+ }
+
+ if(!tmpCurve->is_empty()){
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment());
+ if(cubic){
+ if(this->bspline){
+ SPCurve * WPower = new SPCurve();
+ Geom::D2< Geom::SBasis > SBasisWPower;
+ WPower->moveto(tmpCurve->last_segment()->finalPoint());
+ WPower->lineto(tmpCurve->last_segment()->initialPoint());
+ float WP = Geom::nearest_point((*cubic)[2],*WPower->first_segment());
+ WPower->reset();
+ WPower->moveto(this->red_curve->last_segment()->initialPoint());
+ WPower->lineto(this->red_curve->last_segment()->finalPoint());
+ SBasisWPower = WPower->first_segment()->toSBasis();
+ WPower->reset();
+ this->p[1] = SBasisWPower.valueAt(WP);
+ if(!Geom::are_near(this->p[1],this->p[0]))
+ this->p[1] = Geom::Point(this->p[1][X] + 0.005,this->p[1][Y] + 0.005);
+ if(shift)
+ this->p[2] = this->p[3];
+ }else{
+ this->p[1] = (*cubic)[3] + (Geom::Point)((*cubic)[3] - (*cubic)[2] );
+ this->p[1] = Geom::Point(this->p[1][X] + 0.005,this->p[1][Y] + 0.005);
+ }
+ }else{
+ this->p[1] = this->p[0];
+ if(shift)
+ this->p[2] = this->p[3];
+ }
+ }
+
+ if(this->anchor_statusbar && !this->red_curve->is_empty()){
+ if(shift){
+ this->_bspline_spiro_end_anchor_off();
+ }else{
+ this->_bspline_spiro_end_anchor_on();
+ }
+ }
+
+ this->_bspline_spiro_build();
+}
+
+void PenTool::_bspline_spiro_end_anchor_on()
+{
+
+ using Geom::X;
+ using Geom::Y;
+ this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]);
+ this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005);
+ SPCurve *tmpCurve = new SPCurve();
+ SPCurve *lastSeg = new SPCurve();
+ Geom::Point C(0,0);
+ bool reverse = false;
+ if( this->green_anchor && this->green_anchor->active ){
+ tmpCurve = this->green_curve->create_reverse();
+ if(this->green_curve->get_segment_count()==0){
+ return;
+ }
+ reverse = true;
+ } else if(this->sa){
+ tmpCurve = this->overwriteCurve;
+ if(!this->sa->start){
+ tmpCurve = tmpCurve->create_reverse();
+ reverse = true;
+ }
+ }else{
+ return;
+ }
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment());
+ if(this->bspline){
+ C = tmpCurve->last_segment()->finalPoint() + (1./3)*(tmpCurve->last_segment()->initialPoint() - tmpCurve->last_segment()->finalPoint());
+ C = Geom::Point(C[X] + 0.005,C[Y] + 0.005);
+ }else{
+ C = this->p[3] + (Geom::Point)(this->p[3] - this->p[2] );
+ }
+ if(cubic){
+ lastSeg->moveto((*cubic)[0]);
+ lastSeg->curveto((*cubic)[1],C,(*cubic)[3]);
+ }else{
+ lastSeg->moveto(tmpCurve->last_segment()->initialPoint());
+ lastSeg->curveto(tmpCurve->last_segment()->initialPoint(),C,tmpCurve->last_segment()->finalPoint());
+ }
+ if( tmpCurve->get_segment_count() == 1){
+ tmpCurve = lastSeg;
+ }else{
+ //we eliminate the last segment
+ tmpCurve->backspace();
+ //and we add it again with the recreation
+ tmpCurve->append_continuous(lastSeg, 0.0625);
+ }
+ if (reverse) {
+ tmpCurve = tmpCurve->create_reverse();
+ }
+ if( this->green_anchor && this->green_anchor->active )
+ {
+ this->green_curve->reset();
+ this->green_curve = tmpCurve;
+ }else{
+ this->overwriteCurve->reset();
+ this->overwriteCurve = tmpCurve;
+ }
+}
+
+void PenTool::_bspline_spiro_end_anchor_off()
+{
+
+ SPCurve *tmpCurve = new SPCurve();
+ SPCurve *lastSeg = new SPCurve();
+ bool reverse = false;
+ this->p[2] = this->p[3];
+ if( this->green_anchor && this->green_anchor->active ){
+ tmpCurve = this->green_curve->create_reverse();
+ if(this->green_curve->get_segment_count()==0){
+ return;
+ }
+ reverse = true;
+ } else if(this->sa){
+ tmpCurve = this->overwriteCurve;
+ if(!this->sa->start){
+ tmpCurve = tmpCurve->create_reverse();
+ reverse = true;
+ }
+ }else{
+ return;
+ }
+ Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment());
+ if(cubic){
+ lastSeg->moveto((*cubic)[0]);
+ lastSeg->curveto((*cubic)[1],(*cubic)[3],(*cubic)[3]);
+ if( tmpCurve->get_segment_count() == 1){
+ tmpCurve = lastSeg;
+ }else{
+ //we eliminate the last segment
+ tmpCurve->backspace();
+ //and we add it again with the recreation
+ tmpCurve->append_continuous(lastSeg, 0.0625);
+ }
+ if (reverse) {
+ tmpCurve = tmpCurve->create_reverse();
+ }
+ if( this->green_anchor && this->green_anchor->active )
+ {
+ this->green_curve->reset();
+ this->green_curve = tmpCurve;
+ }else{
+ this->overwriteCurve->reset();
+ this->overwriteCurve = tmpCurve;
+ }
+ }
+}
+
+//prepares the curves for its transformation into BSpline curve.
+void PenTool::_bspline_spiro_build()
+{
+ if(!this->spiro && !this->bspline){
+ return;
+ }
+
+ //We create the base curve
+ SPCurve *curve = new SPCurve();
+ //If we continuate the existing curve we add it at the start
+ if(this->sa && !this->sa->curve->is_empty()){
+ curve = this->overwriteCurve->copy();
+ if (this->sa->start) {
+ curve = curve->create_reverse();
+ }
+ }
+
+ if (!this->green_curve->is_empty()){
+ curve->append_continuous(this->green_curve, 0.0625);
+ }
+
+ //and the red one
+ if (!this->red_curve->is_empty()){
+ this->red_curve->reset();
+ this->red_curve->moveto(this->p[0]);
+ if(this->anchor_statusbar && !this->sa && !(this->green_anchor && this->green_anchor->active)){
+ this->red_curve->curveto(this->p[1],this->p[3],this->p[3]);
+ }else{
+ this->red_curve->curveto(this->p[1],this->p[2],this->p[3]);
+ }
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->red_bpath), this->red_curve);
+ curve->append_continuous(this->red_curve, 0.0625);
+ }
+
+ if(!curve->is_empty()){
+ // close the curve if the final points of the curve are close enough
+ if(Geom::are_near(curve->first_path()->initialPoint(), curve->last_path()->finalPoint())){
+ curve->closepath_current();
+ }
+ //TODO: CALL TO CLONED FUNCTION SPIRO::doEffect IN lpe-spiro.cpp
+ //For example
+ //using namespace Inkscape::LivePathEffect;
+ //LivePathEffectObject *lpeobj = static_cast<LivePathEffectObject*> (curve);
+ //Effect *spr = static_cast<Effect*> ( new LPEbspline(lpeobj) );
+ //spr->doEffect(curve);
+ if(this->bspline){
+ this->_bspline_doEffect(curve);
+ }else{
+ this->_spiro_doEffect(curve);
+ }
+
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(this->blue2_bpath), curve);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->blue2_bpath), this->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+ sp_canvas_item_show(this->blue2_bpath);
+ curve->unref();
+ this->blue2_curve->reset();
+ //We hide the holders that doesn't contribute anything
+ if(this->spiro){
+ sp_canvas_item_show(this->c1);
+ SP_CTRL(this->c1)->moveto(this->p[0]);
+ }else
+ sp_canvas_item_hide(this->c1);
+ sp_canvas_item_hide(this->cl1);
+ sp_canvas_item_hide(this->c0);
+ sp_canvas_item_hide(this->cl0);
+ }else{
+ //if the curve is empty
+ sp_canvas_item_hide(this->blue2_bpath);
+
+ }
+}
+
+void PenTool::_bspline_doEffect(SPCurve * curve)
+{
+ // commenting the function doEffect in src/live_effects/lpe-bspline.cpp
+ if(curve->get_segment_count() < 2)
+ return;
+ Geom::PathVector const original_pathv = curve->get_pathvector();
+ curve->reset();
+
+ for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
+ if (path_it->empty())
+ continue;
+
+ Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
+ Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
+ SPCurve *nCurve = new SPCurve();
+ Geom::Point previousNode(0,0);
+ Geom::Point node(0,0);
+ Geom::Point pointAt1(0,0);
+ Geom::Point pointAt2(0,0);
+ Geom::Point nextPointAt1(0,0);
+ Geom::Point nextPointAt2(0,0);
+ Geom::Point nextPointAt3(0,0);
+ Geom::D2< Geom::SBasis > SBasisIn;
+ Geom::D2< Geom::SBasis > SBasisOut;
+ Geom::D2< Geom::SBasis > SBasisHelper;
+ Geom::CubicBezier const *cubic = NULL;
+ if (path_it->closed()) {
+ const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type Geom::LineSegment.
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ curve_endit = path_it->end_open();
+ }
+ }
+ while ( curve_it2 != curve_endit )
+ {
+ SPCurve * in = new SPCurve();
+ in->moveto(curve_it1->initialPoint());
+ in->lineto(curve_it1->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1);
+ if(cubic){
+ SBasisIn = in->first_segment()->toSBasis();
+ pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1],*in->first_segment()));
+ pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2],*in->first_segment()));
+ }else{
+ pointAt1 = in->first_segment()->initialPoint();
+ pointAt2 = in->first_segment()->finalPoint();
+ }
+ in->reset();
+ delete in;
+ SPCurve * out = new SPCurve();
+ out->moveto(curve_it2->initialPoint());
+ out->lineto(curve_it2->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it2);
+ if(cubic){
+ SBasisOut = out->first_segment()->toSBasis();
+ nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1],*out->first_segment()));
+ nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2],*out->first_segment()));;
+ nextPointAt3 = (*cubic)[3];
+ }else{
+ nextPointAt1 = out->first_segment()->initialPoint();
+ nextPointAt2 = out->first_segment()->finalPoint();
+ nextPointAt3 = out->first_segment()->finalPoint();
+ }
+ out->reset();
+ delete out;
+ SPCurve *lineHelper = new SPCurve();
+ lineHelper->moveto(pointAt2);
+ lineHelper->lineto(nextPointAt1);
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ previousNode = node;
+ node = SBasisHelper.valueAt(0.5);
+ SPCurve *curveHelper = new SPCurve();
+ curveHelper->moveto(previousNode);
+ curveHelper->curveto(pointAt1, pointAt2, node);
+ nCurve->append_continuous(curveHelper, 0.0625);
+ curveHelper->reset();
+ delete curveHelper;
+ ++curve_it1;
+ ++curve_it2;
+ }
+ SPCurve *curveHelper = new SPCurve();
+ curveHelper->moveto(node);
+ Geom::Point startNode(0,0);
+ if (path_it->closed()) {
+ SPCurve * start = new SPCurve();
+ start->moveto(path_it->begin()->initialPoint());
+ start->lineto(path_it->begin()->finalPoint());
+ Geom::D2< Geom::SBasis > SBasisStart = start->first_segment()->toSBasis();
+ SPCurve *lineHelper = new SPCurve();
+ cubic = dynamic_cast<Geom::CubicBezier const*>(&*path_it->begin());
+ if(cubic){
+ lineHelper->moveto(SBasisStart.valueAt(Geom::nearest_point((*cubic)[1],*start->first_segment())));
+ }else{
+ lineHelper->moveto(start->first_segment()->initialPoint());
+ }
+ start->reset();
+ delete start;
+
+ SPCurve * end = new SPCurve();
+ end->moveto(curve_it1->initialPoint());
+ end->lineto(curve_it1->finalPoint());
+ Geom::D2< Geom::SBasis > SBasisEnd = end->first_segment()->toSBasis();
+ cubic = dynamic_cast<Geom::CubicBezier const*>(&*curve_it1);
+ if(cubic){
+ lineHelper->lineto(SBasisEnd.valueAt(Geom::nearest_point((*cubic)[2],*end->first_segment())));
+ }else{
+ lineHelper->lineto(end->first_segment()->finalPoint());
+ }
+ end->reset();
+ delete end;
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ startNode = SBasisHelper.valueAt(0.5);
+ curveHelper->curveto(nextPointAt1, nextPointAt2, startNode);
+ nCurve->append_continuous(curveHelper, 0.0625);
+ nCurve->move_endpoints(startNode,startNode);
+ }else{
+ SPCurve * start = new SPCurve();
+ start->moveto(path_it->begin()->initialPoint());
+ start->lineto(path_it->begin()->finalPoint());
+ startNode = start->first_segment()->initialPoint();
+ start->reset();
+ delete start;
+ curveHelper->curveto(nextPointAt1, nextPointAt2, nextPointAt3);
+ nCurve->append_continuous(curveHelper, 0.0625);
+ nCurve->move_endpoints(startNode,nextPointAt3);
+ }
+ curveHelper->reset();
+ delete curveHelper;
+ if (path_it->closed()) {
+ nCurve->closepath_current();
+ }
+ curve->append(nCurve,false);
+ nCurve->reset();
+ delete nCurve;
+ }
+}
+
+//Spiro function cloned from lpe-spiro.cpp
+// commenting the function "doEffect" from src/live_effects/lpe-spiro.cpp
+void PenTool::_spiro_doEffect(SPCurve * curve)
+{
+ using Geom::X;
+ using Geom::Y;
+
+ Geom::PathVector const original_pathv = curve->get_pathvector();
+ guint len = curve->get_segment_count() + 2;
+
+ curve->reset();
+ Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len);
+ int ip = 0;
+
+ for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
+ if (path_it->empty())
+ continue;
+
+ {
+ Geom::Point p = path_it->front().pointAt(0);
+ path[ip].x = p[X];
+ path[ip].y = p[Y];
+ path[ip].ty = '{' ;
+ ip++;
+ }
+
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
+
+ Geom::Path::const_iterator curve_endit = path_it->end_default();
+ if (path_it->closed()) {
+ const Geom::Curve &closingline = path_it->back_closed();
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ curve_endit = path_it->end_open();
+ }
+ }
+
+ while ( curve_it2 != curve_endit )
+ {
+ Geom::Point p = curve_it1->finalPoint();
+ path[ip].x = p[X];
+ path[ip].y = p[Y];
+
+ bool this_is_line = is_straight_curve(*curve_it1);
+ bool next_is_line = is_straight_curve(*curve_it2);
+
+ Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
+
+ if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
+ {
+ if (this_is_line && !next_is_line) {
+ path[ip].ty = ']';
+ } else if (next_is_line && !this_is_line) {
+ path[ip].ty = '[';
+ } else {
+ path[ip].ty = 'c';
+ }
+ } else {
+ path[ip].ty = 'v';
+ }
+
+ ++curve_it1;
+ ++curve_it2;
+ ip++;
+ }
+
+ Geom::Point p = curve_it1->finalPoint();
+ path[ip].x = p[X];
+ path[ip].y = p[Y];
+ if (path_it->closed()) {
+ Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
+ switch (nodetype) {
+ case Geom::NODE_NONE:
+ path[ip].ty = '}';
+ ip++;
+ break;
+ case Geom::NODE_CUSP:
+ path[0].ty = path[ip].ty = 'v';
+ break;
+ case Geom::NODE_SMOOTH:
+ case Geom::NODE_SYMM:
+ path[0].ty = path[ip].ty = 'c';
+ break;
+ }
+ } else {
+ path[ip].ty = '}';
+ ip++;
+ }
+
+ int sp_len = ip;
+ Spiro::spiro_run(path, sp_len, *curve);
+ ip = 0;
+ }
+
+ g_free (path);
}
void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint status) {
g_assert( this->npoints != 0 );
+
// todo: Check callers to see whether 2 <= npoints is guaranteed.
this->p[2] = p;
@@ -1182,7 +2072,7 @@ void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint sta
is_curve = false;
} else {
// one of the 'regular' modes
- if (this->p[1] != this->p[0]) {
+ if (this->p[1] != this->p[0] || this->spiro) {
this->red_curve->curveto(this->p[1], p, p);
is_curve = true;
} else {
@@ -1197,10 +2087,16 @@ void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint sta
gchar *message = is_curve ?
_("<b>Curve segment</b>: angle %3.2f&#176;, distance %s; with <b>Ctrl</b> to snap angle, <b>Enter</b> to finish the path" ):
_("<b>Line segment</b>: angle %3.2f&#176;, distance %s; with <b>Ctrl</b> to snap angle, <b>Enter</b> to finish the path");
+ if(this->spiro || this->bspline){
+ message = is_curve ?
+ _("<b>Curve segment</b>: angle %3.2f&#176;, distance %s; with <b>Shift+Click</b> make a cusp node, <b>Enter</b> to finish the path" ):
+ _("<b>Line segment</b>: angle %3.2f&#176;, distance %s; with <b>Shift+Click</b> make a cusp node, <b>Enter</b> to finish the path");
+ }
this->_setAngleDistanceStatusMessage(p, 0, message);
}
}
+
void PenTool::_setCtrl(Geom::Point const p, guint const state) {
sp_canvas_item_show(this->c1);
sp_canvas_item_show(this->cl1);
@@ -1211,7 +2107,6 @@ void PenTool::_setCtrl(Geom::Point const p, guint const state) {
sp_canvas_item_hide(this->cl0);
SP_CTRL(this->c1)->moveto(this->p[1]);
this->cl1->setCoords(this->p[0], this->p[1]);
-
this->_setAngleDistanceStatusMessage(p, 0, _("<b>Curve handle</b>: angle %3.2f&#176;, length %s; with <b>Ctrl</b> to snap angle"));
} else if ( this->npoints == 5 ) {
this->p[4] = p;
@@ -1233,6 +2128,8 @@ void PenTool::_setCtrl(Geom::Point const p, guint const state) {
SP_CTRL(this->c1)->moveto(this->p[4]);
this->cl1->setCoords(this->p[3], this->p[4]);
+
+
gchar *message = is_symm ?
_("<b>Curve handle, symmetric</b>: angle %3.2f&#176;, length %s; with <b>Ctrl</b> to snap angle, with <b>Shift</b> to move this handle only") :
_("<b>Curve handle</b>: angle %3.2f&#176;, length %s; with <b>Ctrl</b> to snap angle, with <b>Shift</b> to move this handle only");
@@ -1249,9 +2146,12 @@ void PenTool::_finishSegment(Geom::Point const p, guint const state) {
++this->num_clicks;
+
if (!this->red_curve->is_empty()) {
+ this->_bspline_spiro(state & GDK_SHIFT_MASK);
this->green_curve->append_continuous(this->red_curve, 0.0625);
SPCurve *curve = this->red_curve->copy();
+
/// \todo fixme:
SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(this->desktop), curve);
curve->unref();
@@ -1273,15 +2173,19 @@ void PenTool::_finish(gboolean const closed) {
return;
}
+
this->num_clicks = 0;
this->_disableEvents();
this->message_context->clear();
+
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Drawing finished"));
+ // cancelate line without a created segment
this->red_curve->reset();
spdc_concat_colors_and_flush(this, closed);
+ this->overwriteCurve = NULL;
this->sa = NULL;
this->ea = NULL;
@@ -1330,7 +2234,7 @@ int PenTool::nextParaxialDirection(Geom::Point const &pt, Geom::Point const &ori
//
// num_clicks is not reliable because spdc_pen_finish_segment is sometimes called too early
// (on first mouse release), in which case num_clicks immediately becomes 1.
- // if (pc->num_clicks == 0) {
+ // if (this->num_clicks == 0) {
if (this->green_curve->is_empty()) {
// first mouse click
diff --git a/src/ui/tools/pen-tool.h b/src/ui/tools/pen-tool.h
index 4dec7b4fe..98fd0a43e 100644
--- a/src/ui/tools/pen-tool.h
+++ b/src/ui/tools/pen-tool.h
@@ -49,6 +49,9 @@ public:
bool polylines_only;
bool polylines_paraxial;
+ // propiety which saves if Spiro mode is active or not
+ bool spiro;
+ bool bspline;
int num_clicks;
unsigned int expecting_clicks_for_LPE; // if positive, finish the path after this many clicks
@@ -85,6 +88,34 @@ private:
bool _handleButtonRelease(GdkEventButton const &revent);
bool _handle2ButtonPress(GdkEventButton const &bevent);
bool _handleKeyPress(GdkEvent *event);
+ //adds spiro & bspline modes
+ void _pen_context_set_mode(guint mode);
+ //this function changes the colors red, green and blue making them transparent or not depending on if the function uses spiro
+ void _bspline_spiro_color();
+ //creates a node in bspline or spiro modes
+ void _bspline_spiro(bool shift);
+ //creates a node in bspline or spiro modes
+ void _bspline_spiro_on();
+ //creates a CUSP node
+ void _bspline_spiro_off();
+ //continues the existing curve in bspline or spiro mode
+ void _bspline_spiro_start_anchor(bool shift);
+ //continues the existing curve with the union node in bspline or spiro modes
+ void _bspline_spiro_start_anchor_on();
+ //continues an existing curve with the union node in CUSP mode
+ void _bspline_spiro_start_anchor_off();
+ //modifies the "red_curve" when it detects movement
+ void _bspline_spiro_motion(bool shift);
+ //closes the curve with the last node in bspline or spiro mode
+ void _bspline_spiro_end_anchor_on();
+ //closes the curve with the last node in CUSP mode
+ void _bspline_spiro_end_anchor_off();
+ //CHECK: join all the curves "in game" and we call doEffect function
+ void _bspline_spiro_build();
+ //function bspline cloned from lpe-bspline.cpp
+ void _bspline_doEffect(SPCurve * curve);
+ //function spiro cloned from lpe-spiro.cpp
+ void _spiro_doEffect(SPCurve * curve);
void _setInitialPoint(Geom::Point const p);
void _setSubsequentPoint(Geom::Point const p, bool statusbar, guint status = 0);
diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp
index fb4e82c32..0e8660248 100644
--- a/src/ui/tools/pencil-tool.cpp
+++ b/src/ui/tools/pencil-tool.cpp
@@ -200,6 +200,7 @@ bool PencilTool::_handleButtonPress(GdkEventButton const &bevent) {
}
if (anchor) {
p = anchor->dp;
+ this->overwriteCurve = anchor->curve;
desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Continuing selected path"));
} else {
m.setup(desktop);
@@ -301,7 +302,7 @@ bool PencilTool::_handleMotionNotify(GdkEventMotion const &mevent) {
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 pc->ps (apparently)
+ // 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
// wheter we're going into freehand mode or not
@@ -640,6 +641,9 @@ void PencilTool::_interpolate() {
return;
}
+ using Geom::X;
+ using Geom::Y;
+
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
double const tol = prefs->getDoubleLimited("/tools/freehand/pencil/tolerance", 10.0, 1.0, 100.0) * 0.4;
double const tolerance_sq = 0.02 * square(this->desktop->w2d().descrim() * tol) * exp(0.2 * tol - 2);
@@ -661,10 +665,21 @@ void PencilTool::_interpolate() {
if (n_segs > 0) {
/* Fit and draw and reset state */
- this->green_curve->moveto(b[0]);
+ 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++) {
- this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]);
+ // if we are in BSpline we modify the trace to create adhoc nodes
+ if(mode == 2){
+ Geom::Point BP = b[4*c+0] + (1./3)*(b[4*c+3] - b[4*c+0]);
+ BP = Geom::Point(BP[X] + 0.0001,BP[Y] + 0.0001);
+ Geom::Point CP = b[4*c+3] + (1./3)*(b[4*c+0] - b[4*c+3]);
+ CP = Geom::Point(CP[X] + 0.0001,CP[Y] + 0.0001);
+ this->green_curve->curveto(BP,CP,b[4*c+3]);
+ }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);
@@ -795,9 +810,23 @@ void PencilTool::_fitAndSplit() {
&& unsigned(this->npoints) < G_N_ELEMENTS(this->p) )
{
/* Fit and draw and reset state */
+
this->red_curve->reset();
this->red_curve->moveto(b[0]);
- this->red_curve->curveto(b[1], b[2], b[3]);
+ using Geom::X;
+ using Geom::Y;
+ // if we are in BSpline we modify the trace to create adhoc nodes
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0);
+ if(mode == 2){
+ Geom::Point B = b[0] + (1./3)*(b[3] - b[0]);
+ B = Geom::Point(B[X] + 0.0001,B[Y] + 0.0001);
+ Geom::Point C = b[3] + (1./3)*(b[0] - b[3]);
+ C = Geom::Point(C[X] + 0.0001,C[Y] + 0.0001);
+ this->red_curve->curveto(B,C,b[3]);
+ }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);
this->red_curve_is_valid = true;
} else {
diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp
index eb16cfece..a96f47124 100644
--- a/src/widgets/icon.cpp
+++ b/src/widgets/icon.cpp
@@ -685,6 +685,7 @@ void IconImpl::setupLegacyNaming() {
legacyNames["draw-star"] ="star_angled";
legacyNames["path-mode-bezier"] ="bezier_mode";
legacyNames["path-mode-spiro"] ="spiro_splines_mode";
+ legacyNames["path-mode-bspline"] ="bspline_mode";
legacyNames["path-mode-polyline"] ="polylines_mode";
legacyNames["path-mode-polyline-paraxial"] ="paraxial_lines_mode";
legacyNames["draw-use-tilt"] ="guse_tilt";
diff --git a/src/widgets/mappings.xml b/src/widgets/mappings.xml
index 2de3ff545..089bf76ea 100644
--- a/src/widgets/mappings.xml
+++ b/src/widgets/mappings.xml
@@ -197,6 +197,7 @@
<!-- bezier toolbar -->
<remap id='bezier_mode' newid='path-mode-bezier'/>
<remap id='spiro_splines_mode' newid='path-mode-spiro'/>
+ <remap id='bspline_mode' newid='path-mode-bspline'/>
<remap id='polylines_mode' newid='path-mode-polyline'/>
<remap id='paraxial_lines_mode' newid='path-mode-polyline-paraxial'/>
diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp
index 682de8594..772107101 100644
--- a/src/widgets/pencil-toolbar.cpp
+++ b/src/widgets/pencil-toolbar.cpp
@@ -104,7 +104,12 @@ static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* ho
1, _("Create Spiro path"),
2, INKSCAPE_ICON("path-mode-spiro"),
-1 );
-
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("BSpline"),
+ 1, _("Create BSpline path"),
+ 2, INKSCAPE_ICON("path-mode-bspline"),
+ -1 );
if (!tool_is_pencil) {
gtk_list_store_append( model, &iter );
gtk_list_store_set( model, &iter,