static void gimp_paint_core_stroke_emulate_dynamics (GimpCoords *coords, gint length) { const gint ramp_length = length / 3; /* Calculate and create pressure ramp parameters */ if (ramp_length > 0) { gdouble slope = 1.0 / (gdouble) (ramp_length); gint i; /* Calculate pressure start ramp */ for (i = 0; i < ramp_length; i++) { coords[i].pressure = i * slope; } /* Calculate pressure end ramp */ for (i = length - ramp_length; i < length; i++) { coords[i].pressure = 1.0 - (i - (length - ramp_length)) * slope; } } /* Calculate and create velocity ramp parameters */ if (length > 0) { gdouble slope = 1.0 / length; gint i; /* Calculate velocity end ramp */ for (i = 0; i < length; i++) { coords[i].velocity = i * slope; } } if (length > 1) { gint i; /* Fill in direction */ for (i = 1; i < length; i++) { coords[i].direction = gimp_coords_direction (&coords[i-1], &coords[i]); } coords[0].direction = coords[1].direction; } }
/** * gimp_motion_buffer_motion_event: * @buffer: * @coords: * @time: * @event_fill: * * This function evaluates the event to decide if the change is big * enough to need handling and returns FALSE, if change is less than * set filter level taking a whole lot of load off any draw tools that * have no use for these events anyway. If the event is seen fit at * first look, it is evaluated for speed and smoothed. Due to lousy * time resolution of events pretty strong smoothing is applied to * timestamps for sensible speed result. This function is also ideal * for other event adjustment like pressure curve or calculating other * derived dynamics factors like angular velocity calculation from * tilt values, to allow for even more dynamic brushes. Calculated * distance to last event is stored in GimpCoords because its a * sideproduct of velocity calculation and is currently calculated in * each tool. If they were to use this distance, more resouces on * recalculating the same value would be saved. * * Return value: %TRUE if the motion was significant enough to be * processed, %FALSE otherwise. **/ gboolean gimp_motion_buffer_motion_event (GimpMotionBuffer *buffer, GimpCoords *coords, guint32 time, gboolean event_fill) { gdouble delta_time = 0.001; gdouble delta_x = 0.0; gdouble delta_y = 0.0; gdouble distance = 1.0; gdouble scale_x = coords->xscale; gdouble scale_y = coords->yscale; g_return_val_if_fail (GIMP_IS_MOTION_BUFFER (buffer), FALSE); g_return_val_if_fail (coords != NULL, FALSE); /* the last_read_motion_time most be set unconditionally, so set * it early */ buffer->last_read_motion_time = time; delta_time = (buffer->last_motion_delta_time * (1 - SMOOTH_FACTOR) + (time - buffer->last_motion_time) * SMOOTH_FACTOR); if (buffer->last_motion_time == 0) { /* First pair is invalid to do any velocity calculation, so we * apply a constant value. */ coords->velocity = 1.0; } else { GimpCoords last_dir_event = buffer->last_coords; gdouble filter; gdouble dist; gdouble delta_dir; gdouble dir_delta_x = 0.0; gdouble dir_delta_y = 0.0; delta_x = last_dir_event.x - coords->x; delta_y = last_dir_event.y - coords->y; /* Events with distances less than the screen resolution are * not worth handling. */ filter = MIN (1.0 / scale_x, 1.0 / scale_y) / 2.0; if (fabs (delta_x) < filter && fabs (delta_y) < filter) { return FALSE; } distance = dist = sqrt (SQR (delta_x) + SQR (delta_y)); /* If even smoothed time resolution does not allow to guess for * speed, use last velocity. */ if (delta_time == 0) { coords->velocity = buffer->last_coords.velocity; } else { /* We need to calculate the velocity in screen coordinates * for human interaction */ gdouble screen_distance = (distance * MIN (scale_x, scale_y)); /* Calculate raw valocity */ coords->velocity = ((screen_distance / delta_time) / VELOCITY_UNIT); /* Adding velocity dependent smoothing, feels better in tools. */ coords->velocity = (buffer->last_coords.velocity * (1 - MIN (SMOOTH_FACTOR, coords->velocity)) + coords->velocity * MIN (SMOOTH_FACTOR, coords->velocity)); /* Speed needs upper limit */ coords->velocity = MIN (coords->velocity, 1.0); } if (((fabs (delta_x) > DIRECTION_RADIUS) && (fabs (delta_y) > DIRECTION_RADIUS)) || (buffer->event_history->len < 4)) { dir_delta_x = delta_x; dir_delta_y = delta_y; } else { gint x = CLAMP ((buffer->event_history->len - 1), 3, 15); while (((fabs (dir_delta_x) < DIRECTION_RADIUS) || (fabs (dir_delta_y) < DIRECTION_RADIUS)) && (x >= 0)) { last_dir_event = g_array_index (buffer->event_history, GimpCoords, x); dir_delta_x = last_dir_event.x - coords->x; dir_delta_y = last_dir_event.y - coords->y; x--; } } if ((fabs (dir_delta_x) < DIRECTION_RADIUS) || (fabs (dir_delta_y) < DIRECTION_RADIUS)) { coords->direction = buffer->last_coords.direction; } else { coords->direction = gimp_coords_direction (&last_dir_event, coords); } coords->direction = coords->direction - floor (coords->direction); delta_dir = coords->direction - buffer->last_coords.direction; if (delta_dir < -0.5) { coords->direction = (0.5 * coords->direction + 0.5 * (buffer->last_coords.direction - 1.0)); } else if (delta_dir > 0.5) { coords->direction = (0.5 * coords->direction + 0.5 * (buffer->last_coords.direction + 1.0)); } else { coords->direction = (0.5 * coords->direction + 0.5 * buffer->last_coords.direction); } coords->direction = coords->direction - floor (coords->direction); /* do event fill for devices that do not provide enough events */ if (distance >= EVENT_FILL_PRECISION && event_fill && buffer->event_history->len >= 2) { if (buffer->event_delay) { gimp_motion_buffer_interpolate_stroke (buffer, coords); } else { buffer->event_delay = TRUE; gimp_motion_buffer_push_event_history (buffer, coords); } } else { if (buffer->event_delay) buffer->event_delay = FALSE; gimp_motion_buffer_push_event_history (buffer, coords); } #ifdef EVENT_VERBOSE g_printerr ("DIST: %f, DT:%f, Vel:%f, Press:%f,smooth_dd:%f, POS: (%f, %f)\n", distance, delta_time, buffer->last_coords.velocity, coords->pressure, distance - dist, coords->x, coords->y); #endif } g_array_append_val (buffer->event_queue, *coords); buffer->last_coords = *coords; buffer->last_motion_time = time; buffer->last_motion_delta_time = delta_time; buffer->last_motion_delta_x = delta_x; buffer->last_motion_delta_y = delta_y; buffer->last_motion_distance = distance; return TRUE; }