dyna-draw-context.cpp revision 7a7345e59acafc35c32fdae5f0ceb5cf4b62bdfc
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Handwriting-like drawing mode
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Mitsuru Oka <oka326@parkcity.ne.jp>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Lauris Kaplinski <lauris@kaplinski.com>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * bulia byak <buliabyak@users.sf.net>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * MenTaLguY <mental@rydia.net>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Abhishek Sharma
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Jon A. Cruz <jon@joncruz.org>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * The original dynadraw code:
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Paul Haeberli <paul@sgi.com>
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Copyright (C) 1998 The Free Software Foundation
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Copyright (C) 1999-2005 authors
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Copyright (C) 2001-2002 Ximian, Inc.
11f343b8117dbf56931f537820c2749a8232fec2Liam P. White * Copyright (C) 2005-2007 bulia byak
11f343b8117dbf56931f537820c2749a8232fec2Liam P. White * Copyright (C) 2006 MenTaLguY
36a048753a41b465ae130b361fb3b68c605e3e86kiirala * Released under GNU GPL, read the file 'COPYING' for more information
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void sp_dyna_draw_context_class_init(SPDynaDrawContextClass *klass);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void sp_dyna_draw_context_init(SPDynaDrawContext *ddc);
2a67507e7a675c7c516ab207418d3a5c7bfe5989buliabyakstatic void sp_dyna_draw_context_dispose(GObject *object);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void sp_dyna_draw_context_setup(SPEventContext *ec);
2a67507e7a675c7c516ab207418d3a5c7bfe5989buliabyakstatic void sp_dyna_draw_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *value);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic gint sp_dyna_draw_context_root_handler(SPEventContext *ec, GdkEvent *event);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void set_to_accumulated(SPDynaDrawContext *dc, bool unionize, bool subtract);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void add_cap(SPCurve *curve, Geom::Point const &from, Geom::Point const &to, double rounding);
11f343b8117dbf56931f537820c2749a8232fec2Liam P. Whitestatic bool accumulate_calligraphic(SPDynaDrawContext *dc);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void fit_and_split(SPDynaDrawContext *ddc, gboolean release);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void sp_dyna_draw_reset(SPDynaDrawContext *ddc, Geom::Point p);
11f343b8117dbf56931f537820c2749a8232fec2Liam P. Whitestatic Geom::Point sp_dyna_draw_get_npoint(SPDynaDrawContext const *ddc, Geom::Point v);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic Geom::Point sp_dyna_draw_get_vpoint(SPDynaDrawContext const *ddc, Geom::Point n);
36a048753a41b465ae130b361fb3b68c605e3e86kiiralastatic void draw_temporary_box(SPDynaDrawContext *dc);
36a048753a41b465ae130b361fb3b68c605e3e86kiirala 0, // base_init
36a048753a41b465ae130b361fb3b68c605e3e86kiirala 0, // base_finalize
36a048753a41b465ae130b361fb3b68c605e3e86kiirala 0, // class_finalize
36a048753a41b465ae130b361fb3b68c605e3e86kiirala 0, // class_data
7df9bc9728133b770919198ecfd034602696d46fbuliabyak 0, // n_preallocs
36a048753a41b465ae130b361fb3b68c605e3e86kiirala 0 // value_table
36a048753a41b465ae130b361fb3b68c605e3e86kiirala type = g_type_register_static(SP_TYPE_COMMON_CONTEXT, "SPDynaDrawContext", &info, static_cast<GTypeFlags>(0));
36a048753a41b465ae130b361fb3b68c605e3e86kiiralasp_dyna_draw_context_class_init(SPDynaDrawContextClass *klass)
36a048753a41b465ae130b361fb3b68c605e3e86kiirala GObjectClass *object_class = (GObjectClass *) klass;
36a048753a41b465ae130b361fb3b68c605e3e86kiirala SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
36a048753a41b465ae130b361fb3b68c605e3e86kiirala dd_parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
a4030d5ca449e7e384bc699cd249ee704faaeab0Chris Morgan object_class->dispose = sp_dyna_draw_context_dispose;
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(ddc->currentshape), 0x00000000, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
g_signal_connect(G_OBJECT(ddc->currentshape), "event", G_CALLBACK(sp_desktop_root_handler), ec->desktop);
/* TODO: this can be done either with an arcto, and should maybe also be put in a general file (other tools use this as well) */
c->closepath();
c->unref();
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(ddc->hatch_area), 0x0000007f, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
return Geom::Point(( v[Geom::X] - drect.min()[Geom::X] ) / max, ( v[Geom::Y] - drect.min()[Geom::Y] ) / max);
return Geom::Point(n[Geom::X] * max + drect.min()[Geom::X], n[Geom::Y] * max + drect.min()[Geom::Y]);
static gboolean
if ( Geom::L2(force) < DYNA_EPSILON || (dc->vel_max < DYNA_VEL_START && Geom::L2(force) < DYNA_EPSILON_START)) {
return FALSE;
double a1;
if (length > 0) {
return FALSE;
bool flipped = false;
flipped = true;
// FIXME: when dc->vel is oscillating around the fixed angle, the new_ang flips back and forth. How to avoid this?
return FALSE;
// 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);
return TRUE;
sp_canvas_arena_render_surface(SP_CANVAS_ARENA(sp_desktop_drawing(SP_EVENT_CONTEXT(dc)->desktop)), s, area);
ink_cairo_surface_average_color_premul(s, R, G, B, A);
// (2) deflection depends on width, but is upped for small widths for better visual uniformity across widths;
case GDK_BUTTON_PRESS:
return TRUE;
NULL,
case GDK_MOTION_NOTIFY:
double hatch_dist = 0;
boost::optional<Path::cut_position> position = get_nearest_position_on_Path(dc->hatch_livarot_path, pointer);
dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Guide path selected</b>; start drawing along the guide with <b>Ctrl</b>"));
dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Select a guide path</b> to track with <b>Ctrl</b>"));
double nearest_sum = std::accumulate (dc->hatch_nearest_past.begin(), dc->hatch_nearest_past.end(), 0.0);
double pointer_sum = std::accumulate (dc->hatch_pointer_past.begin(), dc->hatch_pointer_past.end(), 0.0);
//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);
double target;
dc->_message_context->set(Inkscape::NORMAL_MESSAGE, dc->hatch_escaped? _("Tracking: <b>connection to guide path lost!</b>") : _("<b>Tracking</b> a guide path"));
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(dc->hatch_area), 0x7f7f7fff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(dc->hatch_area), 0x00FF00ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(dc->hatch_area), 0xFF0000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(dc->hatch_area), 0x7f7f7fff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
case GDK_BUTTON_RELEASE:
set_to_accumulated(dc, event->button.state & GDK_SHIFT_MASK, event->button.state & GDK_MOD1_MASK); // performs document_done
case GDK_KEY_PRESS:
case GDK_KEY_Up:
case GDK_KEY_KP_Up:
if (!MOD__CTRL_ONLY) {
case GDK_KEY_Down:
case GDK_KEY_KP_Down:
if (!MOD__CTRL_ONLY) {
case GDK_KEY_Right:
case GDK_KEY_KP_Right:
if (!MOD__CTRL_ONLY) {
sp_ddc_update_toolbox (desktop, "altx-calligraphy", dc->width * 100); // the same spinbutton is for alt+x
case GDK_KEY_Left:
case GDK_KEY_KP_Left:
if (!MOD__CTRL_ONLY) {
case GDK_KEY_Home:
case GDK_KEY_KP_Home:
case GDK_KEY_End:
case GDK_KEY_KP_End:
case GDK_KEY_x:
case GDK_KEY_X:
if (MOD__ALT_ONLY) {
case GDK_KEY_Escape:
case GDK_KEY_z:
case GDK_KEY_Z:
case GDK_KEY_RELEASE:
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
if (!ret) {
return ret;
if (unionize) {
} else if (subtract) {
double rounding)
add_cap(dc->accumulated, dc_cal1_lastseg->finalPoint(), rev_cal2_firstseg->initialPoint(), dc->cap_rounding);
add_cap(dc->accumulated, rev_cal2_lastseg->finalPoint(), dc_cal1_firstseg->initialPoint(), dc->cap_rounding);
static double square(double const x)
#ifdef DYNA_DRAW_VERBOSE
#ifdef DYNA_DRAW_VERBOSE
#ifdef DYNA_DRAW_VERBOSE
if (! release) {
#ifdef DYNA_DRAW_VERBOSE
#ifdef DYNA_DRAW_VERBOSE
if (!release) {
NULL);
//guint32 strokeColor = sp_desktop_get_color_tool (desktop, "/tools/calligraphic", false);
//double strokeOpacity = sp_desktop_get_opacity_tool (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);