index 303573c4680116a5824472382ead7c4535977d06..ba44df3ce784e05ebcc0a9a18a4598277cc7463c 100644 (file)
* 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-6
+#define DYNA_EPSILON_START 0.5e-2
+#define DYNA_VEL_START 1e-5
#define DYNA_MIN_WIDTH 1.0e-6
@@ -77,17 +79,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);
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);
/* 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;
ddc->vel_thin = 0.1;
ddc->flatness = 0.9;
+ ddc->cap_rounding = 0.0;
+
+ ddc->abs_width = false;
+ ddc->keep_selected = true;
}
static void
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, "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;
@@ -244,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);
@@ -266,6 +268,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);
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)
{
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);
/* 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;
}
/* 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;
// 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;
{
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;
-
- 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));
- }
-
- if ( width < 0.02 * dc->width ) {
- width = 0.02 * dc->width;
- }
-
- NR::Point del_left = 0.05 * (width + tremble_left) * dc->ang;
- NR::Point del_right = 0.05 * (width + tremble_right) * dc->ang;
-
- dc->point1[dc->npoints] = sp_dyna_draw_get_vpoint(dc, dc->cur + del_left);
- dc->point2[dc->npoints] = sp_dyna_draw_get_vpoint(dc, dc->cur - del_right);
+ // 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 = 0.5*(del_left + del_right);
- } 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
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)
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);
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;
}
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;
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;
}
break;
case GDK_Escape:
- SP_DT_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;
}
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);
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 {
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);
}
}
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);
}
}
-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 );
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);
}
} 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);
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);
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);
}