1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - PRC -
3 File: prc_algo_common.c
4
5 Copyright 2003-2008 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Date:: 2009-06-19#$
14 $Rev: 10786 $
15 $Author: okajima_manabu $
16 *---------------------------------------------------------------------------*/
17
18 #include <nitro.h>
19 #include <nitro/prc/resample.h>
20
21 /*===========================================================================*
22 Prototype Declarations
23 *===========================================================================*/
24
25
26 /*===========================================================================*
27 Variable Definitions
28 *===========================================================================*/
29
30 /*===========================================================================*
31 Functions
32 *===========================================================================*/
33
34 static BOOL
PRCi_TerminateStrokes(u16 * selectedPoints,int * pSelectedPointNum,int maxPointCount,const PRCStrokes * strokes)35 PRCi_TerminateStrokes(u16 *selectedPoints,
36 int *pSelectedPointNum, int maxPointCount, const PRCStrokes *strokes)
37 {
38 int selectedPointNum = *pSelectedPointNum;
39 const PRCPoint *inputPoints;
40
41 inputPoints = strokes->points;
42 if (selectedPointNum < 2)
43 {
44 // If length of dot series is 0 or 1, ignore
45 *pSelectedPointNum = 0;
46 return FALSE;
47 }
48 if (!PRC_IsPenUpMarker(&inputPoints[selectedPoints[selectedPointNum - 1]]))
49 {
50 // Doesn't finish with Pen Up Marker
51 if (!PRC_IsPenUpMarker(&inputPoints[selectedPoints[selectedPointNum - 2]]))
52 {
53 // Next to last is not Pen Up Marker either
54 if (selectedPointNum < maxPointCount)
55 {
56 // If there is room at the end, append Pen Up Marker
57 selectedPoints[selectedPointNum] = (u16)-1;
58 selectedPointNum++;
59 }
60 else
61 {
62 // If no space at the end
63 if (selectedPointNum >= 3
64 && !PRC_IsPenUpMarker(&inputPoints[selectedPoints[selectedPointNum - 3]]))
65 {
66 // If the third from last is also not a Pen Up Marker, overwrite the last one with Pen Up
67 selectedPoints[selectedPointNum - 1] = (u16)-1;
68 }
69 else
70 {
71 // Otherwise delete two
72 selectedPointNum -= 2;
73 }
74 }
75 }
76 else
77 {
78 // If the next to last is a Pen Up Marker, reduce by one without any pause to think
79 selectedPointNum--;
80 }
81 }
82
83 *pSelectedPointNum = selectedPointNum;
84 return TRUE;
85 }
86
87
88 /*---------------------------------------------------------------------------*
89 Name: PRC_ResampleStrokes_None
90
91 Description: Does not resample.
92
93 Arguments: selectedPoints, pSelectedPointNum: Pointers which return results
94 maxPointCount: Upper limit of number of input points (includes pen up marker)
95 maxStrokeCount: Upper limit of stroke count
96 strokes: Raw input coordinate values before sorting
97 threshold: Resampling threshold
98 buffer: Work area ([sizeof(int)*maxPointCount] required)
99
100 Returns: TRUE if resampling succeeded.
101 *---------------------------------------------------------------------------*/
102 BOOL
PRC_ResampleStrokes_None(u16 * selectedPoints,int * pSelectedPointNum,int maxPointCount,int maxStrokeCount,const PRCStrokes * strokes,int threshold,void * buffer)103 PRC_ResampleStrokes_None(u16 *selectedPoints,
104 int *pSelectedPointNum,
105 int maxPointCount,
106 int maxStrokeCount, const PRCStrokes *strokes, int threshold, void *buffer)
107 {
108 // Use the raw dot sequence data without resampling
109 u16 iPoint;
110 int size = strokes->size;
111
112 (void)maxStrokeCount;
113 (void)threshold;
114 (void)buffer;
115
116 if (size > maxPointCount)
117 {
118 size = maxPointCount;
119 }
120 if (size < 2)
121 {
122 // If length of dot series is 0 or 1, ignore
123 *pSelectedPointNum = 0;
124 }
125 else
126 {
127 // Select point automatically
128 for (iPoint = 0; iPoint < size; iPoint++)
129 {
130 selectedPoints[iPoint] = iPoint;
131 }
132 *pSelectedPointNum = iPoint;
133
134 if (!PRC_IsPenUpMarker(&strokes->points[size - 1]))
135 {
136 // Not terminated with Pen Up Marker
137 (void)PRCi_TerminateStrokes(selectedPoints, pSelectedPointNum, maxPointCount, strokes);
138 }
139 }
140
141 return (*pSelectedPointNum > 0);
142 }
143
144 #define PRCi_ABS(x) (((x)>=0)?(x):-(x))
145
146 /*---------------------------------------------------------------------------*
147 Name: PRC_ResampleStrokes_Distance
148
149 Description: Resamples based on city block distance.
150
151 Arguments: selectedPoints, pSelectedPointNum: Pointers which return results
152 maxPointCount: Upper limit of number of input points (includes pen up marker)
153 maxStrokeCount: Upper limit of stroke count
154 strokes: Raw input coordinate values before sorting
155 threshold: Resampling threshold
156 buffer: Work area ([sizeof(int)*maxPointCount] required)
157
158 Returns: TRUE if resampling succeeded.
159 *---------------------------------------------------------------------------*/
160 BOOL
PRC_ResampleStrokes_Distance(u16 * selectedPoints,int * pSelectedPointNum,int maxPointCount,int maxStrokeCount,const PRCStrokes * strokes,int threshold,void * buffer)161 PRC_ResampleStrokes_Distance(u16 *selectedPoints,
162 int *pSelectedPointNum,
163 int maxPointCount,
164 int maxStrokeCount,
165 const PRCStrokes *strokes, int threshold, void *buffer)
166 {
167 int selectedPointNum;
168 int strokeCount;
169 int iPoint;
170 int size;
171 PRCPoint prevPoint;
172 PRCPoint *point;
173 BOOL newFlag;
174 int length;
175
176 SDK_ASSERT(maxPointCount > 0);
177 SDK_ASSERT(maxStrokeCount > 0);
178
179 (void)buffer;
180
181 selectedPointNum = 0;
182 strokeCount = 0;
183
184 size = strokes->size;
185 point = strokes->points;
186
187 newFlag = TRUE;
188 for (iPoint = 0; iPoint < size && selectedPointNum < maxPointCount; iPoint++, point++)
189 {
190 if (!PRC_IsPenUpMarker(point))
191 {
192 if (newFlag)
193 {
194 // The starting point is always selected
195 selectedPoints[selectedPointNum] = (u16)iPoint;
196 selectedPointNum++;
197 length = 0;
198 newFlag = FALSE;
199 }
200 else
201 {
202 length += PRCi_ABS(point->x - prevPoint.x) + PRCi_ABS(point->y - prevPoint.y);
203 if (length >= threshold)
204 {
205 selectedPoints[selectedPointNum] = (u16)iPoint;
206 selectedPointNum++;
207 length = 0;
208 }
209 }
210 prevPoint = *point;
211 }
212 else
213 {
214 if (newFlag)
215 {
216 // Ignore several Pen Up Markers in a row
217 continue;
218 }
219 else
220 {
221 if (selectedPoints[selectedPointNum - 1] != iPoint - 1) // When execution comes here, [selectedPointNum>0] is always true
222 {
223 // End point is always selected
224 selectedPoints[selectedPointNum] = (u16)(iPoint - 1);
225 selectedPointNum++;
226 if (selectedPointNum >= maxPointCount)
227 {
228 break;
229 }
230 }
231 selectedPoints[selectedPointNum] = (u16)iPoint;
232 selectedPointNum++;
233 newFlag = TRUE;
234
235 strokeCount++;
236 if (strokeCount >= maxStrokeCount)
237 {
238 // Max number of strokes exceeded
239 iPoint++; // For the processing after the loop. // At the present time, has no meaning because newFlag == TRUE
240 break;
241 }
242 }
243 }
244 }
245
246 *pSelectedPointNum = selectedPointNum;
247
248 if (!newFlag)
249 {
250 // Not terminated with Pen Up Marker
251
252 // First, confirm that end point is selected
253 if (selectedPointNum > 0 && selectedPoints[selectedPointNum - 1] != iPoint - 1
254 && selectedPointNum < maxPointCount)
255 {
256 // End point is always selected
257 selectedPoints[*pSelectedPointNum] = (u16)(iPoint - 1);
258 (*pSelectedPointNum)++;
259 }
260
261 (void)PRCi_TerminateStrokes(selectedPoints, pSelectedPointNum, maxPointCount, strokes);
262 }
263
264 return (*pSelectedPointNum > 0);
265 }
266
267 /*---------------------------------------------------------------------------*
268 Name: PRC_ResampleStrokes_Angle
269
270 Description: Resamples, based on the angle difference.
271
272 Arguments: selectedPoints, pSelectedPointNum: Pointers which return results
273 maxPointCount: Upper limit of number of input points (includes pen up marker)
274 maxStrokeCount: Upper limit of stroke count
275 strokes: Raw input coordinate values before sorting
276 threshold: Resampling threshold
277 buffer: Work area ([sizeof(int)*maxPointCount] required)
278
279 Returns: TRUE if resampling succeeded.
280 *---------------------------------------------------------------------------*/
281 BOOL
PRC_ResampleStrokes_Angle(u16 * selectedPoints,int * pSelectedPointNum,int maxPointCount,int maxStrokeCount,const PRCStrokes * strokes,int threshold,void * buffer)282 PRC_ResampleStrokes_Angle(u16 *selectedPoints,
283 int *pSelectedPointNum,
284 int maxPointCount,
285 int maxStrokeCount,
286 const PRCStrokes *strokes, int threshold, void *buffer)
287 {
288 #define PRC_RESAMPLE_ANGLE_LENGTH_THRESHOLD 6 // A valid angle cannot be obtained unless one moves about 6 city block lengths away
289 int selectedPointNum;
290 int strokeCount;
291 int iPoint;
292 int size;
293 PRCPoint *point;
294 BOOL newFlag;
295 u16 prevAngle;
296 PRCPoint prevPoint;
297 BOOL firstFlag;
298
299 SDK_ASSERT(maxPointCount > 0);
300 SDK_ASSERT(maxStrokeCount > 0);
301
302 (void)buffer;
303
304 selectedPointNum = 0;
305 strokeCount = 0;
306
307 size = strokes->size;
308 point = strokes->points;
309
310 newFlag = TRUE;
311 for (iPoint = 0; iPoint < size && selectedPointNum < maxPointCount; iPoint++, point++)
312 {
313 if (!PRC_IsPenUpMarker(point))
314 {
315 if (newFlag)
316 {
317 // The starting point is always selected
318 selectedPoints[selectedPointNum] = (u16)iPoint;
319 selectedPointNum++;
320 prevPoint = *point;
321 if (iPoint + 1 < size)
322 {
323 prevAngle =
324 FX_Atan2Idx(((point + 1)->y - point->y) << FX32_SHIFT,
325 ((point + 1)->x - point->x) << FX32_SHIFT);
326 newFlag = FALSE;
327 firstFlag = TRUE;
328 }
329 }
330 else
331 {
332 int length;
333 length = PRCi_ABS(point->x - prevPoint.x) + PRCi_ABS(point->y - prevPoint.y);
334 if (length >= PRC_RESAMPLE_ANGLE_LENGTH_THRESHOLD)
335 {
336 if (firstFlag)
337 {
338 // Watch the angle formed with the next point until the second point is selected.
339 // Otherwise, the direction of the initial segment will be lost.
340 if (iPoint + 1 < size && !PRC_IsPenUpMarker(point + 1))
341 // If [point+1] is the Pen Up Marker, then it is always selected as the endpoint and the following conditions are not needed, but...
342 //
343 {
344 u16 currAngle, nextAngle;
345 nextAngle =
346 FX_Atan2Idx(((point + 1)->y - point->y) << FX32_SHIFT,
347 ((point + 1)->x - point->x) << FX32_SHIFT);
348 if (PRCi_ABS((s16)(prevAngle - nextAngle)) >= threshold)
349 {
350 currAngle =
351 FX_Atan2Idx((point->y - prevPoint.y) << FX32_SHIFT,
352 (point->x - prevPoint.x) << FX32_SHIFT);
353 selectedPoints[selectedPointNum] = (u16)iPoint;
354 selectedPointNum++;
355 prevAngle = currAngle;
356 firstFlag = FALSE;
357 }
358 }
359 }
360 else
361 {
362 u16 currAngle;
363 currAngle =
364 FX_Atan2Idx((point->y - prevPoint.y) << FX32_SHIFT,
365 (point->x - prevPoint.x) << FX32_SHIFT);
366 if (PRCi_ABS((s16)(prevAngle - currAngle)) >= threshold)
367 {
368 selectedPoints[selectedPointNum] = (u16)iPoint;
369 selectedPointNum++;
370 prevAngle = currAngle;
371 }
372 }
373 prevPoint = *point;
374 }
375 }
376 }
377 else
378 {
379 if (newFlag)
380 {
381 // Ignore several Pen Up Markers in a row
382 continue;
383 }
384 else
385 {
386 if (selectedPoints[selectedPointNum - 1] != iPoint - 1) // Always selectedPointNum>0 when comes to here
387 {
388 // End point is always selected
389 selectedPoints[selectedPointNum] = (u16)(iPoint - 1);
390 selectedPointNum++;
391 if (selectedPointNum >= maxPointCount)
392 {
393 break;
394 }
395 }
396 selectedPoints[selectedPointNum] = (u16)iPoint;
397 selectedPointNum++;
398 newFlag = TRUE;
399
400 strokeCount++;
401 if (strokeCount >= maxStrokeCount)
402 {
403 // Max number of strokes exceeded
404 iPoint++; // For the processing after the loop. // At the present time, has no meaning because newFlag == TRUE
405 break;
406 }
407 }
408 }
409 }
410
411 *pSelectedPointNum = selectedPointNum;
412
413 if (!newFlag)
414 {
415 // Not terminated with Pen Up Marker
416
417 // First, confirm that end point is selected
418 if (selectedPointNum > 0 && selectedPoints[selectedPointNum - 1] != iPoint - 1
419 && selectedPointNum < maxPointCount)
420 {
421 // End point is always selected
422 selectedPoints[*pSelectedPointNum] = (u16)(iPoint - 1);
423 (*pSelectedPointNum)++;
424 }
425
426 (void)PRCi_TerminateStrokes(selectedPoints, pSelectedPointNum, maxPointCount, strokes);
427 }
428
429 return (*pSelectedPointNum > 0);
430 }
431
432 /*---------------------------------------------------------------------------*
433 Name: PRC_ResampleStrokes_Recursive
434
435 Description: Resamples with recursive method.
436
437 Arguments: selectedPoints, pSelectedPointNum: Pointers which return results
438 maxPointCount: Upper limit of number of input points (includes pen up marker)
439 maxStrokeCount: Upper limit of stroke count
440 strokes: Raw input coordinate values before sorting
441 threshold: Resampling threshold
442 buffer: Work area ([sizeof(int)*maxPointCount] required)
443
444 Returns: TRUE if resampling succeeded.
445 *---------------------------------------------------------------------------*/
446 BOOL
PRC_ResampleStrokes_Recursive(u16 * selectedPoints,int * pSelectedPointNum,int maxPointCount,int maxStrokeCount,const PRCStrokes * strokes,int threshold,void * buffer)447 PRC_ResampleStrokes_Recursive(u16 *selectedPoints,
448 int *pSelectedPointNum,
449 int maxPointCount,
450 int maxStrokeCount,
451 const PRCStrokes *strokes, int threshold, void *buffer)
452 {
453 u16 beginIndex, endIndex;
454 int stackSize;
455 int stackTop, stackTail;
456 int strokeCount;
457 int selectedPointNum;
458 int size;
459 PRCPoint *inputPoints;
460 u16 *stackP1;
461 u16 *stackP2;
462 int squaredThreshold;
463
464 stackP1 = (u16 *)buffer;
465 stackP2 = (u16 *)buffer + maxPointCount;
466
467 squaredThreshold = threshold * threshold;
468
469 beginIndex = 0;
470 endIndex = 0;
471 strokeCount = 0;
472 selectedPointNum = 0;
473
474 inputPoints = strokes->points;
475 size = strokes->size;
476
477 while (1)
478 {
479 if (selectedPointNum + 3 > maxPointCount || strokeCount > maxStrokeCount)
480 {
481 // No space to store the next stroke
482 // To store a single 'stroke' you must have at least three available spaces: starting point, end point, and PenUpMarker.
483 //
484 break;
485 }
486
487 // Skip over PenUpMarker
488 while (endIndex < size && PRC_IsPenUpMarker(&inputPoints[endIndex]))
489 {
490 endIndex++;
491 }
492
493 beginIndex = endIndex;
494 if (beginIndex >= size)
495 {
496 // Quit
497 break;
498 }
499
500 // Search for next PenUpMarker
501 while (endIndex < size && !PRC_IsPenUpMarker(&inputPoints[endIndex]))
502 {
503 endIndex++;
504 }
505 if (endIndex < size)
506 {
507 selectedPoints[selectedPointNum] = endIndex;
508 selectedPointNum++; // Select required PenUpMarker
509 }
510 else
511 {
512 selectedPoints[selectedPointNum] = (u16)-1;
513 selectedPointNum++; // -1 is specially reserved to indicate the final PenUpMarker
514 }
515
516 SDK_ASSERT(endIndex > 0);
517 selectedPoints[selectedPointNum] = beginIndex;
518 selectedPointNum++;
519 selectedPoints[selectedPointNum] = (u16)(endIndex - 1);
520 selectedPointNum++;
521
522 strokeCount++; // strokeCount is only counted for the sake of limiting it with maxStrokeCount
523
524 if (selectedPointNum >= maxPointCount)
525 {
526 // Number of vertices is at the limit
527 break;
528 }
529
530 if (endIndex - beginIndex <= 2)
531 continue;
532
533 // Using the stack, recursively extract characteristic points
534 stackP1[0] = beginIndex;
535 stackP2[0] = (u16)(endIndex - 1);
536 stackSize = 1;
537 stackTop = 0;
538 stackTail = 1;
539 while (stackSize > 0)
540 {
541 u16 p1, p2;
542 int x1, y1, x2, y2, xDir, yDir, offs;
543 int lastX, lastY;
544 int i;
545 int maxDist;
546 u16 maxP;
547
548 p1 = stackP1[stackTop];
549 p2 = stackP2[stackTop];
550 stackTop++;
551 if (stackTop >= maxPointCount)
552 {
553 stackTop = 0;
554 }
555 stackSize--; // pop
556
557 if (p2 - p1 <= 1)
558 continue;
559
560 x1 = inputPoints[p1].x; // Starting point
561 y1 = inputPoints[p1].y;
562 x2 = inputPoints[p2].x; // End point
563 y2 = inputPoints[p2].y;
564 xDir = x2 - x1; // Direction vector
565 yDir = y2 - y1;
566 offs = -(x1 * y2 - x2 * y1); // Items bundled together in order to reduce the amount of calculation.
567
568 maxDist = -1;
569 maxP = (u16)-1;
570 lastX = -1;
571 lastY = -1;
572 for (i = p1 + 1; i < p2; i++)
573 {
574 int dist;
575 int x, y;
576 x = inputPoints[i].x;
577 y = inputPoints[i].y;
578
579 if (lastX == x && lastY == y)
580 continue;
581 lastX = x;
582 lastY = y;
583
584 // Calculate the distance between the straight line and the point
585 // The actual value is the original distance multiplied by the distance from the start point to end point
586 dist = x * yDir - y * xDir + offs;
587 if (dist < 0)
588 {
589 dist = -dist;
590 }
591
592 if (maxDist < dist)
593 {
594 maxP = (u16)i;
595 maxDist = dist;
596 }
597 }
598
599 // Because [maxDist == 0] in cases where the start coordinates and end coordinates exactly match, it would be preferable if the 'dist' calculation separately found the Euclidean distance between (x, y) and the start point, but because we don't want to introduce special processing for rare cases, try coding so that [p1+1] is always used when [maxDist==0] and [maxP==p1+1]. (Note that [xDir*xDir+yDir*yDir == 0] at such times.)
600 //
601 //
602 //
603 if (maxDist * maxDist >= (xDir * xDir + yDir * yDir) * squaredThreshold)
604 {
605 // Adopt points located at or outside the threshold as characteristic points
606 // Note that maxDist was the original distance multiplied by the distance from the start point to end point.
607 selectedPoints[selectedPointNum] = maxP;
608 selectedPointNum++;
609 stackP1[stackTail] = maxP;
610 stackP2[stackTail] = p2;
611 stackTail++;
612 if (stackTail >= maxPointCount)
613 {
614 stackTail = 0;
615 }
616 stackSize++; // push
617 stackP1[stackTail] = p1;
618 stackP2[stackTail] = maxP;
619 stackTail++;
620 if (stackTail >= maxPointCount)
621 {
622 stackTail = 0;
623 }
624 stackSize++; // push
625 SDK_ASSERT(stackSize <= maxPointCount);
626 if (selectedPointNum >= maxPointCount)
627 {
628 // Number of vertices is at the limit
629 break;
630 }
631 }
632 }
633 }
634
635 *pSelectedPointNum = selectedPointNum;
636
637 //{OSTick start, end; start = OS_GetTick();
638 // Sort in ascending order before returning
639 // To Do: Examine to what degree it is sped up by quick sort
640 {
641 int i, j;
642 for (i = 0; i < selectedPointNum - 1; i++)
643 {
644 for (j = i + 1; j < selectedPointNum; j++)
645 {
646 if (selectedPoints[i] > selectedPoints[j])
647 {
648 u16 tmp;
649 tmp = selectedPoints[i];
650 selectedPoints[i] = selectedPoints[j];
651 selectedPoints[j] = tmp;
652 }
653 }
654 }
655 }
656 //end = OS_GetTick(); OS_TPrintf("// sort in resample: %lldmicro s selectedPointNum=%d\n", OS_TicksToMicroSeconds(end-start), selectedPointNum); }
657 return (*pSelectedPointNum > 0);
658 }
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 /*===========================================================================*
683 Static Functions
684 *===========================================================================*/
685
686
687 /*---------------------------------------------------------------------------*
688 End of file
689 *---------------------------------------------------------------------------*/
690