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