Code

switch pref/repr utility methods to using long long ints; with
[inkscape.git] / src / dyna-draw-context.cpp
index 8b90ce034c3484e916be536adc7c308fa83177d6..91079aaa4dd278a287fc7034a6b1a2d32a3a94c3 100644 (file)
@@ -7,6 +7,7 @@
  *   Mitsuru Oka <oka326@parkcity.ne.jp>
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   bulia byak <buliabyak@users.sf.net>
+ *   MenTaLguY <mental@rydia.net>
  *
  * The original dynadraw code:
  *   Paul Haeberli <paul@sgi.com>
  * 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
  */
 
-/*
- * 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"
 
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
+#include <glibmm/i18n.h>
 
 #include "svg/svg.h"
 #include "display/canvas-bpath.h"
 #include "display/bezier-utils.h"
 
+#include <glib/gmem.h>
 #include "macros.h"
 #include "document.h"
 #include "selection.h"
 #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
 #define TOLERANCE_CALLIGRAPHIC 3.0
-#define DYNA_EPSILON 1.0e-6
+#define DYNA_EPSILON 0.5e-2
 
 #define DYNA_MIN_WIDTH 1.0e-6
 
@@ -77,17 +76,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 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_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);
 
 
@@ -136,7 +132,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc)
     event_context->cursor_shape = cursor_calligraphy_xpm;
     event_context->hot_x = 4;
     event_context->hot_y = 4;
-    
+
     ddc->accumulated = NULL;
     ddc->segments = NULL;
     ddc->currentcurve = NULL;
@@ -155,11 +151,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 +160,10 @@ 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;
 }
 
 static void
@@ -214,21 +210,24 @@ 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? */
     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");
+    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");
+    sp_event_context_read(ec, "cap_rounding");
 
     ddc->is_drawing = false;
 
@@ -243,9 +242,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);
@@ -255,6 +254,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 +264,12 @@ 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"));
+    } 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);
@@ -291,14 +299,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 +412,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
@@ -490,7 +488,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
             if (Inkscape::have_viable_layer(desktop, dc->_message_context) == false) {
                 return TRUE;
             }
-            
+
             NR::Point const button_w(event->button.x,
                                      event->button.y);
             NR::Point const button_dt(desktop->w2d(button_w));
@@ -506,29 +504,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 +542,28 @@ 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;
-                    }
-                }
+            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));
+                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;
         }
@@ -596,7 +574,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
         case GDK_KP_Up:
             if (!MOD__CTRL_ONLY) {
                 dc->angle += 5.0;
-                if (dc->angle > 90.0) 
+                if (dc->angle > 90.0)
                     dc->angle = 90.0;
                 sp_ddc_update_toolbox (desktop, "calligraphy-angle", dc->angle);
                 ret = TRUE;
@@ -606,7 +584,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
         case GDK_KP_Down:
             if (!MOD__CTRL_ONLY) {
                 dc->angle -= 5.0;
-                if (dc->angle < -90.0) 
+                if (dc->angle < -90.0)
                     dc->angle = -90.0;
                 sp_ddc_update_toolbox (desktop, "calligraphy-angle", dc->angle);
                 ret = TRUE;
@@ -616,9 +594,9 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
         case GDK_KP_Right:
             if (!MOD__CTRL_ONLY) {
                 dc->width += 0.01;
-                if (dc->width > 1.0) 
+                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;
@@ -626,9 +604,9 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
         case GDK_KP_Left:
             if (!MOD__CTRL_ONLY) {
                 dc->width -= 0.01;
-                if (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 +618,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,12 +673,16 @@ 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);
         g_assert( str != NULL );
-        nr_free(abp);
+        g_free(abp);
         dc->repr->setAttribute("d", str);
         g_free(str);
     } else {
@@ -710,30 +692,37 @@ set_to_accumulated(SPDynaDrawContext *dc)
         dc->repr = NULL;
     }
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_CALLIGRAPHIC, 
+                     _("Create calligraphic stroke"));
 }
 
 static void
-concat_current_line(SPDynaDrawContext *dc)
+add_cap(SPCurve *curve,
+        NR::Point const &pre, NR::Point const &from,
+        NR::Point const &to, NR::Point const &post,
+        double rounding)
 {
-    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();
-        }
+    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);
     }
 }
 
@@ -743,8 +732,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);
@@ -754,69 +759,13 @@ accumulate_calligraphic(SPDynaDrawContext *dc)
     }
 }
 
-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);
-    }
-}
-
 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)
+fit_and_split(SPDynaDrawContext *dc, gboolean release)
 {
     double const tolerance_sq = square( NR::expansion(SP_EVENT_CONTEXT(dc)->desktop->w2d()) * TOLERANCE_CALLIGRAPHIC );
 
@@ -876,6 +825,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);
             }
@@ -890,7 +843,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);
 
@@ -909,13 +862,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);
@@ -942,6 +903,7 @@ draw_temporary_box(SPDynaDrawContext *dc)
     for (gint i = dc->npoints-1; i >= 0; i--) {
         sp_curve_lineto(dc->currentcurve, dc->point2[i]);
     }
+    add_cap(dc->currentcurve, dc->point2[1], dc->point2[0], dc->point1[0], dc->point1[1], dc->cap_rounding);
     sp_curve_closepath(dc->currentcurve);
     sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(dc->currentshape), dc->currentcurve);
 }