Programming by Example

A BB4W Compendium

Object Axes

So far, we've rotated templates of objects around the fixed (Z, X, Y) axes of our 'universe' to orient the objects however we wish. However, there are occasions when we want to rotate an object about its own axes e.g. a plane in flight can roll, pitch and yaw relative to its current orientation.

One solution to this problem is to use vectors:

Once the relevant local axes ('alignment vectors') have been transformed by the rotation vectors, we use the positive z & y alignment vectors to determine the correct angles of rotation (about the fixed Z, X, Y axes) to achieve this new orientation.

I.e. We drag the alignment vectors into a new position, mimicking the act of rotation, and then work out the z, x & y angles needed to rotate the template into the same orientation.

The following program demonstrates this process. Use the mouse to click on the '< >' arrows to rotate the object about its local axes.

10 MODE 9:OFF

20 ORIGIN 640,512

30 *REFRESH OFF

40 DIM nd{(17) xt,yt,zt,xr,yr,zr,x2,y2}:REM Nodes

50 DIM link{(17) type,n1,n2,n3}:REM Links (colours & triangles)

60 DIM td{(1) np,lp,nc,lc}:REM Net pointers

70 FOR a=0 TO 17

80 READ nd{(a)}.xt,nd{(a)}.yt,nd{(a)}.zt

90 NEXT

100 FOR a=0 TO 17

110 READ link{(a)}.type,link{(a)}.n1,link{(a)}.n2,link{(a)}.n3

120 NEXT

130 FOR a=0 TO 0

140 READ td{(a)}.np,td{(a)}.lp,td{(a)}.nc,td{(a)}.lc

150 NEXT

160 REM Create a unit vector pointing towards a light source

170 lightx=100:lighty=100:lightz=10

180 lm=SQR(lightx^2+lighty^2+lightz^2)

190 lightx/=lm:lighty/=lm:lightz/=lm

200

210 za=0:xa=0:ya=0:REM Object orientation

220

230 REPEAT

240 TIME=0:CLS

250 PRINTTAB(0,0);"Object's Z-axis: < >"

260 PRINTTAB(0,1);"Object's X-axis: < >"

270 PRINTTAB(0,2);"Object's Y-axis: < >"

280

290 MOUSE mx,my,mz

300 mx DIV=32:my DIV=32:dir$=""

310 IF mx=-2 dir$="A"

320 IF mx=0 dir$="C"

330 IF dir$<>"" AND my>=13 AND my<=15 THEN

340 GCOL 3,4:RECTANGLEFILL mx*32,my*32,-32,32

350 IF mz<>0 THEN

360 CASE my OF

370 WHEN 13 : PROCflybywire("Y",2,dir$,za,xa,ya)

380 WHEN 14 : PROCflybywire("X",2,dir$,za,xa,ya)

390 WHEN 15 : PROCflybywire("Z",2,dir$,za,xa,ya)

400 ENDCASE

410 ENDIF

420 ENDIF

430

440 PROCdraw(0,0,0,-40,za,xa,ya)

450 *REFRESH

460 WAIT 4-TIME

470 UNTIL FALSE

480 END

490

500 DEF PROCflybywire(ax$,angle,dir$,RETURN za,RETURN xa,RETURN ya)

510 LOCAL f,a,ca,sa,x,y,z

520 f=(SQR(2)-(TAN(RAD(45-angle))*SQR(2)))/(SQR(2)*2)

530 FOR a=0 TO 4:PROCrotzxy(a,0,0,0,za,xa,ya):NEXT

540 CASE ax$ OF

550 WHEN "Z"

560 IF dir$="C" THEN

570 nd{(1)}.xr+=f*(nd{(3)}.xr-nd{(1)}.xr)

580 nd{(1)}.yr+=f*(nd{(3)}.yr-nd{(1)}.yr)

590 nd{(1)}.zr+=f*(nd{(3)}.zr-nd{(1)}.zr)

600 ELSE

610 nd{(1)}.xr+=f*(nd{(4)}.xr-nd{(1)}.xr)

620 nd{(1)}.yr+=f*(nd{(4)}.yr-nd{(1)}.yr)

630 nd{(1)}.zr+=f*(nd{(4)}.zr-nd{(1)}.zr)

640 ENDIF

650 WHEN "X"

660 IF dir$="C" THEN

670 nd{(0)}.xr+=f*(nd{(1)}.xr-nd{(0)}.xr)

680 nd{(0)}.yr+=f*(nd{(1)}.yr-nd{(0)}.yr)

690 nd{(0)}.zr+=f*(nd{(1)}.zr-nd{(0)}.zr)

700 ELSE

710 nd{(0)}.xr+=f*(nd{(2)}.xr-nd{(0)}.xr)

720 nd{(0)}.yr+=f*(nd{(2)}.yr-nd{(0)}.yr)

730 nd{(0)}.zr+=f*(nd{(2)}.zr-nd{(0)}.zr)

740 ENDIF

750 WHEN "Y"

760 IF dir$="C" THEN

770 nd{(0)}.xr+=f*(nd{(4)}.xr-nd{(0)}.xr)

780 nd{(0)}.yr+=f*(nd{(4)}.yr-nd{(0)}.yr)

790 nd{(0)}.zr+=f*(nd{(4)}.zr-nd{(0)}.zr)

800 ELSE

810 nd{(0)}.xr+=f*(nd{(3)}.xr-nd{(0)}.xr)

820 nd{(0)}.yr+=f*(nd{(3)}.yr-nd{(0)}.yr)

830 nd{(0)}.zr+=f*(nd{(3)}.zr-nd{(0)}.zr)

840 ENDIF

850 ENDCASE

860 ya=FNgetbearing(nd{(0)}.xr,nd{(0)}.zr)

870 REM Unwind 2 main alignment vectors about y-axis

880 ca=COS(RAD(ya)):sa=SIN(RAD(ya))

890 z=nd{(0)}.zr*ca+nd{(0)}.xr*sa

900 x=nd{(0)}.xr*ca-nd{(0)}.zr*sa

910 nd{(0)}.zr=z

920 nd{(0)}.xr=x

930 z=nd{(1)}.zr*ca+nd{(1)}.xr*sa

940 x=nd{(1)}.xr*ca-nd{(1)}.zr*sa

950 nd{(1)}.zr=z

960 nd{(1)}.xr=x

970 xa=FNgetbearing(nd{(0)}.yr,nd{(0)}.zr)

980 REM Unwind vertical alignment vectors about x-axis

990 ca=COS(RAD(360-xa)):sa=SIN(RAD(360-xa))

1000 y=nd{(1)}.yr*ca+nd{(1)}.zr*sa

1010 z=nd{(1)}.zr*ca-nd{(1)}.yr*sa

1020 nd{(1)}.yr=y

1030 nd{(1)}.zr=z

1040 za=FNgetbearing(nd{(1)}.xr,nd{(1)}.yr)

1050 IF ya<>0 ya=360-ya

1060 ENDPROC

1070

1080 DEF FNgetbearing(x,y)

1090 LOCAL a,h

1100 a=0:h=SQR(x*x+y*y)

1110 IF h>0 THEN

1120 IF ABS(x)<ABS(y) THEN

1130 a=DEG(ACS(y/h))

1140 ELSE

1150 a=90-DEG(ACS(ABS(x)/h)):IF y<0 AND a<>180 a=180-a

1160 ENDIF

1170 IF x<0 AND a<>0 a=360-a

1180 ENDIF

1190 =a

1200

1210 DEF PROCdraw(net,x,y,z,za,xa,ya)

1220 LOCAL f,abort,col,a,n1,n2,n3,v1x,v1y,v1z,v2x,v2y,v2z,nx,ny,nz,nm,v

1230 f=1280:abort=FALSE:col=7:GCOL 0,15

1240 FOR a=td{(net)}.np TO td{(net)}.np+td{(net)}.nc-1

1250 PROCrotzxy(a,x,y,z,za,xa,ya):REM Rotate node & add offset

1260 IF nd{(a)}.zr>-1 THEN

1270 abort=TRUE:REM Node behind viewer

1280 ELSE

1290 nd{(a)}.x2=f*nd{(a)}.xr/-nd{(a)}.zr:REM 3D to 2D

1300 nd{(a)}.y2=f*nd{(a)}.yr/-nd{(a)}.zr

1310 ENDIF

1320 NEXT

1330 IF abort ENDPROC

1340 FOR a=td{(net)}.lp TO td{(net)}.lp+td{(net)}.lc-1

1350 CASE link{(a)}.type OF

1360 WHEN 1 : col=link{(a)}.n1

1370 WHEN 7

1380 n1=link{(a)}.n1+td{(net)}.np

1390 n2=link{(a)}.n2+td{(net)}.np

1400 n3=link{(a)}.n3+td{(net)}.np

1410 REM Calc two vectors from 3 co-ords (triangle): b-a and c-a

1420 v1x=nd{(n2)}.xr-nd{(n1)}.xr

1430 v1y=nd{(n2)}.yr-nd{(n1)}.yr

1440 v1z=nd{(n2)}.zr-nd{(n1)}.zr

1450 v2x=nd{(n3)}.xr-nd{(n1)}.xr

1460 v2y=nd{(n3)}.yr-nd{(n1)}.yr

1470 v2z=nd{(n3)}.zr-nd{(n1)}.zr

1480 REM Find the 'normal' of a triangle, (cross product of two vectors)