Programming by Example

 

A BB4W Compendium

freeman69@gmx.com

IDIC BBC_Owl2 M&P

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:

LocalRotation

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)

1490       nx=v1y*v2z-v1z*v2y

1500       ny=v1z*v2x-v1x*v2z

1510       nz=v1x*v2y-v1y*v2x

1520       REM Vector towards viewer

1530       v1x=0-nd{(n1)}.xr

1540       v1y=0-nd{(n1)}.yr

1550       v1z=0-nd{(n1)}.zr

1560       REM Triangle faces viewer (normal points towards viewer)?

1570       IF (nx*v1x+ny*v1y+nz*v1z)>0 THEN

1580         nm=SQR(nx^2+ny^2+nz^2)

1590         REM Calc triangle shade from: A.B / |A||B|

1600         v=(nx*lightx+ny*lighty+nz*lightz)/nm

1610         IF v<-1 v=0 ELSE IF v>1 v=255 ELSE v=255-ACS(v)/PI*255

1620         COLOUR 15,v*(col AND 1),v*((col AND 2)/2),v*((col AND 4)/4)

1630         MOVE nd{(n1)}.x2,nd{(n1)}.y2

1640         MOVE nd{(n2)}.x2,nd{(n2)}.y2

1650         PLOT 85,nd{(n3)}.x2,nd{(n3)}.y2

1660       ENDIF

1670   ENDCASE

1680 NEXT

1690 ENDPROC

1700

1710 REM Rotate a node about 3 axes and add any offset

1720 DEF PROCrotzxy(n,x,y,z,za,xa,ya)

1730 LOCAL ca,sa,x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4

1740 x1=nd{(n)}.xt:y1=nd{(n)}.yt:z1=nd{(n)}.zt

1750 z2=z1:ca=COS(RAD(za)):sa=SIN(RAD(za)):x2=x1*ca+y1*sa:y2=y1*ca-x1*sa

1760 x3=x2:ca=COS(RAD(xa)):sa=SIN(RAD(xa)):y3=y2*ca+z2*sa:z3=z2*ca-y2*sa

1770 y4=y3:ca=COS(RAD(ya)):sa=SIN(RAD(ya)):z4=z3*ca+x3*sa:x4=x3*ca-z3*sa

1780 nd{(n)}.xr=x4+x:nd{(n)}.yr=y4+y:nd{(n)}.zr=z4+z

1790 ENDPROC

1800

1810 REM Alignment vectors

1820 DATA 0,0,1,0,1,0,0,-1,0,1,0,0,-1,0,0

1830 REM Ship nodes (x,y,z)

1840 DATA 0,4,-8,0,-4,-8,8,0,-4,-8,0,-4,0,0,8,6,4,-8

1850 DATA 6,-4,-8,-6,-4,-8,-6,4,-8,8,0,8,-8,0,8,8,0,-8,-8,0,-8

1860 REM Ship links (type,n1,n2,n3: type 1=colour, 7=triangle)

1870 DATA 1,1,0,0,7,7,12,10,7,12,8,10,7,11,6,9,7,9,5,11,1,7,0,0

1880 DATA 7,0,4,2,7,0,3,4,1,6,0,0,7,4,3,1,7,1,2,4,7,2,1,0,7,1,3,0

1890 DATA 1,1,0,0,7,7,10,12,7,12,10,8,7,9,6,11,7,9,11,5

1900 REM Net pointers

1910 DATA 5,0,13,18

Arrow black large Arrow black large ObjectAxes