Sloppy pyramid worm

February 7th, 2009

A growing pyramid worm, with faces and corners aligned, mostly. It doesn’t line up exactly, and I’m not sure if that’s because of the shape of the pyramid or my dodgy code.

It’s easy to find out how far away something is, but how do you tell left from right? Things I thought I knew, until I tried to explain them. I may have to resort to particle physics.

MEL code follows…


//find the center of a face
proc vector centerOfFace(string $face) {
  // get locations of all vertices in an array
  float $pos[] = `xform -q -ws -t ($face)`;

  //get length of the array: # vertices * 3
  int $posSize = size($pos);

  //put the average of all vectors into 1st vector 
  for ($i = 0; $i < 3; $i++){
    for ($j = $i + 3; $j < $posSize; $j += 3){
      $pos[$i] += $pos[$j];
    $pos[$i] /= ($posSize / 3);
  vector $averagePos = <<$pos[0], $pos[1], $pos[2]>>;
  return $averagePos;

//given three distances, get angle - law of cosines
proc float getAngle(float $distance) {
  //cos angle c = a^2+b^2-c^2/2ab
  $cSquared = `pow $distance 2`;
  //a and b in this case are both 1
  $cosAngleC = (2-$cSquared)/2;
  //fix more sloppiness
  if ($cosAngleC < -1) {$cosAngleC = -1;}
  //solve for angle with arccosine, in degrees
  $degrees = `acosd $cosAngleC`;
  //get angle to closest corner
  $deg2 = $degrees % 120;
  return $deg2;

//get distance between two vertices
proc float getDistance(string $vertA, string $vertB) {
  //get location of both vertices
  $aLoc = `xform -q -ws -t $vertA`;
  $bLoc = `xform -q -ws -t $vertB`;

  //get distance with a temporary distanceDimension
  $distObj = `distanceDimension -sp $aLoc[0] $aLoc[1] $aLoc[2]
    -ep $bLoc[0] $bLoc[1] $bLoc[2]`;
  float $dist = `getAttr $distObj.distance`;
  $distObj = firstParentOf($distObj);
  delete $distObj;
  delete "*cator*";
  return $dist;

proc doit() {

//make a 3-sided cone and select it
select `polyCone -r 1 -h 1.414214 -sx 3 -n "pCone1"`;
//move its pivot point to the bottom face
xform -rp 0 -0.707107 0;
//move to origin and freeze xforms
xform -t 0 0.707107 0;
makeIdentity -apply true -t 1;

//turn off constraint warnings
cycleCheck -e off;

for ($i=0; $i<500; ++$i) {
  //select cone
  $old = `ls -sl`;

  //select a random face that isn't the base
  int $r = rand(3)+1;
  $selectedFace = ( $old[0] + ".f[" + $r + "]" );
  select $selectedFace;

  //instance cone and move to face
  select `instance $old`;
  $new = `ls -sl`;
  vector $fc = centerOfFace($selectedFace);
  xform -a -t ($fc.x) ($fc.y) ($fc.z) $new;

  //align instance to face with a temporary normal constraint
  string $tmpConst[] = `normalConstraint -aim 0 1 0 $selectedFace $new`;
  delete $tmpConst[0];

  //pick a vertex on each cone
  $newVertex = $new[0]+".vtx[0]"; // 0 is on the base
  $oldVertex = $old[0]+".vtx[3]"; // 3 is the tip

  //until i can tell left from right, repeat until it works
  float $angle = 60; // start with a non-zero number
  while ($angle > 1) { 
    // 1) get distance between two vertices
    float $dist = getDistance($newVertex, $oldVertex);
    // 2) get angle between two vertices
    $angle = getAngle($dist);
    // 3) rotate the instance to align the corners
    xform -r -os -ro 0 $angle 0 $new;

  //animate visibility
  setKeyframe -attribute "visibility" -v 0 -t 0 $new; 
  setKeyframe -attribute "visibility" -v 1 -t ($i+1) $new;

  select $new;
  cycleCheck -e on;

« previously: Aligned faces pyramid worm | Home | next: Something I Learned Today »