X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdyna-draw-context.cpp;h=ba44df3ce784e05ebcc0a9a18a4598277cc7463c;hb=34ef23794a1b75ae38874fe7a8462770c95010e1;hp=3eb84570d071b69bb5ab3221c8877782337eb87e;hpb=54b714b1029fe4efc22eb3093e4d3c2a84df5b9a;p=inkscape.git diff --git a/src/dyna-draw-context.cpp b/src/dyna-draw-context.cpp index 3eb84570d..ba44df3ce 100644 --- a/src/dyna-draw-context.cpp +++ b/src/dyna-draw-context.cpp @@ -7,6 +7,7 @@ * Mitsuru Oka * Lauris Kaplinski * bulia byak + * MenTaLguY * * The original dynadraw code: * Paul Haeberli @@ -14,6 +15,8 @@ * Copyright (C) 1998 The Free Software Foundation * Copyright (C) 1999-2005 authors * Copyright (C) 2001-2002 Ximian, Inc. + * Copyright (C) 2005-2006 bulia byak + * Copyright (C) 2006 MenTaLguY * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -24,6 +27,7 @@ #include #include +#include #include "svg/svg.h" #include "display/canvas-bpath.h" @@ -40,7 +44,6 @@ #include "desktop-style.h" #include "message-context.h" #include "pixmaps/cursor-calligraphy.xpm" -#include "pixmaps/cursor-calligraphy.pixbuf" #include "dyna-draw-context.h" #include "libnr/n-art-bpath.h" #include "libnr/nr-path.h" @@ -55,7 +58,10 @@ #define SAMPLE_TIMEOUT 10 #define TOLERANCE_LINE 1.0 #define TOLERANCE_CALLIGRAPHIC 3.0 -#define DYNA_EPSILON 1.0e-2 + +#define DYNA_EPSILON 0.5e-6 +#define DYNA_EPSILON_START 0.5e-2 +#define DYNA_VEL_START 1e-5 #define DYNA_MIN_WIDTH 1.0e-6 @@ -73,10 +79,10 @@ static gint sp_dyna_draw_context_root_handler(SPEventContext *ec, GdkEvent *even static void clear_current(SPDynaDrawContext *dc); static void set_to_accumulated(SPDynaDrawContext *dc); +static void add_cap(SPCurve *curve, NR::Point const &pre, NR::Point const &from, NR::Point const &to, NR::Point const &post, double rounding); static void accumulate_calligraphic(SPDynaDrawContext *dc); static void fit_and_split(SPDynaDrawContext *ddc, gboolean release); -static void fit_and_split_calligraphics(SPDynaDrawContext *ddc, gboolean release); static void sp_dyna_draw_reset(SPDynaDrawContext *ddc, NR::Point p); static NR::Point sp_dyna_draw_get_npoint(SPDynaDrawContext const *ddc, NR::Point v); @@ -127,11 +133,6 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) SPEventContext *event_context = SP_EVENT_CONTEXT(ddc); event_context->cursor_shape = cursor_calligraphy_xpm; - event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline( - -1, - cursor_calligraphy_pixbuf, - FALSE, - NULL); event_context->hot_x = 4; event_context->hot_y = 4; @@ -148,6 +149,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) ddc->cur = NR::Point(0,0); ddc->last = NR::Point(0,0); ddc->vel = NR::Point(0,0); + ddc->vel_max = 0; ddc->acc = NR::Point(0,0); ddc->ang = NR::Point(0,0); ddc->del = NR::Point(0,0); @@ -162,6 +164,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) ddc->vel_thin = 0.1; ddc->flatness = 0.9; + ddc->cap_rounding = 0.0; ddc->abs_width = false; ddc->keep_selected = true; @@ -218,7 +221,7 @@ sp_dyna_draw_context_setup(SPEventContext *ec) g_signal_connect(G_OBJECT(ddc->currentshape), "event", G_CALLBACK(sp_desktop_root_handler), ec->desktop); sp_event_context_read(ec, "mass"); - sp_event_context_read(ec, "drag"); + sp_event_context_read(ec, "wiggle"); sp_event_context_read(ec, "angle"); sp_event_context_read(ec, "width"); sp_event_context_read(ec, "thinning"); @@ -228,6 +231,7 @@ sp_dyna_draw_context_setup(SPEventContext *ec) sp_event_context_read(ec, "usetilt"); sp_event_context_read(ec, "abs_width"); sp_event_context_read(ec, "keep_selected"); + sp_event_context_read(ec, "cap_rounding"); ddc->is_drawing = false; @@ -242,9 +246,9 @@ sp_dyna_draw_context_set(SPEventContext *ec, gchar const *key, gchar const *val) if (!strcmp(key, "mass")) { double const dval = ( val ? g_ascii_strtod (val, NULL) : 0.2 ); ddc->mass = CLAMP(dval, -1000.0, 1000.0); - } else if (!strcmp(key, "drag")) { - double const dval = ( val ? g_ascii_strtod (val, NULL) : DRAG_DEFAULT ); - ddc->drag = CLAMP(dval, DRAG_MIN, DRAG_MAX); + } else if (!strcmp(key, "wiggle")) { + double const dval = ( val ? g_ascii_strtod (val, NULL) : (1 - DRAG_DEFAULT)); + ddc->drag = CLAMP((1 - dval), DRAG_MIN, DRAG_MAX); // drag is inverse to wiggle } else if (!strcmp(key, "angle")) { double const dval = ( val ? g_ascii_strtod (val, NULL) : 0.0); ddc->angle = CLAMP (dval, -90, 90); @@ -268,6 +272,8 @@ sp_dyna_draw_context_set(SPEventContext *ec, gchar const *key, gchar const *val) ddc->abs_width = (val && strcmp(val, "0")); } else if (!strcmp(key, "keep_selected")) { ddc->keep_selected = (val && strcmp(val, "0")); + } else if (!strcmp(key, "cap_rounding")) { + ddc->cap_rounding = ( val ? g_ascii_strtod (val, NULL) : 0.0 ); } //g_print("DDC: %g %g %g %g\n", ddc->mass, ddc->drag, ddc->angle, ddc->width); @@ -302,6 +308,7 @@ sp_dyna_draw_reset(SPDynaDrawContext *dc, NR::Point p) { dc->last = dc->cur = sp_dyna_draw_get_npoint(dc, p); dc->vel = NR::Point(0,0); + dc->vel_max = 0; dc->acc = NR::Point(0,0); dc->ang = NR::Point(0,0); dc->del = NR::Point(0,0); @@ -338,7 +345,7 @@ sp_dyna_draw_apply(SPDynaDrawContext *dc, NR::Point p) /* Calculate force and acceleration */ NR::Point force = n - dc->cur; - if ( NR::L2(force) < DYNA_EPSILON ) { + if ( NR::L2(force) < DYNA_EPSILON || (dc->vel_max < DYNA_VEL_START && NR::L2(force) < DYNA_EPSILON_START)) { return FALSE; } @@ -347,6 +354,9 @@ sp_dyna_draw_apply(SPDynaDrawContext *dc, NR::Point p) /* Calculate new velocity */ dc->vel += dc->acc; + if (NR::L2(dc->vel) > dc->vel_max) + dc->vel_max = NR::L2(dc->vel); + /* Calculate angle of drawing tool */ double a1; @@ -392,9 +402,18 @@ sp_dyna_draw_apply(SPDynaDrawContext *dc, NR::Point p) // find the flatness-weighted bisector angle, unflip if a2 was flipped // FIXME: when dc->vel is oscillating around the fixed angle, the new_ang flips back and forth. How to avoid this? double new_ang = a1 + (1 - dc->flatness) * (a2 - a1) - (flipped? M_PI : 0); + + double angle_delta = NR::L2(NR::Point (cos (new_ang), sin (new_ang)) - dc->ang); + + if ( angle_delta / NR::L2(dc->vel) > 4000 ) { + return FALSE; + } + // convert to point dc->ang = NR::Point (cos (new_ang), sin (new_ang)); +// g_print ("force %g acc %g vel_max %g vel %g a1 %g a2 %g new_ang %g\n", NR::L2(force), NR::L2(dc->acc), dc->vel_max, NR::L2(dc->vel), a1, a2, new_ang); + /* Apply drag */ dc->vel *= 1.0 - drag; @@ -468,6 +487,27 @@ sp_ddc_update_toolbox (SPDesktop *desktop, const gchar *id, double value) desktop->setToolboxAdjustmentValue (id, value); } +static void +calligraphic_cancel(SPDynaDrawContext *dc) +{ + SPDesktop *desktop = SP_EVENT_CONTEXT(dc)->desktop; + dc->dragging = FALSE; + dc->is_drawing = false; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + /* Remove all temporary line segments */ + while (dc->segments) { + gtk_object_destroy(GTK_OBJECT(dc->segments->data)); + dc->segments = g_slist_remove(dc->segments, dc->segments->data); + } + /* reset accumulated curve */ + sp_curve_reset(dc->accumulated); + clear_current(dc); + if (dc->repr) { + dc->repr = NULL; + } +} + + gint sp_dyna_draw_context_root_handler(SPEventContext *event_context, GdkEvent *event) @@ -540,9 +580,14 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, case GDK_BUTTON_RELEASE: sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); dc->is_drawing = false; + if ( dc->dragging && event->button.button == 1 ) { dc->dragging = FALSE; + NR::Point const motion_w(event->button.x, event->button.y); + NR::Point const motion_dt(desktop->w2d(motion_w)); + sp_dyna_draw_apply(dc, motion_dt); + /* Remove all temporary line segments */ while (dc->segments) { gtk_object_destroy(GTK_OBJECT(dc->segments->data)); @@ -611,9 +656,20 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } break; case GDK_Escape: - sp_desktop_selection(desktop)->clear(); + if (dc->is_drawing) { + // if drawing, cancel, otherwise pass it up for deselecting + calligraphic_cancel (dc); + ret = TRUE; + } + break; + case GDK_z: + case GDK_Z: + if (MOD__CTRL_ONLY && dc->is_drawing) { + // if drawing, cancel, otherwise pass it up for undo + calligraphic_cancel (dc); + ret = TRUE; + } break; - default: break; } @@ -655,7 +711,8 @@ set_to_accumulated(SPDynaDrawContext *dc) if (!dc->repr) { /* Create object */ - Inkscape::XML::Node *repr = sp_repr_new("svg:path"); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); /* Set style */ sp_desktop_apply_style_tool (desktop, repr, "tools.calligraphic", false); @@ -686,7 +743,37 @@ set_to_accumulated(SPDynaDrawContext *dc) } sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_CALLIGRAPHIC, - /* TODO: annotate */ "dyna-draw-context.cpp:689"); + _("Create calligraphic stroke")); +} + +static void +add_cap(SPCurve *curve, + NR::Point const &pre, NR::Point const &from, + NR::Point const &to, NR::Point const &post, + double rounding) +{ + NR::Point vel = rounding * NR::rot90( to - from ) / sqrt(2.0); + double mag = NR::L2(vel); + + NR::Point v_in = from - pre; + double mag_in = NR::L2(v_in); + if ( mag_in > DYNA_EPSILON ) { + v_in = mag * v_in / mag_in; + } else { + v_in = NR::Point(0, 0); + } + + NR::Point v_out = to - post; + double mag_out = NR::L2(v_out); + if ( mag_out > DYNA_EPSILON ) { + v_out = mag * v_out / mag_out; + } else { + v_out = NR::Point(0, 0); + } + + if ( NR::L2(v_in) > DYNA_EPSILON || NR::L2(v_out) > DYNA_EPSILON ) { + sp_curve_curveto(curve, from + v_in, to + v_out, to); + } } static void @@ -695,8 +782,24 @@ accumulate_calligraphic(SPDynaDrawContext *dc) if ( !sp_curve_empty(dc->cal1) && !sp_curve_empty(dc->cal2) ) { sp_curve_reset(dc->accumulated); /* Is this required ?? */ SPCurve *rev_cal2 = sp_curve_reverse(dc->cal2); + + g_assert(dc->cal1->end > 1); + g_assert(rev_cal2->end > 1); + g_assert(SP_CURVE_SEGMENT(dc->cal1, 0)->code == NR_MOVETO_OPEN); + g_assert(SP_CURVE_SEGMENT(rev_cal2, 0)->code == NR_MOVETO_OPEN); + g_assert(SP_CURVE_SEGMENT(dc->cal1, 1)->code == NR_CURVETO); + g_assert(SP_CURVE_SEGMENT(rev_cal2, 1)->code == NR_CURVETO); + g_assert(SP_CURVE_SEGMENT(dc->cal1, dc->cal1->end-1)->code == NR_CURVETO); + g_assert(SP_CURVE_SEGMENT(rev_cal2, rev_cal2->end-1)->code == NR_CURVETO); + sp_curve_append(dc->accumulated, dc->cal1, FALSE); + + add_cap(dc->accumulated, SP_CURVE_SEGMENT(dc->cal1, dc->cal1->end-1)->c(2), SP_CURVE_SEGMENT(dc->cal1, dc->cal1->end-1)->c(3), SP_CURVE_SEGMENT(rev_cal2, 0)->c(3), SP_CURVE_SEGMENT(rev_cal2, 1)->c(1), dc->cap_rounding); + sp_curve_append(dc->accumulated, rev_cal2, TRUE); + + add_cap(dc->accumulated, SP_CURVE_SEGMENT(rev_cal2, rev_cal2->end-1)->c(2), SP_CURVE_SEGMENT(rev_cal2, rev_cal2->end-1)->c(3), SP_CURVE_SEGMENT(dc->cal1, 0)->c(3), SP_CURVE_SEGMENT(dc->cal1, 1)->c(1), dc->cap_rounding); + sp_curve_closepath(dc->accumulated); sp_curve_unref(rev_cal2); @@ -706,20 +809,13 @@ accumulate_calligraphic(SPDynaDrawContext *dc) } } -static void -fit_and_split(SPDynaDrawContext *dc, - gboolean release) -{ - fit_and_split_calligraphics(dc, release); -} - static double square(double const x) { return x * x; } static void -fit_and_split_calligraphics(SPDynaDrawContext *dc, gboolean release) +fit_and_split(SPDynaDrawContext *dc, gboolean release) { double const tolerance_sq = square( NR::expansion(SP_EVENT_CONTEXT(dc)->desktop->w2d()) * TOLERANCE_CALLIGRAPHIC ); @@ -779,6 +875,10 @@ fit_and_split_calligraphics(SPDynaDrawContext *dc, gboolean release) for (NR::Point *bp2 = b2 + BEZIER_SIZE * ( nb2 - 1 ); bp2 >= b2; bp2 -= BEZIER_SIZE) { sp_curve_curveto(dc->currentcurve, bp2[2], bp2[1], bp2[0]); } + // FIXME: dc->segments is always NULL at this point?? + if (!dc->segments) { // first segment + add_cap(dc->currentcurve, b2[1], b2[0], b1[0], b1[1], dc->cap_rounding); + } sp_curve_closepath(dc->currentcurve); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->currentshape), dc->currentcurve); } @@ -793,7 +893,7 @@ fit_and_split_calligraphics(SPDynaDrawContext *dc, gboolean release) } else { /* fixme: ??? */ #ifdef DYNA_DRAW_VERBOSE - g_print("[fit_and_split_calligraphics] failed to fit-cubic.\n"); + g_print("[fit_and_split] failed to fit-cubic.\n"); #endif draw_temporary_box(dc); @@ -846,13 +946,18 @@ static void draw_temporary_box(SPDynaDrawContext *dc) { sp_curve_reset(dc->currentcurve); - sp_curve_moveto(dc->currentcurve, dc->point1[0]); - for (gint i = 1; i < dc->npoints; i++) { + + sp_curve_moveto(dc->currentcurve, dc->point1[dc->npoints-1]); + for (gint i = dc->npoints-2; i >= 0; i--) { sp_curve_lineto(dc->currentcurve, dc->point1[i]); } - for (gint i = dc->npoints-1; i >= 0; i--) { + for (gint i = 0; i < dc->npoints; i++) { sp_curve_lineto(dc->currentcurve, dc->point2[i]); } + if (dc->npoints >= 2) { + add_cap(dc->currentcurve, dc->point2[dc->npoints-2], dc->point2[dc->npoints-1], dc->point1[dc->npoints-1], dc->point1[dc->npoints-2], dc->cap_rounding); + } + sp_curve_closepath(dc->currentcurve); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->currentshape), dc->currentcurve); }