Exemple #1
0
double qmQuadrangleAngles (MQuadrangle *e) {
  double a = 100;
  double worst_quality = std::numeric_limits<double>::max();
  double mat[3][3];
  double mat2[3][3];
  double den = atan(a*(M_PI/4)) + atan(a*(2*M_PI/4 - (M_PI/4)));

  // This matrix is used to "rotate" the triangle to get each vertex
  // as the "origin" of the mapping in turn
  //double rot[3][3];
  //rot[0][0]=-1; rot[0][1]=1; rot[0][2]=0;
  //rot[1][0]=-1; rot[1][1]=0; rot[1][2]=0;
  //rot[2][0]= 0; rot[2][1]=0; rot[2][2]=1;
  //double tmp[3][3];

  const double u[9] = {-1,-1, 1, 1, 0,0,1,-1,0};
  const double v[9] = {-1, 1, 1,-1, -1,1,0,0,0};

  for (int i = 0; i < 9; i++) {

    e->getJacobian(u[i], v[i], 0, mat);
    e->getPrimaryJacobian(u[i],v[i],0,mat2);
    //for (int j = 0; j < i; j++) {
    //  matmat(rot,mat,tmp);
    //  memcpy(mat, tmp, sizeof(mat));
    //}

    //get angle
    double v1[3] = {mat[0][0],  mat[0][1],  mat[0][2] };
    double v2[3] = {mat[1][0],  mat[1][1],  mat[1][2] };
    double v3[3] = {mat2[0][0],  mat2[0][1],  mat2[0][2] };
    double v4[3] = {mat2[1][0],  mat2[1][1],  mat2[1][2] };
    norme(v1);
    norme(v2);
    norme(v3);
    norme(v4);
    double v12[3], v34[3];
    prodve(v1,v2,v12);
    prodve(v3,v4,v34);
    norme(v12);
    norme(v34);
    double orientation;
    prosca(v12,v34,&orientation);

    // If the if the triangle is "flipped" it's no good
    //    if (orientation < 0)
    //      return -std::numeric_limits<double>::max();

    double c;
    prosca(v1,v2,&c);
    double x = fabs(acos(c))-M_PI/2;
    //double angle = fabs(acos(c))*180/M_PI;
    double quality = (atan(a*(x+M_PI/4)) + atan(a*(2*M_PI/4 - (x+M_PI/4))))/den;
    worst_quality = std::min(worst_quality, quality);
  }
  
  return worst_quality;

}
Exemple #2
0
double qmTriangle(const double &xa, const double &ya, const double &za,
                  const double &xb, const double &yb, const double &zb,
                  const double &xc, const double &yc, const double &zc,
                  const qualityMeasure4Triangle &cr)
{
  double quality;
  switch(cr){
  case QMTRI_RHO:
    {
      // quality = rho / R = 2 * inscribed radius / circumradius
      double a [3] = {xc - xb, yc - yb, zc - zb};
      double b [3] = {xa - xc, ya - yc, za - zc};
      double c [3] = {xb - xa, yb - ya, zb - za};
      norme(a);
      norme(b);
      norme(c);
      double pva [3]; prodve(b, c, pva); const double sina = norm3(pva);
      double pvb [3]; prodve(c, a, pvb); const double sinb = norm3(pvb);
      double pvc [3]; prodve(a, b, pvc); const double sinc = norm3(pvc);

      if (sina == 0.0 && sinb == 0.0 && sinc == 0.0) quality = 0.0;
      else quality = 2 * (2 * sina * sinb * sinc / (sina + sinb + sinc));
    }
    break;
    // condition number
  case QMTRI_COND:
    {
      /*
      double a [3] = {xc - xa, yc - ya, zc - za};
      double b [3] = {xb - xa, yb - ya, zb - za};
      double c [3] ; prodve(a, b, c); norme(c);
      double A[3][3] = {{a[0] , b[0] , c[0]} ,
                        {a[1] , b[1] , c[1]} ,
                        {a[2] , b[2] , c[2]}};
      */
      quality = -1;
    }
    break;
  default:
    Msg::Error("Unknown quality measure");
    return 0.;
  }

  return quality;
}
Exemple #3
0
int IsoSimplex(double *X, double *Y, double *Z, double *Val, double V,
               double *Xp, double *Yp, double *Zp, double n[3])
{
  if(Val[0] == Val[1] && Val[0] == Val[2] && Val[0] == Val[3])
    return 0;

  int nb = 0;
  if((Val[0] >= V && Val[1] <= V) || (Val[1] >= V && Val[0] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 0, 1, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }
  if((Val[0] >= V && Val[2] <= V) || (Val[2] >= V && Val[0] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 0, 2, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }
  if((Val[0] >= V && Val[3] <= V) || (Val[3] >= V && Val[0] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 0, 3, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }
  if((Val[1] >= V && Val[2] <= V) || (Val[2] >= V && Val[1] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 1, 2, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }
  if((Val[1] >= V && Val[3] <= V) || (Val[3] >= V && Val[1] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 1, 3, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }
  if((Val[2] >= V && Val[3] <= V) || (Val[3] >= V && Val[2] <= V)) {
    InterpolateIso(X, Y, Z, Val, V, 2, 3, &Xp[nb], &Yp[nb], &Zp[nb]);
    nb++;
  }

  // Remove identical nodes (this can happen if an edge belongs to the
  // zero levelset). We should be doing this even for nb < 4, but it
  // would slow us down even more (and we don't really care if some
  // nodes in a postprocessing element are identical)
  if(nb > 4) {
    double xi[6], yi[6], zi[6];
    affect(xi, yi, zi, 0, Xp, Yp, Zp, 0);
    int ni = 1;
    for(int j = 1; j < nb; j++) {
      for(int i = 0; i < ni; i++) {
        if(fabs(Xp[j] - xi[i]) < 1.e-12 &&
           fabs(Yp[j] - yi[i]) < 1.e-12 &&
           fabs(Zp[j] - zi[i]) < 1.e-12) {
          break;
        }
        if(i == ni - 1) {
          affect(xi, yi, zi, i + 1, Xp, Yp, Zp, j);
          ni++;
        }
      }
    }
    for(int i = 0; i < ni; i++)
      affect(Xp, Yp, Zp, i, xi, yi, zi, i);
    nb = ni;
  }

  if(nb < 3 || nb > 4)
    return 0;

  // 3 possible quads at this point: (0,2,5,3), (0,1,5,4) or
  // (1,2,4,3), so simply invert the 2 last vertices for having the
  // quad ordered
  if(nb == 4) {
    double x = Xp[3], y = Yp[3], z = Zp[3];
    Xp[3] = Xp[2];
    Yp[3] = Yp[2];
    Zp[3] = Zp[2];
    Xp[2] = x;
    Yp[2] = y;
    Zp[2] = z;
  }

  // to get a nice isosurface, we should have n . grad v > 0, where n
  // is the normal to the polygon and v is the unknown field we want
  // to draw
  double v1[3] = {Xp[2] - Xp[0], Yp[2] - Yp[0], Zp[2] - Zp[0]};
  double v2[3] = {Xp[1] - Xp[0], Yp[1] - Yp[0], Zp[1] - Zp[0]};
  prodve(v1, v2, n);
  norme(n);

  double g[3];
  gradSimplex(X, Y, Z, Val, g);

  double gdotn;
  prosca(g, n, &gdotn);

  if(gdotn > 0.) {
    double Xpi[6], Ypi[6], Zpi[6];
    for(int i = 0; i < nb; i++) {
      Xpi[i] = Xp[i];
      Ypi[i] = Yp[i];
      Zpi[i] = Zp[i];
    }
    for(int i = 0; i < nb; i++) {
      Xp[i] = Xpi[nb - i - 1];
      Yp[i] = Ypi[nb - i - 1];
      Zp[i] = Zpi[nb - i - 1];
    }
  }
  else {
    n[0] = -n[0];
    n[1] = -n[1];
    n[2] = -n[2];
  }

  return nb;
}
Exemple #4
0
int CutTriangle(double *X, double *Y, double *Z, double *Val,
                double V1, double V2, 
                double *Xp2, double *Yp2, double *Zp2, double *Vp2)
{
  // fill io so that it contains an indexing of the nodes such that
  // Val[io[i]] > Val[io[j]] if i > j
  int io[3] = {0, 1, 2};
  for(int i = 0; i < 2; i++) {
    for(int j = i + 1; j < 3; j++) {
      if(Val[io[i]] > Val[io[j]]) {
        int iot = io[i];
        io[i] = io[j];
        io[j] = iot;
      }
    }
  }

  if(Val[io[0]] > V2 || Val[io[2]] < V1) return 0;

  if(V1 <= Val[io[0]] && Val[io[2]] <= V2) {
    for(int i = 0; i < 3; i++) {
      Vp2[i] = Val[i];
      Xp2[i] = X[i];
      Yp2[i] = Y[i];
      Zp2[i] = Z[i];
    }
    return 3;
  }

  int Np = 0, Fl = 0;
  double Xp[10], Yp[10], Zp[10], Vp[10];

  if(V1 <= Val[io[0]]) {
    Vp[Np] = Val[io[0]];
    Xp[Np] = X[io[0]];
    Yp[Np] = Y[io[0]];
    Zp[Np] = Z[io[0]];
    Np++;
    Fl = 1;
  }
  else if(Val[io[0]] < V1 && V1 <= Val[io[1]]) {
    Vp[Np] = V1;
    InterpolateIso(X, Y, Z, Val, V1, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Vp[Np] = V1;
    InterpolateIso(X, Y, Z, Val, V1, io[0], io[1], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Fl = 1;
  }
  else {
    Vp[Np] = V1;
    InterpolateIso(X, Y, Z, Val, V1, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Vp[Np] = V1;
    InterpolateIso(X, Y, Z, Val, V1, io[1], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Fl = 0;
  }

  if(V2 == Val[io[0]]) {
    return 0;
  }
  else if((Val[io[0]] < V2) && (V2 < Val[io[1]])) {
    Vp[Np] = V2;
    InterpolateIso(X, Y, Z, Val, V2, io[0], io[1], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Vp[Np] = V2;
    InterpolateIso(X, Y, Z, Val, V2, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
  }
  else if(V2 < Val[io[2]]) {
    if(Fl) {
      Vp[Np] = Val[io[1]];
      Xp[Np] = X[io[1]];
      Yp[Np] = Y[io[1]];
      Zp[Np] = Z[io[1]];
      Np++;
    }
    Vp[Np] = V2;
    InterpolateIso(X, Y, Z, Val, V2, io[1], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
    Vp[Np] = V2;
    InterpolateIso(X, Y, Z, Val, V2, io[0], io[2], &Xp[Np], &Yp[Np], &Zp[Np]);
    Np++;
  }
  else {
    if(Fl) {
      Vp[Np] = Val[io[1]];
      Xp[Np] = X[io[1]];
      Yp[Np] = Y[io[1]];
      Zp[Np] = Z[io[1]];
      Np++;
    }
    Vp[Np] = Val[io[2]];
    Xp[Np] = X[io[2]];
    Yp[Np] = Y[io[2]];
    Zp[Np] = Z[io[2]];
    Np++;
  }

  Vp2[0] = Vp[0];
  Xp2[0] = Xp[0];
  Yp2[0] = Yp[0];
  Zp2[0] = Zp[0];

  int Np2 = 1;

  for(int i = 1; i < Np; i++) {
    if((Xp[i] != Xp2[Np2 - 1]) || (Yp[i] != Yp2[Np2 - 1]) || 
       (Zp[i] != Zp2[Np2 - 1])){
      Vp2[Np2] = Vp[i];
      Xp2[Np2] = Xp[i];
      Yp2[Np2] = Yp[i];
      Zp2[Np2] = Zp[i];
      Np2++;
    }
  }

  if(Xp2[0] == Xp2[Np2 - 1] && Yp2[0] == Yp2[Np2 - 1] && 
     Zp2[0] == Zp2[Np2 - 1]) {
    Np2--;
  }

  // check and fix orientation
  double in1[3] = {X[1] - X[0], Y[1] - Y[0], Z[1] - Z[0]};
  double in2[3] = {X[2] - X[0], Y[2] - Y[0], Z[2] - Z[0]};
  double inn[3];
  prodve(in1, in2, inn);
  double out1[3] = {Xp2[1] - Xp2[0], Yp2[1] - Yp2[0], Zp2[1] - Zp2[0]};
  double out2[3] = {Xp2[2] - Xp2[0], Yp2[2] - Yp2[0], Zp2[2] - Zp2[0]};
  double outn[3];
  prodve(out1, out2, outn);
  double res;
  prosca(inn, outn, &res);
  if(res < 0){
    for(int i = 0; i < Np2; i++){
      Vp[i] = Vp2[Np2 - i - 1];
      Xp[i] = Xp2[Np2 - i - 1];
      Yp[i] = Yp2[Np2 - i - 1];
      Zp[i] = Zp2[Np2 - i - 1];
    }
    for(int i = 0; i < Np2; i++){
      Vp2[i] = Vp[i];
      Xp2[i] = Xp[i];
      Yp2[i] = Yp[i];
      Zp2[i] = Zp[i];
    }
  }

  return Np2;
}
Exemple #5
0
static void eigen(std::vector<double> &inList, int inNb,
                  std::vector<double> &outList, int *outNb, int nbTime,
                  int nbNod, int nbComp, int lam)
{
  if(!inNb || (nbComp != 3 && nbComp != 9) || lam < 1 || lam > 3) return;

  // loop on elements
  int nb = inList.size() / inNb;
  for(std::size_t i = 0; i < inList.size(); i += nb) {
    // copy node coordinates
    for(int j = 0; j < 3 * nbNod; j++) outList.push_back(inList[i + j]);

    // loop on time steps
    for(int j = 0; j < nbTime; j++) {
      double *x = &inList[i];
      double *y = &inList[i + nbNod];
      double *z = &inList[i + 2 * nbNod];

      double GradVel[3][3];

      if(nbComp == 9) {
        // val is the velocity gradient tensor: we assume that it is
        // constant per element
        double *v = &inList[i + 3 * nbNod + nbNod * nbComp * j + nbComp * 0];
        GradVel[0][0] = v[0];
        GradVel[0][1] = v[1];
        GradVel[0][2] = v[2];
        GradVel[1][0] = v[3];
        GradVel[1][1] = v[4];
        GradVel[1][2] = v[5];
        GradVel[2][0] = v[6];
        GradVel[2][1] = v[7];
        GradVel[2][2] = v[8];
      }
      else if(nbComp == 3) {
        // FIXME: the following could be greatly simplified and
        // generalized by using the classes in shapeFunctions.h

        // val contains the velocities: compute the gradient tensor
        // from them
        const int MAX_NOD = 4;
        double val[3][MAX_NOD];
        for(int k = 0; k < nbNod; k++) {
          double *v = &inList[i + 3 * nbNod + nbNod * nbComp * j + nbComp * k];
          for(int l = 0; l < 3; l++) {
            val[l][k] = v[l];
          }
        }
        // compute gradient of shape functions
        double GradPhi_x[MAX_NOD][3];
        double GradPhi_ksi[MAX_NOD][3];
        double dx_dksi[3][3];
        double dksi_dx[3][3];
        double det;
        if(nbNod == 3) { // triangles
          double a[3], b[3], cross[3];
          a[0] = x[1] - x[0];
          a[1] = y[1] - y[0];
          a[2] = z[1] - z[0];
          b[0] = x[2] - x[0];
          b[1] = y[2] - y[0];
          b[2] = z[2] - z[0];
          prodve(a, b, cross);
          dx_dksi[0][0] = x[1] - x[0];
          dx_dksi[0][1] = x[2] - x[0];
          dx_dksi[0][2] = cross[0];
          dx_dksi[1][0] = y[1] - y[0];
          dx_dksi[1][1] = y[2] - y[0];
          dx_dksi[1][2] = cross[1];
          dx_dksi[2][0] = z[1] - z[0];
          dx_dksi[2][1] = z[2] - z[0];
          dx_dksi[2][2] = cross[2];
          inv3x3tran(dx_dksi, dksi_dx, &det);
          GradPhi_ksi[0][0] = -1;
          GradPhi_ksi[0][1] = -1;
          GradPhi_ksi[0][2] = 0;
          GradPhi_ksi[1][0] = 1;
          GradPhi_ksi[1][1] = 0;
          GradPhi_ksi[1][2] = 0;
          GradPhi_ksi[2][0] = 0;
          GradPhi_ksi[2][1] = 1;
          GradPhi_ksi[2][2] = 0;
        }
        else if(nbNod == 4) { // tetrahedra
          dx_dksi[0][0] = x[1] - x[0];
          dx_dksi[0][1] = x[2] - x[0];
          dx_dksi[0][2] = x[3] - x[0];
          dx_dksi[1][0] = y[1] - y[0];
          dx_dksi[1][1] = y[2] - y[0];
          dx_dksi[1][2] = y[3] - y[0];
          dx_dksi[2][0] = z[1] - z[0];
          dx_dksi[2][1] = z[2] - z[0];
          dx_dksi[2][2] = z[3] - z[0];
          inv3x3tran(dx_dksi, dksi_dx, &det);
          GradPhi_ksi[0][0] = -1;
          GradPhi_ksi[0][1] = -1;
          GradPhi_ksi[0][2] = -1;
          GradPhi_ksi[1][0] = 1;
          GradPhi_ksi[1][1] = 0;
          GradPhi_ksi[1][2] = 0;
          GradPhi_ksi[2][0] = 0;
          GradPhi_ksi[2][1] = 1;
          GradPhi_ksi[2][2] = 0;
          GradPhi_ksi[3][0] = 0;
          GradPhi_ksi[3][1] = 0;
          GradPhi_ksi[3][2] = 1;
        }
        else {
          Msg::Error("Lambda2 not ready for this type of element");
          return;
        }
        for(int k = 0; k < nbNod; k++) {
          for(int l = 0; l < 3; l++) {
            GradPhi_x[k][l] = 0.0;
            for(int m = 0; m < 3; m++) {
              GradPhi_x[k][l] += GradPhi_ksi[k][m] * dksi_dx[l][m];
            }
          }
        }
        // compute gradient of velocities
        for(int k = 0; k < 3; k++) {
          for(int l = 0; l < 3; l++) {
            GradVel[k][l] = 0.0;
            for(int m = 0; m < nbNod; m++) {
              GradVel[k][l] += val[k][m] * GradPhi_x[m][l];
            }
          }
        }
      }
      else
        for(int k = 0; k < 3; k++)
          for(int l = 0; l < 3; l++) GradVel[k][l] = 0.0;

      // compute the sym and antisymetric parts
      double sym[3][3];
      double asym[3][3];
      for(int m = 0; m < 3; m++) {
        for(int n = 0; n < 3; n++) {
          sym[m][n] = 0.5 * (GradVel[m][n] + GradVel[n][m]);
          asym[m][n] = 0.5 * (GradVel[m][n] - GradVel[n][m]);
        }
      }
      double a[3][3];
      for(int m = 0; m < 3; m++) {
        for(int n = 0; n < 3; n++) {
          a[m][n] = 0.0;
          for(int l = 0; l < 3; l++)
            a[m][n] += sym[m][l] * sym[l][n] + asym[m][l] * asym[l][n];
        }
      }

      // compute the eigenvalues
      double lambda[3];
      eigenvalue(a, lambda);
      for(int k = 0; k < nbNod; k++) outList.push_back(lambda[lam - 1]);
    }

    (*outNb)++;
  }
}
Exemple #6
0
double qmTriangleAngles (MTriangle *e) {
  double a = 500;
  double worst_quality = std::numeric_limits<double>::max();
  double mat[3][3];
  double mat2[3][3];
  double den = atan(a*(M_PI/9)) + atan(a*(M_PI/9));

  // This matrix is used to "rotate" the triangle to get each vertex
  // as the "origin" of the mapping in turn
  double rot[3][3];
  rot[0][0]=-1; rot[0][1]=1; rot[0][2]=0;
  rot[1][0]=-1; rot[1][1]=0; rot[1][2]=0;
  rot[2][0]= 0; rot[2][1]=0; rot[2][2]=1;
  double tmp[3][3];

  //double minAngle = 120.0;
  for (int i = 0; i < e->getNumPrimaryVertices(); i++) {
    const double u = i == 1 ? 1 : 0;
    const double v = i == 2 ? 1 : 0;
    const double w = 0;
    e->getJacobian(u, v, w, mat);
    e->getPrimaryJacobian(u,v,w,mat2);
    for (int j = 0; j < i; j++) {
      matmat(rot,mat,tmp);
      memcpy(mat, tmp, sizeof(mat));
    }
    //get angle
    double v1[3] = {mat[0][0],  mat[0][1],  mat[0][2] };
    double v2[3] = {mat[1][0],  mat[1][1],  mat[1][2] };
    double v3[3] = {mat2[0][0],  mat2[0][1],  mat2[0][2] };
    double v4[3] = {mat2[1][0],  mat2[1][1],  mat2[1][2] };
    norme(v1);
    norme(v2);
    norme(v3);
    norme(v4);
    double v12[3], v34[3];
    prodve(v1,v2,v12);
    prodve(v3,v4,v34);
    norme(v12);
    norme(v34);
    double orientation;
    prosca(v12,v34,&orientation);

    // If the triangle is "flipped" it's no good
    if (orientation < 0)
      return -std::numeric_limits<double>::max();

    double c;
    prosca(v1,v2,&c);
    double x = acos(c)-M_PI/3;
    //double angle = (x+M_PI/3)/M_PI*180;
    double quality = (atan(a*(x+M_PI/9)) + atan(a*(M_PI/9-x)))/den;
    worst_quality = std::min(worst_quality, quality);

    //minAngle = std::min(angle, minAngle);
    //printf("Angle %g ", angle);
    // printf("Quality %g\n",quality);
  }
  //printf("MinAngle %g \n", minAngle);
  //return minAngle;

  return worst_quality;
}
Exemple #7
0
void GMSH_LevelsetPlugin::_cutAndAddElements(PViewData *vdata, PViewData *wdata,
                                             int ent, int ele, int vstep, int wstep,
                                             double x[8], double y[8], double z[8],
                                             double levels[8], double scalarValues[8],
                                             PViewDataList* out)
{
  int stepmin = vstep, stepmax = vstep + 1, otherstep = wstep;
  if(stepmin < 0){
    stepmin = vdata->getFirstNonEmptyTimeStep();
    stepmax = vdata->getNumTimeSteps();
  }
  if(wstep < 0) otherstep = wdata->getFirstNonEmptyTimeStep();

  int numNodes = vdata->getNumNodes(stepmin, ent, ele);
  int numEdges = vdata->getNumEdges(stepmin, ent, ele);
  int numComp = wdata->getNumComponents(otherstep, ent, ele);
  int type = vdata->getType(stepmin, ent, ele);

  // decompose the element into simplices
  for(int simplex = 0; simplex < numSimplexDec(type); simplex++){

    int n[4], ep[12], nsn, nse;
    getSimplexDec(numNodes, numEdges, type, simplex, n[0], n[1], n[2], n[3],
                  nsn, nse);

    // loop over time steps
    for(int step = stepmin; step < stepmax; step++){

      // check which edges cut the iso and interpolate the value
      if(wstep < 0) otherstep = step;

      if(!wdata->hasTimeStep(otherstep)) continue;

      int np = 0;
      double xp[12], yp[12], zp[12], valp[12][9];
      for(int i = 0; i < nse; i++){
        int n0 = exn[nse][i][0], n1 = exn[nse][i][1];
        if(levels[n[n0]] * levels[n[n1]] <= 0.) {
          double c = InterpolateIso(x, y, z, levels, 0., n[n0], n[n1],
                                    &xp[np], &yp[np], &zp[np]);
          for(int comp = 0; comp < numComp; comp++){
            double v0, v1;
            wdata->getValue(otherstep, ent, ele, n[n0], comp, v0);
            wdata->getValue(otherstep, ent, ele, n[n1], comp, v1);
            valp[np][comp] = v0 + c * (v1 - v0);
          }
          ep[np++] = i + 1;
        }
      }

      // remove identical nodes (this can happen if an edge actually
      // belongs to the zero levelset, i.e., if levels[] * levels[] == 0)
      if(np > 1) removeIdenticalNodes(&np, numComp, xp, yp, zp, valp, ep);

      // if there are no cuts and we extract the volume, save the full
      // element if it is on the correct side of the levelset
      if(np <= 1 && _extractVolume){
        bool add = true;
        for(int nod = 0; nod < nsn; nod++){
          if((_extractVolume < 0. && levels[n[nod]] > 0.) ||
             (_extractVolume > 0. && levels[n[nod]] < 0.)){
            add = false;
            break;
          }
        }
        if(add){
          for(int nod = 0; nod < nsn; nod++){
            xp[nod] = x[n[nod]];
            yp[nod] = y[n[nod]];
            zp[nod] = z[n[nod]];
            for(int comp = 0; comp < numComp; comp++)
              wdata->getValue(otherstep, ent, ele, n[nod], comp, valp[nod][comp]);
          }
          _addElement(nsn, nse, numComp, xp, yp, zp, valp, out, step == stepmin);
        }
        continue;
      }

      // discard invalid cases
      if(numEdges > 4 && np < 3) // 3D input should only lead to 2D output
        continue;
      else if(numEdges > 1 && np < 2) // 2D input should only lead to 1D output
        continue;
      else if(np < 1 || np > 4) // can't deal with this
        continue;

      // avoid "butterflies"
      if(np == 4) reorderQuad(numComp, xp, yp, zp, valp, ep);

      // orient the triangles and the quads to get the normals right
      if(!_extractVolume && (np == 3 || np == 4)) {
        // compute invertion test only once for spatially-fixed views
        if(step == stepmin || !_valueIndependent) {
          double v1[3] = {xp[2] - xp[0], yp[2] - yp[0], zp[2] - zp[0]};
          double v2[3] = {xp[1] - xp[0], yp[1] - yp[0], zp[1] - zp[0]};
          double gr[3], normal[3];
          prodve(v1, v2, normal);
          switch (_orientation) {
          case MAP:
            gradSimplex(x, y, z, scalarValues, gr);
            prosca(gr, normal, &_invert);
            break;
          case PLANE:
            prosca(normal, _ref, &_invert);
            break;
          case SPHERE:
            gr[0] = xp[0] - _ref[0];
            gr[1] = yp[0] - _ref[1];
            gr[2] = zp[0] - _ref[2];
            prosca(gr, normal, &_invert);
          case NONE:
          default:
            break;
          }
        }
        if(_invert > 0.) {
          double xpi[12], ypi[12], zpi[12], valpi[12][9];
          int epi[12];
          for(int k = 0; k < np; k++)
            affect(xpi, ypi, zpi, valpi, epi, k, xp, yp, zp, valp, ep, k, numComp);
          for(int k = 0; k < np; k++)
            affect(xp, yp, zp, valp, ep, k, xpi, ypi, zpi, valpi, epi, np - k - 1, numComp);
        }
      }

      // if we extract volumes, add the nodes on the chosen side
      // (FIXME: when cutting 2D views, the elts can have the wrong
      // orientation)
      if(_extractVolume){
        int nbCut = np;
        for(int nod = 0; nod < nsn; nod++){
          if((_extractVolume < 0. && levels[n[nod]] < 0.) ||
             (_extractVolume > 0. && levels[n[nod]] > 0.)){
            xp[np] = x[n[nod]];
            yp[np] = y[n[nod]];
            zp[np] = z[n[nod]];
            for(int comp = 0; comp < numComp; comp++)
              wdata->getValue(otherstep, ent, ele, n[nod], comp, valp[np][comp]);
            ep[np] = -(nod + 1); // store node num!
            np++;
          }
        }
        removeIdenticalNodes(&np, numComp, xp, yp, zp, valp, ep);
        if(np == 4 && numEdges <= 4)
          reorderQuad(numComp, xp, yp, zp, valp, ep);
        if(np == 6)
          reorderPrism(numComp, xp, yp, zp, valp, ep, nbCut);
        if(np > 8) // can't deal with this
          continue;
      }

      // finally, add the new element
      _addElement(np, numEdges, numComp, xp, yp, zp, valp, out, step == stepmin);

    }

    if(vstep < 0){
      for(int i = stepmin; i < stepmax; i++) {
	out->Time.push_back(vdata->getTime(i));
      }
    }

  }
}