From afcec343e905389d2645115d60f14dfd45f5f2d0 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Cenoz Date: Fri, 14 Dec 2012 01:01:57 +0100 Subject: bzr history lost by a killed merge (bzr r11950.1.1) --- src/draw-context.cpp | 5 + src/live_effects/CMakeLists.txt | 2 + src/live_effects/Makefile_insert | 2 + src/live_effects/effect-enum.h | 3 + src/live_effects/effect.cpp | 13 +- src/pen-context.cpp | 988 +++++++++++++++++++++++++++++++++++++-- src/pen-context.h | 5 + src/widgets/pencil-toolbar.cpp | 9 +- 8 files changed, 989 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/draw-context.cpp b/src/draw-context.cpp index 5996d600b..21e2a6883 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -314,6 +314,11 @@ static void spdc_check_for_and_apply_waiting_LPE(SPDrawContext *dc, SPItem *item if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 1) { Effect::createAndApply(SPIRO, dc->desktop->doc(), item); } + //BSpline + if (prefs->getInt(tool_name(dc) + "/freehand-mode", 0) == 2) { + Effect::createAndApply(BSPLINE, dc->desktop->doc(), item); + } + //BSPline End int shape = prefs->getInt(tool_name(dc) + "/shape", 0); bool shape_applied = false; 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..cf97dd87f 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -45,6 +45,9 @@ enum EffectType { PATH_LENGTH, LINE_SEGMENT, DOEFFECTSTACK_TEST, + //BSpline + BSPLINE, + //BSpline End DYNASTROKE, RECURSIVE_SKELETON, EXTRUDE, diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 3b57de25c..e9ec2076f 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -10,7 +10,7 @@ #include "live_effects/effect.h" #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif // include effects: @@ -22,6 +22,9 @@ #include "live_effects/lpe-rough-hatches.h" #include "live_effects/lpe-dynastroke.h" #include "live_effects/lpe-test-doEffect-stack.h" +//BSpline +#include "live_effects/lpe-bspline.h" +//BSpline End #include "live_effects/lpe-gears.h" #include "live_effects/lpe-curvestitch.h" #include "live_effects/lpe-circle_with_radius.h" @@ -123,6 +126,9 @@ const Util::EnumData LPETypeData[] = { /* 0.49 */ {POWERSTROKE, N_("Power stroke"), "powerstroke"}, {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"}, + //BSpline + {BSPLINE, N_("BSpline"), "bspline"}, + //BSpline End }; const Util::EnumDataConverter LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData)); @@ -231,6 +237,11 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case DOEFFECTSTACK_TEST: neweffect = static_cast ( new LPEdoEffectStackTest(lpeobj) ); break; + //BSpline + case BSPLINE: + neweffect = static_cast ( new LPEBSpline(lpeobj) ); + break; + //BSpline End case DYNASTROKE: neweffect = static_cast ( new LPEDynastroke(lpeobj) ); break; diff --git a/src/pen-context.cpp b/src/pen-context.cpp index cb20eb3eb..be25b6a7f 100644 --- a/src/pen-context.cpp +++ b/src/pen-context.cpp @@ -44,7 +44,26 @@ #include "context-fns.h" #include "tools-switch.h" #include "ui/control-manager.h" +//BSpline +//Incluimos +#define INKSCAPE_LPE_SPIRO_C +#include "live_effects/lpe-spiro.h" +#include "display/curve.h" +#include +#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" + +#include "live_effects/spiro.h" + +#define INKSCAPE_LPE_BSPLINE_C +#include "live_effects/lpe-bspline.h" + +//BSpline End using Inkscape::ControlManager; @@ -59,6 +78,34 @@ static gint sp_pen_context_root_handler(SPEventContext *ec, GdkEvent *event); static gint sp_pen_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); static void spdc_pen_set_initial_point(SPPenContext *pc, Geom::Point const p); + +//BSpline +//Added functions +//Sobrecarga la función "sp_pen_context_set_polyline_mode" +//Le da valor a la nueva propiedad "pc->spiro" que como se auto define indica si estamos en modo spiro +//En el futuro se la dará a "pc->bspline" +static void sp_pen_context_set_mode(SPPenContext *const pc, guint mode); +//Esta función cambia los colores rojo,verde y azul haciendolos transparentes o no en función de si se usa spiro +static void spiro_color(SPPenContext *const pc); +//Combierte el el último nodo a CUSP +static void spiro_node_cusp (SPPenContext *const pc); +//Combierte el el último nodo a SYMM +static void spiro_node_symm (SPPenContext *const pc); +//preparates the curves for its trasformation into spiro curves. +static void spiro_build(SPPenContext *const pc, bool Shift); +//function spiro cloned from lpe-spiro.cpp +static void spiro_doEffect(SPCurve * curve); +//Combierte el el último nodo a CUSP +static void bspline_node_cusp (SPPenContext *const pc); +//Combierte el el último nodo a BSpline +static void bspline_node (SPPenContext *const pc); +//preparates the curves for its trasformation into bspline curves. +static void bspline_build(SPPenContext *const pc, bool Shift); +//function bspline cloned from lpe-bspline.cpp +static void bspline_doEffect(SPCurve * curve); +//BSpline end + + static void spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point const p, bool statusbar, guint status = 0); static void spdc_pen_set_ctrl(SPPenContext *pc, Geom::Point const p, guint state); static void spdc_pen_finish_segment(SPPenContext *pc, Geom::Point p, guint state); @@ -192,10 +239,21 @@ static void sp_pen_context_dispose(GObject *object) void sp_pen_context_set_polyline_mode(SPPenContext *const pc) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0); - pc->polylines_only = (mode == 2 || mode == 3); - pc->polylines_paraxial = (mode == 3); + pc->polylines_only = (mode == 3 || mode == 4); + pc->polylines_paraxial = (mode == 4); + //BSpline + //we call the function which defines the Spiro modes and the B-spline in the future + //todo: merge to one function only + sp_pen_context_set_mode(pc, mode); + //BSpline End } - +//BSpline +//Set the mode of draw now spiro, and later b-splines +void sp_pen_context_set_mode(SPPenContext *const pc, guint mode) { + pc->spiro = (mode == 1); + pc->bspline = (mode == 2); +} +//BSpline End /** * Callback to initialize SPPenContext object. */ @@ -414,11 +472,18 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const Geom::Point const event_w(bevent.x, bevent.y); Geom::Point event_dt(desktop->w2d(event_w)); SPEventContext *event_context = SP_EVENT_CONTEXT(pc); - + //Test whether we hit any anchor. + SPDrawAnchor * const anchor = spdc_test_inside(pc, event_w); + //BSpline + //with this we avoid creating a new point over the existing one + if((pc->spiro || pc->bspline) && (!anchor || pc->p[0] == pc->p[3]) && pc->p[0] == pc->desktop->w2d(event_w)){ + return FALSE; + } + //BSpline end gint ret = FALSE; if (bevent.button == 1 && !event_context->space_panning // make sure this is not the last click for a waiting LPE (otherwise we want to finish the path) - && pc->expecting_clicks_for_LPE != 1) { + && (pc->expecting_clicks_for_LPE != 1)) { if (Inkscape::have_viable_layer(desktop, dc->_message_context) == false) { return TRUE; @@ -436,9 +501,6 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const pen_drag_origin_w = event_w; pen_within_tolerance = true; - // Test whether we hit any anchor. - SPDrawAnchor * const anchor = spdc_test_inside(pc, event_w); - switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: // In click mode we add point on release @@ -460,7 +522,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const case SP_PEN_CONTEXT_STOP: // This is allowed, if we just canceled curve case SP_PEN_CONTEXT_POINT: - if (pc->npoints == 0) { + if (pc->npoints == 0 ) { Geom::Point p; if ((bevent.state & GDK_CONTROL_MASK) && (pc->polylines_only || pc->polylines_paraxial)) { @@ -533,6 +595,11 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const } pc->state = pc->polylines_only ? SP_PEN_CONTEXT_POINT : SP_PEN_CONTEXT_CONTROL; + //BSpline + //Esto evita arrastrar los manejadores ya que el punto se crea + //al soltar el botón del ratón. + if((pc->spiro || pc->bspline) && pc->state != SP_PEN_CONTEXT_CLOSE ) pc->state = SP_PEN_CONTEXT_POINT; + //BSpline End ret = TRUE; break; case SP_PEN_CONTEXT_CONTROL: @@ -594,14 +661,19 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons } Geom::Point const event_w(mevent.x, - mevent.y); + mevent.y); + //BSpline + //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); + //"spiro_color" lo ejecutamos siempre sea o no spiro + spiro_color(pc); 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 ) { + if ( Geom::LInfty( event_w - pen_drag_origin_w ) < tolerance && mevent.time != 0) { return FALSE; // Do not drag if we're within tolerance from origin. } } + //BSpline END // Once the user has moved farther than tolerance from the original location // (indicating they intend to move the object, not click), then always process the // motion notify coordinates as given (no snapping back to origin) @@ -612,7 +684,6 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons // Test, whether we hit any anchor SPDrawAnchor *anchor = spdc_test_inside(pc, event_w); - switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: switch (pc->state) { @@ -657,7 +728,11 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons } if (anchor && !pc->anchor_statusbar) { - pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to close and finish the path.")); + if(!pc->spiro && !pc->bspline){ + pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to close and finish the path.")); + }else{ + pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to close and finish the path. Shift to cusp node")); + } pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); @@ -667,7 +742,11 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons ret = TRUE; } else { if (anchor && !pc->anchor_statusbar) { - pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to continue the path from this point.")); + if(!pc->spiro && !pc->bspline){ + pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to continue the path from this point.")); + }else{ + pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Click or click and drag to continue the path from this point. Shift to cusp node")); + } pc->anchor_statusbar = true; } else if (!anchor && pc->anchor_statusbar) { pc->_message_context->clear(); @@ -712,6 +791,42 @@ static gint pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion cons default: break; } + //BSpline + if(pc->spiro){ + //Here we redraw the Spiro curve when the cursor is moved + //and the tolerance distance is exceeded. + //the tolerance value can be multiplied to improve the performance + //but it would lost fluency. + //If the cursor stops the curve is also redrawed. + if ( Geom::LInfty( event_w - pen_drag_origin_w ) > tolerance || mevent.time == 0) { + //and we redraw the spiro + if ((mevent.state & GDK_SHIFT_MASK)){ + spiro_build(pc,true); + }else{ + spiro_build(pc,false); + } + //we update the distance and the tolerance + pen_drag_origin_w = event_w; + } + } + if(pc->bspline){ + //Here we redraw the Bspline curve when the cursor is moved + //and the tolerance distance is exceeded. + //the tolerance value can be multiplied to improve the performance + //but it would lost fluency. + //If the cursor stops the curve is also redrawed. + if ( Geom::LInfty( event_w - pen_drag_origin_w ) > tolerance || mevent.time == 0) { + //and we redraw the spiro + if ((mevent.state & GDK_SHIFT_MASK)){ + bspline_build(pc,true); + }else{ + bspline_build(pc,false); + } + //we update the distance and the tolerance + pen_drag_origin_w = event_w; + } + } + //BSpline End return ret; } @@ -724,11 +839,11 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con // skip event processing if events are disabled return FALSE; } - + gint ret = FALSE; + SPEventContext *event_context = SP_EVENT_CONTEXT(pc); - if ( revent.button == 1 && !event_context->space_panning) { - + if ( revent.button == 1 && !event_context->space_panning ) { SPDrawContext *dc = SP_DRAW_CONTEXT (pc); Geom::Point const event_w(revent.x, @@ -738,7 +853,14 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con // Test whether we hit any anchor. SPDrawAnchor *anchor = spdc_test_inside(pc, event_w); - + //BSpline + if(pc->spiro || pc->bspline){ + //Si intentamos crear un nodo en el mismo sitio que el origen, paramos. + if((!anchor || pc->p[0] == pc->p[3]) && pc->p[0] == p){ + return FALSE; + } + } + //BSpline End switch (pc->mode) { case SP_PEN_CONTEXT_MODE_CLICK: switch (pc->state) { @@ -774,6 +896,12 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con } spdc_pen_finish_segment(pc, p, revent.state); spdc_pen_finish(pc, TRUE); + //BSpline + //Ocultamos la guia del penultimo nodo al cerrar la curva + if(pc->spiro){ + sp_canvas_item_hide(pc->c1); + } + //BSpline End pc->state = SP_PEN_CONTEXT_POINT; ret = TRUE; break; @@ -803,6 +931,12 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con // finishing at some other anchor, finish curve but not close spdc_pen_finish(pc, FALSE); } + //BSpline + if(pc->spiro){ + //Ocultamos la guia del penultimo nodo al cerrar la curva + sp_canvas_item_hide(pc->c1); + } + //BSpline End break; case SP_PEN_CONTEXT_STOP: // This is allowed, if we just cancelled curve @@ -816,7 +950,6 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con default: break; } - if (pc->grab) { // Release grab now sp_canvas_item_ungrab(pc->grab, revent.time); @@ -827,6 +960,28 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con dc->green_closed = FALSE; } + //BSpline + if(pc->spiro){ + //Ejecutamos la función tipo de nodo segun tengamos la mayuscula presionada + if ((revent.state & GDK_SHIFT_MASK)){ + spiro_node_cusp(pc); + spiro_build(pc,true); + }else{ + spiro_node_symm(pc); + spiro_build(pc,false); + } + } + if(pc->bspline){ + //Ejecutamos la función tipo de nodo segun tengamos la mayuscula presionada + if ((revent.state & GDK_SHIFT_MASK)){ + bspline_node_cusp(pc); + bspline_build(pc, true); + }else{ + bspline_node(pc); + bspline_build(pc, false); + } + } + //BSpline End // TODO: can we be sure that the path was created correctly? // TODO: should we offer an option to collect the clicks in a list? @@ -846,7 +1001,6 @@ static gint pen_handle_button_release(SPPenContext *const pc, GdkEventButton con // handled in spdc_check_for_and_apply_waiting_LPE() in draw-context.cpp } } - return ret; } @@ -874,13 +1028,10 @@ static void pen_redraw_all (SPPenContext *const pc) SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(pc->desktop), pc->green_curve); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), pc->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); - pc->green_bpaths = g_slist_prepend(pc->green_bpaths, cshape); } - if (pc->green_anchor) SP_CTRL(pc->green_anchor->ctrl)->moveto(pc->green_anchor->dp); - pc->red_curve->reset(); pc->red_curve->moveto(pc->p[0]); pc->red_curve->curveto(pc->p[1], pc->p[2], pc->p[3]); @@ -913,6 +1064,19 @@ static void pen_redraw_all (SPPenContext *const pc) sp_canvas_item_hide(pc->cl0); } } + //BSpline + //Simplemente redibujamos la spiro teniendo en cuenta si el nodo es cusp o symm. + //como es un redibujo simplemente no llamamos a la función global + //sino al final de esta + if(pc->spiro){ + //we redraw spiro + spiro_build(pc, true); + } + if(pc->bspline){ + //we redraw bspline + bspline_build(pc, false); + } + //BSpline End } static void pen_lastpoint_move (SPPenContext *const pc, gdouble x, gdouble y) @@ -943,8 +1107,11 @@ static void pen_lastpoint_move_screen (SPPenContext *const pc, gdouble x, gdoubl static void pen_lastpoint_tocurve (SPPenContext *const pc) { - if (pc->npoints != 5) + //BSpline + //Evitamos que si la "red_curve" tiene solo dos puntos -recta- no se pare aqui. + if (pc->npoints != 5 && !pc->spiro && !pc->bspline) return; + //BSpline Geom::CubicBezier const * cubic = dynamic_cast( pc->green_curve->last_segment() ); if ( cubic ) { @@ -953,16 +1120,86 @@ static void pen_lastpoint_tocurve (SPPenContext *const pc) pc->p[1] = pc->p[0] + (1./3)*(pc->p[3] - pc->p[0]); } + //BSpline + //Para formar una curva necesitamos un nodo symm en el "lastpoint" + //de esta manera modificamos el final de la "curva_verde" para que sea symm con el pricipio de la "red_curve" + if(pc->spiro){ + Geom::Point A(0,0); + Geom::Point B(0,0); + Geom::Point C(0,0); + Geom::Point D(0,0); + SPCurve * previous = new SPCurve(); + //We obtain the last segment 4 points in the previous curve + if ( cubic ){ + A = (*cubic)[0]; + B = (*cubic)[1]; + C = pc->p[0] + (Geom::Point)(pc->p[0] - pc->p[1]); + D = (*cubic)[3]; + }else{ + A = pc->green_curve->last_segment()->initialPoint(); + B = pc->green_curve->last_segment()->initialPoint(); + C = pc->p[0] + (Geom::Point)(pc->p[0] - pc->p[1]); + D = pc->green_curve->last_segment()->finalPoint(); + } + previous->moveto(A); + previous->curveto(B, C, D); + //we eliminate the last segment + pc->green_curve->backspace(); + //and we add it again with the recreation + pc->green_curve->append_continuous(previous, 0.0625); + } + //Para formar una curva bspline necesitamos un nodo cusp en el "lastpoint" + //de esta manera modificamos el final de la "curva_verde" para que sea cusp con el pricipio de la "red_curve" + //Que se quedará recta + if(pc->bspline){ + Geom::Point A(0,0); + Geom::Point B(0,0); + SPCurve * previous = new SPCurve(); + //We obtain the last segment 2 points in the previous curve + A = pc->green_curve->last_segment()->initialPoint(); + B = pc->green_curve->last_segment()->finalPoint(); + previous->moveto(A); + previous->lineto(B); + //we eliminate the last segment + pc->green_curve->backspace(); + //and we add it again with the recreation + pc->green_curve->append_continuous(previous, 0.0625); + } + //Spiro Live pen_redraw_all(pc); } static void pen_lastpoint_toline (SPPenContext *const pc) { - if (pc->npoints != 5) + //BSpline + //Si no es bspline + if (pc->npoints != 5 && !pc->bspline) return; - + //BSpline End pc->p[1] = pc->p[0]; + //BSpline + //Para formar una recta necesitamos un nodo con manejador en el "lastpoint" + //de esta manera modificamos el final de la "curva_verde" para que tenga manejador + if(pc->bspline){ + Geom::Point A(0,0); + Geom::Point B(0,0); + Geom::Point C(0,0); + Geom::Point D(0,0); + SPCurve * previous = new SPCurve(); + //We obtain the last segment 4 points in the previous curve + A = pc->green_curve->last_segment()->initialPoint(); + B = pc->green_curve->last_segment()->initialPoint(); + C = pc->p[0] + (Geom::Point)(pc->p[0] - pc->p[1]); + D = pc->green_curve->last_segment()->finalPoint(); + previous->moveto(A); + previous->curveto(B, C, D); + //we eliminate the last segment + pc->green_curve->backspace(); + //and we add it again with the recreation + pc->green_curve->append_continuous(previous, 0.0625); + } + pen_redraw_all(pc); } @@ -1148,8 +1385,6 @@ static gint pen_handle_key_press(SPPenContext *const pc, GdkEvent *event) : pc->p[3] )); pc->npoints = 2; pc->green_curve->backspace(); - sp_canvas_item_hide(pc->c0); - sp_canvas_item_hide(pc->c1); sp_canvas_item_hide(pc->cl0); sp_canvas_item_hide(pc->cl1); pc->state = SP_PEN_CONTEXT_POINT; @@ -1168,6 +1403,7 @@ static void spdc_reset_colors(SPPenContext *pc) { // Red pc->red_curve->reset(); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), NULL); // Blue pc->blue_curve->reset(); @@ -1223,6 +1459,677 @@ static void spdc_pen_set_angle_distance_status_message(SPPenContext *const pc, G g_string_free(dist, FALSE); } + +//BSpline Set Functions +//Creates a new curve resetting the original +static SPCurve * reverse_then_reset(SPCurve *orig) +{ + SPCurve *ret = orig->create_reverse(); + orig->reset(); + return ret; +} + +//Esta función cambia los colores rojo,verde y azul haciendolos transparentes o no en función de si se usa spiro +static void spiro_color(SPPenContext *const pc) +{ + bool remake_green_bpaths = false; + if(pc->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(pc->green_color != 0x00ff000){ + //We change the green and red colours to transparent, so this lines are not necessary + //to the drawing with spiro + pc->red_color = 0xff00000; + pc->green_color = 0x00ff000; + 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(pc->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 + pc->red_color = 0xff00007f; + pc->green_color = 0x00ff007f; + remake_green_bpaths = true; + } + //we hide the spiro/bspline rests + if(!pc->bspline){ + sp_canvas_item_hide(pc->blue_bpath); + } + } + //We erase all the "green_bpaths" to recreate them after with the colour + //transparency recently modified + if (pc->green_bpaths && remake_green_bpaths) { + // remove old piecewise green canvasitems + while (pc->green_bpaths) { + sp_canvas_item_destroy(SP_CANVAS_ITEM(pc->green_bpaths->data)); + pc->green_bpaths = g_slist_remove(pc->green_bpaths, pc->green_bpaths->data); + } + // one canvas bpath for all of green_curve + SPCanvasItem *cshape = sp_canvas_bpath_new(sp_desktop_sketch(pc->desktop), pc->green_curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cshape), pc->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); + pc->green_bpaths = g_slist_prepend(pc->green_bpaths, cshape); + } + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(pc->red_bpath), pc->red_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); +} + +//Combierte el el último nodo a CUSP +static void spiro_node_cusp (SPPenContext *const pc) +{ + if(pc->green_curve->is_empty()) + return; + Geom::Point P0(0,0); + Geom::Point P1(0,0); + Geom::Point P2(0,0); + Geom::Point P3(0,0); + Geom::CubicBezier const * cubic = dynamic_cast( pc->green_curve->last_segment()); + if(cubic){ + P0 = (*cubic)[0]; + P1 = (*cubic)[1]; + P2 = (*cubic)[3]; + P3 = (*cubic)[3]; + } + pc->p[0] + P3; + pc->p[1] = P3 - (1./3)*(Geom::Point)( P0 - P3 ); + SPCurve * last = new SPCurve(); + last->moveto(P0); + last->curveto(P1,P2,P3); + if( pc->green_curve->get_segment_count() == 1){ + pc->green_curve = last; + }else{ + pc->green_curve->backspace(); + pc->green_curve->append_continuous(last, 0.0625); + } +} + +//Combierte el el último nodo a SYMM +//Solo se ejecuta al final de el evento release button +static void spiro_node_symm(SPPenContext *const pc) +{ + //Si empezamos sobre una curva ya existente + if(pc->green_curve->is_empty() && pc->sa && !pc->sa->curve->is_empty()){ + Geom::CubicBezier const * cubic; + if (pc->sa->start) { + cubic = dynamic_cast( pc->sa->curve->first_segment() ); + if ( cubic ) { + pc->p[1] = (pc->p[0]-(*cubic)[1]) + pc->p[0]; + } + }else{ + cubic = dynamic_cast( pc->sa->curve->last_segment() ); + if ( cubic ) { + pc->p[1] = (pc->p[0]-(*cubic)[2]) + pc->p[0]; + } + } + } + //Cuando cerramos el segmento + if (!pc->green_curve->is_empty()){ + Geom::Point P0(0,0); + Geom::Point P1(0,0); + Geom::Point P2(0,0); + Geom::Point P3(0,0); + Geom::CubicBezier const * cubic = dynamic_cast( pc->green_curve->last_segment()); + if(cubic){ + P0 = (*cubic)[0]; + P1 = (*cubic)[1]; + P2 = (*cubic)[3] + (1./3)*((*cubic)[0] - (*cubic)[3]); + P3 = (*cubic)[3]; + }else{ + P0 = pc->green_curve->last_segment()->initialPoint(); + P1 = P0; + P3 = pc->green_curve->last_segment()->finalPoint(); + P2 = P3 + (1./3)*(P0 - P3); + } + pc->p[1] = pc->p[0] + (Geom::Point)( pc->p[0] - P2 ); + SPCurve * last = new SPCurve(); + last->moveto(P0); + last->curveto(P1,P2,P3); + if( pc->green_curve->get_segment_count() == 1){ + pc->green_curve = last; + }else{ + pc->green_curve->backspace(); + pc->green_curve->append_continuous(last, 0.0625); + } + } + //por si cerramos la curva + if(pc->ea && !pc->ea->curve->is_empty()){ + Geom::CubicBezier const * cubic; + if(pc->ea->start){ + cubic = dynamic_cast( pc->ea->curve->first_segment()); + if(cubic){ + pc->p[2] = pc->p[3] + (Geom::Point)( pc->p[3] - (*cubic)[1] ); + } + }else{ + cubic = dynamic_cast( pc->ea->curve->last_segment()); + if(cubic){ + pc->p[2] = pc->p[3] + (Geom::Point)( pc->p[3] - (*cubic)[2] ); + } + } + } +} + +//preparates the curves for its trasformation into spiro curves. +//Parámetro opcional que indica si la curva se cierra en que modo lo hace Cusp o Symm +static void spiro_build(SPPenContext *const pc, bool Shift) +{ + //We create the base curve + SPCurve *curve = new SPCurve(); + //If we continuate the existing curve we add it at the start + if(pc->sa && !pc->sa->curve->is_empty()){ + curve = pc->sa->curve->copy(); + if (pc->sa->start) { + curve = curve->create_reverse(); + } + } + //We add also the green curve + if (!pc->green_curve->is_empty()) { + curve->append_continuous(pc->green_curve, 0.0625); + } + //and the red one + if (!pc->red_curve->is_empty()) { + //we add the curve to the modified values + if(!Shift){ + if(pc->ea && !pc->ea->curve->is_empty()){ + Geom::CubicBezier const * cubic; + if(pc->ea->start){ + cubic = dynamic_cast( pc->ea->curve->first_segment()); + if(cubic){ + pc->p[2] = pc->p[3] + (Geom::Point)( pc->p[3] - (*cubic)[1] ); + } + }else{ + cubic = dynamic_cast( pc->ea->curve->last_segment()); + if(cubic){ + pc->p[2] = pc->p[3] + (Geom::Point)( pc->p[3] - (*cubic)[2] ); + } + } + } else if(pc->green_anchor && pc->green_anchor->active){ + Geom::CubicBezier const * cubic; + cubic = dynamic_cast( pc->green_curve->first_segment()); + if(cubic){ + pc->p[2] = pc->p[3] + (Geom::Point)( pc->p[3] - (*cubic)[1] ); + } + } + if(pc->p[2] != pc->p[3]){ + pc->red_curve->reset(); + pc->npoints = 5; + pc->red_curve->moveto(pc->p[0]); + pc->red_curve->curveto(pc->p[1],pc->p[2],pc->p[3]); + } + }else{ + if((pc->ea && !pc->ea->curve->is_empty()) || (pc->green_anchor && pc->green_anchor->active)){ + pc->p[2] == pc->p[3]; + pc->red_curve->reset(); + pc->npoints = 5; + pc->red_curve->moveto(pc->p[0]); + pc->red_curve->curveto(pc->p[1],pc->p[2],pc->p[3]); + } + } + curve->append_continuous(pc->red_curve, 0.0625); + } + if(!curve->is_empty()){ + //cerramos la curva si estan cerca los puntos finales de la curva spiro + 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 (curve); + //Effect *spr = static_cast ( new LPESpiro(lpeobj) ); + //spr->doEffect(curve); + spiro_doEffect(curve); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->blue_bpath), curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(pc->blue_bpath), pc->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_item_show(pc->blue_bpath); + curve->unref(); + sp_canvas_item_show(pc->c1); + pc->blue_curve->reset(); + SP_CTRL(pc->c1)->moveto(pc->p[0]); + //We hide the holders that doesn't contribute anything + sp_canvas_item_hide(pc->cl1); + sp_canvas_item_hide(pc->c0); + sp_canvas_item_hide(pc->cl0); + }else{ + //if the curve is empty + sp_canvas_item_hide(pc->blue_bpath); + } +} +//Spiro function cloned from lpe-spiro.cpp +static void spiro_doEffect(SPCurve * curve) +{ + using Geom::X; + using Geom::Y; + + // Make copy of old path as it is changed during processing + 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; + + // start of path + { + Geom::Point p = path_it->front().pointAt(0); + path[ip].x = p[X]; + path[ip].y = p[Y]; + path[ip].ty = '{' ; // for closed paths, this is overwritten + ip++; + } + + // midpoints + 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 + 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(); + } + } + + while ( curve_it2 != curve_endit ) + { + /* This deals with the node between curve_it1 and curve_it2. + * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end + * and then curve_it1 points to end or closing segment */ + Geom::Point p = curve_it1->finalPoint(); + path[ip].x = p[X]; + path[ip].y = p[Y]; + + // Determine type of spiro node this is, determined by the tangents (angles) of the curves + // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype + 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++; + } + + // add last point to the spiropath + Geom::Point p = curve_it1->finalPoint(); + path[ip].x = p[X]; + path[ip].y = p[Y]; + if (path_it->closed()) { + // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node) + Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front()); + switch (nodetype) { + case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-) + 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 { + // set type to path closer + path[ip].ty = '}'; + ip++; + } + + // run subpath through spiro + int sp_len = ip; + Spiro::spiro_run(path, sp_len, *curve); + ip = 0; + } + + g_free (path); +} + +//Combierte el el último nodo a CUSP +//Crea una curva en el punto final de la green_curve +static void bspline_node_cusp(SPPenContext *const pc) +{ + + if (!pc->green_curve->is_empty()){ + using Geom::X; + using Geom::Y; + Geom::Point P0(0,0); + Geom::Point P1(0,0); + Geom::Point P2(0,0); + Geom::Point P3(0,0); + P0 = pc->green_curve->last_segment()->initialPoint(); + P1 = P0; + P3 = pc->green_curve->last_segment()->finalPoint(); + P2 = P3 + (1./3)*(P0 - P3); + P2 = Geom::Point(P2[X]+1,P2[Y]+1); + pc->p[0] = P3; + pc->p[1] = P3; + SPCurve * last = new SPCurve(); + last->moveto(P0); + last->curveto(P1, P2,P3); + if( pc->green_curve->get_segment_count() == 1){ + pc->green_curve = last; + }else{ + pc->green_curve->backspace(); + pc->green_curve->append_continuous(last, 0.0625); + } + } + if (pc->sa && !pc->sa->curve->is_empty() && pc->green_curve->get_segment_count() == 1){ + using Geom::X; + using Geom::Y; + if(pc->sa->start){ + pc->sa->curve = reverse_then_reset(pc->sa->curve); + } + Geom::Point P0(0,0); + Geom::Point P1(0,0); + Geom::Point P2(0,0); + Geom::Point P3(0,0); + P0 = pc->sa->curve->last_segment()->initialPoint(); + P1 = P0; + P3 = pc->sa->curve->last_segment()->finalPoint(); + P2 = P3 + (1./3)*(P0 - P3); + P2 = Geom::Point(P2[X]+1,P2[Y]+1); + pc->p[0] = P3; + pc->p[1] = P3; + SPCurve * last = new SPCurve(); + last->moveto(P0); + last->curveto(P1,P2,P3); + if( pc->sa->curve->get_segment_count() == 1){ + pc->sa->curve = last; + }else{ + pc->sa->curve->backspace(); + pc->sa->curve->append_continuous(last, 0.0625); + } + } + //Lo ejacutamos para que se actualize tosa la curva por un fallo de redibujo + bspline_build(pc,true); +} + +//Combierte el el último nodo a SYMM +//crea lineas rectas +static void bspline_node(SPPenContext *const pc) +{ + + if (!pc->green_curve->is_empty()){ + Geom::Point P0(0,0); + Geom::Point P1(0,0); + P0 = pc->green_curve->last_segment()->initialPoint(); + P1 = pc->green_curve->last_segment()->finalPoint(); + pc->p[1] = P1; + pc->p[0] = P1; + SPCurve * last = new SPCurve(); + last->moveto(P0); + last->lineto(P1); + if( pc->green_curve->get_segment_count() == 1){ + pc->green_curve = last; + }else{ + pc->green_curve->backspace(); + pc->green_curve->append_continuous(last, 0.0625); + } + } +} + +//preparates the curves for its trasformation into BSline curves. +static void bspline_build(SPPenContext *const pc, bool Shift) +{ + if (pc->green_curve->is_empty() && !pc->sa) + return; + //We create the base curve + SPCurve *curve = new SPCurve(); + //If we continuate the existing curve we add it at the start + if(pc->sa && !pc->sa->curve->is_empty()){ + curve = pc->sa->curve->copy(); + if (pc->sa->start) { + curve = curve->create_reverse(); + } + } + //We add also the green curve + if (!pc->green_curve->is_empty()) { + curve->append_continuous(pc->green_curve, 0.0625); + } + //and the red one + if (!pc->red_curve->is_empty()) { + //we add the curve to the modified values + if((pc->ea && !pc->ea->curve->is_empty()) || (pc->green_anchor && pc->green_anchor->active)){ + if(Shift){ + using Geom::X; + using Geom::Y; + pc->p[2] = pc->p[3] + (1./3)*(pc->p[0] - pc->p[3]); + pc->p[2] = Geom::Point(pc->p[2][X]+1,pc->p[2][Y]+1); + pc->red_curve->reset(); + pc->npoints = 5; + pc->red_curve->moveto(pc->p[0]); + pc->red_curve->curveto(pc->p[1],pc->p[2],pc->p[3]); + }else{ + pc->red_curve->reset(); + pc->npoints = 2; + pc->red_curve->moveto(pc->p[0]); + pc->red_curve->lineto(pc->red_curve->first_segment()->finalPoint()); + } + } + curve->append_continuous(pc->red_curve, 0.0625); + } + if(curve->get_segment_count() > 1 ){ + //cerramos la curva si estan cerca los puntos finales de la curva spiro + 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 (curve); + //Effect *spr = static_cast ( new LPEBspline(lpeobj) ); + //spr->doEffect(curve); + bspline_doEffect(curve); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->blue_bpath), curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(pc->blue_bpath), pc->blue_color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_item_show(pc->blue_bpath); + curve->unref(); + pc->blue_curve->reset(); + //We hide the holders that doesn't contribute anything + sp_canvas_item_hide(pc->c1); + sp_canvas_item_hide(pc->cl1); + sp_canvas_item_hide(pc->c0); + sp_canvas_item_hide(pc->cl0); + }else { + //if the curve is empty + sp_canvas_item_hide(pc->blue_bpath); + } +} + +static void bspline_doEffect(SPCurve * curve) +{ + using Geom::X; + using Geom::Y; + + // Make copy of old path as it is changed during processing + Geom::PathVector const original_pathv = curve->get_pathvector(); + curve->reset(); + int ip = 0; + //Sbasis + Geom::D2< Geom::SBasis > curveIn; + Geom::D2< Geom::SBasis > curveOut; + Geom::D2< Geom::SBasis > rectCurve; + Geom::D2< Geom::SBasis > rectCurveExtra; + //Puntos a usar. Ponemos todos los posibles para hacer más inteligible el código + Geom::Point startAnchor(0,0); + Geom::Point previousAnchor(0,0); + Geom::Point anchor(0,0); + Geom::Point previousSBasis0(0,0); + Geom::Point previousSBasis1(0,0); + Geom::Point previousSBasis2(0,0); + //Geom::Point previousSBasis3(0,0); + Geom::Point SBasis0(0,0); + Geom::Point SBasis1(0,0); + Geom::Point SBasis2(0,0); + Geom::Point SBasis3(0,0); + //Geom::Point nextSBasis0(0,0); + Geom::Point nextSBasis1(0,0); + Geom::Point nextSBasis2(0,0); + Geom::Point nextSBasis3(0,0); + //Curvas temporales + SPCurve *BSplineRectTemp = new SPCurve(); + SPCurve *BSplineCurveTemp = new SPCurve(); + SPCurve *BSplinePreviousCurveTemp = new SPCurve(); + //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; + //Declaramos las variables + //Itreador + 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_end = path_it->end(); // last curve + Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop + //por defecto la linea es recta. + //Para formar las bsplines contamos que si el nodo es cusp -sin manejadores + //se calcula el nodo resultante como BSpline. Si por el contrario tiene un manejador + //en el últimp punto de la cuva (pc->p[2]) calculamos la curva resultante para que forme un nodo cusp + bool is_line = true; + bool previous_is_line = true; + if (path_it->closed()) { + SBasis2 = curve_it1->toSBasis().valueAt(0.3334); + nextSBasis1 = curve_end->toSBasis().valueAt(0.6664); + BSplineRectTemp->moveto(nextSBasis1); + BSplineRectTemp->lineto(SBasis2); + startAnchor = BSplineRectTemp->first_segment()->toSBasis().valueAt(0.5); + anchor = startAnchor; + BSplineRectTemp->reset(); + } + while ( curve_it2 != curve_endit ) + { + //Calculamos los dos puntos para seleccional el punto central de la línea que los une + curveIn = curve_it1->toSBasis(); + curveOut = curve_it2->toSBasis(); + previousSBasis0 = SBasis0; + previousSBasis1 = SBasis1; + previousSBasis2 = SBasis2; + //previousSBasis3 = SBasis3; + SBasis0 = curveIn.valueAt(0); + SBasis1 = curveIn.valueAt(0.3334); + SBasis2 = curveIn.valueAt(0.6667); + SBasis3 = curveIn.valueAt(1); + //nextSBasis0 = curveOut.valueAt(0); + nextSBasis1 = curveOut.valueAt(0.3334); + nextSBasis2 = curveOut.valueAt(0.6667);; + nextSBasis3 = curveOut.valueAt(1); + //Se forma con la union de dos puntos, el primero está en la curva entrante + // y se conoce dividiendo por tres la curva y seleccionado el + //punto mas cercano de los dos al nodo de union con la curva saliente. + //El otro punto es el puntio más cercano al nodo de union + //de la curva saliente dividida por tres. + BSplineRectTemp->moveto(SBasis2); + BSplineRectTemp->lineto(nextSBasis1); + previousAnchor = anchor; + anchor = BSplineRectTemp->first_segment()->toSBasis().valueAt(0.5); + BSplineRectTemp->reset(); + //Vemos si el nodo es curva + previous_is_line = is_line; + is_line = is_straight_curve(*curve_it1); + if(!is_line && curve->get_segment_count() > 1){ + //Si no es curva convertimos la curva resultante a un nodo cusp + //aberiguamos el punto en donde deveria estar de manera fija + //independientemente de el manejador y movemos + BSplineRectTemp->moveto(SBasis0); + BSplineRectTemp->lineto(SBasis3); + SBasis1 = BSplineRectTemp->first_segment()->toSBasis().valueAt(0.3334); + SBasis2 = BSplineRectTemp->first_segment()->toSBasis().valueAt(0.6667); + BSplineRectTemp->reset(); + BSplineRectTemp->moveto(SBasis1); + BSplineRectTemp->lineto(previousSBasis2); + previousAnchor = BSplineRectTemp->first_segment()->toSBasis().valueAt(0.5); + anchor = SBasis3; + BSplineRectTemp->reset(); + //averiguamos el nuevo vinal de curva entrante recreando de nuevo + //una linea y seleccionando su centro como origen + if(previous_is_line){ + BSplineCurveTemp->moveto(previousAnchor); + BSplineCurveTemp->curveto(SBasis1, SBasis2, anchor); + }else{ + BSplineCurveTemp->moveto(SBasis0); + BSplineCurveTemp->lineto(anchor); + } + BSplineRectTemp->reset(); + + //Como el origen ha cambiado, seleccionamos el ultimo punto + //de la curva ya creada y lo movemos al nuevo origen/final + Geom::CubicBezier const * cubic = dynamic_cast( curve->last_segment()); + if(cubic){ + BSplinePreviousCurveTemp->moveto((*cubic)[0]); + if(previous_is_line){ + BSplinePreviousCurveTemp->curveto((*cubic)[1],(*cubic)[2],previousAnchor); + } else { + BSplinePreviousCurveTemp->curveto((*cubic)[1],(*cubic)[2],curve->last_segment()->finalPoint()); + } + }else{ + BSplinePreviousCurveTemp->moveto(curve->last_segment()->initialPoint()); + BSplinePreviousCurveTemp->lineto(curve->last_segment()->finalPoint()); + } + if( curve->get_segment_count() == 1){ + curve = BSplinePreviousCurveTemp; + }else{ + curve->backspace(); + curve->append_continuous(BSplinePreviousCurveTemp, 0.0625); + } + BSplinePreviousCurveTemp->reset(); + }else{ + //Creamos la curva correspondiente a la posición actual del + //Iterador, para anexarla a la curva de salida + //almacena la SPCurve de un solo segmento que se va añadir al resiultado final + //en la itineración + if (ip == 0 && !path_it->closed()){ + BSplineCurveTemp->moveto(SBasis0); + }else{ + //Empezamos la curva el el final de la anterior curva + BSplineCurveTemp->moveto(previousAnchor); + } + BSplineCurveTemp->curveto(SBasis1, SBasis2, anchor); + } + curve->append_continuous(BSplineCurveTemp, 0.0625); + BSplineCurveTemp->reset(); + ++curve_it1; + ++curve_it2; + ip++; + } + //terminamos el trazado + BSplineCurveTemp->moveto(anchor); + is_line = is_straight_curve(*curve_it1); + if (path_it->closed() && is_line) { + BSplineCurveTemp->curveto(nextSBasis1, nextSBasis2, startAnchor); + }else{ + BSplineCurveTemp->curveto(nextSBasis1, nextSBasis2, nextSBasis3); + } + curve->append_continuous(BSplineCurveTemp, 0.0625); + BSplineCurveTemp->reset(); + if (path_it->closed()) { + curve->closepath_current(); + } + } +} + +//BSpline end + static void spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point const p, bool statusbar, guint status) { g_assert( pc->npoints != 0 ); @@ -1249,7 +2156,9 @@ static void spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point co is_curve = false; } else { // one of the 'regular' modes - if (pc->p[1] != pc->p[0]) { + //SpiroLive + if (pc->p[1] != pc->p[0] || pc->spiro) { + //SpiroLive End pc->red_curve->curveto(pc->p[1], p, p); is_curve = true; } else { @@ -1257,13 +2166,18 @@ static void spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point co is_curve = false; } } - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(pc->red_bpath), pc->red_curve); - if (statusbar) { gchar *message = is_curve ? _("Curve segment: angle %3.2f°, distance %s; with Ctrl to snap angle, Enter to finish the path" ): _("Line segment: angle %3.2f°, distance %s; with Ctrl to snap angle, Enter to finish the path"); + //BSpline + if(pc->spiro || pc->bspline){ + message = is_curve ? + _("Curve segment: angle %3.2f°, distance %s; with Shift to cusp node, Enter to finish the path" ): + _("Line segment: angle %3.2f°, distance %s; with Shift to cusp node, Enter to finish the path"); + } + //BSpline End spdc_pen_set_angle_distance_status_message(pc, p, 0, message); } } @@ -1277,9 +2191,12 @@ static void spdc_pen_set_ctrl(SPPenContext *const pc, Geom::Point const p, guint pc->p[1] = p; sp_canvas_item_hide(pc->c0); sp_canvas_item_hide(pc->cl0); - SP_CTRL(pc->c1)->moveto(pc->p[1]); + if(pc->spiro){ + SP_CTRL(pc->c1)->moveto(pc->p[1]); + }else{ + SP_CTRL(pc->c1)->moveto(pc->p[1]); + } pc->cl1->setCoords(pc->p[0], pc->p[1]); - spdc_pen_set_angle_distance_status_message(pc, p, 0, _("Curve handle: angle %3.2f°, length %s; with Ctrl to snap angle")); } else if ( pc->npoints == 5 ) { pc->p[4] = p; @@ -1300,7 +2217,6 @@ static void spdc_pen_set_ctrl(SPPenContext *const pc, Geom::Point const p, guint pc->cl0 ->setCoords(pc->p[3], pc->p[2]); SP_CTRL(pc->c1)->moveto(pc->p[4]); pc->cl1->setCoords(pc->p[3], pc->p[4]); - gchar *message = is_symm ? _("Curve handle, symmetric: angle %3.2f°, length %s; with Ctrl to snap angle, with Shift to move this handle only") : _("Curve handle: angle %3.2f°, length %s; with Ctrl to snap angle, with Shift to move this handle only"); diff --git a/src/pen-context.h b/src/pen-context.h index a68b76ff5..4485539cd 100644 --- a/src/pen-context.h +++ b/src/pen-context.h @@ -43,6 +43,11 @@ struct SPPenContext : public SPDrawContext { bool polylines_only; bool polylines_paraxial; + //SpiroLive + //Propiedad que guarda si el modo Spiro está activo o no + bool spiro; + bool bspline; + //SpiroLIve End int num_clicks; unsigned int expecting_clicks_for_LPE; // if positive, finish the path after this many clicks diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp index d0e71d2b0..7e28f7c8c 100644 --- a/src/widgets/pencil-toolbar.cpp +++ b/src/widgets/pencil-toolbar.cpp @@ -139,7 +139,14 @@ static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* ho 1, _("Create Spiro path"), 2, INKSCAPE_ICON("path-mode-spiro"), -1 ); - + //BSpline + 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 ); + //BSpline if (!tool_is_pencil) { gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, -- cgit v1.2.3