TouchPoint: add horizontalDiameter, verticalDiameter; deprecate rects

The contact patch of a finger on a touchscreen tends to be roughly
elliptical.  If we model it as a QRectF, it's not clear whether the
ellipse should be considered to be inscribed in the rectangle and then
rotated, or whether the rectangle represents the outer bounds of the
rotated ellipse.  In practice, since most touchscreens can't measure
rotation, it is effectively the latter.  But modeling it that way means
information is lost if the touchscreen can measure rotation: you can
determine the bounds of a rotated ellipse, but you cannot derive the
rotated ellipse from its bounds.  So it's better to model the axes
of the ellipse explicitly.  This has the added benefit of saving a
little storage space: we replace 3 QRectF instances, whose width
and height will normally be the same, with 3 positions (bringing the
total to 12 QPointF's) and one set of axes.  Further, most applications
only care about the center of each contact patch, so it's better to
store that explicitly instead of calculating QRectF::center() repeatedly.

In the past there may have been an assumption that the width of the rect is
the same as the horizontalDiameter of the ellipse, so the rect could be
considered to be rotated, and the ellipse to be inscribed.  But in
d0b1c646b4 and
40e4949674 the point was made that the rect
is actually the bounding box of the rotated ellipse.

[ChangeLog][QtGui][QTouchEvent] TouchPoint::rect(), sceneRect() and
screenRect() are deprecated; a touchpoint is now modeled as an ellipse,
so please use pos(), scenePos(), screenPos(), horizontalDiameter()
and verticalDiameter() instead.

Change-Id: Ic06f6165e2d90fc9d4cc19cf4938d4faf5766bb4
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
Shawn Rutledge 2016-03-23 22:26:08 +01:00
parent f7253b2568
commit 3c159957f8
7 changed files with 119 additions and 30 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -4417,6 +4417,8 @@ QTouchEvent::~QTouchEvent()
\brief The TouchPoint class provides information about a touch point in a QTouchEvent. \brief The TouchPoint class provides information about a touch point in a QTouchEvent.
\since 4.6 \since 4.6
\inmodule QtGui \inmodule QtGui
\image touchpoint-metrics.png
*/ */
/*! \enum TouchPoint::InfoFlag /*! \enum TouchPoint::InfoFlag
@ -4502,7 +4504,7 @@ Qt::TouchPointState QTouchEvent::TouchPoint::state() const
*/ */
QPointF QTouchEvent::TouchPoint::pos() const QPointF QTouchEvent::TouchPoint::pos() const
{ {
return d->rect.center(); return d->pos;
} }
/*! /*!
@ -4517,7 +4519,7 @@ QPointF QTouchEvent::TouchPoint::pos() const
*/ */
QPointF QTouchEvent::TouchPoint::scenePos() const QPointF QTouchEvent::TouchPoint::scenePos() const
{ {
return d->sceneRect.center(); return d->scenePos;
} }
/*! /*!
@ -4527,7 +4529,7 @@ QPointF QTouchEvent::TouchPoint::scenePos() const
*/ */
QPointF QTouchEvent::TouchPoint::screenPos() const QPointF QTouchEvent::TouchPoint::screenPos() const
{ {
return d->screenRect.center(); return d->screenPos;
} }
/*! /*!
@ -4650,10 +4652,19 @@ QPointF QTouchEvent::TouchPoint::lastNormalizedPos() const
around the point returned by pos(). around the point returned by pos().
\note This function returns an empty rect if the device does not report touch point sizes. \note This function returns an empty rect if the device does not report touch point sizes.
\obsolete This function is deprecated in 5.9 because it returns the outer bounds
of the touchpoint regardless of rotation, whereas a touchpoint is more correctly
modeled as an ellipse at position pos() with horizontalDiameter() and
verticalDiameter() which are independent of rotation().
\sa scenePos(), horizontalDiameter(), verticalDiameter()
*/ */
QRectF QTouchEvent::TouchPoint::rect() const QRectF QTouchEvent::TouchPoint::rect() const
{ {
return d->rect; QRectF ret(0, 0, d->horizontalDiameter, d->verticalDiameter);
ret.moveCenter(d->pos);
return ret;
} }
/*! /*!
@ -4661,11 +4672,18 @@ QRectF QTouchEvent::TouchPoint::rect() const
\note This function returns an empty rect if the device does not report touch point sizes. \note This function returns an empty rect if the device does not report touch point sizes.
\sa scenePos(), rect() \obsolete This function is deprecated in 5.9 because it returns the outer bounds
of the touchpoint regardless of rotation, whereas a touchpoint is more correctly
modeled as an ellipse at position scenePos() with horizontalDiameter() and
verticalDiameter() which are independent of rotation().
\sa scenePos(), horizontalDiameter(), verticalDiameter()
*/ */
QRectF QTouchEvent::TouchPoint::sceneRect() const QRectF QTouchEvent::TouchPoint::sceneRect() const
{ {
return d->sceneRect; QRectF ret(0, 0, d->horizontalDiameter, d->verticalDiameter);
ret.moveCenter(d->scenePos);
return ret;
} }
/*! /*!
@ -4673,11 +4691,18 @@ QRectF QTouchEvent::TouchPoint::sceneRect() const
\note This function returns an empty rect if the device does not report touch point sizes. \note This function returns an empty rect if the device does not report touch point sizes.
\sa screenPos(), rect() \obsolete This function is deprecated because it returns the outer bounds of the
touchpoint regardless of rotation, whereas a touchpoint is more correctly
modeled as an ellipse at position screenPos() with horizontalDiameter() and
verticalDiameter() which are independent of rotation().
\sa screenPos(), horizontalDiameter(), verticalDiameter()
*/ */
QRectF QTouchEvent::TouchPoint::screenRect() const QRectF QTouchEvent::TouchPoint::screenRect() const
{ {
return d->screenRect; QRectF ret(0, 0, d->horizontalDiameter, d->verticalDiameter);
ret.moveCenter(d->screenPos);
return ret;
} }
/*! /*!
@ -4702,6 +4727,30 @@ qreal QTouchEvent::TouchPoint::rotation() const
return d->rotation; return d->rotation;
} }
/*!
\since 5.9
Returns the width of the bounding ellipse of this touch point.
The return value is in logical pixels, along the horizontal axis
when rotation() is zero. Most touchscreens do not detect the shape of the
contact point, so zero is the most common value.
*/
qreal QTouchEvent::TouchPoint::horizontalDiameter() const
{
return d->horizontalDiameter;
}
/*!
\since 5.9
Returns the height of the bounding ellipse of this touch point.
The return value is in logical pixels, along the vertical axis
when rotation() is zero. Most touchscreens do not detect the shape
of the contact point, so zero is the most common value.
*/
qreal QTouchEvent::TouchPoint::verticalDiameter() const
{
return d->verticalDiameter;
}
/*! /*!
Returns a velocity vector for this touch point. Returns a velocity vector for this touch point.
The vector is in the screen's coordinate system, using pixels per seconds for the magnitude. The vector is in the screen's coordinate system, using pixels per seconds for the magnitude.
@ -4773,7 +4822,7 @@ void QTouchEvent::TouchPoint::setPos(const QPointF &pos)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->rect.moveCenter(pos); d->pos = pos;
} }
/*! \internal */ /*! \internal */
@ -4781,7 +4830,7 @@ void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->sceneRect.moveCenter(scenePos); d->scenePos = scenePos;
} }
/*! \internal */ /*! \internal */
@ -4789,7 +4838,7 @@ void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->screenRect.moveCenter(screenPos); d->screenPos = screenPos;
} }
/*! \internal */ /*! \internal */
@ -4864,28 +4913,35 @@ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalized
d->lastNormalizedPos = lastNormalizedPos; d->lastNormalizedPos = lastNormalizedPos;
} }
/*! \internal */ // ### remove the following 3 setRect functions and their usages soon
/*! \internal \obsolete */
void QTouchEvent::TouchPoint::setRect(const QRectF &rect) void QTouchEvent::TouchPoint::setRect(const QRectF &rect)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->rect = rect; d->pos = rect.center();
d->verticalDiameter = rect.height();
d->horizontalDiameter = rect.width();
} }
/*! \internal */ /*! \internal \obsolete */
void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->sceneRect = sceneRect; d->scenePos = sceneRect.center();
d->verticalDiameter = sceneRect.height();
d->horizontalDiameter = sceneRect.width();
} }
/*! \internal */ /*! \internal \obsolete */
void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect)
{ {
if (d->ref.load() != 1) if (d->ref.load() != 1)
d = d->detach(); d = d->detach();
d->screenRect = screenRect; d->screenPos = screenRect.center();
d->verticalDiameter = screenRect.height();
d->horizontalDiameter = screenRect.width();
} }
/*! \internal */ /*! \internal */
@ -4904,6 +4960,22 @@ void QTouchEvent::TouchPoint::setRotation(qreal angle)
d->rotation = angle; d->rotation = angle;
} }
/*! \internal */
void QTouchEvent::TouchPoint::setVerticalDiameter(qreal dia)
{
if (d->ref.load() != 1)
d = d->detach();
d->verticalDiameter = dia;
}
/*! \internal */
void QTouchEvent::TouchPoint::setHorizontalDiameter(qreal dia)
{
if (d->ref.load() != 1)
d = d->detach();
d->horizontalDiameter = dia;
}
/*! \internal */ /*! \internal */
void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v) void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v)
{ {

View File

@ -870,6 +870,9 @@ public:
qreal pressure() const; qreal pressure() const;
qreal rotation() const; qreal rotation() const;
qreal horizontalDiameter() const;
qreal verticalDiameter() const;
QVector2D velocity() const; QVector2D velocity() const;
InfoFlags flags() const; InfoFlags flags() const;
QVector<QPointF> rawScreenPositions() const; QVector<QPointF> rawScreenPositions() const;
@ -890,11 +893,13 @@ public:
void setLastScenePos(const QPointF &lastScenePos); void setLastScenePos(const QPointF &lastScenePos);
void setLastScreenPos(const QPointF &lastScreenPos); void setLastScreenPos(const QPointF &lastScreenPos);
void setLastNormalizedPos(const QPointF &lastNormalizedPos); void setLastNormalizedPos(const QPointF &lastNormalizedPos);
void setRect(const QRectF &rect); void setRect(const QRectF &rect); // deprecated
void setSceneRect(const QRectF &sceneRect); void setSceneRect(const QRectF &sceneRect); // deprecated
void setScreenRect(const QRectF &screenRect); void setScreenRect(const QRectF &screenRect); // deprecated
void setPressure(qreal pressure); void setPressure(qreal pressure);
void setRotation(qreal angle); void setRotation(qreal angle);
void setVerticalDiameter(qreal dia);
void setHorizontalDiameter(qreal dia);
void setVelocity(const QVector2D &v); void setVelocity(const QVector2D &v);
void setFlags(InfoFlags flags); void setFlags(InfoFlags flags);
void setRawScreenPositions(const QVector<QPointF> &positions); void setRawScreenPositions(const QVector<QPointF> &positions);

View File

@ -66,7 +66,9 @@ public:
id(id), id(id),
state(Qt::TouchPointReleased), state(Qt::TouchPointReleased),
pressure(qreal(-1.)), pressure(qreal(-1.)),
rotation(qreal(0.)) rotation(qreal(0.)),
verticalDiameter(0),
horizontalDiameter(0)
{ } { }
inline QTouchEventTouchPointPrivate *detach() inline QTouchEventTouchPointPrivate *detach()
@ -82,12 +84,13 @@ public:
int id; int id;
QPointerUniqueId uniqueId; QPointerUniqueId uniqueId;
Qt::TouchPointStates state; Qt::TouchPointStates state;
QRectF rect, sceneRect, screenRect; QPointF pos, scenePos, screenPos, normalizedPos,
QPointF normalizedPos,
startPos, startScenePos, startScreenPos, startNormalizedPos, startPos, startScenePos, startScreenPos, startNormalizedPos,
lastPos, lastScenePos, lastScreenPos, lastNormalizedPos; lastPos, lastScenePos, lastScreenPos, lastNormalizedPos;
qreal pressure; qreal pressure;
qreal rotation; qreal rotation;
qreal verticalDiameter;
qreal horizontalDiameter;
QVector2D velocity; QVector2D velocity;
QTouchEvent::TouchPoint::InfoFlags flags; QTouchEvent::TouchPoint::InfoFlags flags;
QVector<QPointF> rawScreenPositions; QVector<QPointF> rawScreenPositions;

View File

@ -2564,7 +2564,9 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
Q_ASSERT(w.data() != 0); Q_ASSERT(w.data() != 0);
// make the *scene* functions return the same as the *screen* functions // make the *scene* functions return the same as the *screen* functions
touchPoint.d->sceneRect = touchPoint.screenRect(); touchPoint.d->scenePos = touchPoint.screenPos();
touchPoint.d->verticalDiameter = touchPoint.screenRect().height();
touchPoint.d->horizontalDiameter = touchPoint.screenRect().width();
touchPoint.d->startScenePos = touchPoint.startScreenPos(); touchPoint.d->startScenePos = touchPoint.startScreenPos();
touchPoint.d->lastScenePos = touchPoint.lastScreenPos(); touchPoint.d->lastScenePos = touchPoint.lastScreenPos();
@ -2634,8 +2636,9 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
const QPointF screenPos = rect.center(); const QPointF screenPos = rect.center();
const QPointF delta = screenPos - screenPos.toPoint(); const QPointF delta = screenPos - screenPos.toPoint();
rect.moveCenter(w->mapFromGlobal(screenPos.toPoint()) + delta); touchPoint.d->pos = w->mapFromGlobal(screenPos.toPoint()) + delta;
touchPoint.d->rect = rect; touchPoint.d->verticalDiameter = rect.height();
touchPoint.d->horizontalDiameter = rect.width();
if (touchPoint.state() == Qt::TouchPointPressed) { if (touchPoint.state() == Qt::TouchPointPressed) {
touchPoint.d->startPos = w->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; touchPoint.d->startPos = w->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta;
touchPoint.d->lastPos = w->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; touchPoint.d->lastPos = w->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta;

View File

@ -131,9 +131,11 @@ public:
qint64 uniqueId; // for TUIO: object/token ID; otherwise empty qint64 uniqueId; // for TUIO: object/token ID; otherwise empty
// TODO for TUIO 2.0: add registerPointerUniqueID(QPointerUniqueId) // TODO for TUIO 2.0: add registerPointerUniqueID(QPointerUniqueId)
QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1) QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1)
QRectF area; // the touched area, centered at position in screen coordinates QRectF area; // dimensions of the elliptical contact patch, unrotated, and centered at position in screen coordinates
// width is the horizontal diameter, height is the vertical diameter
qreal pressure; // 0 to 1 qreal pressure; // 0 to 1
qreal rotation; // 0 means pointing straight up; 0 if unknown (like QTabletEvent::rotation) qreal rotation; // rotation applied to the elliptical contact patch
// 0 means pointing straight up; 0 if unknown (like QTabletEvent::rotation)
Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released} Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released}
QVector2D velocity; // in screen coordinate system, pixels / seconds QVector2D velocity; // in screen coordinate system, pixels / seconds
QTouchEvent::TouchPoint::InfoFlags flags; QTouchEvent::TouchPoint::InfoFlags flags;

View File

@ -3552,7 +3552,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
QTouchEvent::TouchPoint &pt = touchEvent->_touchPoints[i]; QTouchEvent::TouchPoint &pt = touchEvent->_touchPoints[i];
QRectF rect = pt.rect(); QRectF rect = pt.rect();
rect.translate(offset); rect.translate(offset);
pt.d->rect = rect; pt.d->pos = rect.center();
pt.d->verticalDiameter = rect.height();
pt.d->horizontalDiameter = rect.width();
pt.d->startPos = pt.startPos() + offset; pt.d->startPos = pt.startPos() + offset;
pt.d->lastPos = pt.lastPos() + offset; pt.d->lastPos = pt.lastPos() + offset;
} }
@ -4254,7 +4256,9 @@ bool QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEven
const QPointF delta = screenPos - screenPos.toPoint(); const QPointF delta = screenPos - screenPos.toPoint();
rect.moveCenter(widget->mapFromGlobal(screenPos.toPoint()) + delta); rect.moveCenter(widget->mapFromGlobal(screenPos.toPoint()) + delta);
touchPoint.d->rect = rect; touchPoint.d->pos = rect.center();
touchPoint.d->verticalDiameter = rect.height();
touchPoint.d->horizontalDiameter = rect.width();
touchPoint.d->startPos = widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta; touchPoint.d->startPos = widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta;
touchPoint.d->lastPos = widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta; touchPoint.d->lastPos = widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta;