00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qfile.h>
00022 #include <qtl.h>
00023 #include <math.h>
00024 #include <limits.h>
00025 #include <qdatastream.h>
00026 #include "qimpenstroke.h"
00027 #include "opie2/odebug.h"
00028
00029 #define QIMPEN_CORRELATION_POINTS 25
00030
00031
00039 QIMPenStroke::QIMPenStroke()
00040 {
00041 }
00042
00043 QIMPenStroke::QIMPenStroke( const QIMPenStroke &st )
00044 {
00045 startPoint = st.startPoint;
00046 lastPoint = st.lastPoint;
00047 links = st.links.copy();
00048 }
00049
00050 QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s )
00051 {
00052 clear();
00053
00054 startPoint = s.startPoint;
00055 lastPoint = s.lastPoint;
00056 links = s.links.copy();
00057
00058 return *this;
00059 }
00060
00061 void QIMPenStroke::clear()
00062 {
00063 startPoint = QPoint(0,0);
00064 lastPoint = QPoint( 0, 0 );
00065 links.resize( 0 );
00066 tsig.resize( 0 );
00067 dsig.resize( 0 );
00068 asig.resize( 0 );
00069 }
00070
00074 void QIMPenStroke::beginInput( QPoint p )
00075 {
00076 clear();
00077 startPoint = p;
00078 bounding = QRect();
00079 internalAddPoint( p );
00080 }
00081
00086 bool QIMPenStroke::addPoint( QPoint p )
00087 {
00088 if ( links.count() > 500 )
00089 return FALSE;
00090
00091 int dx = p.x() - lastPoint.x();
00092 int dy = p.y() - lastPoint.y();
00093 if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) {
00094
00095
00096
00097 int x = lastPoint.x();
00098 int y = lastPoint.y();
00099 int ix = 1;
00100 int iy = 1;
00101 if ( dx < 0 ) {
00102 ix = -1;
00103 dx = -dx;
00104 }
00105 if ( dy < 0 ) {
00106 iy = -1;
00107 dy = -dy;
00108 }
00109 int d = 0;
00110 if ( dx < dy ) {
00111 d = dx;
00112 do {
00113 y += iy;
00114 d += dx;
00115 if ( d > dy ) {
00116 x += ix;
00117 d -= dy;
00118 }
00119 internalAddPoint( QPoint( x, y ) );
00120 } while ( y != p.y() );
00121 } else {
00122 d = dy;
00123 do {
00124 x += ix;
00125 d += dy;
00126 if ( d > dx ) {
00127 y += iy;
00128 d -= dx;
00129 }
00130 internalAddPoint( QPoint( x, y ) );
00131 } while ( x != p.x() );
00132 }
00133 } else {
00134 internalAddPoint( p );
00135 }
00136
00137 return TRUE;
00138 }
00139
00143 void QIMPenStroke::endInput()
00144 {
00145 if ( links.count() < 3 ) {
00146 QIMPenGlyphLink gl;
00147 links.resize(1);
00148 gl.dx = 1;
00149 gl.dy = 0;
00150 links[0] = gl;
00151 }
00152
00153
00154 }
00155
00160 unsigned int QIMPenStroke::match( QIMPenStroke *pen )
00161 {
00162 double lratio;
00163
00164 if ( links.count() > pen->links.count() )
00165 lratio = (links.count()+2) / (pen->links.count()+2);
00166 else
00167 lratio = (pen->links.count()+2) / (links.count()+2);
00168
00169 lratio -= 1.0;
00170
00171 if ( lratio > 2.0 ) {
00172 #ifdef DEBUG_QIMPEN
00173 odebug << "stroke length too different" << oendl;
00174 #endif
00175 return 400000;
00176 }
00177
00178 createSignatures();
00179 pen->createSignatures();
00180
00181
00182 int vdiff = QABS(startPoint.y() - pen->startPoint.y());
00183
00184
00185 if ( vdiff > 18 ) {
00186 return 400000;
00187 }
00188
00189 vdiff -= 4;
00190 if ( vdiff < 0 )
00191 vdiff = 0;
00192
00193
00194 int evdiff = QABS(lastPoint.y() - pen->lastPoint.y());
00195
00196 if ( evdiff > 20 ) {
00197 return 400000;
00198 }
00199
00200 evdiff -= 5;
00201 if ( evdiff < 0 )
00202 evdiff = 0;
00203
00204
00205 int err1 = INT_MAX;
00206 int err2 = INT_MAX;
00207 int err3 = INT_MAX;
00208
00209
00210
00211 QArray<int> base = createBase( tsig, 2 );
00212 for ( int i = 0; i < 4; i++ ) {
00213 int e = calcError( base, pen->tsig, i, TRUE );
00214 if ( e < err1 )
00215 err1 = e;
00216 }
00217 if ( err1 > 40 ) {
00218 #ifdef DEBUG_QIMPEN
00219 odebug << "tsig too great: " << err1 << oendl;
00220 #endif
00221 return 400000;
00222 }
00223
00224
00225 err2 = calcError( dsig, pen->dsig, 0, FALSE );
00226 if ( err2 > 100 ) {
00227 #ifdef DEBUG_QIMPEN
00228 odebug << "dsig too great: " << err2 << oendl;
00229 #endif
00230 return 400000;
00231 }
00232
00233 err3 = calcError( asig, pen->asig, 0, TRUE );
00234 if ( err3 > 60 ) {
00235 #ifdef DEBUG_QIMPEN
00236 odebug << "asig too great: " << err3 << oendl;
00237 #endif
00238 return 400000;
00239 }
00240
00241
00242
00243
00244
00245
00246 unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) +
00247 vdiff * 1000 + evdiff * 500 +
00248 (unsigned int)(lratio * 5000.0);
00249
00250 #ifdef DEBUG_QIMPEN
00251 odebug << "err " << err << "( " << err1 << ", " << err2 << ", " << err3 << ", " << vdiff << oendl;
00252 #endif
00253
00254 return err;
00255 }
00256
00260 QRect QIMPenStroke::boundingRect()
00261 {
00262 if ( !bounding.isValid() ) {
00263 int x = startPoint.x();
00264 int y = startPoint.y();
00265 bounding = QRect( x, y, 1, 1 );
00266
00267 for ( unsigned i = 0; i < links.count(); i++ ) {
00268 x += links[i].dx;
00269 y += links[i].dy;
00270 if ( x < bounding.left() )
00271 bounding.setLeft( x );
00272 if ( x > bounding.right() )
00273 bounding.setRight( x );
00274 if ( y < bounding.top() )
00275 bounding.setTop( y );
00276 if ( y > bounding.bottom() )
00277 bounding.setBottom( y );
00278 }
00279 }
00280
00281 return bounding;
00282 }
00283
00284
00293 int QIMPenStroke::calcError( const QArray<int> &base,
00294 const QArray<int> &win, int off, bool t )
00295 {
00296 int err = 0;
00297
00298 for ( unsigned i = 0; i < win.count(); i++ ) {
00299 int d = QABS( base[i+off] - win[i] );
00300 if ( t && d > 128 )
00301 d -= 256;
00302 err += QABS( d );
00303 }
00304
00305 err /= win.count();
00306
00307 return err;
00308 }
00309
00313 void QIMPenStroke::createSignatures()
00314 {
00315 if ( tsig.isEmpty() )
00316 createTanSignature();
00317 if ( asig.isEmpty() )
00318 createAngleSignature();
00319 if ( dsig.isEmpty() )
00320 createDistSignature();
00321 }
00322
00326 void QIMPenStroke::createTanSignature()
00327 {
00328 int dist = 5;
00329 if ( (int)links.count() <= dist ) {
00330 tsig.resize(1);
00331 int dx = 0;
00332 int dy = 0;
00333 for ( unsigned j = 0; j < links.count(); j++ ) {
00334 dx += links[j].dx;
00335 dy += links[j].dy;
00336 }
00337 tsig[0] = arcTan( dy, dx );
00338 } else {
00339 tsig.resize( (links.count()-dist+1) / 2 );
00340 int idx = 0;
00341 for ( unsigned i = 0; i < links.count() - dist; i += 2 ) {
00342 int dx = 0;
00343 int dy = 0;
00344 for ( int j = 0; j < dist; j++ ) {
00345 dx += links[i+j].dx;
00346 dy += links[i+j].dy;
00347 }
00348 tsig[idx++] = arcTan( dy, dx );
00349 }
00350 }
00351
00352 tsig = scale( tsig, QIMPEN_CORRELATION_POINTS, TRUE );
00353
00354 }
00355
00359 void QIMPenStroke::createAngleSignature()
00360 {
00361 QPoint c = calcCenter();
00362
00363 int dist = 3;
00364 if ( (int)links.count() <= dist ) {
00365 asig.resize(1);
00366 asig[0] = 1;
00367 } else {
00368 asig.resize( links.count() );
00369 QPoint current(0, 0);
00370 int idx = 0;
00371 for ( unsigned i = 0; i < links.count(); i++ ) {
00372 int dx = c.x() - current.x();
00373 int dy = c.y() - current.y();
00374 int md = QMAX( QABS(dx), QABS(dy) );
00375 if ( md > 5 ) {
00376 dx = dx * 5 / md;
00377 dy = dy * 5 / md;
00378 }
00379 asig[idx++] = arcTan( dy, dx );
00380 current += QPoint( links[i].dx, links[i].dy );
00381 }
00382 }
00383
00384 asig = scale( asig, QIMPEN_CORRELATION_POINTS, TRUE );
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401 }
00402
00407 void QIMPenStroke::createDistSignature()
00408 {
00409 dsig.resize( (links.count()+1)/2 );
00410 QPoint c = calcCenter();
00411 QPoint pt( 0, 0 );
00412
00413 int minval = INT_MAX;
00414 int maxval = 0;
00415 int idx = 0;
00416 for ( unsigned i = 0; i < links.count(); i += 2 ) {
00417 int dx = c.x() - pt.x();
00418 int dy = c.y() - pt.y();
00419 if ( dx == 0 && dy == 0 )
00420 dsig[idx] = 0;
00421 else
00422 dsig[idx] = dx*dx + dy*dy;
00423
00424 if ( dsig[idx] > maxval )
00425 maxval = dsig[idx];
00426 if ( dsig[idx] < minval )
00427 minval = dsig[idx];
00428 pt.rx() += links[i].dx;
00429 pt.ry() += links[i].dy;
00430 idx++;
00431 }
00432
00433
00434 int div = maxval - minval;
00435 if ( div == 0 ) div = 1;
00436 for ( unsigned i = 0; i < dsig.count(); i++ ) {
00437 dsig[i] = (dsig[i] - minval ) * 255 / div;
00438 }
00439
00440 dsig = scale( dsig, QIMPEN_CORRELATION_POINTS );
00441 }
00442
00443
00449 QArray<int> QIMPenStroke::scale( const QArray<int> &s, unsigned count, bool t )
00450 {
00451 QArray<int> d(count);
00452
00453 unsigned si = 0;
00454 if ( s.count() > count ) {
00455 unsigned next = 0;
00456 for ( unsigned i = 0; i < count; i++ ) {
00457 next = (i+1) * s.count() / count;
00458 int maxval = 0;
00459 if ( t ) {
00460 for ( unsigned j = si; j < next; j++ ) {
00461 maxval = s[j] > maxval ? s[j] : maxval;
00462 }
00463 }
00464 int sum = 0;
00465 for ( unsigned j = si; j < next; j++ ) {
00466 if ( t && maxval - s[j] > 128 )
00467 sum += 256;
00468 sum += s[j];
00469 }
00470 d[i] = sum / (next-si);
00471 if ( t && d[i] > 256 )
00472 d[i] %= 256;
00473 si = next;
00474 }
00475 } else {
00476 for ( unsigned i = 0; i < count; i++ ) {
00477 si = i * s.count() / count;
00478 d[i] = s[si];
00479 }
00480 }
00481
00482 return d;
00483 }
00484
00488 void QIMPenStroke::internalAddPoint( QPoint p )
00489 {
00490 if ( p == lastPoint )
00491 return;
00492
00493 if ( !lastPoint.isNull() ) {
00494 QIMPenGlyphLink gl;
00495 gl.dx = p.x() - lastPoint.x();
00496 gl.dy = p.y() - lastPoint.y();
00497 links.resize( links.size() + 1 );
00498 links[links.size() - 1] = gl;
00499 }
00500
00501 lastPoint = p;
00502 bounding = QRect();
00503 }
00504
00508 QPoint QIMPenStroke::calcCenter()
00509 {
00510 QPoint pt( 0, 0 );
00511 int ax = 0;
00512 int ay = 0;
00513
00514 for ( unsigned i = 0; i < links.count(); i++ ) {
00515 pt.rx() += links[i].dx;
00516 pt.ry() += links[i].dy;
00517 ax += pt.x();
00518 ay += pt.y();
00519 }
00520
00521 ax /= (int)links.count();
00522 ay /= (int)links.count();
00523
00524 return QPoint( ax, ay );
00525 }
00526
00532 int QIMPenStroke::arcTan( int dy, int dx )
00533 {
00534 if ( dx == 0 ) {
00535 if ( dy >= 0 )
00536 return 64;
00537 else
00538 return 192;
00539 }
00540
00541 if ( dy == 0 ) {
00542 if ( dx >= 0 )
00543 return 0;
00544 else
00545 return 128;
00546 }
00547
00548 static int table[5][5] = {
00549 { 32, 19, 13, 10, 8 },
00550 { 45, 32, 24, 19, 16 },
00551 { 51, 40, 32, 26, 22 },
00552 { 54, 45, 37, 32, 27 },
00553 { 56, 49, 42, 37, 32 } };
00554
00555 if ( dy > 0 ) {
00556 if ( dx > 0 )
00557 return table[dy-1][dx-1];
00558 else
00559 return 128 - table[dy-1][QABS(dx)-1];
00560 } else {
00561 if ( dx > 0 )
00562 return 256 - table[QABS(dy)-1][dx-1];
00563 else
00564 return 128 + table[QABS(dy)-1][QABS(dx)-1];
00565 }
00566
00567 return 0;
00568 }
00569
00570
00575 QArray<int> QIMPenStroke::createBase( const QArray<int> a, int e )
00576 {
00577 QArray<int> ra( a.count() + 2*e );
00578
00579 for ( int i = 0; i < e; i++ ) {
00580 ra[i] = a[e - i - 1];
00581 ra[a.count() + i] = a[a.count() - i - 1];
00582 }
00583 for ( unsigned i = 0; i < a.count(); i++ ) {
00584 ra[i+e] = a[i];
00585 }
00586
00587 return ra;
00588 }
00589
00590
00594 void QIMPenStroke::smooth( QArray<int> &sig)
00595 {
00596 QArray<int> nsig = sig.copy();
00597
00598 int a;
00599 for ( unsigned i = 1; i < sig.count()-2; i++ ) {
00600 a = 0;
00601 for ( int j = -1; j <= 1; j++ ) {
00602 a += sig[ i + j ];
00603 }
00604 nsig[i] = a / 3;
00605 }
00606
00607 sig = nsig;
00608 }
00609
00613 QDataStream &operator<< (QDataStream &s, const QIMPenStroke &ws)
00614 {
00615 s << ws.startPoint;
00616 s << ws.links.count();
00617 for ( unsigned i = 0; i < ws.links.count(); i++ ) {
00618 s << (Q_INT8)ws.links[i].dx;
00619 s << (Q_INT8)ws.links[i].dy;
00620 }
00621
00622 return s;
00623 }
00624
00628 QDataStream &operator>> (QDataStream &s, QIMPenStroke &ws)
00629 {
00630 Q_INT8 i8;
00631 s >> ws.startPoint;
00632 ws.lastPoint = ws.startPoint;
00633 unsigned size;
00634 s >> size;
00635 ws.links.resize( size );
00636 for ( unsigned i = 0; i < size; i++ ) {
00637 s >> i8;
00638 ws.links[i].dx = i8;
00639 s >> i8;
00640 ws.links[i].dy = i8;
00641 ws.lastPoint += QPoint( ws.links[i].dx, ws.links[i].dy );
00642 }
00643
00644 return s;
00645 }
00646
00647