/* Extrusion.C: Extrusion nodes (public source) */

#include "Extrusion.H"
#include "renderer.H"

namespace xrml {

void Extrusion::set_crossSection(const MFVec2f&)
{
  Warning(NULL, "Extrusion::set_crossSection(): not yet implemented");
}

void Extrusion::set_orientation(const MFRotation&)
{
  Warning(NULL, "Extrusion::set_orientation(): not yet implemented");
}

void Extrusion::set_scale(const MFVec2f&)
{
  Warning(NULL, "Extrusion::set_scale(): not yet implemented");
}

void Extrusion::set_spine(const MFVec3f&)
{
  Warning(NULL, "Extrusion::set_spine(): not yet implemented");
}

void Extrusion::render(void)
{
  world->renderer->extrusion(this);
}

void Extrusion::discretize(void)
{
  int i, j, k, n = spine.size;
  if (n <= 0) return;

  array<Vec3> X(n), Y(n), Z(n);

  // compute SCP X,Y,Z vectors for each spine point
  if (n == 1) {
    Y[0] = Vec3(0,1,0);
    Z[0] = Vec3(0,0,1);
  } else if (n == 2) {
    Vec3 s0(spine[0]), s1(spine[1]);
    if ((s1-s0)|=EPSILON)
      Y[0] = Vec3(0,1,0);
    else {
      Y[0] = s1-s0; Y[0].normalize();
    }
    Y[1] = Y[0];

    if ((Y[0] ^ Vec3(0,0,1))|=EPSILON)
      Z[0] = Vec3(1,0,0);
    else
      Z[0] = Vec3(0,0,1);
    Z[1] = Z[0];
  } else /* if (n>2) */ {
    for (i=0; i<n; i++) {
      if (i>0 && i<n-1) {
	Y[i] = Vec3(spine[i+1]) - Vec3(spine[i-1]);
	Z[i] = (Vec3(spine[i+1]) - Vec3(spine[i])) ^ (Vec3(spine[i-1]) - Vec3(spine[i]));
      } else {
	Vec3 s0(spine[0]), s1(spine[1]), s2(spine[2]), 
	       sn2(spine[n-2]), sn1(spine[n-1]);
	if ((s0-sn1)|=EPSILON) {
	  // spine curve is closed
	  Y[i] = s1 - sn2;
	  Z[i] = (s1 - s0) ^ (sn2 - s0);
	} else if (i == 0) {
	  Y[0] = s1 - s0;
	  Z[0] = (s2 - s1) ^ (s0 - s1);
	} else /* if (i == n-1) */ {
	  Y[n-1] = sn1 - sn2;
	  Z[n-1] = Z[n-2];
	}
      }

      Y[i].normalize();
      Z[i].normalize();
    }

    // look for the first point with defined Z axis
    for (i=0; i<n && (Z[i]|=EPSILON); i++) {}
    if (i == n) {
      // entire spine collinear

      // find extremal spine points
      Vec3 smin(spine[0]), smax(spine[0]);
      for (j=1; j<n; j++) {
	smin <<= Vec3(spine[j]);
	smax >>= Vec3(spine[j]);
      }

      if ((smax-smin)|=EPSILON)	// all spine points coincident
	Y[0] = Vec3(0,1,0);
      else {
	Y[0] = smax-smin; Y[0].normalize();
      }

      if ((Y[0] ^ Vec3(0,0,1))|=EPSILON)
	Z[0] = Vec3(1,0,0);
      else
	Z[0] = Vec3(0,0,1);

      for (j=1; j<n; j++) {
	Y[j] = Y[0];
	Z[j] = Z[0];
      }
    } else {
      // set Z axis of first i points to Z[i]
      for (j=0; j<i; j++) Z[j] = Z[i];
      
      // if Z axis for a point j>i is undefined, take the last defined one
      for (j=i+1; j<n; j++) {
	if (Z[j]|=EPSILON)	// undefined Z axis
	  Z[j] = Z[i];
	else			// defined Z axis
	  i = j;
      }

      for (i=1; i<n; i++)
	if ((Z[i] & Z[i-1])<0.) Z[i] *= -1.;
    }
  }
  for (i=0; i<n; i++) {
    X[i] = Y[i] ^ Z[i]; X[i].normalize();
  }

  // compute vertex and texture coordinates
  if (co)
    delete co;
  co = new MFVec3f(spine.size * crossSection.size);
  co->size = spine.size * crossSection.size;

  if (texco)
    delete texco;
  texco = new MFVec2f(spine.size * crossSection.size);
  texco->size = spine.size * crossSection.size;

  for (i=0; i<n; i++) {
    // crossSection scaling transform for spine point i
    SFVec2f *sc = (i<scale.size) ? &scale[i] : (scale.size > 0 ? &scale[scale.size-1] : 0);

    // orientation for spine point i
    SFRotation *sr = (i<orientation.size) ? &orientation[i] : (orientation.size>0 ? &orientation[orientation.size-1] : 0);
    Mat3 M;
    if (sr) M = Mat3(Vec4(sr->x, sr->y, sr->z, sr->radians));

    SFVec3f *p = &((*co)[i * crossSection.size]);
    SFVec2f *t = &((*texco)[i * crossSection.size]);
    for (j=0; j<crossSection.size; j++) {
      Vec3 v = Vec3(crossSection[j].s, 0., crossSection[j].t);

      // scale and rotate cross section curve
      if (sc) {
	v.x *= sc->s;
	v.z *= sc->t;
      }
      if (sr)
	v = v * M;

      // translate and orient w.r.t. SCP
      v = Vec3(spine[i]) + (X[i] * v.x) + (Y[i] * v.y) + (Z[i] * v.z);
      p[j] = v;

      // texture coordinate for vertex:
      t[j].s = (float)j / (float)(crossSection.size-1);
      t[j].t = (float)i / (float)(spine.size-1);
    }
  }

  // vertex coordinate indices
  coordIndex.size = 0;
  coordIndex.grow(8 * (spine.size-1) * (crossSection.size-1));
  texCoordIndex.size = 0;
  texCoordIndex.grow(8 * (spine.size-1) * (crossSection.size-1));

  k=0;
  for (i=0; i<spine.size-1; i++) {
    for (j=0; j<crossSection.size-1; j++) {
      texCoordIndex[k] = coordIndex[k] = j     + i     * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = (j+1) + i     * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = j     + (i+1) * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = -1; k++;
      texCoordIndex[k] = coordIndex[k] = j     + (i+1) * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = (j+1) + i     * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = (j+1) + (i+1) * crossSection.size; k++;
      texCoordIndex[k] = coordIndex[k] = -1; k++;
    }
  }

  if (beginCap || endCap) {
    // compute texture coordinates for the begin and/or endCap.
    texco->grow(crossSection.size);

    float mins=HUGE, mint=HUGE, maxs=-HUGE, maxt=-HUGE;
    for (j=0; j<crossSection.size; j++) {
      SFVec2f& t = crossSection[j];
      if (t.s < mins) mins = t.s;
      if (t.s > maxs) maxs = t.s;
      if (t.t < mint) mint = t.t;
      if (t.t > maxt) maxt = t.t;
    }

    SFVec2f *T = &((*texco)[spine.size * crossSection.size]);
    if (maxs-mins >= maxt-mint) {
      float size = maxs-mins;
      for (j=0; j<crossSection.size; j++) {
	SFVec2f& t = crossSection[j];
	T[j].s = (t.s - mins) / size;
	T[j].t = (t.t - mint) / size;
      }
    } else {
      float size = maxt-mint;
      for (j=0; j<crossSection.size; j++) {
	SFVec2f& t = crossSection[j];
	T[j].s = (t.t - mint) / size;
	T[j].t = (t.s - mins) / size;
      }
    }
  }

  if (beginCap) {
    coordIndex.grow(crossSection.size + 1);
    texCoordIndex.grow(crossSection.size + 1);
    for (j=crossSection.size-1; j>=0; j--) {
      texCoordIndex[k] = j + spine.size * crossSection.size;
      coordIndex[k] = j + 0 * crossSection.size;
      k++;
    }
    texCoordIndex[k] = coordIndex[k] = -1; k++;
  }

  if (endCap) {
    coordIndex.grow(crossSection.size + 1);
    texCoordIndex.grow(crossSection.size + 1);
    for (j=0; j<crossSection.size; j++) {
      texCoordIndex[k] = j + spine.size * crossSection.size;
      coordIndex[k] = j + (spine.size-1) * crossSection.size;
      k++;
    }
    texCoordIndex[k] = coordIndex[k] = -1; k++;
  }
}

}  // namespace xrml
