summaryrefslogtreecommitdiff
path: root/inputmethods/handwriting/qimpenstroke.cpp
Side-by-side diff
Diffstat (limited to 'inputmethods/handwriting/qimpenstroke.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--inputmethods/handwriting/qimpenstroke.cpp15
1 files changed, 8 insertions, 7 deletions
diff --git a/inputmethods/handwriting/qimpenstroke.cpp b/inputmethods/handwriting/qimpenstroke.cpp
index 3567d6d..14e435a 100644
--- a/inputmethods/handwriting/qimpenstroke.cpp
+++ b/inputmethods/handwriting/qimpenstroke.cpp
@@ -1,346 +1,347 @@
/**********************************************************************
** Copyright (C) 2000 Trolltech AS. All rights reserved.
**
** This file is part of Qtopia Environment.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
#include <qfile.h>
#include <qtl.h>
#include <math.h>
#include <limits.h>
#include <qdatastream.h>
#include "qimpenstroke.h"
+#include "opie2/odebug.h"
#define QIMPEN_CORRELATION_POINTS 25
//#define DEBUG_QIMPEN
/*!
\class QIMPenStroke qimpenstroke.h
Handles a single stroke. Can calculate closeness of match to
another stroke.
*/
QIMPenStroke::QIMPenStroke()
{
}
QIMPenStroke::QIMPenStroke( const QIMPenStroke &st )
{
startPoint = st.startPoint;
lastPoint = st.lastPoint;
links = st.links.copy();
}
QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s )
{
clear();
- //qDebug( "copy strokes %d", s.links.count() );
+ //odebug << "copy strokes " << s.links.count() << oendl;
startPoint = s.startPoint;
lastPoint = s.lastPoint;
links = s.links.copy();
return *this;
}
void QIMPenStroke::clear()
{
startPoint = QPoint(0,0);
lastPoint = QPoint( 0, 0 );
links.resize( 0 );
tsig.resize( 0 );
dsig.resize( 0 );
asig.resize( 0 );
}
/*!
Begin inputting a new stroke.
*/
void QIMPenStroke::beginInput( QPoint p )
{
clear();
startPoint = p;
bounding = QRect();
internalAddPoint( p );
}
/*!
Add a point to the stroke's shape.
Returns TRUE if the point was successfully added.
*/
bool QIMPenStroke::addPoint( QPoint p )
{
if ( links.count() > 500 ) // sanity check (that the user is sane).
return FALSE;
int dx = p.x() - lastPoint.x();
int dy = p.y() - lastPoint.y();
if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) {
// The point is not adjacent to the previous point, so we fill
// in with a straight line. Some kind of non-linear
// interpolation might be better.
int x = lastPoint.x();
int y = lastPoint.y();
int ix = 1;
int iy = 1;
if ( dx < 0 ) {
ix = -1;
dx = -dx;
}
if ( dy < 0 ) {
iy = -1;
dy = -dy;
}
int d = 0;
if ( dx < dy ) {
d = dx;
do {
y += iy;
d += dx;
if ( d > dy ) {
x += ix;
d -= dy;
}
internalAddPoint( QPoint( x, y ) );
} while ( y != p.y() );
} else {
d = dy;
do {
x += ix;
d += dy;
if ( d > dx ) {
y += iy;
d -= dx;
}
internalAddPoint( QPoint( x, y ) );
} while ( x != p.x() );
}
} else {
internalAddPoint( p );
}
return TRUE;
}
/*!
Finish inputting a stroke.
*/
void QIMPenStroke::endInput()
{
if ( links.count() < 3 ) {
QIMPenGlyphLink gl;
links.resize(1);
gl.dx = 1;
gl.dy = 0;
links[0] = gl;
}
- //qDebug("Points: %d", links.count() );
+ //odebug << "Points: " << links.count() << oendl;
}
/*!
Return an indicator of the closeness of this stroke to \a pen.
Lower value is better.
*/
unsigned int QIMPenStroke::match( QIMPenStroke *pen )
{
double lratio;
if ( links.count() > pen->links.count() )
lratio = (links.count()+2) / (pen->links.count()+2);
else
lratio = (pen->links.count()+2) / (links.count()+2);
lratio -= 1.0;
if ( lratio > 2.0 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "stroke length too different" );
+ odebug << "stroke length too different" << oendl;
#endif
return 400000;
}
createSignatures();
pen->createSignatures();
// Starting point offset
int vdiff = QABS(startPoint.y() - pen->startPoint.y());
// Insanely offset?
if ( vdiff > 18 ) {
return 400000;
}
vdiff -= 4;
if ( vdiff < 0 )
vdiff = 0;
// Ending point offset
int evdiff = QABS(lastPoint.y() - pen->lastPoint.y());
// Insanely offset?
if ( evdiff > 20 ) {
return 400000;
}
evdiff -= 5;
if ( evdiff < 0 )
evdiff = 0;
// do a correlation with the three available signatures.
int err1 = INT_MAX;
int err2 = INT_MAX;
int err3 = INT_MAX;
// base has extra points at the start and end to enable
// correlation of a sliding window with the pen supplied.
QArray<int> base = createBase( tsig, 2 );
for ( int i = 0; i < 4; i++ ) {
int e = calcError( base, pen->tsig, i, TRUE );
if ( e < err1 )
err1 = e;
}
if ( err1 > 40 ) { // no need for more matching
#ifdef DEBUG_QIMPEN
- qDebug( "tsig too great: %d", err1 );
+ odebug << "tsig too great: " << err1 << oendl;
#endif
return 400000;
}
// maybe a sliding window is worthwhile for these too.
err2 = calcError( dsig, pen->dsig, 0, FALSE );
if ( err2 > 100 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "dsig too great: %d", err2 );
+ odebug << "dsig too great: " << err2 << oendl;
#endif
return 400000;
}
err3 = calcError( asig, pen->asig, 0, TRUE );
if ( err3 > 60 ) {
#ifdef DEBUG_QIMPEN
- qDebug( "asig too great: %d", err3 );
+ odebug << "asig too great: " << err3 << oendl;
#endif
return 400000;
}
// Some magic numbers here - the addition reduces the weighting of
// the error and compensates for the different error scales. I
// consider the tangent signature to be the best indicator, so it
// has the most weight. This ain't rocket science.
// Basically, these numbers are the tuning factors.
unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) +
vdiff * 1000 + evdiff * 500 +
(unsigned int)(lratio * 5000.0);
#ifdef DEBUG_QIMPEN
- qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff );
+ odebug << "err " << err << "( " << err1 << ", " << err2 << ", " << err3 << ", " << vdiff << oendl;
#endif
return err;
}
/*!
Return the bounding rect of this stroke.
*/
QRect QIMPenStroke::boundingRect()
{
if ( !bounding.isValid() ) {
int x = startPoint.x();
int y = startPoint.y();
bounding = QRect( x, y, 1, 1 );
for ( unsigned i = 0; i < links.count(); i++ ) {
x += links[i].dx;
y += links[i].dy;
if ( x < bounding.left() )
bounding.setLeft( x );
if ( x > bounding.right() )
bounding.setRight( x );
if ( y < bounding.top() )
bounding.setTop( y );
if ( y > bounding.bottom() )
bounding.setBottom( y );
}
}
return bounding;
}
/*!
Perform a correlation of the supplied arrays. \a base should have
win.count() + 2 * off points to enable sliding \a win over the
\a base data. If \a t is TRUE, the comparison takes into account
the circular nature of the angular data.
Returns the best (lowest error) match.
*/
int QIMPenStroke::calcError( const QArray<int> &base,
const QArray<int> &win, int off, bool t )
{
int err = 0;
for ( unsigned i = 0; i < win.count(); i++ ) {
int d = QABS( base[i+off] - win[i] );
if ( t && d > 128 )
d -= 256;
err += QABS( d );
}
err /= win.count();
return err;
}
/*!
Creates signatures used in matching if not already created.
*/
void QIMPenStroke::createSignatures()
{
if ( tsig.isEmpty() )
createTanSignature();
if ( asig.isEmpty() )
createAngleSignature();
if ( dsig.isEmpty() )
createDistSignature();
}
/*!
Create a signature of the tangents to the user's stroke.
*/
void QIMPenStroke::createTanSignature()
{
int dist = 5; // number of points to include in calculation
if ( (int)links.count() <= dist ) {
tsig.resize(1);
int dx = 0;
int dy = 0;
for ( unsigned j = 0; j < links.count(); j++ ) {
dx += links[j].dx;
dy += links[j].dy;
}
tsig[0] = arcTan( dy, dx );
} else {
tsig.resize( (links.count()-dist+1) / 2 );
int idx = 0;
for ( unsigned i = 0; i < links.count() - dist; i += 2 ) {
int dx = 0;
int dy = 0;
for ( int j = 0; j < dist; j++ ) {
dx += links[i+j].dx;
dy += links[i+j].dy;
}