1 /*---------------------------------------------------------------------------*
2   Project:    WPAD Health Demo Program
3   File:       handling_weight.c
4 
5   Copyright (C) 2007 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   $Log: handling_weight.c,v $
14   Revision 1.5.6.1  2009/12/08 02:11:43  yasuda_shumpei
15   Updated wbcdemo. Solved the warnings.
16 
17   Revision 1.5  2008/08/22 04:46:21  urata
18   Added WPADRegisterBLCWorkarea so that it could run correctly on jp-mode.
19 
20   Revision 1.4  2008/06/17 08:03:33  tojo
21   Removed unused headers.
22 
23   Revision 1.3  2008/04/23 04:05:48  tojo
24   Updated WBCGetTGCWeight to latest spec.
25 
26   Revision 1.2  2008/04/17 05:25:33  tojo
27   Changed API for WBCSetupCalibration.
28 
29   Revision 1.1  2008/04/17 02:55:42  tojo
30   Initial check-in.
31 
32  *---------------------------------------------------------------------------*/
33 
34 #include <revolution.h>
35 #include <revolution/wpad.h>
36 #include <revolution/wpadBalance.h>
37 #include <revolution/wbc.h>
38 
39 #define DEMO_USE_MEMLIB=1 // This turns on the DEMO library's MEM heaps.
40 #include <demo.h>
41 
42 /*---------------------------------------------------------------------------*
43  * Local Definitions
44  *---------------------------------------------------------------------------*/
45 
46 #define SCREEN_WIDTH  320
47 #define SCREEN_HEIGHT 240
48 #define FONT_HEIGHT     8
49 
50 GXColor smoke_clr    = {  61, 61, 61, 255 } ;
51 GXColor red_clr      = { 237, 28, 36, 255 } ;
52 GXColor blue_clr     = {   0, 84,166, 255 } ;
53 GXColor yellow_clr   = { 255,242,  0, 255 } ;
54 GXColor peagreen_clr = { 141,198, 63, 255 } ;
55 
56 WPADBLStatus status;
57 
58 
59 u8  rxBuf[128] ATTRIBUTE_ALIGN(32);
60 u8  txBuf[128] ATTRIBUTE_ALIGN(32);
61 
62 
63 /*---------------------------------------------------------------------------*
64  * Function prototypes
65  *---------------------------------------------------------------------------*/
66 
67 
68 // MEM2 memory allocation routines. The application must provide these to
69 // WPAD, so it can setup the data transfer buffer. This buffer must reside
70 // in MEM2.
71 static void *myAlloc                 ( u32 size );
72 static u8    myFree                  ( void *ptr );
73 
74 
75 // Callbacks for CONNECT and EXTENSION events.
76 void         connectCallback         ( s32 chan, s32 reason );
77 void         extensionCallback       ( s32 chan, s32 result );
78 
79 // Internal functions
80 static void  initialize              ( void );
81 static void  renderStatus            ( void );
82 static void  init_draw_graphic       ( u16 fb_width, u16 fb_height );
83 static void  draw_line               ( f32 x1, f32 y1, f32 x2, f32 y2, GXColor clr, f32 width );
84 static void  draw_box                ( f32 x1, f32 y1, f32 x2, f32 y2, GXColor clr, f32 width );
85 
86 static double zero_point[WPAD_PRESS_UNITS];
87 static double sum;
88 static double ave;
89 static u32    counter;
90 static BOOL   get_weight_flag = FALSE;
91 static BOOL   set_zero_flag   = FALSE;
92 
93 static u8  workarea[WPAD_BLCINT_WORK_LEN] ATTRIBUTE_ALIGN(32);
94 
95 #define NSAMPLES_2SEC 120
96 
97 /*===========================================================================*
98  *                   F U N C T I O N    D E F I N I T I O N S
99  *===========================================================================*/
100 /*---------------------------------------------------------------------------*
101  * Name        : main()
102  * Description :
103  * Arguments   : None.
104  * Returns     : None.
105  *---------------------------------------------------------------------------*/
ctrlWbc(s32 chan,s32 result)106 static void ctrlWbc( s32 chan, s32 result )
107 {
108     OSReport("chan%d [%d]\n", chan, result);
109 }
110 
WaitMilliTime(s32 msec)111 static void WaitMilliTime(s32 msec)
112 {
113     OSTime t=OSGetTime();
114     while(OSTicksToMilliseconds(OSGetTime()-t) < msec)
115         ;
116 }
117 
118 
ZeroSetStart2(s32 chan,s32 result)119 static void ZeroSetStart2( s32 chan, s32 result )
120 {
121     #pragma unused(chan, result)
122 
123     // Wait about 200 ms so that Board's press values rarely become unstable
124     // after updating Balance Wii Board's temperature.
125     WaitMilliTime(200);
126 
127 
128     // Start Zero point correction.
129     set_zero_flag = TRUE;
130     counter = 0;
131     zero_point[0] = 0;
132     zero_point[1] = 0;
133     zero_point[2] = 0;
134     zero_point[3] = 0;
135 }
136 
137 
ZeroSetStart(s32 chan,s32 result)138 static void ZeroSetStart( s32 chan, s32 result )
139 {
140     #pragma unused(chan, result)
141 
142     // Check Board's temperature.
143     WPADRead(WPAD_CHAN3, &status);
144 
145 
146     if((status.temp == 127) || (status.temp == -128))
147     {
148         // Update Board's temperature again if you can not get correct Board's temperature.
149         WPADControlBLC(WPAD_CHAN3, WPAD_BLCMD_UPDATE_TEMP, ZeroSetStart2);
150     }
151     else
152     {
153         // Wait about 200 ms so that Board's press values rarely become unstable
154         // after updating Balance Wii Board's temperature.
155         WaitMilliTime(200);
156 
157         // Start Zero point correction.
158         set_zero_flag = TRUE;
159         counter = 0;
160         zero_point[0] = 0;
161         zero_point[1] = 0;
162         zero_point[2] = 0;
163         zero_point[3] = 0;
164     }
165 }
166 
main(void)167 int main( void )
168 {
169 
170     initialize();
171 
172     // For the Japanese version only, set the working buffer because it is required when the Wii Balance Board is initialized.
173     //
174     // After library initialization is complete, the working buffer will no longer be used and can be released.
175     //
176     //
177     // For markets outside Japan, no problems will occur if this function is not called.
178     //
179     // Additionally, no problems will occur if it is called.
180     WPADRegisterBLCWorkarea( workarea );
181 
182     WPADRegisterAllocator(myAlloc, myFree);
183 
184     WPADInit();
185 
186     WPADSetConnectCallback(WPAD_CHAN3, connectCallback);
187 
188     while (WPAD_STATE_SETUP != WPADGetStatus())
189     {
190         ;
191     }
192 
193     while(1)
194     {
195         DEMOPadRead();
196 
197         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_A)
198         {
199             // Turn on Board's power.
200             WPADControlBLC(WPAD_CHAN3, WPAD_BLCMD_ON, ctrlWbc);
201         }
202         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_B)
203         {
204             // Turn off Board's power.
205             WPADControlBLC(WPAD_CHAN3, WPAD_BLCMD_OFF, ctrlWbc);
206         }
207         if (DEMOPadGetButtonDown(0) & PAD_TRIGGER_Z)
208         {
209             // Update Board's temperature.
210             WPADControlBLC(WPAD_CHAN3, WPAD_BLCMD_UPDATE_TEMP, ctrlWbc);
211         }
212         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_MENU)
213         {
214             if((set_zero_flag == FALSE) && (get_weight_flag == FALSE))
215             {
216                 // Update Board's temperature the eve of zero point correction.
217                 WPADControlBLC(WPAD_CHAN3, WPAD_BLCMD_UPDATE_TEMP, ZeroSetStart);
218             }
219         }
220         if (DEMOPadGetButtonDown(0) & PAD_BUTTON_LEFT)
221         {
222             if((set_zero_flag == FALSE) && (get_weight_flag == FALSE))
223             {
224                 // Get an average value for 2 seconds
225                 get_weight_flag = TRUE;
226                 counter = 0;
227                 sum     = 0;
228             }
229         }
230 
231 
232         // Caption
233         DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
234         GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE );
235         GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
236 
237         DEMOBeforeRender();
238         renderStatus();
239         DEMODoneRender();
240     }
241 
242 } // End main()
243 
244 /*---------------------------------------------------------------------------*
245  * Name        : connectCallback()
246  *
247  * Description : This callback is invoked when a controller is connected or
248  *               disconnected.
249  *
250  * Arguments   : The channel (chan) for which the event has occurred.
251  *               The channel status (reason):
252  *                 WPAD_ERR_NONE means a controller has been connected.
253  *                 WPAD_ERR_NO_CONTROLLER means a controller disconnected.
254  *
255  * Returns     : None.
256  *---------------------------------------------------------------------------*/
connectCallback(s32 chan,s32 reason)257 void connectCallback(s32 chan, s32 reason)
258 {
259     u32 type;
260 
261     if (reason == WPAD_ERR_NONE)
262     {
263         // Disconnect 4P if 4P is not Wii Balance Board.
264         // 4P is reserved for Wii Balance Board.
265         WPADProbe(chan, &type);
266         if (chan == WPAD_CHAN3 && type != WPAD_DEV_BALANCE_CHECKER)
267         {
268             OSReport("Channel%d is reserved for the balance checker.\n", chan);
269             WPADDisconnect(chan);
270         }
271         else
272         {
273             // Read the calibration value for calculating a weight.
274             if(!WBCSetupCalibration())
275             {
276                 OSHalt("WBC FATAL ERROR!!\n");
277             }
278 
279             OSReport("Channel%d is connected.\n", chan);
280             WPADSetExtensionCallback(chan, extensionCallback);
281         }
282     }
283     else
284     {
285         OSReport("Channel%d is disconnected.\n", chan);
286     }
287 } // End connectCallback()
288 
289 /*---------------------------------------------------------------------------*
290  * Name        : extensionCallback()
291  *
292  * Description : This callback is invoked when an Extension has been attached.
293  *
294  * Arguments   : The channel (chan) for which the extension event occurred.
295  *               The device type (result):
296  *
297  *                 WPAD_DEV_UNKNOWN means that something has been attached, but
298  *                 it's being initialized, and we won't know what it is until
299  *                 initialization is complete.
300  *
301  *                 WPAD_DEV_CORE means that an extension has been removed and
302  *                 we're back to just the core device.
303  *
304  *                 WPAD_DEV_FREESTYLE means that the "NUNCHUK" extension has
305  *                 been attached and initialized.
306  *
307  *                 WPAD_DEV_CLASSIC means that the "CLASSIC" extension has been
308  *                 attached and initialized.
309  *
310  * Returns     : None.
311  *---------------------------------------------------------------------------*/
extensionCallback(s32 chan,s32 result)312 void extensionCallback(s32 chan, s32 result)
313 {
314     switch(result)
315     {
316         case WPAD_DEV_UNKNOWN:
317             OSReport("Initializing extension on channel%d...\n", chan);
318             break;
319 
320         case WPAD_DEV_CORE:
321             WPADControlDpd(chan, WPAD_DPD_EXP, NULL);
322             WPADSetDataFormat(chan, WPAD_FMT_CORE_ACC_DPD);
323 
324             OSReport("Extension removed on channel%d.\n", chan);
325             break;
326 
327         case WPAD_DEV_NOT_SUPPORTED:
328         case WPAD_DEV_FUTURE:
329             WPADControlDpd(chan, WPAD_DPD_EXP, NULL);
330             WPADSetDataFormat(chan, WPAD_FMT_CORE_ACC_DPD);
331 
332             OSReport("Extension is not useful on channel%d.\n", chan);
333             break;
334 
335         case WPAD_DEV_FREESTYLE:
336             WPADControlDpd(chan, WPAD_DPD_STD, NULL);
337             WPADSetDataFormat(chan, WPAD_FMT_FREESTYLE_ACC_DPD);
338 
339             OSReport("Freestyle initialized on channel%d.\n", chan);
340             break;
341 
342         case WPAD_DEV_CLASSIC:
343             WPADControlDpd(chan, WPAD_DPD_STD, NULL);
344             WPADSetDataFormat(chan, WPAD_FMT_CLASSIC_ACC_DPD);
345 
346             OSReport("Classicstyle initialized on channel%d.\n", chan);
347             break;
348 
349         case WPAD_DEV_BALANCE_CHECKER:
350             WPADControlDpd(chan, WPAD_DPD_OFF, NULL);
351             WPADSetDataFormat(chan, WPAD_FMT_BALANCE_CHECKER);
352             WPADControlBLC(chan, WPAD_BLCMD_ON, NULL);
353 
354             OSReport("Balance checker initialized on channel%d.\n", chan);
355             break;
356 
357         default:
358             // Here is WPAD_DEV_NOT_FOUND.
359             // If the controller is disconnected while the extension is initializing
360             // it reaches here. There is nothing to do.
361             break;
362 
363     } // End
364 
365 } // End extensionCallback()
366 
367 /*---------------------------------------------------------------------------*
368  * Name        : myAlloc()
369  * Description : Callback needed by WPAD to allocate memory from MEM2 heap.
370  * Arguments   : size of block, in bytes.
371  * Returns     : pointer to allocated block.
372  *---------------------------------------------------------------------------*/
myAlloc(u32 size)373 static void *myAlloc(u32 size)
374 {
375     void *ptr;
376 
377     ptr = MEMAllocFromAllocator(&DemoAllocator2, size);
378     ASSERTMSG(ptr, "Memory allocation failed\n");
379 
380     return(ptr);
381 
382 } // myAlloc()
383 
384 /*---------------------------------------------------------------------------*
385  * Name        : myFree()
386  * Description : Callback needed by WPAD to deallocate memory from MEM2 heap.
387  * Arguments   : None.
388  * Returns     : Always 1.
389  *---------------------------------------------------------------------------*/
myFree(void * ptr)390 static u8 myFree(void *ptr)
391 {
392 
393     MEMFreeToAllocator(&DemoAllocator2, ptr);
394 
395     // we should ensure that memory is deallocated properly, but oh well
396     return(1);
397 
398 } // myFree()
399 
400 
renderStatus(void)401 static void renderStatus(void)
402 {
403     u32 type;
404     s32 isExist = 0;
405 
406     if (WPADProbe(WPAD_CHAN3, &type) == WPAD_ERR_NO_CONTROLLER)
407     {
408         if (WPADIsRegisteredBLC())
409         {
410             DEMOPrintf( 50, 100, 0, "WBC is not connected.");
411         }
412         else
413         {
414             DEMOPrintf( 20, 100, 0, "WBC is not registered.");
415             DEMOPrintf( 20, 110, 0, "Please regist WBC with SYNC button.");
416         }
417     }
418     else
419     {
420         if (type == WPAD_DEV_BALANCE_CHECKER)
421         {
422             double weight[WPAD_PRESS_UNITS];
423             double tgc_weight;
424             double all;
425 
426 
427             WPADRead(WPAD_CHAN3, &status);
428 
429             DEMOPrintf( 95,  40, 0, "%5d", status.press[0]);
430             DEMOPrintf( 95, 110, 0, "%5d", status.press[1]);
431             DEMOPrintf( 10,  40, 0, "%5d", status.press[2]);
432             DEMOPrintf( 10, 110, 0, "%5d", status.press[3]);
433 
434             isExist = WBCRead(&status, weight, (u32)(sizeof(weight) / sizeof(weight[0])));
435             all = (double)(weight[0] + weight[1] + weight[2] + weight[3]);
436 
437 
438 
439             if((get_weight_flag || set_zero_flag) && counter < NSAMPLES_2SEC)
440             {
441                 counter++;
442                 sum += all;
443                 zero_point[0] += (double)status.press[0];
444                 zero_point[1] += (double)status.press[1];
445                 zero_point[2] += (double)status.press[2];
446                 zero_point[3] += (double)status.press[3];
447             }
448 
449 
450             if(counter == NSAMPLES_2SEC)
451             {
452                 ave  = sum / NSAMPLES_2SEC;
453                 zero_point[0] /= NSAMPLES_2SEC;
454                 zero_point[1] /= NSAMPLES_2SEC;
455                 zero_point[2] /= NSAMPLES_2SEC;
456                 zero_point[3] /= NSAMPLES_2SEC;
457                 get_weight_flag = FALSE;
458 
459                 if(set_zero_flag)
460                 {
461                     // Set the zero point into WBC library.
462                     if(0 == WBCSetZEROPoint( zero_point,(u32)(sizeof(zero_point) / sizeof(zero_point[0])) ))
463                     {
464                         // Success
465                         set_zero_flag = FALSE;
466                     }
467                     else
468                     {
469                         // Retry if failed.
470                         counter = 0;
471                         zero_point[0] = 0;
472                         zero_point[1] = 0;
473                         zero_point[2] = 0;
474                         zero_point[3] = 0;
475                     }
476                 }
477             }
478 
479             WBCGetTGCWeight(ave, &tgc_weight, &status);
480 
481             if(get_weight_flag || set_zero_flag)
482             {
483                 DEMOPrintf( 95,  55, 0, "wait...");
484                 DEMOPrintf( 95, 125, 0, "wait...");
485                 DEMOPrintf( 10,  55, 0, "wait...");
486                 DEMOPrintf( 10, 125, 0, "wait...");
487 
488                 if(get_weight_flag)
489                 {
490                     DEMOPrintf(175,  60, 0, "Wt     :WAIT");
491                     DEMOPrintf(175,  70, 0, "Wt(TGC):WAIT");
492                 }
493                 else
494                 {
495                     DEMOPrintf(175,  60, 0, "Wt     :%3.1f[kg]", ave);
496                     DEMOPrintf(175,  70, 0, "Wt(TGC):%3.1f[kg]", tgc_weight);
497                 }
498             }
499             else
500             {
501                 DEMOPrintf( 95,  55, 0, "%3.1f", weight[0]);
502                 DEMOPrintf( 95, 125, 0, "%3.1f", weight[1]);
503                 DEMOPrintf( 10,  55, 0, "%3.1f", weight[2]);
504                 DEMOPrintf( 10, 125, 0, "%3.1f", weight[3]);
505                 DEMOPrintf(175,  60, 0, "Wt     :%3.1f[kg]", ave);
506                 DEMOPrintf(175,  70, 0, "Wt(TGC):%3.1f[kg]", tgc_weight);
507                 DEMOPrintf(175,  80, 0, "Setup  :%s", WBCGetCalibrationStatus()==TRUE ? "OK":"NG");
508             }
509 
510             DEMOPrintf(175,  20, 0, "Temp %d", status.temp);
511             DEMOPrintf(175,  30, 0, "Batt %d/Level %d", status.battery, WBCGetBatteryLevel(status.battery));
512             DEMOPrintf(175,  40, 0, "IsEXIST = %s", isExist == 1 ? "EXIST" : "NONE");
513 
514             DEMOPrintf( 10, 160, 0, "A: Turn On  SENSOR");
515             DEMOPrintf( 10, 170, 0, "B: Turn Off SENSOR");
516             DEMOPrintf( 10, 180, 0, "MENU: Set ZERO point");
517             DEMOPrintf( 10, 190, 0, "LEFT: Get AVE Weight");
518             DEMOPrintf( 10, 220, 0, "Z: Update the current temperature");
519 
520             init_draw_graphic(SCREEN_WIDTH, SCREEN_HEIGHT);
521             draw_box(-143, -100, -68, -50, peagreen_clr, 2);
522             init_draw_graphic(SCREEN_WIDTH, SCREEN_HEIGHT);
523             draw_box(-143,  -30, -68,  20, yellow_clr, 2);
524             init_draw_graphic(SCREEN_WIDTH, SCREEN_HEIGHT);
525             draw_box(-63,  -100, 12, -50, blue_clr, 2);
526             init_draw_graphic(SCREEN_WIDTH, SCREEN_HEIGHT);
527             draw_box(-63,   -30, 12,  20, red_clr, 2);
528         }
529         else
530         {
531             DEMOPrintf(50, 100, 0, "No balance checker is attached.");
532         }
533     }
534 
535 } // End renderStatus()
536 
537 
initialize(void)538 static void initialize( void )
539 {
540     OSInit();
541 
542     DEMOInit( &GXNtsc480IntDf );
543 
544     GXSetCopyClear( smoke_clr, GX_MAX_Z24 );
545     GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE );
546 
547     DEMOPadInit();
548 } // End
549 
550 
init_draw_graphic(u16 fb_width,u16 fb_height)551 static void init_draw_graphic( u16 fb_width, u16 fb_height )
552 {
553     Mtx44   proj_mtx ;
554     Mtx     view_mtx ;
555     f32     canvas_wd, canvas_ht ;
556 
557     // CANVAS
558     canvas_wd = fb_width * 0.91346f ;
559     canvas_ht = (f32)fb_height ;
560 
561     // MTX
562     MTXOrtho( proj_mtx, canvas_ht * -0.5f,canvas_ht * 0.5f, canvas_wd * -0.5f,canvas_wd * 0.5f, -10.0f,10.0f ) ;
563     GXSetProjection( proj_mtx, GX_ORTHOGRAPHIC ) ;
564 
565     MTXIdentity( view_mtx ) ;
566     GXLoadPosMtxImm( view_mtx, GX_PNMTX0 ) ;
567     GXSetCurrentMtx( GX_PNMTX0 ) ;
568 
569     // VERTEX
570     GXClearVtxDesc() ;
571     GXSetVtxDesc( GX_VA_POS, GX_DIRECT ) ;
572     GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0 ) ;
573 
574     // CHANNEL
575     GXSetNumChans( 1 ) ;
576     GXSetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ) ;
577     GXSetChanCtrl( GX_COLOR1A1, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ) ;
578 
579     // TEXTURE
580     GXSetNumTexGens( 0 ) ;
581 
582     // TEV
583     GXSetNumTevStages( 1 ) ;
584     GXSetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL ) ;
585     GXSetTevColorIn( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0 ) ;
586     GXSetTevColorOp( GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV ) ;
587     GXSetTevAlphaIn( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0 ) ;
588     GXSetTevAlphaOp( GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV ) ;
589     GXSetAlphaCompare( GX_ALWAYS,0, GX_AOP_OR, GX_ALWAYS,0 ) ;
590 
591     // SCREEN
592     GXSetBlendMode( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP ) ;
593     GXSetAlphaUpdate( GX_DISABLE ) ;
594 
595     GXSetZMode( GX_DISABLE, GX_ALWAYS, GX_DISABLE ) ;
596     GXSetCullMode( GX_CULL_BACK ) ;
597 }
598 
599 /*******************************************************************************
600     Draw object
601 *******************************************************************************/
draw_line(f32 x1,f32 y1,f32 x2,f32 y2,GXColor clr,f32 width)602 static void draw_line( f32 x1, f32 y1, f32 x2, f32 y2, GXColor clr, f32 width )
603 {
604     GXSetTevColor( GX_TEVREG0, clr ) ;
605     GXSetLineWidth( (u8)(s32)(width * 6.0f + 0.5f), GX_TO_ZERO ) ;
606 
607     GXBegin( GX_LINES, GX_VTXFMT0, 2 ) ;
608       GXPosition2f32( x1, y1 ) ;
609       GXPosition2f32( x2, y2 ) ;
610     GXEnd() ;
611 }
612 
draw_box(f32 x1,f32 y1,f32 x2,f32 y2,GXColor clr,f32 width)613 static void draw_box( f32 x1, f32 y1, f32 x2, f32 y2, GXColor clr, f32 width )
614 {
615     draw_line(x1, y1, x2, y1, clr, width);
616     draw_line(x2, y1, x2, y2, clr, width);
617     draw_line(x2, y2, x1, y2, clr, width);
618     draw_line(x1, y2, x1, y1, clr, width);
619 }
620 
621