index 6639613479b6aa1274b8e470fe7e5bfbc1964b99..1ec0d7fde64e5919f21b7bd6a54f20bb31474d99 100644 (file)
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
+#include <glibmm/i18n.h>
#include "svg/svg.h"
#include "display/canvas-bpath.h"
#include "desktop-style.h"
#include "message-context.h"
#include "pixmaps/cursor-calligraphy.xpm"
-#include "pixmaps/cursor-calligraphy.pixbuf"
#include "dyna-draw-context.h"
#include "libnr/n-art-bpath.h"
#include "libnr/nr-path.h"
#define SAMPLE_TIMEOUT 10
#define TOLERANCE_LINE 1.0
#define TOLERANCE_CALLIGRAPHIC 3.0
-#define DYNA_EPSILON 0.5e-2
+
+#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
@@ -76,11 +79,10 @@ 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 add_cap(SPCurve *curve, NR::Point const &from, NR::Point const &to, double rounding);
+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_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);
SPEventContext *event_context = SP_EVENT_CONTEXT(ddc);
event_context->cursor_shape = cursor_calligraphy_xpm;
- event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline(
- -1,
- cursor_calligraphy_pixbuf,
- FALSE,
- NULL);
event_context->hot_x = 4;
event_context->hot_y = 4;
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);
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");
@@ -248,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);
{
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;
case GDK_BUTTON_RELEASE:
sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
dc->is_drawing = false;
+
if ( dc->dragging && event->button.button == 1 ) {
dc->dragging = FALSE;
+ 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));
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);
}
sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_CALLIGRAPHIC,
- /* TODO: annotate */ "dyna-draw-context.cpp:689");
+ _("Create calligraphic stroke"));
}
static void
-add_cap(SPCurve *curve, NR::Point const &from, NR::Point const &to,
+add_cap(SPCurve *curve,
+ NR::Point const &pre, NR::Point const &from,
+ NR::Point const &to, NR::Point const &post,
double rounding)
{
- NR::Point vec = rounding * NR::rot90( ( to - from ) / sqrt(2.0) );
+ 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(vec) > DYNA_EPSILON ) {
- sp_curve_curveto(curve, from + vec, to + vec, to);
+ 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_last_point(dc->cal1), sp_curve_first_point(rev_cal2), dc->cap_rounding);
+
+ 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_last_point(rev_cal2), sp_curve_first_point(dc->cal1), dc->cap_rounding);
+
+ 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)
-{
- fit_and_split_calligraphics(dc, release);
-}
-
static double square(double const x)
{
return x * x;
}
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]);
}
- if (!dc->segments) {
- add_cap(dc->currentcurve, b2[0], b1[0], dc->cap_rounding);
+ // 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);
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]);
}
- add_cap(dc->currentcurve, dc->point2[0], dc->point1[0], dc->cap_rounding);
+ 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);
}