/* find the most plausible velocity. That is, the most distant
 * (in time) tracker which isn't too old, beyond a linear partition,
 * or simply too much off initial velocity.
 *
 * May return 0.
 */
static float
QueryTrackers(DeviceVelocityPtr vel, int cur_t){
    int n, offset, dir = 255, i = -1, age_ms;
    /* initial velocity: a low-offset, valid velocity */
    float iveloc = 0, res = 0, tmp, vdiff;
    float vfac =  vel->corr_mul * vel->const_acceleration; /* premultiply */
    /* loop from current to older data */
    for(offset = 1; offset < vel->num_tracker; offset++){
	n = TRACKER_INDEX(vel, offset);

	age_ms = cur_t - vel->tracker[n].time;

	/* bail out if data is too old and protect from overrun */
	if (age_ms >= vel->reset_time || age_ms < 0) {
	    DebugAccelF("(dix prtacc) query: tracker too old\n");
	    break;
	}

	/*
	 * this heuristic avoids using the linear-motion velocity formula
	 * in CalcTracker() on motion that isn't exactly linear. So to get
	 * even more precision we could subdivide as a final step, so possible
	 * non-linearities are accounted for.
	 */
	dir &= vel->tracker[n].dir;
	if(dir == 0){
	    DebugAccelF("(dix prtacc) query: no longer linear\n");
	    /* instead of breaking it we might also inspect the partition after,
	     * but actual improvement with this is probably rare. */
	    break;
	}

	tmp = CalcTracker(vel, offset, cur_t) * vfac;

	if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) {
	    /* set initial velocity and result */
	    res = iveloc = tmp;
	    i = offset;
	} else if (iveloc != 0 && tmp != 0) {
	    vdiff = fabs(iveloc - tmp);
	    if (vdiff <= vel->max_diff ||
		vdiff/(iveloc + tmp) < vel->max_rel_diff) {
		/* we're in range with the initial velocity,
		 * so this result is likely better
		 * (it contains more information). */
		res = tmp;
		i = offset;
	    }else{
		/* we're not in range, quit - it won't get better. */
		DebugAccelF("(dix prtacc) query: tracker too different:"
		            " old %2.2f initial %2.2f diff: %2.2f\n",
		            tmp, iveloc, vdiff);
		break;
	    }
	}
    }
    if(offset == vel->num_tracker){
	DebugAccelF("(dix prtacc) query: last tracker in effect\n");
	i = vel->num_tracker-1;
    }
    if(i>=0){
        n = TRACKER_INDEX(vel, i);
	DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n",
	            i,
	            vel->tracker[n].dx,
	            vel->tracker[n].dy,
	            cur_t - vel->tracker[n].time);
    }
    return res;
}
/**
 * Modifies valuators in-place.
 * This version employs a velocity approximation algorithm to
 * enable fine-grained predictable acceleration profiles.
 */
void
acceleratePointerPredictable(
    DeviceIntPtr dev,
    ValuatorMask* val,
    CARD32 evtime)
{
    float fdx, fdy, tmp, mult; /* no need to init */
    int dx = 0, dy = 0, tmpi;
    DeviceVelocityPtr velocitydata = GetDevicePredictableAccelData(dev);
    Bool soften = TRUE;

    if (!velocitydata)
        return;

    if (velocitydata->statistics.profile_number == AccelProfileNone &&
        velocitydata->const_acceleration == 1.0f) {
        return; /*we're inactive anyway, so skip the whole thing.*/
    }

    if (valuator_mask_isset(val, 0)) {
        dx = valuator_mask_get(val, 0);
    }

    if (valuator_mask_isset(val, 1)) {
        dy = valuator_mask_get(val, 1);
    }

    if (dx || dy){
        /* reset non-visible state? */
        if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
            soften = FALSE;
        }

        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
            /* invoke acceleration profile to determine acceleration */
            mult = ComputeAcceleration (dev, velocitydata,
                                        dev->ptrfeed->ctrl.threshold,
                                        (float)dev->ptrfeed->ctrl.num /
                                            (float)dev->ptrfeed->ctrl.den);

            if(mult != 1.0f || velocitydata->const_acceleration != 1.0f) {
                ApplySofteningAndConstantDeceleration(velocitydata,
                                                      dx, dy,
                                                      &fdx, &fdy,
                                                      (mult > 1.0f) && soften);

                if (dx) {
                    tmp = mult * fdx + dev->last.remainder[0];
                    /* Since it may not be apparent: lrintf() does not offer
                     * strong statements about rounding; however because we
                     * process each axis conditionally, there's no danger
                     * of a toggling remainder. Its lack of guarantees likely
                     * makes it faster on the average target. */
                    tmpi = lrintf(tmp);
                    valuator_mask_set(val, 0, tmpi);
                    dev->last.remainder[0] = tmp - (float)tmpi;
                }
                if (dy) {
                    tmp = mult * fdy + dev->last.remainder[1];
                    tmpi = lrintf(tmp);
                    valuator_mask_set(val, 1, tmpi);
                    dev->last.remainder[1] = tmp - (float)tmpi;
                }
                DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n",
                            *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy);
            }
        }
    }
    /* remember last motion delta (for softening/slow movement treatment) */
    velocitydata->last_dx = dx;
    velocitydata->last_dy = dy;
}
Exemplo n.º 3
0
/**
 * Modifies valuators in-place.
 * This version employs a velocity approximation algorithm to
 * enable fine-grained predictable acceleration profiles.
 */
void
acceleratePointerPredictable(
    DeviceIntPtr dev,
    int first_valuator,
    int num_valuators,
    int *valuators,
    int evtime)
{
    float mult = 0.0;
    int dx = 0, dy = 0;
    int *px = NULL, *py = NULL;
    DeviceVelocityPtr velocitydata =
	(DeviceVelocityPtr) dev->valuator->accelScheme.accelData;
    float fdx, fdy, tmp; /* no need to init */
    Bool soften = TRUE;

    if (!num_valuators || !valuators || !velocitydata)
        return;

    if (velocitydata->statistics.profile_number == AccelProfileNone &&
	velocitydata->const_acceleration == 1.0f) {
	return; /*we're inactive anyway, so skip the whole thing.*/
    }

    if (first_valuator == 0) {
        dx = valuators[0];
        px = &valuators[0];
    }
    if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
        dy = valuators[1 - first_valuator];
        py = &valuators[1 - first_valuator];
    }

    if (dx || dy){
        /* reset non-visible state? */
        if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) {
            soften = FALSE;
        }

        if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
            /* invoke acceleration profile to determine acceleration */
            mult = ComputeAcceleration (dev, velocitydata,
					dev->ptrfeed->ctrl.threshold,
					(float)dev->ptrfeed->ctrl.num /
					(float)dev->ptrfeed->ctrl.den);

            if(mult != 1.0 || velocitydata->const_acceleration != 1.0) {
                ApplySofteningAndConstantDeceleration( velocitydata,
						       dx, dy,
						       &fdx, &fdy,
						       (mult > 1.0) && soften);

                if (dx) {
                    tmp = mult * fdx + dev->last.remainder[0];
                    /* Since it may not be apparent: lrintf() does not offer
                     * strong statements about rounding; however because we
                     * process each axis conditionally, there's no danger
                     * of a toggling remainder. Its lack of guarantees likely
                     * makes it faster on the average target. */
                    *px = lrintf(tmp);
                    dev->last.remainder[0] = tmp - (float)*px;
                }
                if (dy) {
                    tmp = mult * fdy + dev->last.remainder[1];
                    *py = lrintf(tmp);
                    dev->last.remainder[1] = tmp - (float)*py;
                }
                DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n",
                            *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy);
            }
        }
    }
    /* remember last motion delta (for softening/slow movement treatment) */
    velocitydata->last_dx = dx;
    velocitydata->last_dy = dy;
}
Exemplo n.º 4
0
/* find the most plausible velocity. That is, the most distant
 * (in time) tracker which isn't too old, the movement vector was
 * in the same octant, and where the velocity is within an
 * acceptable range to the inital velocity.
 *
 * @return The tracker's velocity or 0 if the above conditions are unmet
 */
static double
QueryTrackers(DeviceVelocityPtr vel, int cur_t)
{
    int offset, dir = UNDEFINED, used_offset = -1, age_ms;

    /* initial velocity: a low-offset, valid velocity */
    double initial_velocity = 0, result = 0, velocity_diff;
    double velocity_factor = vel->corr_mul * vel->const_acceleration;   /* premultiply */

    /* loop from current to older data */
    for (offset = 1; offset < vel->num_tracker; offset++) {
        MotionTracker *tracker = TRACKER(vel, offset);
        double tracker_velocity;

        age_ms = cur_t - tracker->time;

        /* bail out if data is too old and protect from overrun */
        if (age_ms >= vel->reset_time || age_ms < 0) {
            DebugAccelF("query: tracker too old (reset after %d, age is %d)\n",
                        vel->reset_time, age_ms);
            break;
        }

        /*
         * this heuristic avoids using the linear-motion velocity formula
         * in CalcTracker() on motion that isn't exactly linear. So to get
         * even more precision we could subdivide as a final step, so possible
         * non-linearities are accounted for.
         */
        dir &= tracker->dir;
        if (dir == 0) {         /* we've changed octant of movement (e.g. NE → NW) */
            DebugAccelF("query: no longer linear\n");
            /* instead of breaking it we might also inspect the partition after,
             * but actual improvement with this is probably rare. */
            break;
        }

        tracker_velocity = CalcTracker(tracker, cur_t) * velocity_factor;

        if ((initial_velocity == 0 || offset <= vel->initial_range) &&
            tracker_velocity != 0) {
            /* set initial velocity and result */
            result = initial_velocity = tracker_velocity;
            used_offset = offset;
        }
        else if (initial_velocity != 0 && tracker_velocity != 0) {
            velocity_diff = fabs(initial_velocity - tracker_velocity);

            if (velocity_diff > vel->max_diff &&
                velocity_diff / (initial_velocity + tracker_velocity) >=
                vel->max_rel_diff) {
                /* we're not in range, quit - it won't get better. */
                DebugAccelF("query: tracker too different:"
                            " old %2.2f initial %2.2f diff: %2.2f\n",
                            tracker_velocity, initial_velocity, velocity_diff);
                break;
            }
            /* we're in range with the initial velocity,
             * so this result is likely better
             * (it contains more information). */
            result = tracker_velocity;
            used_offset = offset;
        }
    }
    if (offset == vel->num_tracker) {
        DebugAccelF("query: last tracker in effect\n");
        used_offset = vel->num_tracker - 1;
    }
    if (used_offset >= 0) {
#ifdef PTRACCEL_DEBUGGING
        MotionTracker *tracker = TRACKER(vel, used_offset);

        DebugAccelF("result: offset %i [dx: %f dy: %f diff: %i]\n",
                    used_offset, tracker->dx, tracker->dy,
                    cur_t - tracker->time);
#endif
    }
    return result;
}
Exemplo n.º 5
0
/**
 * Perform velocity approximation
 * return true if non-visible state reset is suggested
 */
static short
ProcessVelocityData(
    DeviceVelocityPtr s,
    int dx,
    int dy,
    int time)
{
    float cvelocity;

    int diff = time - s->lrm_time;
    int cur_ax, last_ax;
    short reset = (diff >= s->reset_time);

    /* remember last round's result */
    s->last_velocity = s->velocity;
    cur_ax = GetAxis(dx, dy);
    last_ax = GetAxis(s->last_dx, s->last_dy);

    if(cur_ax != last_ax && cur_ax != -1 && last_ax != -1 && !reset){
        /* correct for the error induced when diagonal movements are
           reported as alternating axis mickeys */
        dx += s->last_dx;
        dy += s->last_dy;
        diff += s->last_diff;
        s->last_diff = time - s->lrm_time; /* prevent repeating add-up */
        DebugAccelF("(dix ptracc) axial correction\n");
    }else{
        s->last_diff = diff;
    }

    /*
     * cvelocity is not a real velocity yet, more a motion delta. constant
     * acceleration is multiplied here to make the velocity an on-screen
     * velocity (pix/t as opposed to [insert unit]/t). This is intended to
     * make multiple devices with widely varying ConstantDecelerations respond
     * similar to acceleration controls.
     */
    cvelocity = (float)sqrt(dx*dx + dy*dy) * s->const_acceleration;

    s->lrm_time = time;

    if (s->reset_time < 0 || diff < 0) { /* reset disabled or timer overrun? */
        /* simply set velocity from current movement, no reset. */
        s->velocity = cvelocity;
        return FALSE;
    }

    if (diff == 0)
        diff = 1; /* prevent div-by-zero, though it shouldn't happen anyway*/

    /* translate velocity to dots/ms (somewhat intractable in integers,
       so we multiply by some per-device adjustable factor) */
    cvelocity = cvelocity * s->corr_mul / (float)diff;

    /* short-circuit: when nv-reset the rest can be skipped */
    if(reset == TRUE){
	/*
	 * we don't really have a velocity here, since diff includes inactive
	 * time. This is dealt with in ComputeAcceleration.
	 */
	StuffFilterChain(s, cvelocity);
	s->velocity = s->last_velocity = cvelocity;
	s->last_reset = TRUE;
	DebugAccelF("(dix ptracc) non-visible state reset\n");
	return TRUE;
    }

    if(s->last_reset == TRUE){
	/*
	 * when here, we're probably processing the second mickey of a starting
	 * stroke. This happens to be the first time we can reasonably pretend
	 * that cvelocity is an actual velocity. Thus, to opt precision, we
	 * stuff that into the filter chain.
	 */
	s->last_reset = FALSE;
	DebugAccelF("(dix ptracc) after-reset vel:%.3f\n", cvelocity);
	StuffFilterChain(s, cvelocity);
	s->velocity = cvelocity;
	return FALSE;
    }

    /* feed into filter chain */
    FeedFilterChain(s, cvelocity, diff);

    /* perform coupling and decide final value */
    s->velocity = QueryFilterChain(s, cvelocity);

    DebugAccelF("(dix ptracc) guess: vel=%.3f diff=%d   %i|%i|%i|%i|%i|%i|%i|%i|%i\n",
           s->velocity, diff,
           s->statistics.filter_usecount[0], s->statistics.filter_usecount[1],
           s->statistics.filter_usecount[2], s->statistics.filter_usecount[3],
           s->statistics.filter_usecount[4], s->statistics.filter_usecount[5],
           s->statistics.filter_usecount[6], s->statistics.filter_usecount[7],
           s->statistics.filter_usecount[8]);
    return FALSE;
}