Esempio n. 1
0
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;
    }
}
Esempio n. 2
0
/**
 * 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;
}