Diffstat (limited to 'inputmethods/handwriting/qimpenstroke.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | inputmethods/handwriting/qimpenstroke.cpp | 15 |
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 @@ | |||
1 | /********************************************************************** | 1 | /********************************************************************** |
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | 2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. |
3 | ** | 3 | ** |
4 | ** This file is part of Qtopia Environment. | 4 | ** This file is part of Qtopia Environment. |
5 | ** | 5 | ** |
6 | ** This file may be distributed and/or modified under the terms of the | 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 | 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 | 8 | ** Foundation and appearing in the file LICENSE.GPL included in the |
9 | ** packaging of this file. | 9 | ** packaging of this file. |
10 | ** | 10 | ** |
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | 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. | 12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
13 | ** | 13 | ** |
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | 14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. |
15 | ** | 15 | ** |
16 | ** Contact info@trolltech.com if any conditions of this licensing are | 16 | ** Contact info@trolltech.com if any conditions of this licensing are |
17 | ** not clear to you. | 17 | ** not clear to you. |
18 | ** | 18 | ** |
19 | **********************************************************************/ | 19 | **********************************************************************/ |
20 | 20 | ||
21 | #include <qfile.h> | 21 | #include <qfile.h> |
22 | #include <qtl.h> | 22 | #include <qtl.h> |
23 | #include <math.h> | 23 | #include <math.h> |
24 | #include <limits.h> | 24 | #include <limits.h> |
25 | #include <qdatastream.h> | 25 | #include <qdatastream.h> |
26 | #include "qimpenstroke.h" | 26 | #include "qimpenstroke.h" |
27 | #include "opie2/odebug.h" | ||
27 | 28 | ||
28 | #define QIMPEN_CORRELATION_POINTS 25 | 29 | #define QIMPEN_CORRELATION_POINTS 25 |
29 | //#define DEBUG_QIMPEN | 30 | //#define DEBUG_QIMPEN |
30 | 31 | ||
31 | /*! | 32 | /*! |
32 | \class QIMPenStroke qimpenstroke.h | 33 | \class QIMPenStroke qimpenstroke.h |
33 | 34 | ||
34 | Handles a single stroke. Can calculate closeness of match to | 35 | Handles a single stroke. Can calculate closeness of match to |
35 | another stroke. | 36 | another stroke. |
36 | */ | 37 | */ |
37 | 38 | ||
38 | QIMPenStroke::QIMPenStroke() | 39 | QIMPenStroke::QIMPenStroke() |
39 | { | 40 | { |
40 | } | 41 | } |
41 | 42 | ||
42 | QIMPenStroke::QIMPenStroke( const QIMPenStroke &st ) | 43 | QIMPenStroke::QIMPenStroke( const QIMPenStroke &st ) |
43 | { | 44 | { |
44 | startPoint = st.startPoint; | 45 | startPoint = st.startPoint; |
45 | lastPoint = st.lastPoint; | 46 | lastPoint = st.lastPoint; |
46 | links = st.links.copy(); | 47 | links = st.links.copy(); |
47 | } | 48 | } |
48 | 49 | ||
49 | QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s ) | 50 | QIMPenStroke &QIMPenStroke::operator=( const QIMPenStroke &s ) |
50 | { | 51 | { |
51 | clear(); | 52 | clear(); |
52 | //qDebug( "copy strokes %d", s.links.count() ); | 53 | //odebug << "copy strokes " << s.links.count() << oendl; |
53 | startPoint = s.startPoint; | 54 | startPoint = s.startPoint; |
54 | lastPoint = s.lastPoint; | 55 | lastPoint = s.lastPoint; |
55 | links = s.links.copy(); | 56 | links = s.links.copy(); |
56 | 57 | ||
57 | return *this; | 58 | return *this; |
58 | } | 59 | } |
59 | 60 | ||
60 | void QIMPenStroke::clear() | 61 | void QIMPenStroke::clear() |
61 | { | 62 | { |
62 | startPoint = QPoint(0,0); | 63 | startPoint = QPoint(0,0); |
63 | lastPoint = QPoint( 0, 0 ); | 64 | lastPoint = QPoint( 0, 0 ); |
64 | links.resize( 0 ); | 65 | links.resize( 0 ); |
65 | tsig.resize( 0 ); | 66 | tsig.resize( 0 ); |
66 | dsig.resize( 0 ); | 67 | dsig.resize( 0 ); |
67 | asig.resize( 0 ); | 68 | asig.resize( 0 ); |
68 | } | 69 | } |
69 | 70 | ||
70 | /*! | 71 | /*! |
71 | Begin inputting a new stroke. | 72 | Begin inputting a new stroke. |
72 | */ | 73 | */ |
73 | void QIMPenStroke::beginInput( QPoint p ) | 74 | void QIMPenStroke::beginInput( QPoint p ) |
74 | { | 75 | { |
75 | clear(); | 76 | clear(); |
76 | startPoint = p; | 77 | startPoint = p; |
77 | bounding = QRect(); | 78 | bounding = QRect(); |
78 | internalAddPoint( p ); | 79 | internalAddPoint( p ); |
79 | } | 80 | } |
80 | 81 | ||
81 | /*! | 82 | /*! |
82 | Add a point to the stroke's shape. | 83 | Add a point to the stroke's shape. |
83 | Returns TRUE if the point was successfully added. | 84 | Returns TRUE if the point was successfully added. |
84 | */ | 85 | */ |
85 | bool QIMPenStroke::addPoint( QPoint p ) | 86 | bool QIMPenStroke::addPoint( QPoint p ) |
86 | { | 87 | { |
87 | if ( links.count() > 500 ) // sanity check (that the user is sane). | 88 | if ( links.count() > 500 ) // sanity check (that the user is sane). |
88 | return FALSE; | 89 | return FALSE; |
89 | 90 | ||
90 | int dx = p.x() - lastPoint.x(); | 91 | int dx = p.x() - lastPoint.x(); |
91 | int dy = p.y() - lastPoint.y(); | 92 | int dy = p.y() - lastPoint.y(); |
92 | if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) { | 93 | if ( QABS( dx ) > 1 || QABS( dy ) > 1 ) { |
93 | // The point is not adjacent to the previous point, so we fill | 94 | // The point is not adjacent to the previous point, so we fill |
94 | // in with a straight line. Some kind of non-linear | 95 | // in with a straight line. Some kind of non-linear |
95 | // interpolation might be better. | 96 | // interpolation might be better. |
96 | int x = lastPoint.x(); | 97 | int x = lastPoint.x(); |
97 | int y = lastPoint.y(); | 98 | int y = lastPoint.y(); |
98 | int ix = 1; | 99 | int ix = 1; |
99 | int iy = 1; | 100 | int iy = 1; |
100 | if ( dx < 0 ) { | 101 | if ( dx < 0 ) { |
101 | ix = -1; | 102 | ix = -1; |
102 | dx = -dx; | 103 | dx = -dx; |
103 | } | 104 | } |
104 | if ( dy < 0 ) { | 105 | if ( dy < 0 ) { |
105 | iy = -1; | 106 | iy = -1; |
106 | dy = -dy; | 107 | dy = -dy; |
107 | } | 108 | } |
108 | int d = 0; | 109 | int d = 0; |
109 | if ( dx < dy ) { | 110 | if ( dx < dy ) { |
110 | d = dx; | 111 | d = dx; |
111 | do { | 112 | do { |
112 | y += iy; | 113 | y += iy; |
113 | d += dx; | 114 | d += dx; |
114 | if ( d > dy ) { | 115 | if ( d > dy ) { |
115 | x += ix; | 116 | x += ix; |
116 | d -= dy; | 117 | d -= dy; |
117 | } | 118 | } |
118 | internalAddPoint( QPoint( x, y ) ); | 119 | internalAddPoint( QPoint( x, y ) ); |
119 | } while ( y != p.y() ); | 120 | } while ( y != p.y() ); |
120 | } else { | 121 | } else { |
121 | d = dy; | 122 | d = dy; |
122 | do { | 123 | do { |
123 | x += ix; | 124 | x += ix; |
124 | d += dy; | 125 | d += dy; |
125 | if ( d > dx ) { | 126 | if ( d > dx ) { |
126 | y += iy; | 127 | y += iy; |
127 | d -= dx; | 128 | d -= dx; |
128 | } | 129 | } |
129 | internalAddPoint( QPoint( x, y ) ); | 130 | internalAddPoint( QPoint( x, y ) ); |
130 | } while ( x != p.x() ); | 131 | } while ( x != p.x() ); |
131 | } | 132 | } |
132 | } else { | 133 | } else { |
133 | internalAddPoint( p ); | 134 | internalAddPoint( p ); |
134 | } | 135 | } |
135 | 136 | ||
136 | return TRUE; | 137 | return TRUE; |
137 | } | 138 | } |
138 | 139 | ||
139 | /*! | 140 | /*! |
140 | Finish inputting a stroke. | 141 | Finish inputting a stroke. |
141 | */ | 142 | */ |
142 | void QIMPenStroke::endInput() | 143 | void QIMPenStroke::endInput() |
143 | { | 144 | { |
144 | if ( links.count() < 3 ) { | 145 | if ( links.count() < 3 ) { |
145 | QIMPenGlyphLink gl; | 146 | QIMPenGlyphLink gl; |
146 | links.resize(1); | 147 | links.resize(1); |
147 | gl.dx = 1; | 148 | gl.dx = 1; |
148 | gl.dy = 0; | 149 | gl.dy = 0; |
149 | links[0] = gl; | 150 | links[0] = gl; |
150 | } | 151 | } |
151 | 152 | ||
152 | //qDebug("Points: %d", links.count() ); | 153 | //odebug << "Points: " << links.count() << oendl; |
153 | } | 154 | } |
154 | 155 | ||
155 | /*! | 156 | /*! |
156 | Return an indicator of the closeness of this stroke to \a pen. | 157 | Return an indicator of the closeness of this stroke to \a pen. |
157 | Lower value is better. | 158 | Lower value is better. |
158 | */ | 159 | */ |
159 | unsigned int QIMPenStroke::match( QIMPenStroke *pen ) | 160 | unsigned int QIMPenStroke::match( QIMPenStroke *pen ) |
160 | { | 161 | { |
161 | double lratio; | 162 | double lratio; |
162 | 163 | ||
163 | if ( links.count() > pen->links.count() ) | 164 | if ( links.count() > pen->links.count() ) |
164 | lratio = (links.count()+2) / (pen->links.count()+2); | 165 | lratio = (links.count()+2) / (pen->links.count()+2); |
165 | else | 166 | else |
166 | lratio = (pen->links.count()+2) / (links.count()+2); | 167 | lratio = (pen->links.count()+2) / (links.count()+2); |
167 | 168 | ||
168 | lratio -= 1.0; | 169 | lratio -= 1.0; |
169 | 170 | ||
170 | if ( lratio > 2.0 ) { | 171 | if ( lratio > 2.0 ) { |
171 | #ifdef DEBUG_QIMPEN | 172 | #ifdef DEBUG_QIMPEN |
172 | qDebug( "stroke length too different" ); | 173 | odebug << "stroke length too different" << oendl; |
173 | #endif | 174 | #endif |
174 | return 400000; | 175 | return 400000; |
175 | } | 176 | } |
176 | 177 | ||
177 | createSignatures(); | 178 | createSignatures(); |
178 | pen->createSignatures(); | 179 | pen->createSignatures(); |
179 | 180 | ||
180 | // Starting point offset | 181 | // Starting point offset |
181 | int vdiff = QABS(startPoint.y() - pen->startPoint.y()); | 182 | int vdiff = QABS(startPoint.y() - pen->startPoint.y()); |
182 | 183 | ||
183 | // Insanely offset? | 184 | // Insanely offset? |
184 | if ( vdiff > 18 ) { | 185 | if ( vdiff > 18 ) { |
185 | return 400000; | 186 | return 400000; |
186 | } | 187 | } |
187 | 188 | ||
188 | vdiff -= 4; | 189 | vdiff -= 4; |
189 | if ( vdiff < 0 ) | 190 | if ( vdiff < 0 ) |
190 | vdiff = 0; | 191 | vdiff = 0; |
191 | 192 | ||
192 | // Ending point offset | 193 | // Ending point offset |
193 | int evdiff = QABS(lastPoint.y() - pen->lastPoint.y()); | 194 | int evdiff = QABS(lastPoint.y() - pen->lastPoint.y()); |
194 | // Insanely offset? | 195 | // Insanely offset? |
195 | if ( evdiff > 20 ) { | 196 | if ( evdiff > 20 ) { |
196 | return 400000; | 197 | return 400000; |
197 | } | 198 | } |
198 | 199 | ||
199 | evdiff -= 5; | 200 | evdiff -= 5; |
200 | if ( evdiff < 0 ) | 201 | if ( evdiff < 0 ) |
201 | evdiff = 0; | 202 | evdiff = 0; |
202 | 203 | ||
203 | // do a correlation with the three available signatures. | 204 | // do a correlation with the three available signatures. |
204 | int err1 = INT_MAX; | 205 | int err1 = INT_MAX; |
205 | int err2 = INT_MAX; | 206 | int err2 = INT_MAX; |
206 | int err3 = INT_MAX; | 207 | int err3 = INT_MAX; |
207 | 208 | ||
208 | // base has extra points at the start and end to enable | 209 | // base has extra points at the start and end to enable |
209 | // correlation of a sliding window with the pen supplied. | 210 | // correlation of a sliding window with the pen supplied. |
210 | QArray<int> base = createBase( tsig, 2 ); | 211 | QArray<int> base = createBase( tsig, 2 ); |
211 | for ( int i = 0; i < 4; i++ ) { | 212 | for ( int i = 0; i < 4; i++ ) { |
212 | int e = calcError( base, pen->tsig, i, TRUE ); | 213 | int e = calcError( base, pen->tsig, i, TRUE ); |
213 | if ( e < err1 ) | 214 | if ( e < err1 ) |
214 | err1 = e; | 215 | err1 = e; |
215 | } | 216 | } |
216 | if ( err1 > 40 ) { // no need for more matching | 217 | if ( err1 > 40 ) { // no need for more matching |
217 | #ifdef DEBUG_QIMPEN | 218 | #ifdef DEBUG_QIMPEN |
218 | qDebug( "tsig too great: %d", err1 ); | 219 | odebug << "tsig too great: " << err1 << oendl; |
219 | #endif | 220 | #endif |
220 | return 400000; | 221 | return 400000; |
221 | } | 222 | } |
222 | 223 | ||
223 | // maybe a sliding window is worthwhile for these too. | 224 | // maybe a sliding window is worthwhile for these too. |
224 | err2 = calcError( dsig, pen->dsig, 0, FALSE ); | 225 | err2 = calcError( dsig, pen->dsig, 0, FALSE ); |
225 | if ( err2 > 100 ) { | 226 | if ( err2 > 100 ) { |
226 | #ifdef DEBUG_QIMPEN | 227 | #ifdef DEBUG_QIMPEN |
227 | qDebug( "dsig too great: %d", err2 ); | 228 | odebug << "dsig too great: " << err2 << oendl; |
228 | #endif | 229 | #endif |
229 | return 400000; | 230 | return 400000; |
230 | } | 231 | } |
231 | 232 | ||
232 | err3 = calcError( asig, pen->asig, 0, TRUE ); | 233 | err3 = calcError( asig, pen->asig, 0, TRUE ); |
233 | if ( err3 > 60 ) { | 234 | if ( err3 > 60 ) { |
234 | #ifdef DEBUG_QIMPEN | 235 | #ifdef DEBUG_QIMPEN |
235 | qDebug( "asig too great: %d", err3 ); | 236 | odebug << "asig too great: " << err3 << oendl; |
236 | #endif | 237 | #endif |
237 | return 400000; | 238 | return 400000; |
238 | } | 239 | } |
239 | 240 | ||
240 | // Some magic numbers here - the addition reduces the weighting of | 241 | // Some magic numbers here - the addition reduces the weighting of |
241 | // the error and compensates for the different error scales. I | 242 | // the error and compensates for the different error scales. I |
242 | // consider the tangent signature to be the best indicator, so it | 243 | // consider the tangent signature to be the best indicator, so it |
243 | // has the most weight. This ain't rocket science. | 244 | // has the most weight. This ain't rocket science. |
244 | // Basically, these numbers are the tuning factors. | 245 | // Basically, these numbers are the tuning factors. |
245 | unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) + | 246 | unsigned int err = (err1+1) * ( err2 + 60 ) * ( err3 + 20 ) + |
246 | vdiff * 1000 + evdiff * 500 + | 247 | vdiff * 1000 + evdiff * 500 + |
247 | (unsigned int)(lratio * 5000.0); | 248 | (unsigned int)(lratio * 5000.0); |
248 | 249 | ||
249 | #ifdef DEBUG_QIMPEN | 250 | #ifdef DEBUG_QIMPEN |
250 | qDebug( "err %d ( %d, %d, %d, %d)", err, err1, err2, err3, vdiff ); | 251 | odebug << "err " << err << "( " << err1 << ", " << err2 << ", " << err3 << ", " << vdiff << oendl; |
251 | #endif | 252 | #endif |
252 | 253 | ||
253 | return err; | 254 | return err; |
254 | } | 255 | } |
255 | 256 | ||
256 | /*! | 257 | /*! |
257 | Return the bounding rect of this stroke. | 258 | Return the bounding rect of this stroke. |
258 | */ | 259 | */ |
259 | QRect QIMPenStroke::boundingRect() | 260 | QRect QIMPenStroke::boundingRect() |
260 | { | 261 | { |
261 | if ( !bounding.isValid() ) { | 262 | if ( !bounding.isValid() ) { |
262 | int x = startPoint.x(); | 263 | int x = startPoint.x(); |
263 | int y = startPoint.y(); | 264 | int y = startPoint.y(); |
264 | bounding = QRect( x, y, 1, 1 ); | 265 | bounding = QRect( x, y, 1, 1 ); |
265 | 266 | ||
266 | for ( unsigned i = 0; i < links.count(); i++ ) { | 267 | for ( unsigned i = 0; i < links.count(); i++ ) { |
267 | x += links[i].dx; | 268 | x += links[i].dx; |
268 | y += links[i].dy; | 269 | y += links[i].dy; |
269 | if ( x < bounding.left() ) | 270 | if ( x < bounding.left() ) |
270 | bounding.setLeft( x ); | 271 | bounding.setLeft( x ); |
271 | if ( x > bounding.right() ) | 272 | if ( x > bounding.right() ) |
272 | bounding.setRight( x ); | 273 | bounding.setRight( x ); |
273 | if ( y < bounding.top() ) | 274 | if ( y < bounding.top() ) |
274 | bounding.setTop( y ); | 275 | bounding.setTop( y ); |
275 | if ( y > bounding.bottom() ) | 276 | if ( y > bounding.bottom() ) |
276 | bounding.setBottom( y ); | 277 | bounding.setBottom( y ); |
277 | } | 278 | } |
278 | } | 279 | } |
279 | 280 | ||
280 | return bounding; | 281 | return bounding; |
281 | } | 282 | } |
282 | 283 | ||
283 | 284 | ||
284 | /*! | 285 | /*! |
285 | Perform a correlation of the supplied arrays. \a base should have | 286 | 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 | 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 | \a base data. If \a t is TRUE, the comparison takes into account |
288 | the circular nature of the angular data. | 289 | the circular nature of the angular data. |
289 | Returns the best (lowest error) match. | 290 | Returns the best (lowest error) match. |
290 | */ | 291 | */ |
291 | 292 | ||
292 | int QIMPenStroke::calcError( const QArray<int> &base, | 293 | int QIMPenStroke::calcError( const QArray<int> &base, |
293 | const QArray<int> &win, int off, bool t ) | 294 | const QArray<int> &win, int off, bool t ) |
294 | { | 295 | { |
295 | int err = 0; | 296 | int err = 0; |
296 | 297 | ||
297 | for ( unsigned i = 0; i < win.count(); i++ ) { | 298 | for ( unsigned i = 0; i < win.count(); i++ ) { |
298 | int d = QABS( base[i+off] - win[i] ); | 299 | int d = QABS( base[i+off] - win[i] ); |
299 | if ( t && d > 128 ) | 300 | if ( t && d > 128 ) |
300 | d -= 256; | 301 | d -= 256; |
301 | err += QABS( d ); | 302 | err += QABS( d ); |
302 | } | 303 | } |
303 | 304 | ||
304 | err /= win.count(); | 305 | err /= win.count(); |
305 | 306 | ||
306 | return err; | 307 | return err; |
307 | } | 308 | } |
308 | 309 | ||
309 | /*! | 310 | /*! |
310 | Creates signatures used in matching if not already created. | 311 | Creates signatures used in matching if not already created. |
311 | */ | 312 | */ |
312 | void QIMPenStroke::createSignatures() | 313 | void QIMPenStroke::createSignatures() |
313 | { | 314 | { |
314 | if ( tsig.isEmpty() ) | 315 | if ( tsig.isEmpty() ) |
315 | createTanSignature(); | 316 | createTanSignature(); |
316 | if ( asig.isEmpty() ) | 317 | if ( asig.isEmpty() ) |
317 | createAngleSignature(); | 318 | createAngleSignature(); |
318 | if ( dsig.isEmpty() ) | 319 | if ( dsig.isEmpty() ) |
319 | createDistSignature(); | 320 | createDistSignature(); |
320 | } | 321 | } |
321 | 322 | ||
322 | /*! | 323 | /*! |
323 | Create a signature of the tangents to the user's stroke. | 324 | Create a signature of the tangents to the user's stroke. |
324 | */ | 325 | */ |
325 | void QIMPenStroke::createTanSignature() | 326 | void QIMPenStroke::createTanSignature() |
326 | { | 327 | { |
327 | int dist = 5; // number of points to include in calculation | 328 | int dist = 5; // number of points to include in calculation |
328 | if ( (int)links.count() <= dist ) { | 329 | if ( (int)links.count() <= dist ) { |
329 | tsig.resize(1); | 330 | tsig.resize(1); |
330 | int dx = 0; | 331 | int dx = 0; |
331 | int dy = 0; | 332 | int dy = 0; |
332 | for ( unsigned j = 0; j < links.count(); j++ ) { | 333 | for ( unsigned j = 0; j < links.count(); j++ ) { |
333 | dx += links[j].dx; | 334 | dx += links[j].dx; |
334 | dy += links[j].dy; | 335 | dy += links[j].dy; |
335 | } | 336 | } |
336 | tsig[0] = arcTan( dy, dx ); | 337 | tsig[0] = arcTan( dy, dx ); |
337 | } else { | 338 | } else { |
338 | tsig.resize( (links.count()-dist+1) / 2 ); | 339 | tsig.resize( (links.count()-dist+1) / 2 ); |
339 | int idx = 0; | 340 | int idx = 0; |
340 | for ( unsigned i = 0; i < links.count() - dist; i += 2 ) { | 341 | for ( unsigned i = 0; i < links.count() - dist; i += 2 ) { |
341 | int dx = 0; | 342 | int dx = 0; |
342 | int dy = 0; | 343 | int dy = 0; |
343 | for ( int j = 0; j < dist; j++ ) { | 344 | for ( int j = 0; j < dist; j++ ) { |
344 | dx += links[i+j].dx; | 345 | dx += links[i+j].dx; |
345 | dy += links[i+j].dy; | 346 | dy += links[i+j].dy; |
346 | } | 347 | } |