Dear Jason, > Have you considered allowing a variable length? Changing the functions > to support polynomials of arbitrary degree would be trivial and could > potentially allow for more accurate calibration (obviously at the cost > of increased CPU use). It's would be great but I have no idea how to pass these parameters because the number of coefficients can vary.
> Do you think it would be reasonable/better/worse to work with the actual > coordinates instead of a fraction? I'm not sure which would be better, > myself. > > Also, my gut says that the correction should be done in the tablet's > native coordindate space (axis_x->min_value; axis_x->max_value) rather > than the currently-set area (priv->topX, priv->topY). I'm not 100% sure I continue the think that it's better to use the currently-set area. My idea is that the currently-set area is set such that in the center of the screen the stylus works perfectly (because I assume that everything is linear in the center). When the currently-set area is well configured we can work on the borders without touching the central part. > though given that it might make it quite difficult for programs to > calculate the polynomial coefficients. I'll have to think a little harder... Yes we need to have an input close to 1 to avoid arithmetic overflow. > The typical idiom is to use something like: > float_atom = XInternAtom(dpy, "FLOAT", False); > which will does the same thing as these two lines and just uses a > hardcoded string. I don't know what to take as dpy. For the other comments I think I fix them. There is the new patch : diff --git include/wacom-properties.h include/wacom-properties.h index b845083..54e7793 100644 --- include/wacom-properties.h +++ include/wacom-properties.h @@ -27,6 +27,9 @@ /* 32 bit, 4 values, top x, top y, bottom x, bottom y */ #define WACOM_PROP_TABLET_AREA "Wacom Tablet Area" +/* 32 bit, 4x5=20 values, 4x[border width, polynomial coefficient x^3, x^2, x, 1] */ +#define WACOM_PROP_TABLET_DISTORTION "Wacom Border Distortion" + /* 8 bit, 1 value, [0 - 3] (NONE, CW, CCW, HALF) */ #define WACOM_PROP_ROTATION "Wacom Rotation" diff --git src/wcmCommon.c src/wcmCommon.c index 9408f42..06315cb 100644 --- src/wcmCommon.c +++ src/wcmCommon.c @@ -438,6 +438,26 @@ static void sendCommonEvents(InputInfoPtr pInfo, const WacomDeviceState* ds, sendWheelStripEvents(pInfo, ds, first_val, num_vals, valuators); } +/* compute the polynomial of order /order + * /polynomial must contain coefficients from the higher order to the constant + * { /in if /in < /limit + * result = { + * { Poly(/in) if /in >= /limit + */ +static float wcmComputePolynomial(float in, float limit, float* polynomial, int order) +{ + if (in < limit) { + int i; + float out = polynomial[0]; + for (i = 1; i <= order; ++i) { + out *= in; + out += polynomial[i]; + } + return out; + } + return in; +} + /* rotate x and y before post X inout events */ void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y) { @@ -446,19 +466,38 @@ void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y) DeviceIntPtr dev = pInfo->dev; AxisInfoPtr axis_x, axis_y; int tmp_coord; + float f; /* scale into on topX/topY area */ axis_x = &dev->valuator->axes[0]; axis_y = &dev->valuator->axes[1]; /* Don't try to scale relative axes */ - if (axis_x->max_value > axis_x->min_value) - *x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value, - priv->bottomX, priv->topX); + if (axis_x->max_value > axis_x->min_value) { + f = (*x - priv->topX) / (float)(priv->bottomX - priv->topX); // f is approximatively in [0,1] - if (axis_y->max_value > axis_y->min_value) - *y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value, - priv->bottomY, priv->topY); + // fix the topX border distortion with a polynomial + f = wcmComputePolynomial(f, priv->distortion_topX_border, priv->distortion_topX_poly, 3); + + // fix the bottomX border distortion with a polynomial + f = 1.0f - wcmComputePolynomial(1.0f - f, priv->distortion_bottomX_border, priv->distortion_bottomX_poly, 3); + + *x = roundf(f * (axis_x->max_value - axis_x->min_value) + axis_x->min_value); + + if (*x < axis_x->min_value) *x = axis_x->min_value; + if (*x > axis_x->max_value) *x = axis_x->max_value; + /* In the case of the two last if, the stylus is out of the screen and no events should be sent */ + } + + if (axis_y->max_value > axis_y->min_value) { + f = (*y - priv->topY) / (float)(priv->bottomY - priv->topY); + f = wcmComputePolynomial(f, priv->distortion_topY_border, priv->distortion_topY_poly, 3); + f = 1.0f - wcmComputePolynomial(1.0f - f, priv->distortion_bottomY_border, priv->distortion_bottomY_poly, 3); + + *y = roundf(f * (axis_y->max_value - axis_y->min_value) + axis_y->min_value); + if (*y < axis_y->min_value) *y = axis_y->min_value; + if (*y > axis_y->max_value) *y = axis_y->max_value; + } /* coordinates are now in the axis rage we advertise for the device */ diff --git src/wcmXCommand.c src/wcmXCommand.c index 346ff61..b2ef4e2 100644 --- src/wcmXCommand.c +++ src/wcmXCommand.c @@ -33,6 +33,8 @@ #define XI_PROP_PRODUCT_ID "Device Product ID" #endif +static Atom float_atom; + static void wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial); /***************************************************************************** @@ -82,6 +84,7 @@ int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode) static Atom prop_devnode; static Atom prop_rotation; static Atom prop_tablet_area; +static Atom prop_distortion; static Atom prop_pressurecurve; static Atom prop_serials; static Atom prop_serial_binding; @@ -204,11 +207,23 @@ static Atom InitWcmAtom(DeviceIntPtr dev, const char *name, Atom type, int forma return atom; } +static Atom InitFloatAtom(DeviceIntPtr dev, const char *name, int nvalues, float *values) +{ + Atom atom; + + atom = MakeAtom(name, strlen(name), TRUE); + XIChangeDeviceProperty(dev, atom, float_atom, 32, + PropModeReplace, nvalues, values, FALSE); + XISetDevicePropertyDeletable(dev, atom, FALSE); + return atom; +} + void InitWcmDeviceProperties(InputInfoPtr pInfo) { WacomDevicePtr priv = (WacomDevicePtr) pInfo->private; WacomCommonPtr common = priv->common; int values[WCM_MAX_BUTTONS]; + float fvalues[20]; int i; DBG(10, priv, "\n"); @@ -227,6 +242,24 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo) prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, XA_INTEGER, 32, 4, values); } + if (!IsPad(priv)) { + //float_atom = XInternAtom(dpy, "FLOAT", FALSE); + float_atom = XIGetKnownProperty("FLOAT"); + if (!float_atom) float_atom = MakeAtom("FLOAT", 5, TRUE); + + if (float_atom) { + // topX, topY, bottomX, bottomY + for (i = 0; i < 4; ++i) { + fvalues[i*5+0] = 0.0; // border + fvalues[i*5+1] = 0.0; // x^3 + fvalues[i*5+2] = 0.0; // x^2 + fvalues[i*5+3] = 1.0; // x + fvalues[i*5+4] = 0.0; // 1 + } + prop_distortion = InitFloatAtom(pInfo->dev, WACOM_PROP_TABLET_DISTORTION, 20, fvalues); + } + } + values[0] = common->wcmRotate; if (!IsPad(priv)) { prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, XA_INTEGER, 8, 1, values); @@ -683,6 +716,20 @@ int wcmDeleteProperty(DeviceIntPtr dev, Atom property) return (i >= 0) ? BadAccess : Success; } +/* help to copy the values from the parameters into the WacomDevice structure + * values[0] is the width of the distoation on a border + * values[1], values[2], ... are coefficients of the polynomials of x^3, x^2, x and constant + * all these values in units (WacomDevice::top, WacomDevice::bottom) -> (0,1) where 0 is mapped to the nearest border + */ +static void setDistortionProperty(float* values, float *border, float *polynomial) +{ + *border = values[0]; + polynomial[0] = values[1]; + polynomial[1] = values[2]; + polynomial[2] = values[3]; + polynomial[3] = values[4]; +} + int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly) { @@ -717,6 +764,20 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, priv->bottomX = values[2]; priv->bottomY = values[3]; } + } else if (property == prop_distortion) + { + float *values = (float*)prop->data; + + if (prop->size != 20 || prop->format != 32 || prop->type != float_atom) + return BadValue; + + if (!checkonly) + { + setDistortionProperty(values, &priv->distortion_topX_border, priv->distortion_topX_poly); + setDistortionProperty(values+5, &priv->distortion_topY_border, priv->distortion_topY_poly); + setDistortionProperty(values+10, &priv->distortion_bottomX_border, priv->distortion_bottomX_poly); + setDistortionProperty(values+15, &priv->distortion_bottomY_border, priv->distortion_bottomY_poly); + } } else if (property == prop_pressurecurve) { INT32 *pcurve; diff --git src/xf86WacomDefs.h src/xf86WacomDefs.h index 1575960..2d76514 100644 --- src/xf86WacomDefs.h +++ src/xf86WacomDefs.h @@ -262,6 +262,16 @@ struct _WacomDeviceRec unsigned int cur_serial; /* current serial in prox */ int cur_device_id; /* current device ID in prox */ + /* distortion */ + float distortion_topX_border; + float distortion_topY_border; + float distortion_bottomX_border; + float distortion_bottomY_border; + float distortion_topX_poly[4]; + float distortion_topY_poly[4]; + float distortion_bottomX_poly[4]; + float distortion_bottomY_poly[4]; + /* button mapping information * * 'button' variables are indexed by physical button number (0..nbuttons) ------------------------------------------------------------------------------ _______________________________________________ Linuxwacom-devel mailing list Linuxwacom-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel