1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - PRC - demo - characterRecognition-1
3   File:     main.c
4 
5   Copyright 2007-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:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro.h>
19 #include <nitro/prc/algo_fine.h>
20 
21 #include "patterns.h"
22 
23 //----- Specify how many places to output recognition result
24 #define RESULT_NUM              5
25 
26 //----- The number of queue levels for recognition process wait
27 #define RECOGNITION_QUEUE_SIZE  2
28 
29 //----- Maximum number of points per input
30 //-----  * As a standard, 60 points are entered each second
31 #define POINT_PER_INPUT_MAX     1024
32 //----- Maximum stroke count per input
33 #define STROKE_PER_INPUT_MAX    20
34 //----- Maximum number of points after resampling
35 #define POINT_AFTER_RESAMPLING_MAX 40
36 
37 #define VRAM_BASE_ADDR          ((u16*)HW_LCDC_VRAM_C)
38 #define VRAM_LINE_SIZE          256
39 #define DRAW_COLOR              0
40 
41 #define MEMORY_MAIN_MAX_HEAPS   1
42 #define MEMORY_MAIN_HEAP_SIZE   0x200000
43 
44 static void InitOS(void);
45 static void InitTP(void);
46 static void InitDisplay(void);
47 static void DrawLine(int x1, int y1, int x2, int y2, u16 col);
48 static void PutDot(int x, int y, u16 col);
49 static void ClearScreen(void);
50 void    DrawStrokes(const PRCStrokes *strokes, int sx, int sy);
51 static void PrintRecognitionResult(PRCPrototypeEntry **result, fx32 *scores);
52 static void VBlankIntr(void);
53 
54 
55 typedef struct RecognitionObject
56 {
57     BOOL    recognized;                // TRUE if variables such as results have valid values
58 
59     PRCStrokes strokes;
60     PRCPoint points[POINT_PER_INPUT_MAX];
61 
62     PRCPrototypeEntry *results[RESULT_NUM];
63     fx32    scores[RESULT_NUM];
64 
65     PRCStrokes inputPatternStrokes;
66     PRCPoint inputPatternPoints[POINT_AFTER_RESAMPLING_MAX];
67 }
68 RecognitionObject;
69 
70 void    InitRecognition(void);
71 void    DestroyRecognition(void);
72 BOOL    RecognizePatternAsync(RecognitionObject * packet);
73 
74 
NitroMain(void)75 void NitroMain(void)
76 {
77     InitOS();
78     InitDisplay();
79     InitTP();
80 
81     {
82         PRCPoint points[POINT_PER_INPUT_MAX];
83         PRCStrokes strokes;
84         RecognitionObject queue[RECOGNITION_QUEUE_SIZE];        // May be consuming a large amount of memory. Be aware of that.
85         int     queueHead, queueTail, queueSize;
86         u16     prevKey, currKey, trgKey;
87         BOOL    touching = FALSE;
88         TPData  prevSample = { 0, 0, 0, 0 };
89 
90         InitRecognition();
91         {
92             // Embed the entry number in entry.data of the sample database to facilitate subsequent reverse lookup
93             int     iEntry;
94             for (iEntry = 0; iEntry < PrototypeList.entrySize; iEntry++)
95             {
96                 ((PRCPrototypeEntry *)&PrototypeList.entries[iEntry])->data = (void *)iEntry;
97             }
98         }
99 
100         PRC_InitStrokes(&strokes, points, POINT_PER_INPUT_MAX);
101 
102         {
103             int     i;
104             for (i = 0; i < RECOGNITION_QUEUE_SIZE; i++)
105             {
106                 PRC_InitStrokes(&queue[i].strokes, queue[i].points, POINT_PER_INPUT_MAX);
107                 PRC_InitStrokes(&queue[i].inputPatternStrokes, queue[i].inputPatternPoints,
108                                 POINT_AFTER_RESAMPLING_MAX);
109             }
110         }
111         queueHead = 0;
112         queueTail = 0;
113         queueSize = 0;
114 
115         ClearScreen();
116 
117         //---- Dummy READ workaround for pushing 'A' at IPL
118         currKey = PAD_Read();
119 
120         while (TRUE)
121         {
122             BOOL    fClearScreen = FALSE;
123             BOOL    fRecognizeChar = FALSE;
124             TPData  sample;
125 
126             prevKey = currKey;
127             currKey = PAD_Read();
128             trgKey = (u16)(currKey & ~prevKey);
129 
130             (void)TP_RequestCalibratedSampling(&sample);
131 
132             if (sample.touch && (sample.validity == 0))
133             {
134                 // Pen is touching the screen
135 
136                 if (!PRC_IsFull(&strokes))
137                 {
138                     if (touching)
139                     {
140                         // Writing
141                         DrawLine(prevSample.x, prevSample.y, sample.x, sample.y, DRAW_COLOR);
142                     }
143                     else
144                     {
145                         // The moment pen touches the screen
146                         PutDot(sample.x, sample.y, DRAW_COLOR);
147                         touching = TRUE;
148                     }
149                     // Store stroke data
150                     PRC_AppendPoint(&strokes, sample.x, sample.y);
151                 }
152             }
153             else
154             {
155                 // Pen is off the screen
156 
157                 if (touching)
158                 {
159                     // The moment the pen comes off the screen
160                     if (!PRC_IsFull(&strokes))
161                     {
162                         PRC_AppendPenUpMarker(&strokes);
163                     }
164                     touching = FALSE;
165                 }
166             }
167 
168             // Recognition begins when A button is pressed
169             if (trgKey & PAD_BUTTON_A)
170             {
171                 fRecognizeChar = TRUE;
172             }
173 
174             // Clear screen when B button is pressed
175             if (trgKey & (PAD_BUTTON_START | PAD_BUTTON_B))
176             {
177                 fClearScreen = TRUE;
178             }
179 
180             // End when SELECT button is pressed
181             if (trgKey & PAD_BUTTON_SELECT)
182             {
183                 break;
184             }
185 
186             if (fRecognizeChar)
187             {
188                 if (PRC_IsEmpty(&strokes))
189                 {
190                     OS_Printf("\n-----------------------------------------------\n");
191                     OS_Printf("No input.\n");
192                 }
193                 else
194                 {
195                     // Want to start graphics recognition
196                     if (queueSize < RECOGNITION_QUEUE_SIZE)
197                     {
198                         // Create data for request
199                         (void)PRC_CopyStrokes(&strokes, &queue[queueTail].strokes);
200                         queue[queueTail].recognized = FALSE;
201 
202                         // Send request for graphics recognition
203                         if (RecognizePatternAsync(&queue[queueTail]))
204                         {
205                             // Put on queue
206                             queueTail++;
207                             queueSize++;
208                             if (queueTail >= RECOGNITION_QUEUE_SIZE)
209                             {
210                                 queueTail = 0;
211                             }
212 
213                             // Clear screen and input stroke data
214                             ClearScreen();
215                             PRC_Clear(&strokes);
216                         }
217                         else
218                         {
219                         }              // If message queue for other thread is full, do nothing
220                         // This should not happen if the sizes of the two queues have been matched
221                     }
222                     else
223                     {
224                     }                  // If queue controlled by main is full, do nothing
225                 }
226             }
227 
228 
229             // Wait for V-Blank interrupt
230             // While waiting with OS_WaitIrq, process moves to different thread.
231             OS_WaitIrq(1, OS_IE_V_BLANK);
232             // Return to main thread with V-Blank interrupt
233 
234 
235             // Is there currently data waiting for recognition?
236             if (queueSize > 0)
237             {
238                 // Get and display any recognition results produced
239                 if (queue[queueHead].recognized == TRUE)
240                 {
241                     // Recognition results were produced
242                     PRCPrototypeEntry **results = queue[queueHead].results;
243                     fx32   *scores = queue[queueHead].scores;
244 
245                     ClearScreen();
246                     // Display again the stroke data currently being input
247                     DrawStrokes(&strokes, 0, 0);
248                     // In the upper-left portion of the screen, display the interpretation of the input
249                     DrawStrokes(&queue[queueHead].inputPatternStrokes, 0, 0);
250 
251                     if (results[0] != NULL)
252                     {
253                         // If something could be recognized, it is pulled from the sample database and displayed on the upper-right corner of the screen
254                         PRCStrokes drawingStrokes;
255                         int     dictIndex = (int)results[0]->data;      // Entry number was stored in data in advance
256                         PRC_GetEntryStrokes(&drawingStrokes, &PrototypeList,
257                                             &PrototypeList.entries[dictIndex]);
258                         DrawStrokes(&drawingStrokes, 256 - PrototypeList.normalizeSize, 0);
259                     }
260 
261                     // Display recognition results in debugging output
262                     PrintRecognitionResult(results, scores);
263 
264                     // Get from queue
265                     queueHead++;
266                     queueSize--;
267                     if (queueHead >= RECOGNITION_QUEUE_SIZE)
268                     {
269                         queueHead = 0;
270                     }
271                 }
272             }
273 
274             if (fClearScreen)
275             {
276                 ClearScreen();
277                 PRC_Clear(&strokes);
278             }
279 
280             prevSample = sample;
281         }
282         DestroyRecognition();
283     }
284     OS_Printf("Terminated.\n");
285     OS_Terminate();
286 }
287 
InitOS(void)288 void InitOS(void)
289 {
290     void   *nstart;
291     void   *heapStart;
292     OSHeapHandle handle;
293 
294     OS_Init();
295 
296     nstart =
297         OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(),
298                      MEMORY_MAIN_MAX_HEAPS);
299     OS_SetMainArenaLo(nstart);
300 
301     heapStart = OS_AllocFromMainArenaLo(MEMORY_MAIN_HEAP_SIZE, 32);
302     handle =
303         OS_CreateHeap(OS_ARENA_MAIN, heapStart, (void *)((u32)heapStart + MEMORY_MAIN_HEAP_SIZE));
304 
305     (void)OS_SetCurrentHeap(OS_ARENA_MAIN, handle);
306 
307     OS_InitTick();
308     OS_InitAlarm();
309     OS_InitThread();
310 }
311 
InitDisplay(void)312 void InitDisplay(void)
313 {
314 
315     GX_Init();
316     FX_Init();
317 
318     GX_DispOff();
319     GXS_DispOff();
320 
321     GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);
322 
323     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
324     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
325     (void)GX_VBlankIntr(TRUE);         // To generate V-Blank interrupt request
326     (void)OS_EnableIrq();
327 
328     GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
329     MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);
330 
331     MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);   // Clear OAM
332     MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);     // Clear the standard palette
333 
334     MI_CpuFillFast((void *)HW_DB_OAM, 192, HW_DB_OAM_SIZE);     // Clear OAM
335     MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);       // Clear the standard palette
336     MI_DmaFill32(3, (void *)HW_LCDC_VRAM_C, 0x7FFF7FFF, 256 * 192 * sizeof(u16));
337 
338     GX_SetGraphicsMode(GX_DISPMODE_VRAM_C,      // VRAM mode
339                        (GXBGMode)0,    // Dummy
340                        (GXBG0As)0);    // Dummy
341 
342     OS_WaitVBlankIntr();               // Waiting for the end of the V-Blank interrupt
343     GX_DispOn();
344 }
345 
InitTP(void)346 void InitTP(void)
347 {
348     TPCalibrateParam calibrate;
349 
350     TP_Init();
351 
352     // Get CalibrateParameter from FlashMemory
353     if (!TP_GetUserInfo(&calibrate))
354     {
355         OS_Panic("Can't get Calibration Parameter from NVRAM\n");
356     }
357     else
358     {
359         OS_Printf("Get Calibration Parameter from NVRAM\n");
360     }
361 
362     TP_SetCalibrateParam(&calibrate);
363 }
364 
DrawLine(int x1,int y1,int x2,int y2,u16 col)365 void DrawLine(int x1, int y1, int x2, int y2, u16 col)
366 {
367     u16     sx, sy, ey, wx, wy;
368     u16     i;
369     int     err, plus, minus;
370     u16    *point = VRAM_BASE_ADDR;
371 
372     SDK_ASSERT(x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0);
373 
374     // Swap (x1, y1) <-> (x2, y2) if x1 > x2
375     if (x1 <= x2)
376     {
377         sx = (u16)x1;
378         sy = (u16)y1;
379         ey = (u16)y2;
380         wx = (u16)(x2 - x1);           // Width
381     }
382     else
383     {
384         sx = (u16)x2;
385         sy = (u16)y2;
386         ey = (u16)y1;
387         wx = (u16)(x1 - x2);           // Width
388     }
389 
390     point += sy * VRAM_LINE_SIZE + sx;
391     if (sy <= ey)
392     {
393         /* Line goes to upper */
394         wy = (u16)(ey - sy);           // Height
395         if (wx > wy)
396         {
397             /* If (X size > Y size) draw a Dot per Xdot */
398             plus = wy * 2;
399             minus = -wx * 2;
400             err = -wx;
401             for (i = 0; i <= wx; i++)
402             {
403                 *point = col;          // PutDot
404                 ++point;
405                 err += plus;
406                 if (err >= 0)
407                 {
408                     point += VRAM_LINE_SIZE;
409                     err += minus;
410                 }
411             }
412 
413         }
414         else
415         {
416             /* If (X size <= Y size) draw a Dot per Ydot */
417             plus = wx * 2;
418             minus = -wy * 2;
419             err = -wy;
420             for (i = 0; i <= wy; i++)
421             {
422                 *point = col;          // PutDot
423                 point += VRAM_LINE_SIZE;
424                 err += plus;
425                 if (err >= 0)
426                 {
427                     ++point;
428                     err += minus;
429                 }
430             }
431         }
432     }
433     else
434     {
435         /* line goes to lower */
436         wy = (u16)(sy - ey);           // Height
437         if (wx > wy)
438         {
439             /* if (X size > Y size) draw a Dot per Xdot */
440             plus = wy * 2;
441             minus = -wx * 2;
442             err = -wx;
443             for (i = 0; i <= wx; i++)
444             {
445                 *point = col;          // PutDot
446                 ++point;
447                 err += plus;
448                 if (err >= 0)
449                 {
450                     point -= VRAM_LINE_SIZE;
451                     err += minus;
452                 }
453             }
454 
455         }
456         else
457         {
458             /* If (X size <= Y size) draw a Dot per Ydot */
459             plus = wx * 2;
460             minus = -wy * 2;
461             err = -wy;
462             for (i = 0; i <= wy; i++)
463             {
464                 *point = col;          // PutDot
465                 point -= VRAM_LINE_SIZE;
466                 err += plus;
467                 if (err >= 0)
468                 {
469                     ++point;
470                     err += minus;
471                 }
472             }
473         }
474     }
475 }
476 
PutDot(int x,int y,u16 col)477 void PutDot(int x, int y, u16 col)
478 {
479     *(u16 *)((u32)VRAM_BASE_ADDR + y * 256 * 2 + x * 2) = col;
480 }
481 
ClearScreen(void)482 void ClearScreen(void)
483 {
484     MI_DmaFill32(3, (void *)HW_LCDC_VRAM_C, 0x7FFF7FFF, 256 * 192 * sizeof(u16));
485 }
486 
DrawStrokes(const PRCStrokes * strokes,int sx,int sy)487 void DrawStrokes(const PRCStrokes *strokes, int sx, int sy)
488 {
489     int     iPoint;
490     PRCPoint prev;
491     BOOL    newFlag;
492     const PRCPoint *point;
493 
494     newFlag = TRUE;
495     point = strokes->points;
496     for (iPoint = 0; iPoint < strokes->size; iPoint++, point++)
497     {
498         if (!PRC_IsPenUpMarker(point))
499         {
500             if (newFlag)
501             {
502                 PutDot(sx + point->x, sy + point->y, 1);
503             }
504             else
505             {
506                 DrawLine(sx + prev.x, sy + prev.y, sx + point->x, sy + point->y, 1);
507             }
508             prev = *point;
509             newFlag = FALSE;
510         }
511         else
512         {
513             newFlag = TRUE;
514         }
515     }
516 }
517 
PrintRecognitionResult(PRCPrototypeEntry ** results,fx32 * scores)518 void PrintRecognitionResult(PRCPrototypeEntry **results, fx32 *scores)
519 {
520     int     iResult;
521     (void)scores;
522 
523     OS_Printf("\n-----------------------------------------------\n");
524 
525     // Recognition results output
526     if (results[0] == NULL)
527     {
528         OS_Printf("Result: can't recognize as char\n");
529     }
530     else
531     {
532         OS_Printf("Result:\n");
533         for (iResult = 0; iResult < RESULT_NUM; iResult++)
534         {
535             int     code;
536 
537             if (results[iResult] == NULL)
538                 break;
539 
540             code = PRC_GetEntryCode(results[iResult]);
541             OS_Printf("  '%s' (score: %d.%03d)\n", PatternName[code], FX_Whole(scores[iResult]),
542                       (scores[iResult] & FX16_DEC_MASK) * 1000 / (1 << FX16_DEC_SIZE));
543         }
544     }
545 }
546 
VBlankIntr(void)547 void VBlankIntr(void)
548 {
549     // Set IRQ check flag
550     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
551 }
552 
553 
554 
555 
556 
557 /****************************************************************************
558   Figure recognition execution section
559  ****************************************************************************/
560 #define RECOGNITION_THREAD_STACK_SIZE   0x4000
561 #define RECOGNITION_THREAD_PRIO         20
562 
563 #define RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT RECOGNITION_QUEUE_SIZE
564 
565 PRCPrototypeDB gProtoDB;
566 PRCInputPattern gInputPattern;
567 void   *gPrototypeListBuffer;
568 void   *gPatternBuffer;
569 void   *gRecognitionBuffer;
570 
571 OSThread gRecognitionThread;
572 void   *gRecognitionThreadStack;
573 
574 OSMessageQueue gRecognitionRequestMessageQueue;
575 OSMessage gRecognitionRequestMessages[RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT];
576 
577 void    RecognizePattern(void *data);
578 
InitRecognition(void)579 void InitRecognition(void)
580 {
581     PRC_Init();
582 
583     // Expand the sample database
584     // PrototypeList is a list of the sample patterns defined in a separate file
585     {
586         u32     size;
587 
588         size = PRC_GetPrototypeDBBufferSize(&PrototypeList);
589         OS_Printf("required buffer for InitPrototypeDB: %d bytes\n", size);
590         gPrototypeListBuffer = OS_Alloc(size);
591         SDK_ASSERT(gPrototypeListBuffer);
592     }
593     if (!PRC_InitPrototypeDB(&gProtoDB, gPrototypeListBuffer, &PrototypeList))
594     {
595         OS_Printf("cannot initialize the prototype DB.\n");
596         OS_Terminate();
597     }
598 
599     {
600         u32     size;
601 
602         // Allocate memory for expansion of input stroke data
603         size = PRC_GetInputPatternBufferSize(POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX);
604         OS_Printf("required buffer for InitInputPattern: %d bytes\n", size);
605         gPatternBuffer = OS_Alloc(size);
606         SDK_ASSERT(gPatternBuffer);
607 
608         // Allocate memory for recognition algorithm
609         size =
610             PRC_GetRecognitionBufferSize(POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX,
611                                          &gProtoDB);
612         OS_Printf("required buffer for Recognition: %d bytes\n", size);
613         gRecognitionBuffer = OS_Alloc(size);
614         SDK_ASSERT(gRecognitionBuffer);
615     }
616 
617     // Initialize the message queue used for communication with the recognition routine thread
618     OS_InitMessageQueue(&gRecognitionRequestMessageQueue, gRecognitionRequestMessages,
619                         RECOGNITION_REQUEST_MESSAGE_QUEUE_COUNT);
620 
621     // Allocate the stack region for the thread
622     gRecognitionThreadStack = OS_Alloc(RECOGNITION_THREAD_STACK_SIZE);
623     SDK_ASSERT(gRecognitionThreadStack);
624 
625     // Create separate thread for recognition routine
626     OS_CreateThread(&gRecognitionThread,
627                     RecognizePattern,
628                     NULL,
629                     (void *)((int)gRecognitionThreadStack + RECOGNITION_THREAD_STACK_SIZE),
630                     RECOGNITION_THREAD_STACK_SIZE, RECOGNITION_THREAD_PRIO);
631 
632     // Start separate thread
633     // However, not immediately executed, because main routine has higher priority.
634     OS_WakeupThreadDirect(&gRecognitionThread);
635 }
636 
DestroyRecognition(void)637 void DestroyRecognition(void)
638 {
639     if (!OS_IsThreadTerminated(&gRecognitionThread))
640     {
641         // Terminate recognition thread
642 
643         // Send termination command
644         (void)OS_JamMessage(&gRecognitionRequestMessageQueue, (OSMessage)NULL, OS_MESSAGE_BLOCK);
645 
646         // Wait for thread to be terminated
647         OS_JoinThread(&gRecognitionThread);
648     }
649 
650     OS_Free(gRecognitionThreadStack);
651     OS_Free(gRecognitionBuffer);
652     OS_Free(gPatternBuffer);
653     OS_Free(gPrototypeListBuffer);
654 }
655 
RecognizePatternAsync(RecognitionObject * object)656 BOOL RecognizePatternAsync(RecognitionObject * object)
657 {
658     if (object == NULL)
659     {
660         return FALSE;
661     }
662     // Request recognition processing from a separate thread
663     return OS_SendMessage(&gRecognitionRequestMessageQueue, (OSMessage)object, OS_MESSAGE_NOBLOCK);
664 }
665 
666 
667 // Functions running in a separate thread
RecognizePattern(void * data)668 void RecognizePattern(void *data)
669 {
670     PRCInputPatternParam inputParam;
671 
672     (void)data;
673 
674     inputParam.normalizeSize = gProtoDB.normalizeSize;
675 //    inputParam.resampleMethod = PRC_RESAMPLE_METHOD_DISTANCE;
676 //    inputParam.resampleThreshold = 12;
677 //    inputParam.resampleMethod = PRC_RESAMPLE_METHOD_ANGLE;
678 //    inputParam.resampleThreshold = 4096;
679     inputParam.resampleMethod = PRC_RESAMPLE_METHOD_RECURSIVE;
680     inputParam.resampleThreshold = 2;
681 
682     while (TRUE)
683     {
684         OSMessage msg;
685         RecognitionObject *obj;
686 
687         // Wait for recognition to be requested
688         (void)OS_ReceiveMessage(&gRecognitionRequestMessageQueue, &msg, OS_MESSAGE_BLOCK);
689         if (msg == (OSMessage)NULL)
690         {
691             // Termination command
692             break;
693         }
694         obj = (RecognitionObject *) msg;
695 
696         if (PRC_InitInputPatternEx(&gInputPattern, gPatternBuffer, &obj->strokes,
697                                    POINT_AFTER_RESAMPLING_MAX, STROKE_PER_INPUT_MAX, &inputParam))
698         {
699             // Compare prototypeList and inputPattern and perform recognition
700             (void)PRC_GetRecognizedEntriesEx(obj->results, obj->scores, RESULT_NUM,
701                                              gRecognitionBuffer, &gInputPattern, &gProtoDB,
702                                              PRC_KIND_ALL, NULL);
703 
704             {
705                 PRCStrokes tmpStrokes;
706                 PRC_GetInputPatternStrokes(&tmpStrokes, &gInputPattern);
707                 (void)PRC_CopyStrokes(&tmpStrokes, &obj->inputPatternStrokes);
708             }
709         }
710         else
711         {
712             // Failure in PRC_InitInputPattern due to incorrect input
713             int     i;
714 
715             PRC_Clear(&obj->inputPatternStrokes);
716             for (i = 0; i < RESULT_NUM; i++)
717             {
718                 obj->results[i] = NULL;
719                 obj->scores[i] = 0;
720             }
721         }
722 
723         // Set flag indicating that recognition was completed
724         obj->recognized = TRUE;
725     }
726 }
727