Just for fun, I've improved the world space tripod from the example. It should be unproblematic moving it to other projects without modification (provided you need just the one).
Also made the camera base-position indicator easier to read (haven't properly formalised it though, so will need modification).
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync : autocam off : color backdrop 0x485A82
global scrW, scrH
scrW = screen width() : scrH = screen height()
global mx, my, mmx, mmy, mmz as float, kLeft, kRight, kUp, kDown, camZoom as float, camBaseX as float, camBaseY as float, camBaseZ as float
SetupUiWST(1000, find free camera(), 24, scrH*0.88 - 24, 24 + scrH*0.12, scrH - 24)
// gen'd demonstration texture.
for n = 1 to 100 : box rnd(128), rnd(128), rnd(128), rnd(128), 0x0880ffff : next n
set text font "Arial" : set text size 100 : center text 64, 14, "2" : set text size 20
get image 1, 0, 0, 128, 128, 1
// example spinning boxes.
for n = 1 to 32
sz = 7 + n/4
MakeSegmentedBox(n, sz, sz, sz, 6, 9, 12)
position object n, -0, 9, 0
set object wireframe n, 1
next n
MakeSegmentedBox(33, 8, 0.1, 8, 1, 1, 1)
// example boxes-in-boxes.
MakeSegmentedBox(50, 64, 64, 64, 8, 8, 8) : position object 50, 16 + 28, 4 + 28, 0 + 28 : set object wireframe 50, 1
MakeSegmentedBox(51, 8, 8, 8, 8, 8, 8) : position object 51, 16, 4, 0 : set object wireframe 51, 1
MakeSegmentedBox(52, 1, 1, 1, 8, 8, 8) : position object 52, 16 - 3.5, 4 - 3.5, 0 - 3.5 : set object wireframe 52, 1
MakeSegmentedBox(53, 0.125, 0.125, 0.125, 8, 8, 8) : position object 53, 16 - 3.9375, 4 - 3.9375, 0 - 3.9375 : set object wireframe 53, 1
// example wavy.
MakeSegmentedBox(60, 160, 8, 32, 40, 1, 32) : position object 60, -4, -3.95, -20 : convert object fvf 60, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
lock vertexdata for limb 60, 6 // top
vc = get vertexdata vertex count() - 1
for vi = 0 to vc
vx# = get vertexdata position x(vi)` : dx# = (80.0 - abs(vx#)) / 80.0
vz# = get vertexdata position z(vi)` : dz# = (16.0 - abs(vz#)) / 16.0
`set vertexdata position vi, vx#, dx#*dz# * sin(vx#^2 + vz#^2) * 2.5, vz#
set vertexdata diffuse vi, rgb(192+rnd(63), 236+rnd(15), 248+rnd(7)) ` 0xff0000ff + rnd(0x080800) ` 0xfff8f8ff + rnd(0x070700)
next vi
unlock vertexdata
lock vertexdata for limb 60, 5 // bottom
for vi = 0 to vc
vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
set vertexdata position vi, vx#, (dx#*dz#)^2.0 * -48.0, vz#
set vertexdata diffuse vi, 0xffc0c0c0 + rnd(0x3f3f3f)
next vi
unlock vertexdata
set object normals 60
// rainbow.
for n = 300 to 400
sz = (n+1-300) * 0.4
MakeSegmentedBox(n, sz, sz, sz, 1+rnd(9), 1+rnd(9), 1+rnd(9))
position object n, 0, 20, -70
rotate object n, rndSign()*0.25, rndSign()*0.25, rndSign()*0.25
`set object wireframe n, 1
set object cull n, 0
color object n, hsv to rgb((n-300)*3.6, 1, 1)
// remove limbs 3,4,5 (as they show grey rather than coloured under normal dbp lighting condition).
remove limb n, 3 // 1 2 _3_ 4 5 6 -> 1 2 4 5 6
remove limb n, 3 // 1 2 _4_ 5 6 -> 1 2 5 6
remove limb n, 3 // 1 2 _5_ 6 -> 1 2 6
next n
// textured.
MakeSegmentedBox(401, 8, 8, 8, 1, 3, 9)
texture object 401, 1
position object 401, 72, 4, 0
// setup camera (with basepos indicator).
make object cone 100, 0.4 : xrotate object 100, 180 : fix object pivot 100 : offset limb 100, 0, 0, -0.24, 0 : set object cull 100, 0
make object sphere 101, 1 : color object 101, 0x808080 : set alpha mapping on 101, 20 : disable object zdepth 101
make object sphere 102, 0.9 : color object 102, 0x808080 : set alpha mapping on 102, 20
camBaseX = 0 : camBaseZ = -30
// main loop.
do
UpdateInput()
if controlkey() then box outline uiWST.viewx1, uiWST.viewy1, uiWST.viewx2, uiWST.viewy2, 0xff00ff00
// update spinning boxes.
for n = 1 to 32
xrotate object n, object angle x(n)+0.0066 + n*0.0033
yrotate object n, object angle y(n)+0.01 + n*0.01
zrotate object n, object angle z(n)+0.003 + n*0.0015
next n
// [ hold right-mouse button to spin camera ]
UpdateCamera(0, (mouseclick()>1 && mouseclick()<3), (mouseclick()>3 && mouseclick()<5), 1, 10)
UpdateUiWST(camera angle x(0), camera angle y(0), camera angle z(0))
// update waves.
lock vertexdata for limb 60, 6
vc = get vertexdata vertex count() - 1
for vi = 0 to vc
vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
set vertexdata position vi, vx#, dx#*dz# * sin(hitimer()*0.1 + vx#^2 + vz#^2) * 2.5, vz#
next vi
unlock vertexdata
q = q - 1 : if q =< 0 then q = 4 : set object normals 60 // [very slow, less frequently called]
// update rainbow.
for n = 300 to 400
sc# = 100.0 + sin(hitimer()*0.1 + n*3.6) * 50.0
scale object n, sc#, sc#, sc#
if rnd(4) = 0
for o = 1 to 3
if rnd(4) = 0
lock vertexdata for limb n, o
vc = get vertexdata vertex count() - 1
for vi = 0 to vc
vx# = get vertexdata position x(vi) + rndSign()*0.1
vy# = get vertexdata position y(vi) + rndSign()*0.1
vz# = get vertexdata position z(vi) + rndSign()*0.1
set vertexdata position vi, vx#, vy#, vz#
next vi
unlock vertexdata
endif
next o
endif
`set object normals n
next n
set cursor 0,0
print "fps: ", screen fps()
print "hold right-mouse and drag to spin camera. Pan with arrow keys, or mouse with mousewheel held. Mousewheel scroll zooms."
print "controlkey: outline tripod viewport"
sync
nice wait 15
loop
function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
local objNegX, objNegZ, objPosX, objPosZ, objNegY, objPosY, meshNegX, meshNegZ, meshPosX, meshPosZ, meshNegY, meshPosY
// start by creating faces as a series of planes: -x, -z, +x, +z, -y, +y.
null = reserve free object(objNum, 1) // ensure final object's number not used.
objNegX = find free object() : make object plane objNegX, d, h, dseg, hseg : rotate object objNegX, -90, 90, 0 : fix object pivot objNegX
objNegZ = find free object() : make object plane objNegZ, w, h, wseg, hseg : rotate object objNegZ, -90, 0, 0 : fix object pivot objNegZ
objPosX = find free object() : make object plane objPosX, d, h, dseg, hseg : rotate object objPosX, -90, -90, 0 : fix object pivot objPosX
objPosZ = find free object() : make object plane objPosZ, w, h, wseg, hseg : rotate object objPosZ, -90, 180, 0 : fix object pivot objPosZ
objNegY = find free object() : make object plane objNegY, w, d, wseg, dseg : rotate object objNegY, 180, 0, 0 : fix object pivot objNegY
objPosY = find free object() : make object plane objPosY, w, d, wseg, dseg
release reserved object objNum
// prepare meshes for object limbs.
meshNegX = find free mesh() : make mesh from object meshNegX, objNegX
meshNegZ = find free mesh() : make mesh from object meshNegZ, objNegZ
meshPosX = find free mesh() : make mesh from object meshPosX, objPosX
meshPosZ = find free mesh() : make mesh from object meshPosZ, objPosZ
meshNegY = find free mesh() : make mesh from object meshNegY, objNegY
meshPosY = find free mesh() : make mesh from object meshPosY, objPosY
// object has faces on limbs 1 thru 6.
begin new object
zerolimb = add new limb()
add new limb from mesh meshNegX : set new limb parent zerolimb : set new limb offset -w/2.0, 0, 0 : set new limb transparent 0
add new limb from mesh meshNegZ : set new limb parent zerolimb : set new limb offset 0, 0, -d/2.0 : set new limb transparent 0
add new limb from mesh meshPosX : set new limb parent zerolimb : set new limb offset w/2.0, 0, 0 : set new limb transparent 0
add new limb from mesh meshPosZ : set new limb parent zerolimb : set new limb offset 0, 0, d/2.0 : set new limb transparent 0
add new limb from mesh meshNegY : set new limb parent zerolimb : set new limb offset 0, -h/2.0, 0 : set new limb transparent 0
add new limb from mesh meshPosY : set new limb parent zerolimb : set new limb offset 0, h/2.0, 0 : set new limb transparent 0
finish new object objNum
// cleanup.
delete object objNegX : delete mesh meshNegX
delete object objNegZ : delete mesh meshNegZ
delete object objPosX : delete mesh meshPosX
delete object objPosZ : delete mesh meshPosZ
delete object objNegY : delete mesh meshNegY
delete object objPosY : delete mesh meshPosY
endfunction
// -- UTILITY FUNCTIONS --
function UpdateInput()
mx = mousex()
my = mousey()
mmx = mousemovex()
mmy = mousemovey()
mmz = mousemovez()
kLeft = leftkey()
kRight = rightkey()
kUp = upkey()
kDown = downkey()
endfunction
global oldEnableYRot as boolean, mouseStaticX, mouseStaticY
function UpdateCamera(camId, enableYRot as boolean, enableXYPan as boolean, forceYRotMouseStatic as boolean, baseDist as float)
camZoom = min(-1.0, camZoom + mmz*0.033) `0.0066
position camera camId, camBaseX, camBaseY, camBaseZ
if enableYRot
yrotate camera camId, camera angle y(camId)+mmx*0.2
xrotate camera camId, clamp(camera angle x(camId)+mmy*0.2, 0, 90)
if forceYRotMouseStatic
if oldEnableYRot = 0 then hide mouse : mouseStaticX = mx : mouseStaticY = my
position mouse mouseStaticX, mouseStaticY
endif
else
if forceYRotMouseStatic and oldEnableYRot = 1 then show mouse
endif
oldEnableYRot = enableYRot
if enableXYPan
move camera right camId, mmx*sqrt(-camZoom)*0.02
yrotate camera camId, camera angle y(camId)-90
move camera right camId, -mmy*sqrt(-camZoom)*0.02
yrotate camera camId, camera angle y(camId)+90
endif
move camera right camId, (kRight-kLeft)*0.15
yrotate camera camId, camera angle y(camId)-90
move camera right camId, (kUp-kDown)*0.15
yrotate camera camId, camera angle y(camId)+90
camBaseX = camera position x(camId)
camBaseY = camera position y(camId)
camBaseZ = camera position z(camId)
move camera camId, -baseDist + camZoom
// update camera base marker.
if enableXYPan or enableYRot
show object 100
position object 100, camBaseX, camBaseY, camBaseZ
scale object 100, 150 - sin(hitimer()*0.3) * 50.0, 300 + sin(hitimer()*0.3) * 200.0, 150 - sin(hitimer()*0.3) * 50.0
show object 101
position object 101, camBaseX, camBaseY, camBaseZ
s# = (-baseDist+camZoom)*10.0 : scale object 101, s#, s#, s#
show object 102
position object 102, camBaseX, camBaseY, camBaseZ
s# = (-baseDist+camZoom)*10.0 : scale object 102, s#, s#, s#
else
hide object 100
hide object 101
hide object 102
endif
endfunction
// uiWST (World Space Tripod)
// uiWST requires four objects with continuous id's, the first of which is the id passed to the SetupUIAxisIndicator function. Ensure objects first .. first+3 are free.
// also requires a camera.
type t_uiWST objFirst, objLast, camId, viewx1, viewy1, viewx2, viewy2, posx as float, posy as float, posz as float endtype
global uiWST as t_uiWST
function SetupUiWST(id, camId, viewx1, viewy1, viewx2, viewy2)
// IDs of meshes for bits of axis model; mesh 1 then recycled for complete axis model.
local mesh1, mesh2, mesh3, mesh4
// WST will be located out in a safe (presumably, empty) spot in space. [note: large numbers cause visible instability (float precision thing, probably)]
uiWST.posx = -2048.0
uiWST.posy = -2048.0
uiWST.posz = -2048.0
// [note: not really used...]
uiWST.objFirst = id
uiWST.objLast = id+3
// model an axis (will be x-facing).
make object box id, 0.25, 0.025, 0.025 : mesh1 = find free mesh() : make mesh from object mesh1, id : delete object id // -ve facing length.
make object box id+1, 0.4, 0.04, 0.04 : mesh2 = find free mesh() : make mesh from object mesh2, id+1 : delete object id+1 // +ve facing length.
make object cone id+2, 0.03*0 : mesh3 = find free mesh() : make mesh from object mesh3, id+2 : delete object id+2 // +ve facing arrowhead [note: hidden].
begin new object
add new limb from mesh mesh1 : set new limb offset -0.25, 0, 0
add new limb from mesh mesh2 : set new limb offset 0.25, 0, 0
add new limb from mesh mesh3 : set new limb offset 0.7, 0, 0 : set new limb rotation 0, 0, -90
finish new object id
delete mesh mesh1 : delete mesh mesh2 : delete mesh mesh3
make mesh from object mesh1, id
delete object id
// make 3 axes from the axis mesh.
make object id, mesh1 : color object id, 0xff0000 : set object emissive id, 0xff0000 : set object cull id, 0 // x.
make object id+1, mesh1 : color object id+1, 0x00ff00 : set object emissive id+1, 0x00ff00 : set object cull id+1, 0 : rotate object id+1, 0, 0, 90 // y.
make object id+2, mesh1 : color object id+2, 0x0000ff : set object emissive id+2, 0x0000ff : set object cull id+2, 0 : rotate object id+2, 0, -90, 0 // z.
// make box at centre, each face axis-coloured.
MakeSegmentedBox(id+3, 0.11, 0.11, 0.11, 1, 1, 1)
convert object fvf id+3, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
set object light id+3, 0
lock vertexdata for limb id+3, 1 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
lock vertexdata for limb id+3, 2 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
lock vertexdata for limb id+3, 3 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
lock vertexdata for limb id+3, 4 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
lock vertexdata for limb id+3, 5 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
lock vertexdata for limb id+3, 6 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
for n = id to id+3
disable object zdepth n
position object n, uiWST.posx, uiWST.posy, uiWST.posz
set object mask n, 1 << camId
set object radius n, -1 // [fixes flickering when posx/y/z are large]
next n
// setup camera for showing tripod.
uiWST.camId = camId
uiWST.viewx1 = viewx1
uiWST.viewy1 = viewy1
uiWST.viewx2 = viewx2
uiWST.viewy2 = viewy2
make camera camId
set camera aspect camId, (viewy2-viewy1)*1.0 / (viewx2-viewx1) ` square: 1.0
set camera fov camId, 45 ` 45, 61.9621391296
set camera view camId, viewx1, viewy1, viewx2, viewy2
set camera range camId, 0.001, 3 // close range, tripod is small.
position camera camId, uiWST.posx, uiWST.posy, uiWST.posz
rotate camera camId, 0, 0, 0
backdrop off camId // allow scene viewport to show behind rendered tripod.
// cleanup.
delete mesh mesh1
endfunction
function UpdateUiWST(ax#, ay#, az#)
// update view angle.
rotate camera uiWST.camId, ax#, ay#, az#
position camera uiWST.camId, uiWST.posx, uiWST.posy, uiWST.posz : move camera 1, -1.7 `-2.0
// label axes (3d-to-2d facilitated by shuffling object objFirst as a proxy).
id = uiWST.objFirst
tmp = current camera()
set current camera uiWST.camId
position object id, uiWST.posx+0.62, uiWST.posy, uiWST.posz : x_x = object screen x(id) : x_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy+0.62, uiWST.posz : y_x = object screen x(id) : y_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy, uiWST.posz+0.62 : z_x = object screen x(id) : z_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy, uiWST.posz
set current camera tmp
halfh = text height("gM") / 2
fill circle x_x, x_y, halfh*1.2, 0x24000000
fill circle y_x, y_y, halfh*1.2, 0x24000000
fill circle z_x, z_y, halfh*1.2, 0x24000000
center text x_x, x_y - halfh, "x"
center text y_x, y_y - halfh, "y"
center text z_x, z_y - halfh, "z"
endfunction
function RndSign()
s = 1 - rnd(1)*2
endfunction s
` FVF constants
#constant FVF_XYZ = 0x002
#constant FVF_XYZRHW = 0x004
#constant FVF_NORMAL = 0x010
#constant FVF_PSIZE = 0x020
#constant FVF_DIFFUSE = 0x040
#constant FVF_SPECULAR = 0x080
#constant FVF_TEX0 = 0x000
#constant FVF_TEX1 = 0x100
#constant FVF_TEX2 = 0x200
#constant FVF_TEX3 = 0x300
#constant FVF_TEX4 = 0x400
#constant FVF_TEX5 = 0x500
#constant FVF_TEX6 = 0x600
#constant FVF_TEX7 = 0x700
#constant FVF_TEX8 = 0x800
Not entirely a tangent - the centre-cube of the tripod is made as a segmented cube so I can colour each limb/face seperately.
The tripod alone:
// uiWST (World Space Tripod)
// uiWST requires four objects with continuous id's, the first of which is the id passed to the SetupUIAxisIndicator function. Ensure objects first .. first+3 are free.
// also requires a camera.
type t_uiWST objFirst, objLast, camId, viewx1, viewy1, viewx2, viewy2, posx as float, posy as float, posz as float endtype
global uiWST as t_uiWST
function SetupUiWST(id, camId, viewx1, viewy1, viewx2, viewy2)
// IDs of meshes for bits of axis model; mesh 1 then recycled for complete axis model.
local mesh1, mesh2, mesh3, mesh4
// WST will be located out in a safe (presumably, empty) spot in space. [note: large numbers cause visible instability (float precision thing, probably)]
uiWST.posx = -2048.0
uiWST.posy = -2048.0
uiWST.posz = -2048.0
// [note: not really used...]
uiWST.objFirst = id
uiWST.objLast = id+3
// model an axis (will be x-facing).
make object box id, 0.25, 0.025, 0.025 : mesh1 = find free mesh() : make mesh from object mesh1, id : delete object id // -ve facing length.
make object box id+1, 0.4, 0.04, 0.04 : mesh2 = find free mesh() : make mesh from object mesh2, id+1 : delete object id+1 // +ve facing length.
make object cone id+2, 0.03*0 : mesh3 = find free mesh() : make mesh from object mesh3, id+2 : delete object id+2 // +ve facing arrowhead [note: hidden].
begin new object
add new limb from mesh mesh1 : set new limb offset -0.25, 0, 0
add new limb from mesh mesh2 : set new limb offset 0.25, 0, 0
add new limb from mesh mesh3 : set new limb offset 0.7, 0, 0 : set new limb rotation 0, 0, -90
finish new object id
delete mesh mesh1 : delete mesh mesh2 : delete mesh mesh3
make mesh from object mesh1, id
delete object id
// make 3 axes from the axis mesh.
make object id, mesh1 : color object id, 0xff0000 : set object emissive id, 0xff0000 : set object cull id, 0 // x.
make object id+1, mesh1 : color object id+1, 0x00ff00 : set object emissive id+1, 0x00ff00 : set object cull id+1, 0 : rotate object id+1, 0, 0, 90 // y.
make object id+2, mesh1 : color object id+2, 0x0000ff : set object emissive id+2, 0x0000ff : set object cull id+2, 0 : rotate object id+2, 0, -90, 0 // z.
// make box at centre, each face axis-coloured.
MakeSegmentedBox(id+3, 0.11, 0.11, 0.11, 1, 1, 1)
convert object fvf id+3, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
set object light id+3, 0
lock vertexdata for limb id+3, 1 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
lock vertexdata for limb id+3, 2 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
lock vertexdata for limb id+3, 3 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
lock vertexdata for limb id+3, 4 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
lock vertexdata for limb id+3, 5 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
lock vertexdata for limb id+3, 6 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
for n = id to id+3
disable object zdepth n
position object n, uiWST.posx, uiWST.posy, uiWST.posz
set object mask n, 1 << camId
set object radius n, -1 // [fixes flickering when posx/y/z are large]
next n
// setup camera for showing tripod.
uiWST.camId = camId
uiWST.viewx1 = viewx1
uiWST.viewy1 = viewy1
uiWST.viewx2 = viewx2
uiWST.viewy2 = viewy2
make camera camId
set camera aspect camId, (viewy2-viewy1)*1.0 / (viewx2-viewx1) ` square: 1.0
set camera fov camId, 45 ` 45, 61.9621391296
set camera view camId, viewx1, viewy1, viewx2, viewy2
set camera range camId, 0.001, 3 // close range, tripod is small.
position camera camId, uiWST.posx, uiWST.posy, uiWST.posz
rotate camera camId, 0, 0, 0
backdrop off camId // allow scene viewport to show behind rendered tripod.
// cleanup.
delete mesh mesh1
endfunction
function UpdateUiWST(ax#, ay#, az#)
// update view angle.
rotate camera uiWST.camId, ax#, ay#, az#
position camera uiWST.camId, uiWST.posx, uiWST.posy, uiWST.posz : move camera 1, -1.7 `-2.0
// label axes (3d-to-2d facilitated by shuffling object objFirst as a proxy).
id = uiWST.objFirst
tmp = current camera()
set current camera uiWST.camId
position object id, uiWST.posx+0.62, uiWST.posy, uiWST.posz : x_x = object screen x(id) : x_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy+0.62, uiWST.posz : y_x = object screen x(id) : y_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy, uiWST.posz+0.62 : z_x = object screen x(id) : z_y = object screen y(id)
position object id, uiWST.posx, uiWST.posy, uiWST.posz
set current camera tmp
halfh = text height("gM") / 2
fill circle x_x, x_y, halfh*1.2, 0x24000000
fill circle y_x, y_y, halfh*1.2, 0x24000000
fill circle z_x, z_y, halfh*1.2, 0x24000000
center text x_x, x_y - halfh, "x"
center text y_x, y_y - halfh, "y"
center text z_x, z_y - halfh, "z"
endfunction
.