author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /inputmethods/handwriting/qimpenstroke.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'inputmethods/handwriting/qimpenstroke.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | inputmethods/handwriting/qimpenstroke.cpp | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/inputmethods/handwriting/qimpenstroke.cpp b/inputmethods/handwriting/qimpenstroke.cpp new file mode 100644 index 0000000..3567d6d --- a/dev/null +++ b/inputmethods/handwriting/qimpenstroke.cpp | |||
@@ -0,0 +1,646 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qtopia Environment. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <qfile.h> | ||
22 | #include <qtl.h> | ||
23 | #include <math.h> | ||
24 | #include <limits.h> | ||
25 | #include <qdatastream.h> | ||
26 | #include "qimpenstroke.h" | ||
27 | |||
28 | #define QIMPEN_CORRELATION_POINTS 25 | ||
29 | //#define DEBUG_QIMPEN | ||
30 | |||
31 | /*! | ||
32 | \class QIMPenStroke qimpenstroke.h | ||
33 | |||
34 | Handles a single stroke. Can calculate closeness of match to | ||
35 | another stroke. | ||
36 | */ | ||
37 | |||
38 | QIMPenStroke::QIMPenStroke() | ||
39 | { | ||
40 | } | ||
41 | |||
42 | QIMPenStroke::QIMPenStroke( const QIMPenStroke &st ) | ||
43 | { | ||
44 | startPoint = st.startPoint; | ||
45 | lastPoint = st.lastPoint; | ||
46 | links = st.links.copy(); | ||
47 | } | ||
48 | |||
49 | QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s ) | ||
50 | { | ||
51 | clear(); | ||
52 | //qDebug( "copy strokes %d", s.links.count() ); | ||
53 | startPoint = s.startPoint; | ||
54 | lastPoint = s.lastPoint; | ||
55 | links = s.links.copy(); | ||
56 | |||
57 | return *this; | ||
58 | } | ||
59 | |||
60 | void QIMPenStroke::clear() | ||
61 | { | ||
62 | startPoint = QPoint(0,0); | ||
63 | lastPoint = QPoint( 0, 0 ); | ||
64 | links.resize( 0 ); | ||
65 | tsig.resize( 0 ); | ||
66 | dsig.resize( 0 ); | ||
67 | asig.resize( 0 ); | ||
68 | } | ||
69 | |||
70 | /*! | ||
71 | Begin inputting a new stroke. | ||
72 | */ | ||
73 | void QIMPenStroke::beginInput( QPoint p ) | ||
74 | { | ||
75 | clear(); | ||
76 | startPoint = p; | ||
77 | bounding = QRect(); | ||
78 | internalAddPoint( p ); | ||
79 | } | ||
80 | |||
81 | /*! | ||
82 | Add a point to the stroke's shape. | ||
83 | Returns TRUE if the point was successfully added. | ||
84 | */ | ||
85 | bool QIMPenStroke::addPoint( QPoint p ) | ||
86 | { | ||
87 | if ( links.count() > 500 ) // sanity check (that the user is sane). | ||
88 | return FALSE; | ||
89 | |||
90 | int dx = p.x() - lastPoint.x(); | ||
91 | int dy = p.y() - lastPoint.y(); | ||
92 | if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) { | ||
93 | // The point is not adjacent to the previous point, so we fill | ||
94 | // in with a straight line. Some kind of non-linear | ||
95 | // interpolation might be better. | ||
96 | int x = lastPoint.x(); | ||
97 | int y = lastPoint.y(); | ||
98 | int ix = 1; | ||
99 | int iy = 1; | ||
100 | if ( dx < 0 ) { | ||
101 | ix = -1; | ||
102 | dx = -dx; | ||
103 | } | ||
104 | if ( dy < 0 ) { | ||
105 | iy = -1; | ||
106 | dy = -dy; | ||
107 | } | ||
108 | int d = 0; | ||
109 | if ( dx < dy ) { | ||
110 | d = dx; | ||
111 | do { | ||
112 | y += iy; | ||
113 | d += dx; | ||
114 | if ( d > dy ) { | ||
115 | x += ix; | ||
116 | d -= dy; | ||
117 | } | ||
118 | internalAddPoint( QPoint( x, y ) ); | ||
119 | } while ( y != p.y() ); | ||
120 | } else { | ||
121 | d = dy; | ||
122 | do { | ||
123 | x += ix; | ||
124 | d += dy; | ||
125 | if ( d > dx ) { | ||
126 | y += iy; | ||
127 | d -= dx; | ||
128 | } | ||
129 | internalAddPoint( QPoint( x, y ) ); | ||
130 | } while ( x != p.x() ); | ||
131 | } | ||
132 | } else { | ||
133 | internalAddPoint( p ); | ||
134 | } | ||
135 | |||
136 | return TRUE; | ||
137 | } | ||
138 | |||
139 | /*! | ||
140 | Finish inputting a stroke. | ||
141 | */ | ||
142 | void QIMPenStroke::endInput() | ||
143 | { | ||
144 | if ( links.count() < 3 ) { | ||
145 | QIMPenGlyphLink gl; | ||
146 | links.resize(1); | ||
147 | gl.dx = 1; | ||
148 | gl.dy = 0; | ||
149 | links[0] = gl; | ||
150 | } | ||
151 | |||
152 | //qDebug("Points: %d", links.count() ); | ||
153 | } | ||
154 | |||
155 | /*! | ||
156 | Return an indicator of the closeness of this stroke to \a pen. | ||
157 | Lower value is better. | ||
158 | */ | ||
159 | unsigned int QIMPenStroke::match( QIMPenStroke *pen ) | ||
160 | { | ||
161 | double lratio; | ||
162 | |||
163 | if ( links.count() > pen->links.count() ) | ||
164 | lratio = (links.count()+2) / (pen->links.count()+2); | ||
165 | else | ||
166 | lratio = (pen->links.count()+2) / (links.count()+2); | ||
167 | |||
168 | lratio -= 1.0; | ||
169 | |||
170 | if ( lratio > 2.0 ) { | ||
171 | #ifdef DEBUG_QIMPEN | ||
172 | qDebug( "stroke length too different" ); | ||
173 | #endif | ||
174 | return 400000; | ||
175 | } | ||
176 | |||
177 | createSignatures(); | ||
178 | pen->createSignatures(); | ||
179 | |||
180 | // Starting point offset | ||
181 | int vdiff = QABS(startPoint.y() - pen->startPoint.y()); | ||
182 | |||
183 | // Insanely offset? | ||
184 | if ( vdiff > 18 ) { | ||
185 | return 400000; | ||
186 | } | ||
187 | |||
188 | vdiff -= 4; | ||
189 | if ( vdiff < 0 ) | ||
190 | vdiff = 0; | ||
191 | |||
192 | // Ending point offset | ||
193 | int evdiff = QABS(lastPoint.y() - pen->lastPoint.y()); | ||
194 | // Insanely offset? | ||
195 | if ( evdiff > 20 ) { | ||
196 | return 400000; | ||
197 | } | ||
198 | |||
199 | evdiff -= 5; | ||
200 | if ( evdiff < 0 ) | ||
201 | evdiff = 0; | ||
202 | |||
203 | // do a correlation with the three available signatures. | ||
204 | int err1 = INT_MAX; | ||
205 | int err2 = INT_MAX; | ||
206 | int err3 = INT_MAX; | ||
207 | |||
208 | // base has extra points at the start and end to enable | ||
209 | // correlation of a sliding window with the pen supplied. | ||
210 | QArray<int> base = createBase( tsig, 2 ); | ||
211 | for ( int i = 0; i < 4; i++ ) { | ||
212 | int e = calcError( base, pen->tsig, i, TRUE ); | ||
213 | if ( e < err1 ) | ||
214 | err1 = e; | ||
215 | } | ||
216 | if ( err1 > 40 ) { // no need for more matching | ||
217 | #ifdef DEBUG_QIMPEN | ||
218 | qDebug( "tsig too great: %d", err1 ); | ||
219 | #endif | ||
220 | return 400000; | ||
221 | } | ||
222 | |||
223 | // maybe a sliding window is worthwhile for these too. | ||
224 | err2 = calcError( dsig, pen->dsig, 0, FALSE ); | ||
225 | if ( err2 > 100 ) { | ||
226 | #ifdef DEBUG_QIMPEN | ||
227 | qDebug( "dsig too great: %d", err2 ); | ||
228 | #endif | ||
229 | return 400000; | ||
230 | } | ||
231 | |||
232 | err3 = calcError( asig, pen->asig, 0, TRUE ); | ||
233 | if ( err3 > 60 ) { | ||
234 | #ifdef DEBUG_QIMPEN | ||
235 | qDebug( "asig too great: %d", err3 ); | ||
236 | #endif | ||
237 | return 400000; | ||
238 | } | ||
239 | |||
240 | // Some magic numbers here - the addition reduces the weighting of | ||
241 | // the error and compensates for the different error scales. I | ||
242 | // consider the tangent signature to be the best indicator, so it | ||
243 | // has the most weight. This ain't rocket science. | ||
244 | // Basically, these numbers are the tuning factors. | ||
245 | unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) + | ||
246 | vdiff * 1000 + evdiff * 500 + | ||
247 | (unsigned int)(lratio * 5000.0); | ||
248 | |||
249 | #ifdef DEBUG_QIMPEN | ||
250 | qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff ); | ||
251 | #endif | ||
252 | |||
253 | return err; | ||
254 | } | ||
255 | |||
256 | /*! | ||
257 | Return the bounding rect of this stroke. | ||
258 | */ | ||
259 | QRect QIMPenStroke::boundingRect() | ||
260 | { | ||
261 | if ( !bounding.isValid() ) { | ||
262 | int x = startPoint.x(); | ||
263 | int y = startPoint.y(); | ||
264 | bounding = QRect( x, y, 1, 1 ); | ||
265 | |||
266 | for ( unsigned i = 0; i < links.count(); i++ ) { | ||
267 | x += links[i].dx; | ||
268 | y += links[i].dy; | ||
269 | if ( x < bounding.left() ) | ||
270 | bounding.setLeft( x ); | ||
271 | if ( x > bounding.right() ) | ||
272 | bounding.setRight( x ); | ||
273 | if ( y < bounding.top() ) | ||
274 | bounding.setTop( y ); | ||
275 | if ( y > bounding.bottom() ) | ||
276 | bounding.setBottom( y ); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | return bounding; | ||
281 | } | ||
282 | |||
283 | |||
284 | /*! | ||
285 | Perform a correlation of the supplied arrays. \a base should have | ||
286 | win.count() + 2 * off points to enable sliding \a win over the | ||
287 | \a base data. If \a t is TRUE, the comparison takes into account | ||
288 | the circular nature of the angular data. | ||
289 | Returns the best (lowest error) match. | ||
290 | */ | ||
291 | |||
292 | int QIMPenStroke::calcError( const QArray<int> &base, | ||
293 | const QArray<int> &win, int off, bool t ) | ||
294 | { | ||
295 | int err = 0; | ||
296 | |||
297 | for ( unsigned i = 0; i < win.count(); i++ ) { | ||
298 | int d = QABS( base[i+off] - win[i] ); | ||
299 | if ( t && d > 128 ) | ||
300 | d -= 256; | ||
301 | err += QABS( d ); | ||
302 | } | ||
303 | |||
304 | err /= win.count(); | ||
305 | |||
306 | return err; | ||
307 | } | ||
308 | |||
309 | /*! | ||
310 | Creates signatures used in matching if not already created. | ||
311 | */ | ||
312 | void QIMPenStroke::createSignatures() | ||
313 | { | ||
314 | if ( tsig.isEmpty() ) | ||
315 | createTanSignature(); | ||
316 | if ( asig.isEmpty() ) | ||
317 | createAngleSignature(); | ||
318 | if ( dsig.isEmpty() ) | ||
319 | createDistSignature(); | ||
320 | } | ||
321 | |||
322 | /*! | ||
323 | Create a signature of the tangents to the user's stroke. | ||
324 | */ | ||
325 | void QIMPenStroke::createTanSignature() | ||
326 | { | ||
327 | int dist = 5; // number of points to include in calculation | ||
328 | if ( (int)links.count() <= dist ) { | ||
329 | tsig.resize(1); | ||
330 | int dx = 0; | ||
331 | int dy = 0; | ||
332 | for ( unsigned j = 0; j < links.count(); j++ ) { | ||
333 | dx += links[j].dx; | ||
334 | dy += links[j].dy; | ||
335 | } | ||
336 | tsig[0] = arcTan( dy, dx ); | ||
337 | } else { | ||
338 | tsig.resize( (links.count()-dist+1) / 2 ); | ||
339 | int idx = 0; | ||
340 | for ( unsigned i = 0; i < links.count() - dist; i += 2 ) { | ||
341 | int dx = 0; | ||
342 | int dy = 0; | ||
343 | for ( int j = 0; j < dist; j++ ) { | ||
344 | dx += links[i+j].dx; | ||
345 | dy += links[i+j].dy; | ||
346 | } | ||
347 | tsig[idx++] = arcTan( dy, dx ); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | tsig = scale( tsig, QIMPEN_CORRELATION_POINTS, TRUE ); | ||
352 | // smooth(tsig); | ||
353 | } | ||
354 | |||
355 | /*! | ||
356 | Create a signature of the change in angle. | ||
357 | */ | ||
358 | void QIMPenStroke::createAngleSignature() | ||
359 | { | ||
360 | QPoint c = calcCenter(); | ||
361 | |||
362 | int dist = 3; // number of points to include in calculation | ||
363 | if ( (int)links.count() <= dist ) { | ||
364 | asig.resize(1); | ||
365 | asig[0] = 1; | ||
366 | } else { | ||
367 | asig.resize( links.count() ); | ||
368 | QPoint current(0, 0); | ||
369 | int idx = 0; | ||
370 | for ( unsigned i = 0; i < links.count(); i++ ) { | ||
371 | int dx = c.x() - current.x(); | ||
372 | int dy = c.y() - current.y(); | ||
373 | int md = QMAX( QABS(dx), QABS(dy) ); | ||
374 | if ( md > 5 ) { | ||
375 | dx = dx * 5 / md; | ||
376 | dy = dy * 5 / md; | ||
377 | } | ||
378 | asig[idx++] = arcTan( dy, dx ); | ||
379 | current += QPoint( links[i].dx, links[i].dy ); | ||
380 | } | ||
381 | } | ||
382 | |||
383 | asig = scale( asig, QIMPEN_CORRELATION_POINTS, TRUE ); | ||
384 | |||
385 | /* | ||
386 | if ( tsig.isEmpty() ) | ||
387 | createTanSignature(); | ||
388 | |||
389 | if ( tsig.count() < 5 ) { | ||
390 | asig.resize( 1 ); | ||
391 | asig[0] = 0; | ||
392 | } else { | ||
393 | asig.resize( tsig.count() - 5 ); | ||
394 | |||
395 | for ( unsigned i = 0; i < asig.count(); i++ ) { | ||
396 | asig[i] = QABS(tsig[i] - tsig[i+5]); | ||
397 | } | ||
398 | } | ||
399 | */ | ||
400 | } | ||
401 | |||
402 | /*! | ||
403 | Create a signature of the distance from the char's center of gravity | ||
404 | to its points. | ||
405 | */ | ||
406 | void QIMPenStroke::createDistSignature() | ||
407 | { | ||
408 | dsig.resize( (links.count()+1)/2 ); | ||
409 | QPoint c = calcCenter(); | ||
410 | QPoint pt( 0, 0 ); | ||
411 | |||
412 | int minval = INT_MAX; | ||
413 | int maxval = 0; | ||
414 | int idx = 0; | ||
415 | for ( unsigned i = 0; i < links.count(); i += 2 ) { | ||
416 | int dx = c.x() - pt.x(); | ||
417 | int dy = c.y() - pt.y(); | ||
418 | if ( dx == 0 && dy == 0 ) | ||
419 | dsig[idx] = 0; | ||
420 | else | ||
421 | dsig[idx] = dx*dx + dy*dy; | ||
422 | |||
423 | if ( dsig[idx] > maxval ) | ||
424 | maxval = dsig[idx]; | ||
425 | if ( dsig[idx] < minval ) | ||
426 | minval = dsig[idx]; | ||
427 | pt.rx() += links[i].dx; | ||
428 | pt.ry() += links[i].dy; | ||
429 | idx++; | ||
430 | } | ||
431 | |||
432 | // normalise 0-255 | ||
433 | int div = maxval - minval; | ||
434 | if ( div == 0 ) div = 1; | ||
435 | for ( unsigned i = 0; i < dsig.count(); i++ ) { | ||
436 | dsig[i] = (dsig[i] - minval ) * 255 / div; | ||
437 | } | ||
438 | |||
439 | dsig = scale( dsig, QIMPEN_CORRELATION_POINTS ); | ||
440 | } | ||
441 | |||
442 | |||
443 | /*! | ||
444 | Scale the points in a array to \a count points. | ||
445 | This is braindead at the moment (no smooth scaling) and fixing this is | ||
446 | probably one of the simpler ways to improve performance. | ||
447 | */ | ||
448 | QArray<int> QIMPenStroke::scale( const QArray<int> &s, unsigned count, bool t ) | ||
449 | { | ||
450 | QArray<int> d(count); | ||
451 | |||
452 | unsigned si = 0; | ||
453 | if ( s.count() > count ) { | ||
454 | unsigned next = 0; | ||
455 | for ( unsigned i = 0; i < count; i++ ) { | ||
456 | next = (i+1) * s.count() / count; | ||
457 | int maxval = 0; | ||
458 | if ( t ) { | ||
459 | for ( unsigned j = si; j < next; j++ ) { | ||
460 | maxval = s[j] > maxval ? s[j] : maxval; | ||
461 | } | ||
462 | } | ||
463 | int sum = 0; | ||
464 | for ( unsigned j = si; j < next; j++ ) { | ||
465 | if ( t && maxval - s[j] > 128 ) | ||
466 | sum += 256; | ||
467 | sum += s[j]; | ||
468 | } | ||
469 | d[i] = sum / (next-si); | ||
470 | if ( t && d[i] > 256 ) | ||
471 | d[i] %= 256; | ||
472 | si = next; | ||
473 | } | ||
474 | } else { | ||
475 | for ( unsigned i = 0; i < count; i++ ) { | ||
476 | si = i * s.count() / count; | ||
477 | d[i] = s[si]; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | return d; | ||
482 | } | ||
483 | |||
484 | /*! | ||
485 | Add another point to the stroke's shape. | ||
486 | */ | ||
487 | void QIMPenStroke::internalAddPoint( QPoint p ) | ||
488 | { | ||
489 | if ( p == lastPoint ) | ||
490 | return; | ||
491 | |||
492 | if ( !lastPoint.isNull() ) { | ||
493 | QIMPenGlyphLink gl; | ||
494 | gl.dx = p.x() - lastPoint.x(); | ||
495 | gl.dy = p.y() - lastPoint.y(); | ||
496 | links.resize( links.size() + 1 ); //### resize by 1 is bad | ||
497 | links[links.size() - 1] = gl; | ||
498 | } | ||
499 | |||
500 | lastPoint = p; | ||
501 | bounding = QRect(); | ||
502 | } | ||
503 | |||
504 | /*! | ||
505 | Calculate the center of gravity of the stroke. | ||
506 | */ | ||
507 | QPoint QIMPenStroke::calcCenter() | ||
508 | { | ||
509 | QPoint pt( 0, 0 ); | ||
510 | int ax = 0; | ||
511 | int ay = 0; | ||
512 | |||
513 | for ( unsigned i = 0; i < links.count(); i++ ) { | ||
514 | pt.rx() += links[i].dx; | ||
515 | pt.ry() += links[i].dy; | ||
516 | ax += pt.x(); | ||
517 | ay += pt.y(); | ||
518 | } | ||
519 | |||
520 | ax /= (int)links.count(); | ||
521 | ay /= (int)links.count(); | ||
522 | |||
523 | return QPoint( ax, ay ); | ||
524 | } | ||
525 | |||
526 | /*! | ||
527 | Calculate the arctan of the lengths supplied. | ||
528 | The angle returned is in the range 0-255. | ||
529 | \a dy and \a dx MUST be in the range 0-5 - I dont even check :-P | ||
530 | */ | ||
531 | int QIMPenStroke::arcTan( int dy, int dx ) | ||
532 | { | ||
533 | if ( dx == 0 ) { | ||
534 | if ( dy >= 0 ) | ||
535 | return 64; | ||
536 | else | ||
537 | return 192; | ||
538 | } | ||
539 | |||
540 | if ( dy == 0 ) { | ||
541 | if ( dx >= 0 ) | ||
542 | return 0; | ||
543 | else | ||
544 | return 128; | ||
545 | } | ||
546 | |||
547 | static int table[5][5] = { | ||
548 | { 32, 19, 13, 10, 8 }, | ||
549 | { 45, 32, 24, 19, 16 }, | ||
550 | { 51, 40, 32, 26, 22 }, | ||
551 | { 54, 45, 37, 32, 27 }, | ||
552 | { 56, 49, 42, 37, 32 } }; | ||
553 | |||
554 | if ( dy > 0 ) { | ||
555 | if ( dx > 0 ) | ||
556 | return table[dy-1][dx-1]; | ||
557 | else | ||
558 | return 128 - table[dy-1][QABS(dx)-1]; | ||
559 | } else { | ||
560 | if ( dx > 0 ) | ||
561 | return 256 - table[QABS(dy)-1][dx-1]; | ||
562 | else | ||
563 | return 128 + table[QABS(dy)-1][QABS(dx)-1]; | ||
564 | } | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | |||
570 | /*! | ||
571 | Silly name. Create an array that has \a e points extra at the start and | ||
572 | end to enable a sliding correlation to be performed. | ||
573 | */ | ||
574 | QArray<int> QIMPenStroke::createBase( const QArray<int> a, int e ) | ||
575 | { | ||
576 | QArray<int> ra( a.count() + 2*e ); | ||
577 | |||
578 | for ( int i = 0; i < e; i++ ) { | ||
579 | ra[i] = a[e - i - 1]; | ||
580 | ra[a.count() + i] = a[a.count() - i - 1]; | ||
581 | } | ||
582 | for ( unsigned i = 0; i < a.count(); i++ ) { | ||
583 | ra[i+e] = a[i]; | ||
584 | } | ||
585 | |||
586 | return ra; | ||
587 | } | ||
588 | |||
589 | |||
590 | /*! | ||
591 | Smooth the points in an array. Probably a bad idea. | ||
592 | */ | ||
593 | void QIMPenStroke::smooth( QArray<int> &sig) | ||
594 | { | ||
595 | QArray<int> nsig = sig.copy(); | ||
596 | |||
597 | int a; | ||
598 | for ( unsigned i = 1; i < sig.count()-2; i++ ) { | ||
599 | a = 0; | ||
600 | for ( int j = -1; j <= 1; j++ ) { | ||
601 | a += sig[ i + j ]; | ||
602 | } | ||
603 | nsig[i] = a / 3; | ||
604 | } | ||
605 | |||
606 | sig = nsig; | ||
607 | } | ||
608 | |||
609 | /*! | ||
610 | Write the character's data to the stream. | ||
611 | */ | ||
612 | QDataStream &operator<< (QDataStream &s, const QIMPenStroke &ws) | ||
613 | { | ||
614 | s << ws.startPoint; | ||
615 | s << ws.links.count(); | ||
616 | for ( unsigned i = 0; i < ws.links.count(); i++ ) { | ||
617 | s << (Q_INT8)ws.links[i].dx; | ||
618 | s << (Q_INT8)ws.links[i].dy; | ||
619 | } | ||
620 | |||
621 | return s; | ||
622 | } | ||
623 | |||
624 | /*! | ||
625 | Read the character's data from the stream. | ||
626 | */ | ||
627 | QDataStream &operator>> (QDataStream &s, QIMPenStroke &ws) | ||
628 | { | ||
629 | Q_INT8 i8; | ||
630 | s >> ws.startPoint; | ||
631 | ws.lastPoint = ws.startPoint; | ||
632 | unsigned size; | ||
633 | s >> size; | ||
634 | ws.links.resize( size ); | ||
635 | for ( unsigned i = 0; i < size; i++ ) { | ||
636 | s >> i8; | ||
637 | ws.links[i].dx = i8; | ||
638 | s >> i8; | ||
639 | ws.links[i].dy = i8; | ||
640 | ws.lastPoint += QPoint( ws.links[i].dx, ws.links[i].dy ); | ||
641 | } | ||
642 | |||
643 | return s; | ||
644 | } | ||
645 | |||
646 | |||