IK Spider Leg

#VRML V2.0 utf8

PROTO SpiderLeg [
		 field SFVec3f base 0 0 0  # hip position in space
		 field SFVec3f target 1 0 0  # where should foot go?
		 eventIn SFVec3f set_base
		 eventIn SFVec3f set_target
		]
{
   Group {
      children [
	 # shadow
	 DEF SHADOW Transform {
	    children Shape {
	       appearance Appearance {
		  material Material { emissiveColor 0 0 0 }
	       }
	       geometry IndexedLineSet {
		  coord DEF SHADCOORDS Coordinate { 
		     point [ 0 .01 0, 2 .01 0 ] }
		  coordIndex [ 0 1 ]
	       }
	    }
	 }
	 
	 # leg
	 DEF HIPY Transform {
	    translation IS base
	    set_translation IS set_base
	    children DEF HIPZ Transform {
	       children [
		  DEF LINE Shape {
		     appearance Appearance {
			material Material { emissiveColor .7 .3 .2 }
		     }
		     geometry IndexedLineSet {
			coord Coordinate { point [ 0 0 0, 1 0 0 ] }
			coordIndex [ 0 1 ]
		     }
		  }
		  DEF KNEE Transform {
		     translation 1 0 0
		     children USE LINE
		  }
		 ]
	    }
	 }

	 # do IK and calc shadow
	 DEF SCRIPT Script {
	    eventIn SFVec3f set_base IS set_base
	    eventIn SFVec3f set_target IS set_target
	    field SFVec3f base IS base
	    field SFVec3f target IS target
	    eventOut SFRotation hipYRotation
	    eventOut SFRotation hipZRotation
	    eventOut SFRotation kneeRotation
	    eventOut MFVec3f shadowPoints
	    eventOut SFVec3f shadowPosition
	    url "vrmlscript:

function calcJointAngles()
{
   // calc shoulder y rotation (angle to rotate 1 0 0 to point at target)
   a = new SFVec3f(1, 0, 0);
   b = new SFVec3f(target.x-base.x, 0, target.z-base.z);
   b = b.normalize();
   angle = Math.acos(a.dot(b));
   if(target.z > base.z) angle = 2*Math.PI - angle;
   hipYRotation = new SFRotation(0, 1, 0, angle);
   
   // calc shoulder z rotation (to get knee calfLength away from target)
   thighLength = 1;
   thighLength2 = Math.pow(thighLength,2);
   calfLength = 1;
   calfLength2 = Math.pow(calfLength,2);
   targDist = target.subtract(base).length();
   targDist2 = Math.pow(targDist,2);

   shadowPoints = new MFVec3f();
   shadowPoints[0] = new SFVec3f(0, 0.01, 0);

   legLength = thighLength + calfLength;
   xztarg = new SFVec3f(target.x-base.x, 0, target.z-base.z);

   if(targDist > legLength) {
      theta = 0;
      alpha = 0;
      shadowPoints[1] = new SFVec3f(legLength, 0.01, 0);
   } 
   else {
      a = new SFVec3f(1, 0, 0);
      b = new SFVec3f(xztarg.length(), -base.y, 0);
      b = b.normalize();
      angle = Math.acos(a.dot(b));
      theta = Math.acos((calfLength2 - thighLength2 - targDist2) / 
			(-2 * thighLength * targDist));
      theta -= angle;

      alpha = Math.PI - Math.acos((targDist2 - calfLength2 - thighLength2) /
				  (-2 * calfLength * thighLength));
      alpha = -alpha;
      shadowPoints[1] = new SFVec3f(xztarg.length(), .01, 0);
   }
   hipZRotation = new SFRotation(0, 0, 1, theta);
   kneeRotation = new SFRotation(0, 0, 1, alpha);
}

// reposition hip attachment
function set_base(val) { 
   base = val; 
   shadowPosition = new SFVec3f(base.x, 0, base.z);
   calcJointAngles();
}

// reposition foot
function set_target(val) {
   target = val;
   calcJointAngles();
}

function initialize() {
   calcJointAngles();
}

"
	 }
	]
   }
   ROUTE SCRIPT.hipYRotation TO HIPY.rotation
   ROUTE SCRIPT.hipYRotation TO SHADOW.rotation
   ROUTE SCRIPT.hipZRotation TO HIPZ.rotation
   ROUTE SCRIPT.kneeRotation TO KNEE.rotation
   ROUTE SCRIPT.shadowPoints TO SHADCOORDS.point
   ROUTE SCRIPT.shadowPosition TO SHADOW.translation
}


mrl