X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdyna-draw-context.cpp;h=acbd6ae80af7bd128d1e21e08ac0947af5d41206;hb=7bce5fe61e3ecdbc13b83ae20cc18214428d69e4;hp=7fd7bfe7223661dfbafc157e794b6307d1df184a;hpb=f951374eef04129c6a0d213e7ab4d9ed2095ca69;p=inkscape.git diff --git a/src/dyna-draw-context.cpp b/src/dyna-draw-context.cpp index 7fd7bfe72..acbd6ae80 100644 --- a/src/dyna-draw-context.cpp +++ b/src/dyna-draw-context.cpp @@ -18,13 +18,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -/* - * TODO: Tue Oct 2 22:57:15 2001 - * - Decide control point behavior when use_calligraphic==1. - * - Decide to use NORMALIZED_COORDINATE or not. - * - Bug fix. - */ - #define noDYNA_DRAW_VERBOSE #include "config.h" @@ -52,9 +45,10 @@ #include "xml/repr.h" #include "context-fns.h" #include "sp-item.h" +#include "inkscape.h" +#include "color.h" #define DDC_RED_RGBA 0xff0000ff -#define DDC_GREEN_RGBA 0x000000ff #define SAMPLE_TIMEOUT 10 #define TOLERANCE_LINE 1.0 @@ -77,17 +71,14 @@ 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 concat_current_line(SPDynaDrawContext *dc); static void accumulate_calligraphic(SPDynaDrawContext *dc); static void fit_and_split(SPDynaDrawContext *ddc, gboolean release); -static void fit_and_split_line(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); static NR::Point sp_dyna_draw_get_vpoint(SPDynaDrawContext const *ddc, NR::Point n); -static NR::Point sp_dyna_draw_get_curr_vpoint(SPDynaDrawContext const *ddc); static void draw_temporary_box(SPDynaDrawContext *dc); @@ -155,11 +146,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) ddc->del = NR::Point(0,0); /* attributes */ - ddc->use_timeout = FALSE; - ddc->use_calligraphic = TRUE; - ddc->timer_id = 0; ddc->dragging = FALSE; - ddc->dynahand = FALSE; ddc->mass = 0.3; ddc->drag = DRAG_DEFAULT; @@ -168,6 +155,9 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc) ddc->vel_thin = 0.1; ddc->flatness = 0.9; + + ddc->abs_width = false; + ddc->keep_selected = true; } static void @@ -214,8 +204,7 @@ sp_dyna_draw_context_setup(SPEventContext *ec) ddc->cal1 = sp_curve_new_sized(32); ddc->cal2 = sp_curve_new_sized(32); - /* style should be changed when dc->use_calligraphc is touched */ - ddc->currentshape = sp_canvas_item_new(SP_DT_SKETCH(ec->desktop), SP_TYPE_CANVAS_BPATH, NULL); + ddc->currentshape = sp_canvas_item_new(sp_desktop_sketch(ec->desktop), SP_TYPE_CANVAS_BPATH, NULL); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(ddc->currentshape), DDC_RED_RGBA, SP_WIND_RULE_EVENODD); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(ddc->currentshape), 0x00000000, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); /* fixme: Cannot we cascade it to root more clearly? */ @@ -226,9 +215,12 @@ sp_dyna_draw_context_setup(SPEventContext *ec) sp_event_context_read(ec, "angle"); sp_event_context_read(ec, "width"); sp_event_context_read(ec, "thinning"); + sp_event_context_read(ec, "tremor"); sp_event_context_read(ec, "flatness"); sp_event_context_read(ec, "usepressure"); sp_event_context_read(ec, "usetilt"); + sp_event_context_read(ec, "abs_width"); + sp_event_context_read(ec, "keep_selected"); ddc->is_drawing = false; @@ -255,6 +247,9 @@ sp_dyna_draw_context_set(SPEventContext *ec, gchar const *key, gchar const *val) } else if (!strcmp(key, "thinning")) { double const dval = ( val ? g_ascii_strtod (val, NULL) : 0.1 ); ddc->vel_thin = CLAMP(dval, -1.0, 1.0); + } else if (!strcmp(key, "tremor")) { + double const dval = ( val ? g_ascii_strtod (val, NULL) : 0.0 ); + ddc->tremor = CLAMP(dval, 0.0, 1.0); } else if (!strcmp(key, "flatness")) { double const dval = ( val ? g_ascii_strtod (val, NULL) : 1.0 ); ddc->flatness = CLAMP(dval, 0, 1.0); @@ -262,6 +257,10 @@ sp_dyna_draw_context_set(SPEventContext *ec, gchar const *key, gchar const *val) ddc->usepressure = (val && strcmp(val, "0")); } else if (!strcmp(key, "usetilt")) { ddc->usetilt = (val && strcmp(val, "0")); + } else if (!strcmp(key, "abs_width")) { + ddc->abs_width = (val && strcmp(val, "0")); + } else if (!strcmp(key, "keep_selected")) { + ddc->keep_selected = (val && strcmp(val, "0")); } //g_print("DDC: %g %g %g %g\n", ddc->mass, ddc->drag, ddc->angle, ddc->width); @@ -291,14 +290,6 @@ sp_dyna_draw_get_vpoint(SPDynaDrawContext const *dc, NR::Point n) return NR::Point(n[NR::X] * max + drect.min()[NR::X], n[NR::Y] * max + drect.min()[NR::Y]); } -/* Get current view point */ -static NR::Point sp_dyna_draw_get_curr_vpoint(SPDynaDrawContext const *dc) -{ - NR::Rect drect = SP_EVENT_CONTEXT(dc)->desktop->get_display_area(); - double const max = MAX ( drect.dimensions()[NR::X], drect.dimensions()[NR::Y] ); - return NR::Point(dc->cur[NR::X] * max + drect.min()[NR::X], dc->cur[NR::Y] * max + drect.min()[NR::Y]); -} - static void sp_dyna_draw_reset(SPDynaDrawContext *dc, NR::Point p) { @@ -412,58 +403,56 @@ sp_dyna_draw_brush(SPDynaDrawContext *dc) { g_assert( dc->npoints >= 0 && dc->npoints < SAMPLING_SIZE ); - if (dc->use_calligraphic) { - /* calligraphics */ - - // How much velocity thins strokestyle - double vel_thin = flerp (0, 160, dc->vel_thin); - - // Influence of pressure on thickness - double pressure_thick = (dc->usepressure ? dc->pressure : 1.0); - - double width = ( pressure_thick - vel_thin * NR::L2(dc->vel) ) * dc->width; - if ( width < 0.02 * dc->width ) { - width = 0.02 * dc->width; - } - - NR::Point del = 0.05 * width * dc->ang; - - dc->point1[dc->npoints] = sp_dyna_draw_get_vpoint(dc, dc->cur + del); - dc->point2[dc->npoints] = sp_dyna_draw_get_vpoint(dc, dc->cur - del); + // How much velocity thins strokestyle + double vel_thin = flerp (0, 160, dc->vel_thin); + + // Influence of pressure on thickness + double pressure_thick = (dc->usepressure ? dc->pressure : 1.0); + + double width = ( pressure_thick - vel_thin * NR::L2(dc->vel) ) * dc->width; + + double tremble_left = 0, tremble_right = 0; + if (dc->tremor > 0) { + // obtain two normally distributed random variables, using polar Box-Muller transform + double x1, x2, w, y1, y2; + do { + x1 = 2.0 * g_random_double_range(0,1) - 1.0; + x2 = 2.0 * g_random_double_range(0,1) - 1.0; + w = x1 * x1 + x2 * x2; + } while ( w >= 1.0 ); + w = sqrt( (-2.0 * log( w ) ) / w ); + y1 = x1 * w; + y2 = x2 * w; + + // deflect both left and right edges randomly and independently, so that: + // (1) dc->tremor=1 corresponds to sigma=1, decreasing dc->tremor narrows the bell curve; + // (2) deflection depends on width, but is upped for small widths for better visual uniformity across widths; + // (3) deflection somewhat depends on speed, to prevent fast strokes looking + // comparatively smooth and slow ones excessively jittery + tremble_left = (y1)*dc->tremor * (0.15 + 0.8*width) * (0.35 + 14*NR::L2(dc->vel)); + tremble_right = (y2)*dc->tremor * (0.15 + 0.8*width) * (0.35 + 14*NR::L2(dc->vel)); + } - dc->del = del; - } else { - dc->point1[dc->npoints] = sp_dyna_draw_get_curr_vpoint(dc); + if ( width < 0.02 * dc->width ) { + width = 0.02 * dc->width; } - dc->npoints++; -} + double dezoomify_factor = 0.05 * 1000; + if (!dc->abs_width) { + dezoomify_factor /= SP_EVENT_CONTEXT(dc)->desktop->current_zoom(); + } -static gint -sp_dyna_draw_timeout_handler(gpointer data) -{ - SPDynaDrawContext *dc = SP_DYNA_DRAW_CONTEXT(data); - SPDesktop *desktop = SP_EVENT_CONTEXT(dc)->desktop; - SPCanvas *canvas = SP_CANVAS(SP_DT_CANVAS(desktop)); + NR::Point del_left = dezoomify_factor * (width + tremble_left) * dc->ang; + NR::Point del_right = dezoomify_factor * (width + tremble_right) * dc->ang; - dc->dragging = TRUE; - dc->dynahand = TRUE; + NR::Point abs_middle = sp_dyna_draw_get_vpoint(dc, dc->cur); - int x, y; - gtk_widget_get_pointer(GTK_WIDGET(canvas), &x, &y); - NR::Point p = sp_canvas_window_to_world(canvas, NR::Point(x, y)); - p = desktop->w2d(p); - if (! sp_dyna_draw_apply(dc, p)) { - return TRUE; - } + dc->point1[dc->npoints] = abs_middle + del_left; + dc->point2[dc->npoints] = abs_middle - del_right; - if ( dc->cur != dc->last ) { - sp_dyna_draw_brush(dc); - g_assert( dc->npoints > 0 ); - fit_and_split(dc, FALSE); - } + dc->del = 0.5*(del_left + del_right); - return TRUE; + dc->npoints++; } void @@ -506,29 +495,21 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, dc->npoints = 0; sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - ( dc->use_timeout - ? ( GDK_KEY_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_PRESS_MASK ) - : ( GDK_KEY_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK ) ), + ( GDK_KEY_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK ), NULL, event->button.time); - if ( dc->use_timeout && !dc->timer_id ) { - dc->timer_id = gtk_timeout_add(SAMPLE_TIMEOUT, sp_dyna_draw_timeout_handler, dc); - } ret = TRUE; dc->is_drawing = true; } break; case GDK_MOTION_NOTIFY: - if ( dc->is_drawing && !dc->use_timeout && ( event->motion.state & GDK_BUTTON1_MASK ) ) { + if ( dc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK ) ) { dc->dragging = TRUE; - dc->dynahand = TRUE; NR::Point const motion_w(event->motion.x, event->motion.y); @@ -552,40 +533,23 @@ 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 ( event->button.button == 1 - && dc->use_timeout - && dc->timer_id != 0 ) - { - gtk_timeout_remove(dc->timer_id); - dc->timer_id = 0; - } if ( dc->dragging && event->button.button == 1 ) { dc->dragging = FALSE; - /* release */ - if (dc->dynahand) { - dc->dynahand = FALSE; - /* 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); - } - /* Create object */ - fit_and_split(dc, TRUE); - if (dc->use_calligraphic) { - accumulate_calligraphic(dc); - } else { - concat_current_line(dc); - } - set_to_accumulated(dc); /* temporal implementation */ - if (dc->use_calligraphic /* || dc->cinside*/) { - /* reset accumulated curve */ - sp_curve_reset(dc->accumulated); - clear_current(dc); - if (dc->repr) { - dc->repr = NULL; - } - } + /* 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); + } + /* Create object */ + fit_and_split(dc, TRUE); + accumulate_calligraphic(dc); + set_to_accumulated(dc); /* temporal implementation */ + /* reset accumulated curve */ + sp_curve_reset(dc->accumulated); + clear_current(dc); + if (dc->repr) { + dc->repr = NULL; } ret = TRUE; } @@ -618,7 +582,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, dc->width += 0.01; if (dc->width > 1.0) dc->width = 1.0; - sp_ddc_update_toolbox (desktop, "altx-calligraphy", dc->width); // the same spinbutton is for alt+x + sp_ddc_update_toolbox (desktop, "altx-calligraphy", dc->width * 100); // the same spinbutton is for alt+x ret = TRUE; } break; @@ -628,7 +592,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, dc->width -= 0.01; if (dc->width < 0.01) dc->width = 0.01; - sp_ddc_update_toolbox (desktop, "altx-calligraphy", dc->width); + sp_ddc_update_toolbox (desktop, "altx-calligraphy", dc->width * 100); ret = TRUE; } break; @@ -640,7 +604,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context, } break; case GDK_Escape: - SP_DT_SELECTION(desktop)->clear(); + sp_desktop_selection(desktop)->clear(); break; default: @@ -695,7 +659,11 @@ set_to_accumulated(SPDynaDrawContext *dc) Inkscape::GC::release(dc->repr); item->transform = SP_ITEM(desktop->currentRoot())->getRelativeTransform(desktop->currentLayer()); item->updateRepr(); - SP_DT_SELECTION(desktop)->set(dc->repr); + if (dc->keep_selected) { + sp_desktop_selection(desktop)->set(dc->repr); + } else { + sp_desktop_selection(desktop)->clear(); + } } abp = nr_artpath_affine(sp_curve_first_bpath(dc->accumulated), sp_desktop_dt2root_affine(desktop)); str = sp_svg_write_path(abp); @@ -710,31 +678,7 @@ set_to_accumulated(SPDynaDrawContext *dc) dc->repr = NULL; } - sp_document_done(SP_DT_DOCUMENT(desktop)); -} - -static void -concat_current_line(SPDynaDrawContext *dc) -{ - if (!sp_curve_empty(dc->currentcurve)) { - NArtBpath *bpath; - if (sp_curve_empty(dc->accumulated)) { - bpath = sp_curve_first_bpath(dc->currentcurve); - g_assert( bpath->code == NR_MOVETO_OPEN ); - sp_curve_moveto(dc->accumulated, bpath->x3, bpath->y3); - } - bpath = sp_curve_last_bpath(dc->currentcurve); - if ( bpath->code == NR_CURVETO ) { - sp_curve_curveto(dc->accumulated, - bpath->x1, bpath->y1, - bpath->x2, bpath->y2, - bpath->x3, bpath->y3); - } else if ( bpath->code == NR_LINETO ) { - sp_curve_lineto(dc->accumulated, bpath->x3, bpath->y3); - } else { - g_assert_not_reached(); - } - } + sp_document_done(sp_desktop_document(desktop)); } static void @@ -758,11 +702,7 @@ static void fit_and_split(SPDynaDrawContext *dc, gboolean release) { - if (dc->use_calligraphic) { - fit_and_split_calligraphics(dc, release); - } else { - fit_and_split_line(dc, release); - } + fit_and_split_calligraphics(dc, release); } static double square(double const x) @@ -770,51 +710,6 @@ static double square(double const x) return x * x; } -static void -fit_and_split_line(SPDynaDrawContext *dc, - gboolean release) -{ - double const tolerance_sq = square( NR::expansion(SP_EVENT_CONTEXT(dc)->desktop->w2d()) * TOLERANCE_LINE ); - - NR::Point b[4]; - double const n_segs = sp_bezier_fit_cubic(b, dc->point1, dc->npoints, tolerance_sq); - if ( n_segs > 0 - && dc->npoints < SAMPLING_SIZE ) - { - /* Fit and draw and reset state */ -#ifdef DYNA_DRAW_VERBOSE - g_print("%d", dc->npoints); -#endif - sp_curve_reset(dc->currentcurve); - sp_curve_moveto(dc->currentcurve, b[0]); - sp_curve_curveto(dc->currentcurve, b[1], b[2], b[3]); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->currentshape), dc->currentcurve); - } else { - /* Fit and draw and copy last point */ -#ifdef DYNA_DRAW_VERBOSE - g_print("[%d]Yup\n", dc->npoints); -#endif - g_assert(!sp_curve_empty(dc->currentcurve)); - concat_current_line(dc); - - SPCanvasItem *cbp = sp_canvas_item_new(SP_DT_SKETCH(SP_EVENT_CONTEXT(dc)->desktop), - SP_TYPE_CANVAS_BPATH, - NULL); - SPCurve *curve = sp_curve_copy(dc->currentcurve); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cbp), curve); - sp_curve_unref(curve); - /* fixme: We have to parse style color somehow */ - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cbp), DDC_GREEN_RGBA, SP_WIND_RULE_EVENODD); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cbp), 0x000000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - /* fixme: Cannot we cascade it to root more clearly? */ - g_signal_connect(G_OBJECT(cbp), "event", G_CALLBACK(sp_desktop_root_handler), SP_EVENT_CONTEXT(dc)->desktop); - - dc->segments = g_slist_prepend(dc->segments, cbp); - dc->point1[0] = dc->point1[dc->npoints - 2]; - dc->npoints = 1; - } -} - static void fit_and_split_calligraphics(SPDynaDrawContext *dc, gboolean release) { @@ -909,13 +804,21 @@ fit_and_split_calligraphics(SPDynaDrawContext *dc, gboolean release) if (!release) { g_assert(!sp_curve_empty(dc->currentcurve)); - SPCanvasItem *cbp = sp_canvas_item_new(SP_DT_SKETCH(SP_EVENT_CONTEXT(dc)->desktop), + SPCanvasItem *cbp = sp_canvas_item_new(sp_desktop_sketch(SP_EVENT_CONTEXT(dc)->desktop), SP_TYPE_CANVAS_BPATH, NULL); SPCurve *curve = sp_curve_copy(dc->currentcurve); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH (cbp), curve); sp_curve_unref(curve); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cbp), 0x000000ff, SP_WIND_RULE_EVENODD); + + guint32 fillColor = sp_desktop_get_color_tool (SP_ACTIVE_DESKTOP, "tools.calligraphic", true); + //guint32 strokeColor = sp_desktop_get_color_tool (SP_ACTIVE_DESKTOP, "tools.calligraphic", false); + double opacity = sp_desktop_get_master_opacity_tool (SP_ACTIVE_DESKTOP, "tools.calligraphic"); + double fillOpacity = sp_desktop_get_opacity_tool (SP_ACTIVE_DESKTOP, "tools.calligraphic", true); + //double strokeOpacity = sp_desktop_get_opacity_tool (SP_ACTIVE_DESKTOP, "tools.calligraphic", false); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cbp), ((fillColor & 0xffffff00) | SP_COLOR_F_TO_U(opacity*fillOpacity)), SP_WIND_RULE_EVENODD); + //on second thougtht don't do stroke yet because we don't have stoke-width yet and because stoke appears between segments while drawing + //sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cbp), ((strokeColor & 0xffffff00) | SP_COLOR_F_TO_U(opacity*strokeOpacity)), 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cbp), 0x00000000, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); /* fixme: Cannot we cascade it to root more clearly? */ g_signal_connect(G_OBJECT(cbp), "event", G_CALLBACK(sp_desktop_root_handler), SP_EVENT_CONTEXT(dc)->desktop);