void JacobianCalculator::get_Jacobian_3D( const MappingFunction3D* mf,
                                          NodeSet ho_bits,
                                          Sample location,
                                          const Vector3D* verts,
                                          size_t num_type_vert,
                                          MsqMatrix<3,3>& J_out,
                                          MsqError& err )
{
  size_t num_vtx = 0;
  mf->derivatives( location, ho_bits, mIndices, mDerivs3D, num_vtx, err ); MSQ_ERRRTN(err);
  mf->convert_connectivity_indices( num_type_vert, mIndices, num_vtx, err ); MSQ_ERRRTN(err);
  const MsqVector<3>* d = mDerivs3D;
  const size_t* const e = mIndices + num_vtx;
  Vector3D c[3] = {Vector3D(0,0,0), Vector3D(0,0,0), Vector3D(0,0,0)};
  for (const size_t* i = mIndices; i != e; ++i, ++d) {
    c[0] += (*d)[0] * verts[*i];;
    c[1] += (*d)[1] * verts[*i];;
    c[2] += (*d)[2] * verts[*i];;
  }
  J_out.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) );
  J_out.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) );
  J_out.set_column( 2, MsqMatrix<3,1>(c[2].to_array()) );
}
/* Do transform M_hat = S_a M_{3x2}, M_{2x2} Theta^-1 M_hat
 * where the plane into which we are projecting is orthogonal
 * to the passed u vector.
 */
static inline bool
project_to_perp_plane(  MsqMatrix<3,2> J,
                        const MsqVector<3>& u,
                        const MsqVector<3>& u_perp,
                        MsqMatrix<2,2>& A,
                        MsqMatrix<3,2>& S_a_transpose_Theta )
{
    MsqVector<3> n_a = J.column(0) * J.column(1);
    double sc, len = length(n_a);
    if (!divide(1.0, len, sc))
        return false;
    n_a *= sc;
    double ndot = n_a % u;
    double sigma = (ndot < 0.0) ? -1 : 1;
    double cosphi = sigma * ndot;
    MsqVector<3> cross = n_a * u;
    double sinphi = length(cross);

    MsqMatrix<3,2> Theta;
    Theta.set_column(0,   u_perp);
    Theta.set_column(1, u*u_perp);

    // If columns of J are not in plane orthogonal to u, then
    // rotate J such that they are.
    if (sinphi > 1e-12) {
        MsqVector<3> m = sigma * cross;
        MsqVector<3> n = (1/sinphi) * m;
        MsqVector<3> p = (1-cosphi) * n;
        double s_a[] =
        {   p[0]*n[0] + cosphi, p[0]*n[1] - m[2],   p[0]*n[2] + m[1],
            p[1]*n[0] + m[2],   p[1]*n[1] + cosphi, p[1]*n[2] - m[0],
            p[2]*n[0] - m[1],   p[2]*n[1] + m[0],   p[2]*n[2] + cosphi
        };
        MsqMatrix<3,3> S_a(s_a);
        J = S_a * J;
        S_a_transpose_Theta = transpose(S_a) * Theta;
    }
    else {
        S_a_transpose_Theta = Theta;
//    J *= sigma;
    }

    // Project to get 2x2 A from A_hat (which might be equal to J)
    A = transpose(Theta) * J;
    return true;
}
bool AffineMapMetric::evaluate( PatchData& pd, size_t handle, double& value, MsqError& err )
{
  Sample s = ElemSampleQM::sample( handle );
  size_t e = ElemSampleQM::  elem( handle );
  MsqMeshEntity& elem = pd.element_by_index( e );
  EntityTopology type = elem.get_element_type();
  unsigned edim = TopologyInfo::dimension( type );
  const size_t* conn = elem.get_vertex_index_array();
  
    // This metric only supports sampling at corners, except for simplices.
    // If element is a simpex, then the Jacobian is constant over a linear 
    // element.  In this case, always evaluate at any vertex.
  //unsigned corner = s.number;
  if (s.dimension != 0) {
    if (type == TRIANGLE || type == TETRAHEDRON)
      /*corner = 0*/;
    else {
      MSQ_SETERR(err)("Invalid sample point for AffineMapMetric", MsqError::UNSUPPORTED_ELEMENT );
      return false;
    }
  }
  
  bool rval;
  if (edim == 3) { // 3x3 or 3x2 targets ?
    Vector3D c[3] = { Vector3D(0,0,0), Vector3D(0,0,0), Vector3D(0,0,0) };
    unsigned n;
    const unsigned* adj = TopologyInfo::adjacent_vertices( type, s.number, n );
    c[0] = pd.vertex_by_index( conn[adj[0]] ) - pd.vertex_by_index( conn[s.number] );
    c[1] = pd.vertex_by_index( conn[adj[1]] ) - pd.vertex_by_index( conn[s.number] );
    c[2] = pd.vertex_by_index( conn[adj[2]] ) - pd.vertex_by_index( conn[s.number] );
    MsqMatrix<3,3> A;
    A.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) );
    A.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) );
    A.set_column( 2, MsqMatrix<3,1>(c[2].to_array()) );
    if (type == TETRAHEDRON)
      A = A * TET_XFORM;

    MsqMatrix<3,3> W;
    targetCalc->get_3D_target( pd, e, s, W, err ); MSQ_ERRZERO(err);
    rval = targetMetric->evaluate( A * inverse(W), value, err ); MSQ_ERRZERO(err);
  }
  else {
    Vector3D c[2] = { Vector3D(0,0,0), Vector3D(0,0,0) };
    unsigned n;
    const unsigned* adj = TopologyInfo::adjacent_vertices( type, s.number, n );
    c[0] = pd.vertex_by_index( conn[adj[0]] ) - pd.vertex_by_index( conn[s.number] );
    c[1] = pd.vertex_by_index( conn[adj[1]] ) - pd.vertex_by_index( conn[s.number] );
    MsqMatrix<3,2> App;
    App.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) );
    App.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) );
    
    MsqMatrix<3,2> Wp;
    targetCalc->get_surface_target( pd, e, s, Wp, err ); MSQ_ERRZERO(err);

    MsqMatrix<2,2> A, W;
    MsqMatrix<3,2> RZ;
    surface_to_2d( App, Wp, W, RZ );
    A = transpose(RZ) * App;
    if (type == TRIANGLE)
      A = A * TRI_XFORM;
    
    rval = targetMetric->evaluate( A*inverse(W), value, err ); MSQ_ERRZERO(err);
  }
  
    // apply target weight to value
  if (weightCalc) {
    double ck = weightCalc->get_weight( pd, e, s, err ); MSQ_ERRZERO(err);
    value *= ck;
  }
  return rval;
}