Velocity-sensitivity added to ink tool.

Sat Apr 10 15:48:46 BST 1999 Adam D. Moss <adam@gimp.org>

        * app/ink.c: Velocity-sensitivity added to ink tool.
This commit is contained in:
BST 1999 Adam D. Moss 1999-04-10 14:55:17 +00:00 committed by Adam D. Moss
parent d6116b8d2c
commit 784ecce7bf
5 changed files with 932 additions and 60 deletions

View File

@ -1,3 +1,7 @@
Sat Apr 10 15:48:46 BST 1999 Adam D. Moss <adam@gimp.org>
* app/ink.c: Velocity-sensitivity added to ink tool.
Fri Apr 9 21:45:10 PDT 1999 Manish Singh <yosh@gimp.org> Fri Apr 9 21:45:10 PDT 1999 Manish Singh <yosh@gimp.org>
* text_tool_cmds.c: new file (from pdbgen) * text_tool_cmds.c: new file (from pdbgen)

247
app/ink.c
View File

@ -44,6 +44,9 @@
#define SUBSAMPLE 8 #define SUBSAMPLE 8
#define DIST_SMOOTHER_BUFFER 10
#define TIME_SMOOTHER_BUFFER 10
/* the Ink structures */ /* the Ink structures */
typedef Blob *(*BlobFunc) (double, double, double, double, double, double); typedef Blob *(*BlobFunc) (double, double, double, double, double, double);
@ -58,6 +61,16 @@ struct _InkTool
int x1, y1; /* image space coordinate */ int x1, y1; /* image space coordinate */
int x2, y2; /* image space coords */ int x2, y2; /* image space coords */
gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; /* circular distance history buffer */
gint dt_index;
guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; /* circular timing history buffer */
gint ts_index;
gdouble last_time; /* previous time of a motion event */
gdouble lastx, lasty; /* previous position of a motion event */
gboolean init_velocity;
}; };
typedef struct _InkOptions InkOptions; typedef struct _InkOptions InkOptions;
@ -71,6 +84,10 @@ struct _InkOptions
double sensitivity_d; double sensitivity_d;
GtkObject *sensitivity_w; GtkObject *sensitivity_w;
double vel_sensitivity;
double vel_sensitivity_d;
GtkObject *vel_sensitivity_w;
double tilt_sensitivity; double tilt_sensitivity;
double tilt_sensitivity_d; double tilt_sensitivity_d;
GtkObject *tilt_sensitivity_w; GtkObject *tilt_sensitivity_w;
@ -123,6 +140,13 @@ static void ink_motion (Tool *, GdkEventMotion *, gpointer);
static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer); static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer);
static void ink_control (Tool *, int, gpointer); static void ink_control (Tool *, int, gpointer);
static void time_smoother_add (InkTool* ink_tool, guint32 value);
static gdouble time_smoother_result (InkTool* ink_tool);
static void time_smoother_init (InkTool* ink_tool, guint32 initval);
static void dist_smoother_add (InkTool* ink_tool, gdouble value);
static gdouble dist_smoother_result (InkTool* ink_tool);
static void dist_smoother_init (InkTool* ink_tool, gdouble initval);
static InkOptions *create_ink_options (void); static InkOptions *create_ink_options (void);
static Argument *ink_invoker (Argument *); static Argument *ink_invoker (Argument *);
@ -201,6 +225,8 @@ reset_ink_options (void)
options->sensitivity_d); options->sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w),
options->tilt_sensitivity_d); options->tilt_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
options->vel_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
options->tilt_angle_d); options->tilt_angle_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE);
@ -227,16 +253,17 @@ create_ink_options (void)
/* the new options structure */ /* the new options structure */
options = (InkOptions *) g_malloc (sizeof (InkOptions)); options = (InkOptions *) g_malloc (sizeof (InkOptions));
options->size = options->size_d = 3.0; options->size = options->size_d = 4.4;
options->sensitivity = options->sensitivity_d = 1.0; options->sensitivity = options->sensitivity_d = 1.0;
options->tilt_sensitivity = options->tilt_sensitivity_d = 1.0; options->vel_sensitivity = options->vel_sensitivity_d = 0.8;
options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
options->tilt_angle = options->tilt_angle_d = 0.0; options->tilt_angle = options->tilt_angle_d = 0.0;
options->function = options->function_d = blob_ellipse; options->function = options->function_d = blob_ellipse;
options->aspect = options->aspect_d = 1.0; options->aspect = options->aspect_d = 1.0;
options->angle = options->angle_d = 0.0; options->angle = options->angle_d = 0.0;
/* the main table */ /* the main table */
table = gtk_table_new (7, 2, FALSE); table = gtk_table_new (8, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1);
gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2);
@ -308,14 +335,15 @@ create_ink_options (void)
&options->tilt_sensitivity); &options->tilt_sensitivity);
gtk_widget_show (slider); gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle")); /* velocity sens slider */
label = gtk_label_new (_("Speed"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label); gtk_widget_show (label);
label = gtk_label_new (_("Adjust:")); label = gtk_label_new (_("Sensitivity:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
@ -326,6 +354,36 @@ create_ink_options (void)
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox); gtk_widget_show (abox);
options->vel_sensitivity_w =
gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w));
gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
gtk_container_add (GTK_CONTAINER (abox), slider);
gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed",
(GtkSignalFunc) ink_scale_update,
&options->vel_sensitivity);
gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
label = gtk_label_new (_("Adjust:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox);
options->tilt_angle_w = options->tilt_angle_w =
gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w));
@ -337,8 +395,9 @@ create_ink_options (void)
&options->tilt_angle); &options->tilt_angle);
/* Brush type radiobuttons */ /* Brush type radiobuttons */
frame = gtk_frame_new (_("Type")); frame = gtk_frame_new (_("Type"));
gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 6, 7, gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 8, 9,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
@ -398,7 +457,7 @@ create_ink_options (void)
/* Brush shape widget */ /* Brush shape widget */
frame = gtk_frame_new (_("Shape")); frame = gtk_frame_new (_("Shape"));
gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 6, 7); gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 8, 9);
gtk_widget_show (frame); gtk_widget_show (frame);
vbox = gtk_vbox_new (FALSE, 2); vbox = gtk_vbox_new (FALSE, 2);
@ -644,9 +703,11 @@ paint_blob (GdkDrawable *drawable, GdkGC *gc,
} }
} }
static Blob * static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center, ink_pen_ellipse (gdouble x_center, gdouble y_center,
gdouble pressure, gdouble xtilt, gdouble ytilt) gdouble pressure, gdouble xtilt, gdouble ytilt,
gdouble velocity)
{ {
double size; double size;
double tsin, tcos; double tsin, tcos;
@ -656,10 +717,38 @@ ink_pen_ellipse (gdouble x_center, gdouble y_center,
double tscale_c; double tscale_c;
double tscale_s; double tscale_s;
size = ink_options->size * (1 + ink_options->sensitivity * (2*pressure - 1)); /* Adjust the size depending on pressure. */
if (size*SUBSAMPLE < 1) size = 1/SUBSAMPLE;
/* Add brush angle/aspect to title vectorially */ size = ink_options->size * (1.0 + ink_options->sensitivity *
(2.0 * pressure - 1.0) );
/* Adjust the size further depending on pointer velocity
and velocity-sensitivity. These 'magic constants' are
'feels natural' tigert-approved. --ADM */
if (velocity < 3.0)
velocity = 3.0;
#ifdef VERBOSE
g_print("%f (%f) -> ", (float)size, (float)velocity);
#endif
size = ink_options->vel_sensitivity *
((4.5 * size) / (1.0 + ink_options->vel_sensitivity * (2.0*(velocity))))
+ (1.0 - ink_options->vel_sensitivity) * size;
#ifdef VERBOSE
g_print("%f\n", (float)size);
#endif
/* Clamp resulting size to sane limits */
if (size > ink_options->size * (1.0 + ink_options->sensitivity))
size = ink_options->size * (1.0 + ink_options->sensitivity);
if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;
/* Add brush angle/aspect to tilt vectorially */
/* I'm not happy with the way the brush widget info is combined with /* I'm not happy with the way the brush widget info is combined with
tilt info from the brush. My personal feeling is that representing tilt info from the brush. My personal feeling is that representing
@ -745,11 +834,18 @@ ink_button_press (Tool *tool,
tool->state = ACTIVE; tool->state = ACTIVE;
b = ink_pen_ellipse (x, y, b = ink_pen_ellipse (x, y,
bevent->pressure, bevent->xtilt, bevent->ytilt); bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
ink_paste (ink_tool, drawable, b); ink_paste (ink_tool, drawable, b);
ink_tool->last_blob = b; ink_tool->last_blob = b;
time_smoother_init (ink_tool, bevent->time);
ink_tool->last_time = bevent->time;
dist_smoother_init (ink_tool, 0.0);
ink_tool->init_velocity = TRUE;
ink_tool->lastx = x;
ink_tool->lasty = y;
gdisplay_flush_now (gdisp); gdisplay_flush_now (gdisp);
} }
@ -779,6 +875,88 @@ ink_button_release (Tool *tool,
gdisplays_flush (); gdisplays_flush ();
} }
static void
dist_smoother_init (InkTool* ink_tool, gdouble initval)
{
gint i;
ink_tool->dt_index = 0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
ink_tool->dt_buffer[i] = initval;
}
}
static gdouble
dist_smoother_result (InkTool* ink_tool)
{
gint i;
gdouble result = 0.0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
result += ink_tool->dt_buffer[i];
}
return (result / (gdouble)DIST_SMOOTHER_BUFFER);
}
static void
dist_smoother_add (InkTool* ink_tool, gdouble value)
{
ink_tool->dt_buffer[ink_tool->dt_index] = value;
if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
ink_tool->dt_index = 0;
}
static void
time_smoother_init (InkTool* ink_tool, guint32 initval)
{
gint i;
ink_tool->ts_index = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
ink_tool->ts_buffer[i] = initval;
}
}
static gdouble
time_smoother_result (InkTool* ink_tool)
{
gint i;
guint64 result = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
result += ink_tool->ts_buffer[i];
}
return (result / TIME_SMOOTHER_BUFFER);
}
static void
time_smoother_add (InkTool* ink_tool, guint32 value)
{
ink_tool->ts_buffer[ink_tool->ts_index] = value;
if ((++ink_tool->ts_index) == TIME_SMOOTHER_BUFFER)
ink_tool->ts_index = 0;
}
static void static void
ink_motion (Tool *tool, ink_motion (Tool *tool,
GdkEventMotion *mevent, GdkEventMotion *mevent,
@ -790,6 +968,11 @@ ink_motion (Tool *tool,
Blob *b, *blob_union; Blob *b, *blob_union;
double x, y; double x, y;
double pressure;
double velocity;
double dist;
gdouble lasttime, thistime;
gdisp = (GDisplay *) gdisp_ptr; gdisp = (GDisplay *) gdisp_ptr;
ink_tool = (InkTool *) tool->private; ink_tool = (InkTool *) tool->private;
@ -797,8 +980,42 @@ ink_motion (Tool *tool,
gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE);
drawable = gimage_active_drawable (gdisp->gimage); drawable = gimage_active_drawable (gdisp->gimage);
b = ink_pen_ellipse (x, y, lasttime = ink_tool->last_time;
mevent->pressure, mevent->xtilt, mevent->ytilt);
time_smoother_add (ink_tool, mevent->time);
thistime = ink_tool->last_time =
time_smoother_result(ink_tool);
/* The time resolution on X-based GDK motion events is
bloody awful, hence the use of the smoothing function.
Sadly this also means that there is always the chance of
having an indeterminite velocity since this event and
the previous several may still appear to issue at the same
instant. -ADM */
if (thistime == lasttime)
thistime = lasttime + 1;
if (ink_tool->init_velocity)
{
dist_smoother_init (ink_tool, dist = sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
ink_tool->init_velocity = FALSE;
}
else
{
dist_smoother_add (ink_tool, sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
dist = dist_smoother_result(ink_tool);
}
ink_tool->lastx = x;
ink_tool->lasty = y;
pressure = mevent->pressure;
velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime));
b = ink_pen_ellipse (x, y, pressure, mevent->xtilt, mevent->ytilt, velocity);
blob_union = blob_convex_union (ink_tool->last_blob, b); blob_union = blob_convex_union (ink_tool->last_blob, b);
g_free (ink_tool->last_blob); g_free (ink_tool->last_blob);
ink_tool->last_blob = b; ink_tool->last_blob = b;

View File

@ -44,6 +44,9 @@
#define SUBSAMPLE 8 #define SUBSAMPLE 8
#define DIST_SMOOTHER_BUFFER 10
#define TIME_SMOOTHER_BUFFER 10
/* the Ink structures */ /* the Ink structures */
typedef Blob *(*BlobFunc) (double, double, double, double, double, double); typedef Blob *(*BlobFunc) (double, double, double, double, double, double);
@ -58,6 +61,16 @@ struct _InkTool
int x1, y1; /* image space coordinate */ int x1, y1; /* image space coordinate */
int x2, y2; /* image space coords */ int x2, y2; /* image space coords */
gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; /* circular distance history buffer */
gint dt_index;
guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; /* circular timing history buffer */
gint ts_index;
gdouble last_time; /* previous time of a motion event */
gdouble lastx, lasty; /* previous position of a motion event */
gboolean init_velocity;
}; };
typedef struct _InkOptions InkOptions; typedef struct _InkOptions InkOptions;
@ -71,6 +84,10 @@ struct _InkOptions
double sensitivity_d; double sensitivity_d;
GtkObject *sensitivity_w; GtkObject *sensitivity_w;
double vel_sensitivity;
double vel_sensitivity_d;
GtkObject *vel_sensitivity_w;
double tilt_sensitivity; double tilt_sensitivity;
double tilt_sensitivity_d; double tilt_sensitivity_d;
GtkObject *tilt_sensitivity_w; GtkObject *tilt_sensitivity_w;
@ -123,6 +140,13 @@ static void ink_motion (Tool *, GdkEventMotion *, gpointer);
static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer); static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer);
static void ink_control (Tool *, int, gpointer); static void ink_control (Tool *, int, gpointer);
static void time_smoother_add (InkTool* ink_tool, guint32 value);
static gdouble time_smoother_result (InkTool* ink_tool);
static void time_smoother_init (InkTool* ink_tool, guint32 initval);
static void dist_smoother_add (InkTool* ink_tool, gdouble value);
static gdouble dist_smoother_result (InkTool* ink_tool);
static void dist_smoother_init (InkTool* ink_tool, gdouble initval);
static InkOptions *create_ink_options (void); static InkOptions *create_ink_options (void);
static Argument *ink_invoker (Argument *); static Argument *ink_invoker (Argument *);
@ -201,6 +225,8 @@ reset_ink_options (void)
options->sensitivity_d); options->sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w),
options->tilt_sensitivity_d); options->tilt_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
options->vel_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
options->tilt_angle_d); options->tilt_angle_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE);
@ -227,16 +253,17 @@ create_ink_options (void)
/* the new options structure */ /* the new options structure */
options = (InkOptions *) g_malloc (sizeof (InkOptions)); options = (InkOptions *) g_malloc (sizeof (InkOptions));
options->size = options->size_d = 3.0; options->size = options->size_d = 4.4;
options->sensitivity = options->sensitivity_d = 1.0; options->sensitivity = options->sensitivity_d = 1.0;
options->tilt_sensitivity = options->tilt_sensitivity_d = 1.0; options->vel_sensitivity = options->vel_sensitivity_d = 0.8;
options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
options->tilt_angle = options->tilt_angle_d = 0.0; options->tilt_angle = options->tilt_angle_d = 0.0;
options->function = options->function_d = blob_ellipse; options->function = options->function_d = blob_ellipse;
options->aspect = options->aspect_d = 1.0; options->aspect = options->aspect_d = 1.0;
options->angle = options->angle_d = 0.0; options->angle = options->angle_d = 0.0;
/* the main table */ /* the main table */
table = gtk_table_new (7, 2, FALSE); table = gtk_table_new (8, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1);
gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2);
@ -308,14 +335,15 @@ create_ink_options (void)
&options->tilt_sensitivity); &options->tilt_sensitivity);
gtk_widget_show (slider); gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle")); /* velocity sens slider */
label = gtk_label_new (_("Speed"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label); gtk_widget_show (label);
label = gtk_label_new (_("Adjust:")); label = gtk_label_new (_("Sensitivity:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
@ -326,6 +354,36 @@ create_ink_options (void)
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox); gtk_widget_show (abox);
options->vel_sensitivity_w =
gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w));
gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
gtk_container_add (GTK_CONTAINER (abox), slider);
gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed",
(GtkSignalFunc) ink_scale_update,
&options->vel_sensitivity);
gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
label = gtk_label_new (_("Adjust:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox);
options->tilt_angle_w = options->tilt_angle_w =
gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w));
@ -337,8 +395,9 @@ create_ink_options (void)
&options->tilt_angle); &options->tilt_angle);
/* Brush type radiobuttons */ /* Brush type radiobuttons */
frame = gtk_frame_new (_("Type")); frame = gtk_frame_new (_("Type"));
gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 6, 7, gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 8, 9,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
@ -398,7 +457,7 @@ create_ink_options (void)
/* Brush shape widget */ /* Brush shape widget */
frame = gtk_frame_new (_("Shape")); frame = gtk_frame_new (_("Shape"));
gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 6, 7); gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 8, 9);
gtk_widget_show (frame); gtk_widget_show (frame);
vbox = gtk_vbox_new (FALSE, 2); vbox = gtk_vbox_new (FALSE, 2);
@ -644,9 +703,11 @@ paint_blob (GdkDrawable *drawable, GdkGC *gc,
} }
} }
static Blob * static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center, ink_pen_ellipse (gdouble x_center, gdouble y_center,
gdouble pressure, gdouble xtilt, gdouble ytilt) gdouble pressure, gdouble xtilt, gdouble ytilt,
gdouble velocity)
{ {
double size; double size;
double tsin, tcos; double tsin, tcos;
@ -656,10 +717,38 @@ ink_pen_ellipse (gdouble x_center, gdouble y_center,
double tscale_c; double tscale_c;
double tscale_s; double tscale_s;
size = ink_options->size * (1 + ink_options->sensitivity * (2*pressure - 1)); /* Adjust the size depending on pressure. */
if (size*SUBSAMPLE < 1) size = 1/SUBSAMPLE;
/* Add brush angle/aspect to title vectorially */ size = ink_options->size * (1.0 + ink_options->sensitivity *
(2.0 * pressure - 1.0) );
/* Adjust the size further depending on pointer velocity
and velocity-sensitivity. These 'magic constants' are
'feels natural' tigert-approved. --ADM */
if (velocity < 3.0)
velocity = 3.0;
#ifdef VERBOSE
g_print("%f (%f) -> ", (float)size, (float)velocity);
#endif
size = ink_options->vel_sensitivity *
((4.5 * size) / (1.0 + ink_options->vel_sensitivity * (2.0*(velocity))))
+ (1.0 - ink_options->vel_sensitivity) * size;
#ifdef VERBOSE
g_print("%f\n", (float)size);
#endif
/* Clamp resulting size to sane limits */
if (size > ink_options->size * (1.0 + ink_options->sensitivity))
size = ink_options->size * (1.0 + ink_options->sensitivity);
if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;
/* Add brush angle/aspect to tilt vectorially */
/* I'm not happy with the way the brush widget info is combined with /* I'm not happy with the way the brush widget info is combined with
tilt info from the brush. My personal feeling is that representing tilt info from the brush. My personal feeling is that representing
@ -745,11 +834,18 @@ ink_button_press (Tool *tool,
tool->state = ACTIVE; tool->state = ACTIVE;
b = ink_pen_ellipse (x, y, b = ink_pen_ellipse (x, y,
bevent->pressure, bevent->xtilt, bevent->ytilt); bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
ink_paste (ink_tool, drawable, b); ink_paste (ink_tool, drawable, b);
ink_tool->last_blob = b; ink_tool->last_blob = b;
time_smoother_init (ink_tool, bevent->time);
ink_tool->last_time = bevent->time;
dist_smoother_init (ink_tool, 0.0);
ink_tool->init_velocity = TRUE;
ink_tool->lastx = x;
ink_tool->lasty = y;
gdisplay_flush_now (gdisp); gdisplay_flush_now (gdisp);
} }
@ -779,6 +875,88 @@ ink_button_release (Tool *tool,
gdisplays_flush (); gdisplays_flush ();
} }
static void
dist_smoother_init (InkTool* ink_tool, gdouble initval)
{
gint i;
ink_tool->dt_index = 0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
ink_tool->dt_buffer[i] = initval;
}
}
static gdouble
dist_smoother_result (InkTool* ink_tool)
{
gint i;
gdouble result = 0.0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
result += ink_tool->dt_buffer[i];
}
return (result / (gdouble)DIST_SMOOTHER_BUFFER);
}
static void
dist_smoother_add (InkTool* ink_tool, gdouble value)
{
ink_tool->dt_buffer[ink_tool->dt_index] = value;
if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
ink_tool->dt_index = 0;
}
static void
time_smoother_init (InkTool* ink_tool, guint32 initval)
{
gint i;
ink_tool->ts_index = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
ink_tool->ts_buffer[i] = initval;
}
}
static gdouble
time_smoother_result (InkTool* ink_tool)
{
gint i;
guint64 result = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
result += ink_tool->ts_buffer[i];
}
return (result / TIME_SMOOTHER_BUFFER);
}
static void
time_smoother_add (InkTool* ink_tool, guint32 value)
{
ink_tool->ts_buffer[ink_tool->ts_index] = value;
if ((++ink_tool->ts_index) == TIME_SMOOTHER_BUFFER)
ink_tool->ts_index = 0;
}
static void static void
ink_motion (Tool *tool, ink_motion (Tool *tool,
GdkEventMotion *mevent, GdkEventMotion *mevent,
@ -790,6 +968,11 @@ ink_motion (Tool *tool,
Blob *b, *blob_union; Blob *b, *blob_union;
double x, y; double x, y;
double pressure;
double velocity;
double dist;
gdouble lasttime, thistime;
gdisp = (GDisplay *) gdisp_ptr; gdisp = (GDisplay *) gdisp_ptr;
ink_tool = (InkTool *) tool->private; ink_tool = (InkTool *) tool->private;
@ -797,8 +980,42 @@ ink_motion (Tool *tool,
gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE);
drawable = gimage_active_drawable (gdisp->gimage); drawable = gimage_active_drawable (gdisp->gimage);
b = ink_pen_ellipse (x, y, lasttime = ink_tool->last_time;
mevent->pressure, mevent->xtilt, mevent->ytilt);
time_smoother_add (ink_tool, mevent->time);
thistime = ink_tool->last_time =
time_smoother_result(ink_tool);
/* The time resolution on X-based GDK motion events is
bloody awful, hence the use of the smoothing function.
Sadly this also means that there is always the chance of
having an indeterminite velocity since this event and
the previous several may still appear to issue at the same
instant. -ADM */
if (thistime == lasttime)
thistime = lasttime + 1;
if (ink_tool->init_velocity)
{
dist_smoother_init (ink_tool, dist = sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
ink_tool->init_velocity = FALSE;
}
else
{
dist_smoother_add (ink_tool, sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
dist = dist_smoother_result(ink_tool);
}
ink_tool->lastx = x;
ink_tool->lasty = y;
pressure = mevent->pressure;
velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime));
b = ink_pen_ellipse (x, y, pressure, mevent->xtilt, mevent->ytilt, velocity);
blob_union = blob_convex_union (ink_tool->last_blob, b); blob_union = blob_convex_union (ink_tool->last_blob, b);
g_free (ink_tool->last_blob); g_free (ink_tool->last_blob);
ink_tool->last_blob = b; ink_tool->last_blob = b;

View File

@ -44,6 +44,9 @@
#define SUBSAMPLE 8 #define SUBSAMPLE 8
#define DIST_SMOOTHER_BUFFER 10
#define TIME_SMOOTHER_BUFFER 10
/* the Ink structures */ /* the Ink structures */
typedef Blob *(*BlobFunc) (double, double, double, double, double, double); typedef Blob *(*BlobFunc) (double, double, double, double, double, double);
@ -58,6 +61,16 @@ struct _InkTool
int x1, y1; /* image space coordinate */ int x1, y1; /* image space coordinate */
int x2, y2; /* image space coords */ int x2, y2; /* image space coords */
gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; /* circular distance history buffer */
gint dt_index;
guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; /* circular timing history buffer */
gint ts_index;
gdouble last_time; /* previous time of a motion event */
gdouble lastx, lasty; /* previous position of a motion event */
gboolean init_velocity;
}; };
typedef struct _InkOptions InkOptions; typedef struct _InkOptions InkOptions;
@ -71,6 +84,10 @@ struct _InkOptions
double sensitivity_d; double sensitivity_d;
GtkObject *sensitivity_w; GtkObject *sensitivity_w;
double vel_sensitivity;
double vel_sensitivity_d;
GtkObject *vel_sensitivity_w;
double tilt_sensitivity; double tilt_sensitivity;
double tilt_sensitivity_d; double tilt_sensitivity_d;
GtkObject *tilt_sensitivity_w; GtkObject *tilt_sensitivity_w;
@ -123,6 +140,13 @@ static void ink_motion (Tool *, GdkEventMotion *, gpointer);
static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer); static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer);
static void ink_control (Tool *, int, gpointer); static void ink_control (Tool *, int, gpointer);
static void time_smoother_add (InkTool* ink_tool, guint32 value);
static gdouble time_smoother_result (InkTool* ink_tool);
static void time_smoother_init (InkTool* ink_tool, guint32 initval);
static void dist_smoother_add (InkTool* ink_tool, gdouble value);
static gdouble dist_smoother_result (InkTool* ink_tool);
static void dist_smoother_init (InkTool* ink_tool, gdouble initval);
static InkOptions *create_ink_options (void); static InkOptions *create_ink_options (void);
static Argument *ink_invoker (Argument *); static Argument *ink_invoker (Argument *);
@ -201,6 +225,8 @@ reset_ink_options (void)
options->sensitivity_d); options->sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w),
options->tilt_sensitivity_d); options->tilt_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
options->vel_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
options->tilt_angle_d); options->tilt_angle_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE);
@ -227,16 +253,17 @@ create_ink_options (void)
/* the new options structure */ /* the new options structure */
options = (InkOptions *) g_malloc (sizeof (InkOptions)); options = (InkOptions *) g_malloc (sizeof (InkOptions));
options->size = options->size_d = 3.0; options->size = options->size_d = 4.4;
options->sensitivity = options->sensitivity_d = 1.0; options->sensitivity = options->sensitivity_d = 1.0;
options->tilt_sensitivity = options->tilt_sensitivity_d = 1.0; options->vel_sensitivity = options->vel_sensitivity_d = 0.8;
options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
options->tilt_angle = options->tilt_angle_d = 0.0; options->tilt_angle = options->tilt_angle_d = 0.0;
options->function = options->function_d = blob_ellipse; options->function = options->function_d = blob_ellipse;
options->aspect = options->aspect_d = 1.0; options->aspect = options->aspect_d = 1.0;
options->angle = options->angle_d = 0.0; options->angle = options->angle_d = 0.0;
/* the main table */ /* the main table */
table = gtk_table_new (7, 2, FALSE); table = gtk_table_new (8, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1);
gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2);
@ -308,14 +335,15 @@ create_ink_options (void)
&options->tilt_sensitivity); &options->tilt_sensitivity);
gtk_widget_show (slider); gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle")); /* velocity sens slider */
label = gtk_label_new (_("Speed"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label); gtk_widget_show (label);
label = gtk_label_new (_("Adjust:")); label = gtk_label_new (_("Sensitivity:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
@ -326,6 +354,36 @@ create_ink_options (void)
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox); gtk_widget_show (abox);
options->vel_sensitivity_w =
gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w));
gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
gtk_container_add (GTK_CONTAINER (abox), slider);
gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed",
(GtkSignalFunc) ink_scale_update,
&options->vel_sensitivity);
gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
label = gtk_label_new (_("Adjust:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox);
options->tilt_angle_w = options->tilt_angle_w =
gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w));
@ -337,8 +395,9 @@ create_ink_options (void)
&options->tilt_angle); &options->tilt_angle);
/* Brush type radiobuttons */ /* Brush type radiobuttons */
frame = gtk_frame_new (_("Type")); frame = gtk_frame_new (_("Type"));
gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 6, 7, gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 8, 9,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
@ -398,7 +457,7 @@ create_ink_options (void)
/* Brush shape widget */ /* Brush shape widget */
frame = gtk_frame_new (_("Shape")); frame = gtk_frame_new (_("Shape"));
gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 6, 7); gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 8, 9);
gtk_widget_show (frame); gtk_widget_show (frame);
vbox = gtk_vbox_new (FALSE, 2); vbox = gtk_vbox_new (FALSE, 2);
@ -644,9 +703,11 @@ paint_blob (GdkDrawable *drawable, GdkGC *gc,
} }
} }
static Blob * static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center, ink_pen_ellipse (gdouble x_center, gdouble y_center,
gdouble pressure, gdouble xtilt, gdouble ytilt) gdouble pressure, gdouble xtilt, gdouble ytilt,
gdouble velocity)
{ {
double size; double size;
double tsin, tcos; double tsin, tcos;
@ -656,10 +717,38 @@ ink_pen_ellipse (gdouble x_center, gdouble y_center,
double tscale_c; double tscale_c;
double tscale_s; double tscale_s;
size = ink_options->size * (1 + ink_options->sensitivity * (2*pressure - 1)); /* Adjust the size depending on pressure. */
if (size*SUBSAMPLE < 1) size = 1/SUBSAMPLE;
/* Add brush angle/aspect to title vectorially */ size = ink_options->size * (1.0 + ink_options->sensitivity *
(2.0 * pressure - 1.0) );
/* Adjust the size further depending on pointer velocity
and velocity-sensitivity. These 'magic constants' are
'feels natural' tigert-approved. --ADM */
if (velocity < 3.0)
velocity = 3.0;
#ifdef VERBOSE
g_print("%f (%f) -> ", (float)size, (float)velocity);
#endif
size = ink_options->vel_sensitivity *
((4.5 * size) / (1.0 + ink_options->vel_sensitivity * (2.0*(velocity))))
+ (1.0 - ink_options->vel_sensitivity) * size;
#ifdef VERBOSE
g_print("%f\n", (float)size);
#endif
/* Clamp resulting size to sane limits */
if (size > ink_options->size * (1.0 + ink_options->sensitivity))
size = ink_options->size * (1.0 + ink_options->sensitivity);
if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;
/* Add brush angle/aspect to tilt vectorially */
/* I'm not happy with the way the brush widget info is combined with /* I'm not happy with the way the brush widget info is combined with
tilt info from the brush. My personal feeling is that representing tilt info from the brush. My personal feeling is that representing
@ -745,11 +834,18 @@ ink_button_press (Tool *tool,
tool->state = ACTIVE; tool->state = ACTIVE;
b = ink_pen_ellipse (x, y, b = ink_pen_ellipse (x, y,
bevent->pressure, bevent->xtilt, bevent->ytilt); bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
ink_paste (ink_tool, drawable, b); ink_paste (ink_tool, drawable, b);
ink_tool->last_blob = b; ink_tool->last_blob = b;
time_smoother_init (ink_tool, bevent->time);
ink_tool->last_time = bevent->time;
dist_smoother_init (ink_tool, 0.0);
ink_tool->init_velocity = TRUE;
ink_tool->lastx = x;
ink_tool->lasty = y;
gdisplay_flush_now (gdisp); gdisplay_flush_now (gdisp);
} }
@ -779,6 +875,88 @@ ink_button_release (Tool *tool,
gdisplays_flush (); gdisplays_flush ();
} }
static void
dist_smoother_init (InkTool* ink_tool, gdouble initval)
{
gint i;
ink_tool->dt_index = 0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
ink_tool->dt_buffer[i] = initval;
}
}
static gdouble
dist_smoother_result (InkTool* ink_tool)
{
gint i;
gdouble result = 0.0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
result += ink_tool->dt_buffer[i];
}
return (result / (gdouble)DIST_SMOOTHER_BUFFER);
}
static void
dist_smoother_add (InkTool* ink_tool, gdouble value)
{
ink_tool->dt_buffer[ink_tool->dt_index] = value;
if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
ink_tool->dt_index = 0;
}
static void
time_smoother_init (InkTool* ink_tool, guint32 initval)
{
gint i;
ink_tool->ts_index = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
ink_tool->ts_buffer[i] = initval;
}
}
static gdouble
time_smoother_result (InkTool* ink_tool)
{
gint i;
guint64 result = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
result += ink_tool->ts_buffer[i];
}
return (result / TIME_SMOOTHER_BUFFER);
}
static void
time_smoother_add (InkTool* ink_tool, guint32 value)
{
ink_tool->ts_buffer[ink_tool->ts_index] = value;
if ((++ink_tool->ts_index) == TIME_SMOOTHER_BUFFER)
ink_tool->ts_index = 0;
}
static void static void
ink_motion (Tool *tool, ink_motion (Tool *tool,
GdkEventMotion *mevent, GdkEventMotion *mevent,
@ -790,6 +968,11 @@ ink_motion (Tool *tool,
Blob *b, *blob_union; Blob *b, *blob_union;
double x, y; double x, y;
double pressure;
double velocity;
double dist;
gdouble lasttime, thistime;
gdisp = (GDisplay *) gdisp_ptr; gdisp = (GDisplay *) gdisp_ptr;
ink_tool = (InkTool *) tool->private; ink_tool = (InkTool *) tool->private;
@ -797,8 +980,42 @@ ink_motion (Tool *tool,
gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE);
drawable = gimage_active_drawable (gdisp->gimage); drawable = gimage_active_drawable (gdisp->gimage);
b = ink_pen_ellipse (x, y, lasttime = ink_tool->last_time;
mevent->pressure, mevent->xtilt, mevent->ytilt);
time_smoother_add (ink_tool, mevent->time);
thistime = ink_tool->last_time =
time_smoother_result(ink_tool);
/* The time resolution on X-based GDK motion events is
bloody awful, hence the use of the smoothing function.
Sadly this also means that there is always the chance of
having an indeterminite velocity since this event and
the previous several may still appear to issue at the same
instant. -ADM */
if (thistime == lasttime)
thistime = lasttime + 1;
if (ink_tool->init_velocity)
{
dist_smoother_init (ink_tool, dist = sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
ink_tool->init_velocity = FALSE;
}
else
{
dist_smoother_add (ink_tool, sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
dist = dist_smoother_result(ink_tool);
}
ink_tool->lastx = x;
ink_tool->lasty = y;
pressure = mevent->pressure;
velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime));
b = ink_pen_ellipse (x, y, pressure, mevent->xtilt, mevent->ytilt, velocity);
blob_union = blob_convex_union (ink_tool->last_blob, b); blob_union = blob_convex_union (ink_tool->last_blob, b);
g_free (ink_tool->last_blob); g_free (ink_tool->last_blob);
ink_tool->last_blob = b; ink_tool->last_blob = b;

View File

@ -44,6 +44,9 @@
#define SUBSAMPLE 8 #define SUBSAMPLE 8
#define DIST_SMOOTHER_BUFFER 10
#define TIME_SMOOTHER_BUFFER 10
/* the Ink structures */ /* the Ink structures */
typedef Blob *(*BlobFunc) (double, double, double, double, double, double); typedef Blob *(*BlobFunc) (double, double, double, double, double, double);
@ -58,6 +61,16 @@ struct _InkTool
int x1, y1; /* image space coordinate */ int x1, y1; /* image space coordinate */
int x2, y2; /* image space coords */ int x2, y2; /* image space coords */
gdouble dt_buffer[DIST_SMOOTHER_BUFFER]; /* circular distance history buffer */
gint dt_index;
guint32 ts_buffer[TIME_SMOOTHER_BUFFER]; /* circular timing history buffer */
gint ts_index;
gdouble last_time; /* previous time of a motion event */
gdouble lastx, lasty; /* previous position of a motion event */
gboolean init_velocity;
}; };
typedef struct _InkOptions InkOptions; typedef struct _InkOptions InkOptions;
@ -71,6 +84,10 @@ struct _InkOptions
double sensitivity_d; double sensitivity_d;
GtkObject *sensitivity_w; GtkObject *sensitivity_w;
double vel_sensitivity;
double vel_sensitivity_d;
GtkObject *vel_sensitivity_w;
double tilt_sensitivity; double tilt_sensitivity;
double tilt_sensitivity_d; double tilt_sensitivity_d;
GtkObject *tilt_sensitivity_w; GtkObject *tilt_sensitivity_w;
@ -123,6 +140,13 @@ static void ink_motion (Tool *, GdkEventMotion *, gpointer);
static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer); static void ink_cursor_update (Tool *, GdkEventMotion *, gpointer);
static void ink_control (Tool *, int, gpointer); static void ink_control (Tool *, int, gpointer);
static void time_smoother_add (InkTool* ink_tool, guint32 value);
static gdouble time_smoother_result (InkTool* ink_tool);
static void time_smoother_init (InkTool* ink_tool, guint32 initval);
static void dist_smoother_add (InkTool* ink_tool, gdouble value);
static gdouble dist_smoother_result (InkTool* ink_tool);
static void dist_smoother_init (InkTool* ink_tool, gdouble initval);
static InkOptions *create_ink_options (void); static InkOptions *create_ink_options (void);
static Argument *ink_invoker (Argument *); static Argument *ink_invoker (Argument *);
@ -201,6 +225,8 @@ reset_ink_options (void)
options->sensitivity_d); options->sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_sensitivity_w),
options->tilt_sensitivity_d); options->tilt_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->vel_sensitivity_w),
options->vel_sensitivity_d);
gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w), gtk_adjustment_set_value (GTK_ADJUSTMENT (options->tilt_angle_w),
options->tilt_angle_d); options->tilt_angle_d);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->function_w), TRUE);
@ -227,16 +253,17 @@ create_ink_options (void)
/* the new options structure */ /* the new options structure */
options = (InkOptions *) g_malloc (sizeof (InkOptions)); options = (InkOptions *) g_malloc (sizeof (InkOptions));
options->size = options->size_d = 3.0; options->size = options->size_d = 4.4;
options->sensitivity = options->sensitivity_d = 1.0; options->sensitivity = options->sensitivity_d = 1.0;
options->tilt_sensitivity = options->tilt_sensitivity_d = 1.0; options->vel_sensitivity = options->vel_sensitivity_d = 0.8;
options->tilt_sensitivity = options->tilt_sensitivity_d = 0.4;
options->tilt_angle = options->tilt_angle_d = 0.0; options->tilt_angle = options->tilt_angle_d = 0.0;
options->function = options->function_d = blob_ellipse; options->function = options->function_d = blob_ellipse;
options->aspect = options->aspect_d = 1.0; options->aspect = options->aspect_d = 1.0;
options->angle = options->angle_d = 0.0; options->angle = options->angle_d = 0.0;
/* the main table */ /* the main table */
table = gtk_table_new (7, 2, FALSE); table = gtk_table_new (8, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 6);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1); gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1);
gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2); gtk_table_set_row_spacing (GTK_TABLE (table), 1, 2);
@ -308,14 +335,15 @@ create_ink_options (void)
&options->tilt_sensitivity); &options->tilt_sensitivity);
gtk_widget_show (slider); gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle")); /* velocity sens slider */
label = gtk_label_new (_("Speed"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label); gtk_widget_show (label);
label = gtk_label_new (_("Adjust:")); label = gtk_label_new (_("Sensitivity:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
@ -326,6 +354,36 @@ create_ink_options (void)
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox); gtk_widget_show (abox);
options->vel_sensitivity_w =
gtk_adjustment_new (options->vel_sensitivity_d, 0.0, 1.0, 0.01, 0.1, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->vel_sensitivity_w));
gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_TOP);
gtk_container_add (GTK_CONTAINER (abox), slider);
gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED);
gtk_signal_connect (GTK_OBJECT (options->vel_sensitivity_w), "value_changed",
(GtkSignalFunc) ink_scale_update,
&options->vel_sensitivity);
gtk_widget_show (slider);
/* angle adjust slider */
label = gtk_label_new (_("Angle"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
label = gtk_label_new (_("Adjust:"));
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8,
GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
gtk_widget_show (label);
abox = gtk_alignment_new (0.5, 1.0, 1.0, 0.0);
gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 6, 8,
GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (abox);
options->tilt_angle_w = options->tilt_angle_w =
gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0); gtk_adjustment_new (options->tilt_angle_d, -90.0, 90.0, 1, 10.0, 0.0);
slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w)); slider = gtk_hscale_new (GTK_ADJUSTMENT (options->tilt_angle_w));
@ -337,8 +395,9 @@ create_ink_options (void)
&options->tilt_angle); &options->tilt_angle);
/* Brush type radiobuttons */ /* Brush type radiobuttons */
frame = gtk_frame_new (_("Type")); frame = gtk_frame_new (_("Type"));
gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 6, 7, gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 8, 9,
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
@ -398,7 +457,7 @@ create_ink_options (void)
/* Brush shape widget */ /* Brush shape widget */
frame = gtk_frame_new (_("Shape")); frame = gtk_frame_new (_("Shape"));
gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 6, 7); gtk_table_attach_defaults (GTK_TABLE (table), frame, 1, 2, 8, 9);
gtk_widget_show (frame); gtk_widget_show (frame);
vbox = gtk_vbox_new (FALSE, 2); vbox = gtk_vbox_new (FALSE, 2);
@ -644,9 +703,11 @@ paint_blob (GdkDrawable *drawable, GdkGC *gc,
} }
} }
static Blob * static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center, ink_pen_ellipse (gdouble x_center, gdouble y_center,
gdouble pressure, gdouble xtilt, gdouble ytilt) gdouble pressure, gdouble xtilt, gdouble ytilt,
gdouble velocity)
{ {
double size; double size;
double tsin, tcos; double tsin, tcos;
@ -656,10 +717,38 @@ ink_pen_ellipse (gdouble x_center, gdouble y_center,
double tscale_c; double tscale_c;
double tscale_s; double tscale_s;
size = ink_options->size * (1 + ink_options->sensitivity * (2*pressure - 1)); /* Adjust the size depending on pressure. */
if (size*SUBSAMPLE < 1) size = 1/SUBSAMPLE;
/* Add brush angle/aspect to title vectorially */ size = ink_options->size * (1.0 + ink_options->sensitivity *
(2.0 * pressure - 1.0) );
/* Adjust the size further depending on pointer velocity
and velocity-sensitivity. These 'magic constants' are
'feels natural' tigert-approved. --ADM */
if (velocity < 3.0)
velocity = 3.0;
#ifdef VERBOSE
g_print("%f (%f) -> ", (float)size, (float)velocity);
#endif
size = ink_options->vel_sensitivity *
((4.5 * size) / (1.0 + ink_options->vel_sensitivity * (2.0*(velocity))))
+ (1.0 - ink_options->vel_sensitivity) * size;
#ifdef VERBOSE
g_print("%f\n", (float)size);
#endif
/* Clamp resulting size to sane limits */
if (size > ink_options->size * (1.0 + ink_options->sensitivity))
size = ink_options->size * (1.0 + ink_options->sensitivity);
if (size*SUBSAMPLE < 1.0) size = 1.0/SUBSAMPLE;
/* Add brush angle/aspect to tilt vectorially */
/* I'm not happy with the way the brush widget info is combined with /* I'm not happy with the way the brush widget info is combined with
tilt info from the brush. My personal feeling is that representing tilt info from the brush. My personal feeling is that representing
@ -745,11 +834,18 @@ ink_button_press (Tool *tool,
tool->state = ACTIVE; tool->state = ACTIVE;
b = ink_pen_ellipse (x, y, b = ink_pen_ellipse (x, y,
bevent->pressure, bevent->xtilt, bevent->ytilt); bevent->pressure, bevent->xtilt, bevent->ytilt, 10.0);
ink_paste (ink_tool, drawable, b); ink_paste (ink_tool, drawable, b);
ink_tool->last_blob = b; ink_tool->last_blob = b;
time_smoother_init (ink_tool, bevent->time);
ink_tool->last_time = bevent->time;
dist_smoother_init (ink_tool, 0.0);
ink_tool->init_velocity = TRUE;
ink_tool->lastx = x;
ink_tool->lasty = y;
gdisplay_flush_now (gdisp); gdisplay_flush_now (gdisp);
} }
@ -779,6 +875,88 @@ ink_button_release (Tool *tool,
gdisplays_flush (); gdisplays_flush ();
} }
static void
dist_smoother_init (InkTool* ink_tool, gdouble initval)
{
gint i;
ink_tool->dt_index = 0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
ink_tool->dt_buffer[i] = initval;
}
}
static gdouble
dist_smoother_result (InkTool* ink_tool)
{
gint i;
gdouble result = 0.0;
for (i=0; i<DIST_SMOOTHER_BUFFER; i++)
{
result += ink_tool->dt_buffer[i];
}
return (result / (gdouble)DIST_SMOOTHER_BUFFER);
}
static void
dist_smoother_add (InkTool* ink_tool, gdouble value)
{
ink_tool->dt_buffer[ink_tool->dt_index] = value;
if ((++ink_tool->dt_index) == DIST_SMOOTHER_BUFFER)
ink_tool->dt_index = 0;
}
static void
time_smoother_init (InkTool* ink_tool, guint32 initval)
{
gint i;
ink_tool->ts_index = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
ink_tool->ts_buffer[i] = initval;
}
}
static gdouble
time_smoother_result (InkTool* ink_tool)
{
gint i;
guint64 result = 0;
for (i=0; i<TIME_SMOOTHER_BUFFER; i++)
{
result += ink_tool->ts_buffer[i];
}
return (result / TIME_SMOOTHER_BUFFER);
}
static void
time_smoother_add (InkTool* ink_tool, guint32 value)
{
ink_tool->ts_buffer[ink_tool->ts_index] = value;
if ((++ink_tool->ts_index) == TIME_SMOOTHER_BUFFER)
ink_tool->ts_index = 0;
}
static void static void
ink_motion (Tool *tool, ink_motion (Tool *tool,
GdkEventMotion *mevent, GdkEventMotion *mevent,
@ -790,6 +968,11 @@ ink_motion (Tool *tool,
Blob *b, *blob_union; Blob *b, *blob_union;
double x, y; double x, y;
double pressure;
double velocity;
double dist;
gdouble lasttime, thistime;
gdisp = (GDisplay *) gdisp_ptr; gdisp = (GDisplay *) gdisp_ptr;
ink_tool = (InkTool *) tool->private; ink_tool = (InkTool *) tool->private;
@ -797,8 +980,42 @@ ink_motion (Tool *tool,
gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE); gdisplay_untransform_coords_f (gdisp, mevent->x, mevent->y, &x, &y, TRUE);
drawable = gimage_active_drawable (gdisp->gimage); drawable = gimage_active_drawable (gdisp->gimage);
b = ink_pen_ellipse (x, y, lasttime = ink_tool->last_time;
mevent->pressure, mevent->xtilt, mevent->ytilt);
time_smoother_add (ink_tool, mevent->time);
thistime = ink_tool->last_time =
time_smoother_result(ink_tool);
/* The time resolution on X-based GDK motion events is
bloody awful, hence the use of the smoothing function.
Sadly this also means that there is always the chance of
having an indeterminite velocity since this event and
the previous several may still appear to issue at the same
instant. -ADM */
if (thistime == lasttime)
thistime = lasttime + 1;
if (ink_tool->init_velocity)
{
dist_smoother_init (ink_tool, dist = sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
ink_tool->init_velocity = FALSE;
}
else
{
dist_smoother_add (ink_tool, sqrt((ink_tool->lastx-x)*(ink_tool->lastx-x) +
(ink_tool->lasty-y)*(ink_tool->lasty-y)));
dist = dist_smoother_result(ink_tool);
}
ink_tool->lastx = x;
ink_tool->lasty = y;
pressure = mevent->pressure;
velocity = 10.0 * sqrt((dist) / (double)(thistime - lasttime));
b = ink_pen_ellipse (x, y, pressure, mevent->xtilt, mevent->ytilt, velocity);
blob_union = blob_convex_union (ink_tool->last_blob, b); blob_union = blob_convex_union (ink_tool->last_blob, b);
g_free (ink_tool->last_blob); g_free (ink_tool->last_blob);
ink_tool->last_blob = b; ink_tool->last_blob = b;