/* CMD: Clay Morph
 *
 * Set up your control points in a layer by themselves and call ClayInit.
 * Then put your object to be morphed in the background layer.
 * Then move your control points and call ClayMorph.
 * That's all there is to it.
 *
 * Note that ClayMorph only moves points.  It doesn't make new polygons.
 * So make sure your object is subdivided enough.
 *
 */
        call addlib("LWModelerARexx.port",0)
        signal on error
        signal on syntax
        call addlib("rexxmathlib.library",0,-30,0)
        call addlib("rexxsupport.library",0,-30,0)
        call main
        exit

        syntax:
        error:
        t=Notify(1,'!Rexx Script Error','@'ErrorText(rc),'Line 'SIGL)
        if (mxx_add) then call remlib(mxx)
        exit

MAIN:

pi = 3.1415926385897932384626

call random(,,time('s'))

syscode = "Clay Morph"
claynam = "ENV:clay.tmp"

/* Store current and background layers and first empty layer. */
fg = curlayer()
bg = curblayer()
emp = emptylayers()
if (words(emp) = 0) then do
    ok = notify(2,'!No empty layer.','Morph will be placed','on current layer.','Original will be','!nuked.')
    if ok = 0 then return
    tlt = 0
end
if (words(emp) > 0) then do
    tl = word(emp,1)
    tlt = 1
end


/* Get original clay positions */

if (~open(state, claynam, 'R')) then break
clays = readln(state)
call meter_begin(clays*2, syscode, "Reading Clay")
ccx = 0
ccy = 0
ccz = 0
do i=1 to clays
    xyz = readln(state)
    parse var xyz ox.i oy.i oz.i
    ccx = ccx + ox.i
    ccy = ccy + oy.i
    ccz = ccz + oz.i
    call meter_step()
end i

do i=1 to clays
    x = ox.i-ccx/clays
    y = oy.i-ccy/clays
    z = oz.i-ccz/clays
    call GETROT
    orx.i = rox
    ory.i = roy
    orz.i = roz
    call meter_step()
end i

call meter_end()
call close(state)

n = xfrm_begin()
if (n ~= clays) then call notify(1,'Clay has been damaged.','Try again.')
if (n ~= clays) then return
call meter_begin(n, syscode, "Computing changes")
do i=1 to n
    xyz = xfrm_getpos(i)
    parse var xyz nx ny nz
    x = nx-ccx/clays
    y = ny-ccy/clays
    z = nz-ccz/clays
    call GETROT
    drx.i = rox-orx.i
    dry.i = roy-ory.i
    drz.i = roz-orz.i
    call meter_step()
end i
call meter_end()
call xfrm_end()


call setlayer(bg)
n = xfrm_begin()
if (n = 0) then return
call meter_begin(n, syscode, "Morphing Points")
do i=1 to n
    xyz = xfrm_getpos(i)
    parse var xyz x y z
    x = x-ccx/clays
    y = y-ccy/clays
    z = z-ccz/clays
    call GETROT
    x = x+ccx/clays
    y = y+ccy/clays
    z = z+ccz/clays
    cx = rox
    cy = roy
    cz = roz
    call Clay
    call SETROT
    call xfrm_setpos i,x y z
    call meter_step()
end i
call meter_end()
call xfrm_end()

CLAY:
dtot = 0
dmx = 0
dmy = 0
dmz = 0
do c=1 to clays
 if dtot ~= -1 then do
  dist = sqrt((x-ox.c)**2 + (y-oy.c)**2 + (z-oz.c)**2)
  if dist = 0 then do
   dtot = -1
   dmx = drx.c
   dmy = dry.c
   dmz = drz.c
  end
  if dist ~= 0 then do
   dtot = dtot + 1/dist
   dmx = dmx + drx.c/dist
   dmy = dmy + dry.c/dist
   dmz = dmz + drz.c/dist
  end
 end
end
/* normalize */
if dtot ~= -1 then do
 dmx = dmx/dtot
 dmy = dmy/dtot
 dmz = dmz/dtot
end
cx = cx+dmx
cy = cy+dmy
cz = cz+dmz
return

GETROT:
/* 3-D Radial Rotation */
/* takes x,y,z, returns with rox,roy,roz */
rox = 0
roy = 0
roz = sqrt(x*x+y*y+z*z)
/* First we adjust our heading */
yf=0
if z=0 then do
    if x>0 then roy=roy+pi/2
    if x<0 then roy=roy-pi/2
    yf = 1
end
if x=0 then do
    if z>0 then roy=roy+0
    if z<0 then roy=roy+pi
    yf = 1
end
if yf=0 then do
    actn=abs(x/z)
    call arctan
    if z>0 then do           /* quadrant 1 */
        if x>0 then roy=roy+th
    end
    if z>0 then do           /* quadrant 2 */
        if x<0 then roy=roy-th
    end
    if x<0 then do           /* quadrant 3 */
        if z<0 then roy=roy+pi+th
    end
    if z<0 then do           /* quadrant 4 */
        if x>0 then roy=roy+pi-th
    end
end
/* Now we pitch up */
nd=sqrt(z*z+x*x)
yf=0
if nd=0 then do
    if y>0 then rox=rox+pi/2
    if y<0 then rox=rox+-pi/2
    yf = 1
end
if y=0 then yf = 1
if yf=0 then do
    actn=abs(y/nd)
    call arctan
    if y>0 then rox=rox+th
    if y<0 then rox=rox-th
end
return

ARCTAN:
actu = actn
if actu < -1 then th = pi/2+atan(1/abs(actn))
if (actu < 1.00001)&(actu > -1.00001) then th = atan(actn)
if actu > 1 then th = pi/2-atan(1/actn)
return

SETROT:
/* takes cx,cy,cz & returns with x,y,z */
/* pitch */
x = 0
y = sin(cx)
z = cos(cx)
/* heading */
x = sin(cy)*z
y = y
z = cos(cy)*z
/* radius */
x = x * cz + ccx/clays
y = y * cz + ccy/clays
z = z * cz + ccz/clays
return

    Source: geocities.com/g_fyffe/lw

               ( geocities.com/g_fyffe)