X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdyna-draw-context.cpp;h=93d4262cd172512dbfe53d9fe6befc064f398009;hb=8a2e76b7021b9b960d7c30801a1a14461d9b5939;hp=257e6f7dc9947d853158c3be03bae2b5917554fa;hpb=a846d9e0a0026a6e54a3bc5d2f300c578b8ef5ac;p=inkscape.git diff --git a/src/dyna-draw-context.cpp b/src/dyna-draw-context.cpp index 257e6f7dc..93d4262cd 100644 --- a/src/dyna-draw-context.cpp +++ b/src/dyna-draw-context.cpp @@ -1,5 +1,3 @@ -#define __SP_DYNA_DRAW_CONTEXT_C__ - /* * Handwriting-like drawing mode * @@ -8,6 +6,7 @@ * Lauris Kaplinski * bulia byak * MenTaLguY + * Abhishek Sharma * * The original dynadraw code: * Paul Haeberli @@ -45,7 +44,6 @@ #include "desktop.h" #include "desktop-events.h" #include "desktop-handles.h" -#include "desktop-affine.h" #include "desktop-style.h" #include "message-context.h" #include "preferences.h" @@ -66,6 +64,8 @@ #include "dyna-draw-context.h" +using Inkscape::DocumentUndo; + #define DDC_RED_RGBA 0xff0000ff #define TOLERANCE_CALLIGRAPHIC 0.1 @@ -85,7 +85,7 @@ static void sp_dyna_draw_context_set(SPEventContext *ec, Inkscape::Preferences:: static gint sp_dyna_draw_context_root_handler(SPEventContext *ec, GdkEvent *event); static void clear_current(SPDynaDrawContext *dc); -static void set_to_accumulated(SPDynaDrawContext *dc, bool unionize); +static void set_to_accumulated(SPDynaDrawContext *dc, bool unionize, bool subtract); static void add_cap(SPCurve *curve, Geom::Point const &from, Geom::Point const &to, double rounding); static bool accumulate_calligraphic(SPDynaDrawContext *dc); @@ -153,15 +153,17 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) ddc->hatch_spacing_step = 0; new (&ddc->hatch_pointer_past) std::list(); new (&ddc->hatch_nearest_past) std::list(); + new (&ddc->inertia_vectors) std::list(); + new (&ddc->hatch_vectors) std::list(); ddc->hatch_last_nearest = Geom::Point(0,0); ddc->hatch_last_pointer = Geom::Point(0,0); - ddc->hatch_vector_accumulated = Geom::Point(0,0); ddc->hatch_escaped = false; ddc->hatch_area = NULL; ddc->hatch_item = NULL; ddc->hatch_livarot_path = NULL; ddc->trace_bg = false; + ddc->just_started_drawing = false; } static void @@ -179,6 +181,8 @@ sp_dyna_draw_context_dispose(GObject *object) ddc->hatch_pointer_past.~list(); ddc->hatch_nearest_past.~list(); + ddc->inertia_vectors.~list(); + ddc->hatch_vectors.~list(); } static void @@ -331,7 +335,7 @@ sp_dyna_draw_apply(SPDynaDrawContext *dc, Geom::Point p) // If force is below the absolute threshold DYNA_EPSILON, // or we haven't yet reached DYNA_VEL_START (i.e. at the beginning of stroke) // _and_ the force is below the (higher) DYNA_EPSILON_START threshold, - // discard this move. + // discard this move. // This prevents flips, blobs, and jerks caused by microscopic tremor of the tablet pen, // especially bothersome at the start of the stroke where we don't yet have the inertia to // smooth them out. @@ -403,7 +407,7 @@ sp_dyna_draw_apply(SPDynaDrawContext *dc, Geom::Point p) // convert to point dc->ang = Geom::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); +// g_print ("force %g acc %g vel_max %g vel %g a1 %g a2 %g new_ang %g\n", Geom::L2(force), Geom::L2(dc->acc), dc->vel_max, Geom::L2(dc->vel), a1, a2, new_ang); /* Apply drag */ dc->vel *= 1.0 - drag; @@ -429,7 +433,7 @@ sp_dyna_draw_brush(SPDynaDrawContext *dc) // get the real brush point, not the same as pointer (affected by hatch tracking and/or mass // drag) Geom::Point brush = sp_dyna_draw_get_vpoint(dc, dc->cur); - Geom::Point brush_w = SP_EVENT_CONTEXT(dc)->desktop->d2w(brush); + Geom::Point brush_w = SP_EVENT_CONTEXT(dc)->desktop->d2w(brush); double trace_thick = 1; if (dc->trace_bg) { @@ -541,12 +545,6 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, return TRUE; } - Geom::Point const button_w(event->button.x, - event->button.y); - Geom::Point const button_dt(desktop->w2d(button_w)); - sp_dyna_draw_reset(dc, button_dt); - sp_dyna_draw_extinput(dc, event); - sp_dyna_draw_apply(dc, button_dt); dc->accumulated->reset(); if (dc->repr) { dc->repr = NULL; @@ -567,6 +565,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3); dc->is_drawing = true; + dc->just_started_drawing = true; } break; case GDK_MOTION_NOTIFY: @@ -601,7 +600,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } // calculate pointer point in the guide item's coords - motion_to_curve = sp_item_dt2i_affine(selected) * sp_item_i2doc_affine(selected); + motion_to_curve = selected->dt2i_affine() * selected->i2doc_affine(); pointer = motion_dt * motion_to_curve; // calculate the nearest point on the guide path @@ -618,16 +617,19 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } else { dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Select a guide path to track with Ctrl")); } - } + } if ( dc->is_drawing && (event->motion.state & GDK_BUTTON1_MASK) && !event_context->space_panning) { dc->dragging = TRUE; if (event->motion.state & GDK_CONTROL_MASK && dc->hatch_item) { // hatching +#define HATCH_VECTOR_ELEMENTS 12 +#define INERTIA_ELEMENTS 24 #define SPEED_ELEMENTS 12 -#define SPEED_MIN 0.12 -#define SPEED_NORMAL 0.65 +#define SPEED_MIN 0.3 +#define SPEED_NORMAL 0.35 +#define INERTIA_FORCE 0.5 // speed is the movement of the nearest point along the guide path, divided by // the movement of the pointer at the same period; it is averaged for the last @@ -672,19 +674,37 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, ) { // We are NOT attracted to the guide! - //g_print ("\nlast_nearest %g %g nearest %g %g pointer %g %g pos %d %g\n", dc->last_nearest[NR::X], dc->last_nearest[NR::Y], nearest[NR::X], nearest[NR::Y], pointer[NR::X], pointer[NR::Y], position->piece, position->t); + //g_print ("\nlast_nearest %g %g nearest %g %g pointer %g %g pos %d %g\n", dc->last_nearest[Geom::X], dc->last_nearest[Geom::Y], nearest[Geom::X], nearest[Geom::Y], pointer[Geom::X], pointer[Geom::Y], position->piece, position->t); // Remember hatch_escaped so we don't get // attracted again until the end of this stroke dc->hatch_escaped = true; + if (dc->inertia_vectors.size() >= INERTIA_ELEMENTS/2) { // move by inertia + Geom::Point moved_past_escape = motion_dt - dc->inertia_vectors.front(); + Geom::Point inertia = + dc->inertia_vectors.front() - dc->inertia_vectors.back(); + + double dot = Geom::dot (moved_past_escape, inertia); + dot /= Geom::L2(moved_past_escape) * Geom::L2(inertia); + + if (dot > 0) { // mouse is still moving in approx the same direction + Geom::Point should_have_moved = + (inertia) * (1/Geom::L2(inertia)) * Geom::L2(moved_past_escape); + motion_dt = dc->inertia_vectors.front() + + (INERTIA_FORCE * should_have_moved + (1 - INERTIA_FORCE) * moved_past_escape); + } + } + } else { // Calculate angle cosine of this vector-to-guide and all past vectors // summed, to detect if we accidentally flipped to the other side of the // guide - double dot = Geom::dot (pointer - nearest, dc->hatch_vector_accumulated); - dot /= Geom::L2(pointer - nearest) * Geom::L2(dc->hatch_vector_accumulated); + Geom::Point hatch_vector_accumulated = std::accumulate + (dc->hatch_vectors.begin(), dc->hatch_vectors.end(), Geom::Point(0,0)); + double dot = Geom::dot (pointer - nearest, hatch_vector_accumulated); + dot /= Geom::L2(pointer - nearest) * Geom::L2(hatch_vector_accumulated); if (dc->hatch_spacing != 0) { // spacing was already set double target; @@ -694,7 +714,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } else { // looks like we're starting to lose speed, // so _gradually_ let go attraction to prevent jerks - target = (dc->hatch_spacing * speed + hatch_dist * (SPEED_NORMAL - speed))/SPEED_NORMAL; + target = (dc->hatch_spacing * speed + hatch_dist * (SPEED_NORMAL - speed))/SPEED_NORMAL; } if (!IS_NAN(dot) && dot < -0.5) {// flip target = -target; @@ -710,15 +730,24 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, // return it to the desktop coords motion_dt = new_pointer * motion_to_curve.inverse(); + if (speed >= SPEED_NORMAL) { + dc->inertia_vectors.push_front(motion_dt); + if (dc->inertia_vectors.size() > INERTIA_ELEMENTS) + dc->inertia_vectors.pop_back(); + } + } else { - // this is the first motion event, set the dist + // this is the first motion event, set the dist dc->hatch_spacing = hatch_dist; } // remember last points dc->hatch_last_pointer = pointer; dc->hatch_last_nearest = nearest; - dc->hatch_vector_accumulated += (pointer - nearest); + + dc->hatch_vectors.push_front(pointer - nearest); + if (dc->hatch_vectors.size() > HATCH_VECTOR_ELEMENTS) + dc->hatch_vectors.pop_back(); } dc->_message_context->set(Inkscape::NORMAL_MESSAGE, dc->hatch_escaped? _("Tracking: connection to guide path lost!") : _("Tracking a guide path")); @@ -727,6 +756,11 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("Drawing a calligraphic stroke")); } + if (dc->just_started_drawing) { + dc->just_started_drawing = false; + sp_dyna_draw_reset(dc, motion_dt); + } + if (!sp_dyna_draw_apply(dc, motion_dt)) { ret = TRUE; break; @@ -741,8 +775,8 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } // Draw the hatching circle if necessary - if (event->motion.state & GDK_CONTROL_MASK) { - if (dc->hatch_spacing == 0 && hatch_dist != 0) { + if (event->motion.state & GDK_CONTROL_MASK) { + if (dc->hatch_spacing == 0 && hatch_dist != 0) { // Haven't set spacing yet: gray, center free, update radius live Geom::Point c = desktop->w2d(motion_w); Geom::Matrix const sm (Geom::Scale(hatch_dist, hatch_dist) * Geom::Translate(c)); @@ -758,7 +792,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, sp_canvas_item_show(dc->hatch_area); } else if (dc->dragging && dc->hatch_escaped) { // Tracking escaped: red, center free, fixed radius - Geom::Point c = desktop->w2d(motion_w); + Geom::Point c = motion_dt; Geom::Matrix const sm (Geom::Scale(dc->hatch_spacing, dc->hatch_spacing) * Geom::Translate(c)); sp_canvas_item_affine_absolute(dc->hatch_area, sm); @@ -804,7 +838,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, /* Create object */ fit_and_split(dc, TRUE); if (accumulate_calligraphic(dc)) - set_to_accumulated(dc, event->button.state & GDK_SHIFT_MASK); // performs document_done + set_to_accumulated(dc, event->button.state & GDK_SHIFT_MASK, event->button.state & GDK_MOD1_MASK); // performs document_done else g_warning ("Failed to create path: invalid data in dc->cal1 or dc->cal2"); @@ -818,14 +852,16 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, if (!dc->hatch_pointer_past.empty()) dc->hatch_pointer_past.clear(); if (!dc->hatch_nearest_past.empty()) dc->hatch_nearest_past.clear(); + if (!dc->inertia_vectors.empty()) dc->inertia_vectors.clear(); + if (!dc->hatch_vectors.empty()) dc->hatch_vectors.clear(); dc->hatch_last_nearest = Geom::Point(0,0); dc->hatch_last_pointer = Geom::Point(0,0); - dc->hatch_vector_accumulated = Geom::Point(0,0); dc->hatch_escaped = false; dc->hatch_item = NULL; dc->hatch_livarot_path = NULL; + dc->just_started_drawing = false; - if (dc->hatch_spacing != 0 && !dc->keep_selected) { + if (dc->hatch_spacing != 0 && !dc->keep_selected) { // we do not select the newly drawn path, so increase spacing by step if (dc->hatch_spacing_step == 0) { dc->hatch_spacing_step = dc->hatch_spacing; @@ -960,14 +996,14 @@ clear_current(SPDynaDrawContext *dc) } static void -set_to_accumulated(SPDynaDrawContext *dc, bool unionize) +set_to_accumulated(SPDynaDrawContext *dc, bool unionize, bool subtract) { SPDesktop *desktop = SP_EVENT_CONTEXT(dc)->desktop; if (!dc->accumulated->is_empty()) { if (!dc->repr) { /* Create object */ - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); /* Set style */ @@ -977,10 +1013,10 @@ set_to_accumulated(SPDynaDrawContext *dc, bool unionize) SPItem *item=SP_ITEM(desktop->currentLayer()->appendChildRepr(dc->repr)); Inkscape::GC::release(dc->repr); - item->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse(); + item->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); item->updateRepr(); } - Geom::PathVector pathv = dc->accumulated->get_pathvector() * sp_desktop_dt2doc_affine(desktop); + Geom::PathVector pathv = dc->accumulated->get_pathvector() * desktop->dt2doc(); gchar *str = sp_svg_write_path(pathv); g_assert( str != NULL ); dc->repr->setAttribute("d", str); @@ -989,10 +1025,13 @@ set_to_accumulated(SPDynaDrawContext *dc, bool unionize) if (unionize) { sp_desktop_selection(desktop)->add(dc->repr); sp_selected_path_union_skip_undo(desktop); + } else if (subtract) { + sp_desktop_selection(desktop)->add(dc->repr); + sp_selected_path_diff_skip_undo(desktop); } else { if (dc->keep_selected) { sp_desktop_selection(desktop)->set(dc->repr); - } + } } } else { @@ -1002,14 +1041,14 @@ set_to_accumulated(SPDynaDrawContext *dc, bool unionize) dc->repr = NULL; } - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_CALLIGRAPHIC, - _("Draw calligraphic stroke")); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_CALLIGRAPHIC, + _("Draw calligraphic stroke")); } static void add_cap(SPCurve *curve, Geom::Point const &from, - Geom::Point const &to, + Geom::Point const &to, double rounding) { if (Geom::L2( to - from ) > DYNA_EPSILON) { @@ -1028,7 +1067,7 @@ accumulate_calligraphic(SPDynaDrawContext *dc) dc->cal1->is_empty() || dc->cal2->is_empty() || (dc->cal1->get_segment_count() <= 0) || - dc->cal1->first_path()->closed() + dc->cal1->first_path()->closed() ) { dc->cal1->reset(); dc->cal2->reset(); @@ -1038,7 +1077,7 @@ accumulate_calligraphic(SPDynaDrawContext *dc) SPCurve *rev_cal2 = dc->cal2->create_reverse(); if ( (rev_cal2->get_segment_count() <= 0) || - rev_cal2->first_path()->closed() + rev_cal2->first_path()->closed() ) { rev_cal2->unref(); dc->cal1->reset(); @@ -1055,7 +1094,7 @@ accumulate_calligraphic(SPDynaDrawContext *dc) !dc_cal1_firstseg || !rev_cal2_firstseg || !dc_cal1_lastseg || - !rev_cal2_lastseg + !rev_cal2_lastseg ) { rev_cal2->unref(); dc->cal1->reset(); @@ -1246,4 +1285,4 @@ draw_temporary_box(SPDynaDrawContext *dc) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :