/*! moves and rotates the quad such that it enables us to use components of ef's */ void localize_quad_for_ef( VerdictVector node_pos[4]) { VerdictVector centroid(node_pos[0]); centroid += node_pos[1]; centroid += node_pos[2]; centroid += node_pos[3]; centroid /= 4.0; node_pos[0] -= centroid; node_pos[1] -= centroid; node_pos[2] -= centroid; node_pos[3] -= centroid; VerdictVector rotate = node_pos[1] + node_pos[2] - node_pos[3] - node_pos[0]; rotate.normalize(); double cosine = rotate.x(); double sine = rotate.y(); double xnew; for (int i=0; i < 4; i++) { xnew = cosine * node_pos[i].x() + sine * node_pos[i].y(); node_pos[i].y( -sine * node_pos[i].x() + cosine * node_pos[i].y() ); node_pos[i].x(xnew); } }
/*! the radius ratio of a triangle NB (P. Pebay 01/13/07): CR / (3.0*IR) where CR is the circumradius and IR is the inradius this quality metric is also known to VERDICT, for tetrahedral elements only, a the "aspect beta" */ C_FUNC_DEF double v_tri_radius_ratio( int /*num_nodes*/, double coordinates[][3] ) { // three vectors for each side VerdictVector a( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); VerdictVector b( coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2] ); VerdictVector c( coordinates[0][0] - coordinates[2][0], coordinates[0][1] - coordinates[2][1], coordinates[0][2] - coordinates[2][2] ); double a2 = a.length_squared(); double b2 = b.length_squared(); double c2 = c.length_squared(); VerdictVector ab = a * b; double denominator = ab.length_squared(); if( denominator < VERDICT_DBL_MIN ) return (double)VERDICT_DBL_MAX; double radius_ratio; radius_ratio = .25 * a2 * b2 * c2 * ( a2 + b2 + c2 ) / denominator; if( radius_ratio > 0 ) return (double) VERDICT_MIN( radius_ratio, VERDICT_DBL_MAX ); return (double) VERDICT_MAX( radius_ratio, -VERDICT_DBL_MAX ); }
double VerdictVector::distance_between(const VerdictVector& test_vector) { double xv = xVal - test_vector.x(); double yv = yVal - test_vector.y(); double zv = zVal - test_vector.z(); return( sqrt( xv * xv + yv * yv + zv * zv ) ); }
/*! The scaled jacobian of a tri minimum of the jacobian divided by the lengths of 2 edge vectors */ C_FUNC_DEF double v_tri_scaled_jacobian( int /*num_nodes*/, double coordinates[][3]) { static const double detw = 2./sqrt(3.0); VerdictVector first, second; double jacobian; VerdictVector edge[3]; edge[0].set(coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2]); edge[1].set(coordinates[2][0] - coordinates[0][0], coordinates[2][1] - coordinates[0][1], coordinates[2][2] - coordinates[0][2]); edge[2].set(coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2]); first = edge[1]-edge[0]; second = edge[2]-edge[0]; VerdictVector cross = first * second; jacobian = cross.length(); double max_edge_length_product; max_edge_length_product = VERDICT_MAX( edge[0].length()*edge[1].length(), VERDICT_MAX( edge[1].length()*edge[2].length(), edge[0].length()*edge[2].length() ) ); if( max_edge_length_product < VERDICT_DBL_MIN ) return (double)0.0; jacobian *= detw; jacobian /= max_edge_length_product; if( compute_normal ) { //center of tri double point[3], surf_normal[3]; point[0] = (coordinates[0][0] + coordinates[1][0] + coordinates[2][0]) / 3; point[1] = (coordinates[0][1] + coordinates[1][1] + coordinates[2][1]) / 3; point[2] = (coordinates[0][2] + coordinates[1][2] + coordinates[2][2]) / 3; //dot product compute_normal( point, surf_normal ); if( (cross.x()*surf_normal[0] + cross.y()*surf_normal[1] + cross.z()*surf_normal[2] ) < 0 ) jacobian *= -1; } if( jacobian > 0 ) return (double) VERDICT_MIN( jacobian, VERDICT_DBL_MAX ); return (double) VERDICT_MAX( jacobian, -VERDICT_DBL_MAX ); }
/*! the aspect of a tet CR / (3.0*IR) where CR is the circumsphere radius and IR is the inscribed sphere radius */ C_FUNC_DEF VERDICT_REAL v_tet_aspect_beta( int /*num_nodes*/, VERDICT_REAL coordinates[][3] ) { //Determine side vectors VerdictVector side[6]; side[0].set( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); side[1].set( coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2] ); side[2].set( coordinates[0][0] - coordinates[2][0], coordinates[0][1] - coordinates[2][1], coordinates[0][2] - coordinates[2][2] ); side[3].set( coordinates[3][0] - coordinates[0][0], coordinates[3][1] - coordinates[0][1], coordinates[3][2] - coordinates[0][2] ); side[4].set( coordinates[3][0] - coordinates[1][0], coordinates[3][1] - coordinates[1][1], coordinates[3][2] - coordinates[1][2] ); side[5].set( coordinates[3][0] - coordinates[2][0], coordinates[3][1] - coordinates[2][1], coordinates[3][2] - coordinates[2][2] ); VerdictVector numerator = side[3].length_squared() * ( side[2] * side[0]) + side[2].length_squared() * ( side[3] * side[0]) + side[0].length_squared() * ( side[3] * side[2]); double area_sum = 0.0; area_sum = ((side[2] * side[0]).length() + (side[3] * side[0]).length() + (side[4] * side[1]).length() + (side[3] * side[2]).length() ) * 0.5; double volume = v_tet_volume(4, coordinates); if( volume < VERDICT_DBL_MIN ) return (VERDICT_REAL)VERDICT_DBL_MAX; else { double aspect_ratio; aspect_ratio = numerator.length() * area_sum / (108*volume*volume); if( aspect_ratio > 0 ) return (VERDICT_REAL) VERDICT_MIN( aspect_ratio, VERDICT_DBL_MAX ); return (VERDICT_REAL) VERDICT_MAX( aspect_ratio, -VERDICT_DBL_MAX ); } }
//- Find next point from this point using a direction and distance void VerdictVector::next_point( const VerdictVector &direction, double distance, VerdictVector& out_point ) { VerdictVector my_direction = direction; my_direction.normalize(); // Determine next point in space out_point.x( xVal + (distance * my_direction.x()) ); out_point.y( yVal + (distance * my_direction.y()) ); out_point.z( zVal + (distance * my_direction.z()) ); return; }
bool VerdictVector::within_tolerance( const VerdictVector &vectorPtr2, double tolerance) const { if (( fabs (this->x() - vectorPtr2.x()) < tolerance) && ( fabs (this->y() - vectorPtr2.y()) < tolerance) && ( fabs (this->z() - vectorPtr2.z()) < tolerance) ) { return true; } return false; }
inline void form_Q( const VerdictVector& v1, const VerdictVector& v2, const VerdictVector& v3, VerdictVector& q1, VerdictVector& q2, VerdictVector& q3 ) { double g11, g12, g13, g22, g23, g33; g11 = v1 % v1; g12 = v1 % v2; g13 = v1 % v3; g22 = v2 % v2; g23 = v2 % v3; g33 = v3 % v3; double rtg11 = sqrt(g11); double rtg22 = sqrt(g22); double rtg33 = sqrt(g33); VerdictVector temp1; temp1 = v1 * v2; double cross = sqrt( temp1 % temp1 ); double q11,q21,q31; double q12,q22,q32; double q13,q23,q33; q11=1; q21=0; q31=0; q12 = g12 / rtg11 / rtg22; q22 = cross / rtg11 / rtg22; q32 = 0; q13 = g13 / rtg11 / rtg33; q23 = ( g11*g23-g12*g13 )/ rtg11 / rtg33 / cross; temp1 = v2 * v3; q33 = ( v1 % temp1 ) / rtg33 / cross; q1.set( q11, q21, q31 ); q2.set( q12, q22, q32 ); q3.set( q13, q23, q33 ); }
double VerdictVector::interior_angle(const VerdictVector &otherVector) { double cosAngle=0., angleRad=0., len1, len2=0.; if (((len1 = this->length()) > 0) && ((len2 = otherVector.length()) > 0)) cosAngle = (*this % otherVector)/(len1 * len2); else { assert(len1 > 0); assert(len2 > 0); } if ((cosAngle > 1.0) && (cosAngle < 1.0001)) { cosAngle = 1.0; angleRad = acos(cosAngle); } else if (cosAngle < -1.0 && cosAngle > -1.0001) { cosAngle = -1.0; angleRad = acos(cosAngle); } else if (cosAngle >= -1.0 && cosAngle <= 1.0) angleRad = acos(cosAngle); else { assert(cosAngle < 1.0001 && cosAngle > -1.0001); } return( (angleRad * 180.) / VERDICT_PI ); }
/*! get the weights based on the average size of a tet */ int get_weight ( VerdictVector &w1, VerdictVector &w2, VerdictVector &w3 ) { static const double rt3 = sqrt(3.0); static const double root_of_2 = sqrt(2.0); w1.set(1,0,0); w2.set(0.5, 0.5*rt3, 0 ); w3.set(0.5, rt3/6.0, root_of_2/rt3); double scale = pow( 6.*verdict_tet_size/determinant(w1,w2,w3),0.3333333333333); w1 *= scale; w2 *= scale; w3 *= scale; return 1; }
/*! the aspect ratio of a triangle NB (P. Pebay 01/14/07): Hmax / ( 2.0 * sqrt(3.0) * IR) where Hmax is the maximum edge length and IR is the inradius note that previous incarnations of verdict used "v_tri_aspect_ratio" to denote what is now called "v_tri_aspect_frobenius" */ C_FUNC_DEF double v_tri_aspect_ratio( int /*num_nodes*/, double coordinates[][3] ) { static const double normal_coeff = sqrt( 3. ) / 6.; // three vectors for each side VerdictVector a( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); VerdictVector b( coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2] ); VerdictVector c( coordinates[0][0] - coordinates[2][0], coordinates[0][1] - coordinates[2][1], coordinates[0][2] - coordinates[2][2] ); double a1 = a.length(); double b1 = b.length(); double c1 = c.length(); double hm = a1 > b1 ? a1 : b1; hm = hm > c1 ? hm : c1; VerdictVector ab = a * b; double denominator = ab.length(); if( denominator < VERDICT_DBL_MIN ) return (double)VERDICT_DBL_MAX; else { double aspect_ratio; aspect_ratio = normal_coeff * hm * (a1 + b1 + c1) / denominator; if( aspect_ratio > 0 ) return (double) VERDICT_MIN( aspect_ratio, VERDICT_DBL_MAX ); return (double) VERDICT_MAX( aspect_ratio, -VERDICT_DBL_MAX ); } }
/*! The area of a tri 0.5 * jacobian at a node */ C_FUNC_DEF double v_tri_area( int /*num_nodes*/, double coordinates[][3] ) { // two vectors for two sides VerdictVector side1( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); VerdictVector side3( coordinates[2][0] - coordinates[0][0], coordinates[2][1] - coordinates[0][1], coordinates[2][2] - coordinates[0][2] ); // the cross product of the two vectors representing two sides of the // triangle VerdictVector tmp = side1 * side3; // return the magnitude of the vector divided by two double area = 0.5 * tmp.length(); if( area > 0 ) return (double) VERDICT_MIN( area, VERDICT_DBL_MAX ); return (double) VERDICT_MAX( area, -VERDICT_DBL_MAX ); }
inline double normalize_jacobian( double jacobi, VerdictVector& v1, VerdictVector& v2, VerdictVector& v3, int tet_flag = 0 ) { double return_value = 0.0; if ( jacobi != 0.0 ) { double l1, l2, l3, length_product; // Note: there may be numerical problems if one is a lot shorter // than the others this way. But scaling each vector before the // triple product would involve 3 square roots instead of just // one. l1 = v1.length_squared(); l2 = v2.length_squared(); l3 = v3.length_squared(); length_product = sqrt( l1 * l2 * l3 ); // if some numerical scaling problem, or just plain roundoff, // then push back into range [-1,1]. if ( length_product < fabs(jacobi) ) { length_product = fabs(jacobi); } if( tet_flag == 1) return_value = v_sqrt_2 * jacobi / length_product; else return_value = jacobi / length_product; } return return_value; }
inline void inverse(VerdictVector x1, VerdictVector x2, VerdictVector x3, VerdictVector& u1, VerdictVector& u2, VerdictVector& u3 ) { double detx = v_determinant(x1, x2, x3); VerdictVector rx1, rx2, rx3; rx1.set(x1.x(), x2.x(), x3.x()); rx2.set(x1.y(), x2.y(), x3.y()); rx3.set(x1.z(), x2.z(), x3.z()); u1 = rx2 * rx3; u2 = rx3 * rx1; u3 = rx1 * rx2; u1 /= detx; u2 /= detx; u3 /= detx; }
void VerdictVector::orthogonal_vectors( VerdictVector &vector2, VerdictVector &vector3 ) { double xv[3]; unsigned short i=0; unsigned short imin=0; double rmin = 1.0E20; unsigned short iperm1[3]; unsigned short iperm2[3]; unsigned short cont_flag = 1; double vec1[3], vec2[3]; double rmag; // Copy the input vector and normalize it VerdictVector vector1 = *this; vector1.normalize(); // Initialize perm flags iperm1[0] = 1; iperm1[1] = 2; iperm1[2] = 0; iperm2[0] = 2; iperm2[1] = 0; iperm2[2] = 1; // Get into the array format we can work with vector1.get_xyz( vec1 ); while (i<3 && cont_flag ) { if (fabs(vec1[i]) < 1e-6) { vec2[i] = 1.0; vec2[iperm1[i]] = 0.0; vec2[iperm2[i]] = 0.0; cont_flag = 0; } if (fabs(vec1[i]) < rmin) { imin = i; rmin = fabs(vec1[i]); } ++i; } if (cont_flag) { xv[imin] = 1.0; xv[iperm1[imin]] = 0.0; xv[iperm2[imin]] = 0.0; // Determine cross product vec2[0] = vec1[1] * xv[2] - vec1[2] * xv[1]; vec2[1] = vec1[2] * xv[0] - vec1[0] * xv[2]; vec2[2] = vec1[0] * xv[1] - vec1[1] * xv[0]; // Unitize rmag = sqrt(vec2[0]*vec2[0] + vec2[1]*vec2[1] + vec2[2]*vec2[2]); vec2[0] /= rmag; vec2[1] /= rmag; vec2[2] /= rmag; } // Copy 1st orthogonal vector into VerdictVector vector2 vector2.set( vec2 ); // Cross vectors to determine last orthogonal vector vector3 = vector1 * vector2; }
double VerdictVector::vector_angle(const VerdictVector &vector1, const VerdictVector &vector2) const { // This routine does not assume that any of the input vectors are of unit // length. This routine does not normalize the input vectors. // Special cases: // If the normal vector is zero length: // If a new one can be computed from vectors 1 & 2: // the normal is replaced with the vector cross product // else the two vectors are colinear and zero or 2PI is returned. // If the normal is colinear with either (or both) vectors // a new one is computed with the cross products // (and checked again). // Check for zero length normal vector VerdictVector normal = *this; double normal_lensq = normal.length_squared(); double len_tol = 0.0000001; if( normal_lensq <= len_tol ) { // null normal - make it the normal to the plane defined by vector1 // and vector2. If still null, the vectors are colinear so check // for zero or 180 angle. normal = vector1 * vector2; normal_lensq = normal.length_squared(); if( normal_lensq <= len_tol ) { double cosine = vector1 % vector2; if( cosine > 0.0 ) return 0.0; else return VERDICT_PI; } } //Trap for normal vector colinear to one of the other vectors. If so, //use a normal defined by the two vectors. double dot_tol = 0.985; double dot = vector1 % normal; if( dot * dot >= vector1.length_squared() * normal_lensq * dot_tol ) { normal = vector1 * vector2; normal_lensq = normal.length_squared(); //Still problems if all three vectors were colinear if( normal_lensq <= len_tol ) { double cosine = vector1 % vector2; if( cosine >= 0.0 ) return 0.0; else return VERDICT_PI; } } else { //The normal and vector1 are not colinear, now check for vector2 dot = vector2 % normal; if( dot * dot >= vector2.length_squared() * normal_lensq * dot_tol ) { normal = vector1 * vector2; } } // Assume a plane such that the normal vector is the plane's normal. // Create yAxis perpendicular to both the normal and vector1. yAxis is // now in the plane. Create xAxis as the perpendicular to both yAxis and // the normal. xAxis is in the plane and is the projection of vector1 // into the plane. normal.normalize(); VerdictVector yAxis = normal; yAxis *= vector1; double yv = vector2 % yAxis; // yAxis memory slot will now be used for xAxis yAxis *= normal; double xv = vector2 % yAxis; // assert(x != 0.0 || y != 0.0); if( xv == 0.0 && yv == 0.0 ) { return 0.0; } double angle = atan2( yv, xv ); if (angle < 0.0) { angle += TWO_VERDICT_PI; } return angle; }
/*! the quality metrics of a tet */ C_FUNC_DEF void v_tet_quality( int num_nodes, VERDICT_REAL coordinates[][3], unsigned int metrics_request_flag, TetMetricVals *metric_vals ) { memset( metric_vals, 0, sizeof(TetMetricVals) ); /* node numbers and edge numbers below 3 + edge 0 is node 0 to 1 +|+ edge 1 is node 1 to 2 3/ | \5 edge 2 is node 0 to 2 / 4| \ edge 3 is node 0 to 3 0 - -|- + 2 edge 4 is node 1 to 3 \ | + edge 5 is node 2 to 3 0\ | /1 +|/ edge 2 is behind edge 4 1 */ // lets start with making the vectors VerdictVector edges[6]; edges[0].set( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); edges[1].set( coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2] ); edges[2].set( coordinates[0][0] - coordinates[2][0], coordinates[0][1] - coordinates[2][1], coordinates[0][2] - coordinates[2][2] ); edges[3].set( coordinates[3][0] - coordinates[0][0], coordinates[3][1] - coordinates[0][1], coordinates[3][2] - coordinates[0][2] ); edges[4].set( coordinates[3][0] - coordinates[1][0], coordinates[3][1] - coordinates[1][1], coordinates[3][2] - coordinates[1][2] ); edges[5].set( coordinates[3][0] - coordinates[2][0], coordinates[3][1] - coordinates[2][1], coordinates[3][2] - coordinates[2][2] ); // common numbers static const double root_of_2 = sqrt(2.0); // calculate the jacobian static const int do_jacobian = V_TET_JACOBIAN | V_TET_VOLUME | V_TET_ASPECT_BETA | V_TET_ASPECT_GAMMA | V_TET_SHAPE | V_TET_RELATIVE_SIZE_SQUARED | V_TET_SHAPE_AND_SIZE | V_TET_SCALED_JACOBIAN | V_TET_CONDITION; if(metrics_request_flag & do_jacobian ) { metric_vals->jacobian = (VERDICT_REAL)(edges[3] % (edges[2] * edges[0])); } // calculate the volume if(metrics_request_flag & V_TET_VOLUME) { metric_vals->volume = (VERDICT_REAL)(metric_vals->jacobian / 6.0); } // calculate aspect ratio if(metrics_request_flag & V_TET_ASPECT_BETA) { double surface_area = ((edges[2] * edges[0]).length() + (edges[3] * edges[0]).length() + (edges[4] * edges[1]).length() + (edges[3] * edges[2]).length() ) * 0.5; VerdictVector numerator = edges[3].length_squared() * ( edges[2] * edges[0] ) + edges[2].length_squared() * ( edges[3] * edges[0] ) + edges[0].length_squared() * ( edges[3] * edges[2] ); double volume = metric_vals->jacobian / 6.0; if(volume < VERDICT_DBL_MIN ) metric_vals->aspect_beta = (VERDICT_REAL)(VERDICT_DBL_MAX); else metric_vals->aspect_beta = (VERDICT_REAL)( numerator.length() * surface_area/ (108*volume*volume) ); } // calculate the aspect gamma if(metrics_request_flag & V_TET_ASPECT_GAMMA) { double volume = fabs( metric_vals->jacobian / 6.0 ); if( fabs( volume ) < VERDICT_DBL_MIN ) metric_vals->aspect_gamma = VERDICT_DBL_MAX; else { double srms = sqrt(( edges[0].length_squared() + edges[1].length_squared() + edges[2].length_squared() + edges[3].length_squared() + edges[4].length_squared() + edges[5].length_squared() ) / 6.0 ); // cube the srms srms *= (srms * srms); metric_vals->aspect_gamma = (VERDICT_REAL)( srms / (8.47967 * volume )); } } // calculate the shape of the tet if(metrics_request_flag & ( V_TET_SHAPE | V_TET_SHAPE_AND_SIZE ) ) { static const double two_thirds = 2.0/3.0; double num = 3.0 * pow(root_of_2 * metric_vals->jacobian, two_thirds); double den = 1.5 * (edges[0] % edges[0] + edges[2] % edges[2] + edges[3] % edges[3]) - (edges[0] % -edges[2] + -edges[2] % edges[3] + edges[3] % edges[0]); if( den < VERDICT_DBL_MIN ) metric_vals->shape = (VERDICT_REAL)0.0; else metric_vals->shape = (VERDICT_REAL)VERDICT_MAX( num/den, 0 ); } // calculate the relative size of the tet if(metrics_request_flag & (V_TET_RELATIVE_SIZE_SQUARED | V_TET_SHAPE_AND_SIZE )) { VerdictVector w1, w2, w3; get_weight(w1,w2,w3); double avg_vol = (w1 % (w2 *w3))/6; if( avg_vol < VERDICT_DBL_MIN ) metric_vals->relative_size_squared = 0.0; else { double tmp = metric_vals->jacobian / (6*avg_vol); if( tmp < VERDICT_DBL_MIN ) metric_vals->relative_size_squared = 0.0; else { tmp *= tmp; metric_vals->relative_size_squared = (VERDICT_REAL)VERDICT_MIN(tmp, 1/tmp); } } } // calculate the shape and size if(metrics_request_flag & V_TET_SHAPE_AND_SIZE) { metric_vals->shape_and_size = (VERDICT_REAL)(metric_vals->shape * metric_vals->relative_size_squared); } // calculate the scaled jacobian if(metrics_request_flag & V_TET_SCALED_JACOBIAN) { //find out which node the normalized jacobian can be calculated at //and it will be the smaller than at other nodes double length_squared[4] = { edges[0].length_squared() * edges[2].length_squared() * edges[3].length_squared(), edges[0].length_squared() * edges[1].length_squared() * edges[4].length_squared(), edges[1].length_squared() * edges[2].length_squared() * edges[5].length_squared(), edges[3].length_squared() * edges[4].length_squared() * edges[5].length_squared() }; int which_node = 0; if(length_squared[1] > length_squared[which_node]) which_node = 1; if(length_squared[2] > length_squared[which_node]) which_node = 2; if(length_squared[3] > length_squared[which_node]) which_node = 3; // find the scaled jacobian at this node double length_product = sqrt( length_squared[which_node] ); if(length_product < fabs(metric_vals->jacobian)) length_product = fabs(metric_vals->jacobian); if( length_product < VERDICT_DBL_MIN ) metric_vals->scaled_jacobian = (VERDICT_REAL) VERDICT_DBL_MAX; else metric_vals->scaled_jacobian = (VERDICT_REAL)(root_of_2 * metric_vals->jacobian / length_product); } // calculate the condition number if(metrics_request_flag & V_TET_CONDITION) { static const double root_of_3 = sqrt(3.0); static const double root_of_6 = sqrt(6.0); VerdictVector c_1, c_2, c_3; c_1 = edges[0]; c_2 = (-2*edges[2] - edges[0])/root_of_3; c_3 = (3*edges[3] + edges[2] - edges[0])/root_of_6; double term1 = c_1 % c_1 + c_2 % c_2 + c_3 % c_3; double term2 = ( c_1 * c_2 ) % ( c_1 * c_2 ) + ( c_2 * c_3 ) % ( c_2 * c_3 ) + ( c_3 * c_1 ) % ( c_3 * c_1 ); double det = c_1 % ( c_2 * c_3 ); if(det <= VERDICT_DBL_MIN) metric_vals->condition = (VERDICT_REAL)VERDICT_DBL_MAX; else metric_vals->condition = (VERDICT_REAL)(sqrt(term1 * term2) / (3.0*det)); } // calculate the distortion if(metrics_request_flag & V_TET_DISTORTION) { metric_vals->distortion = v_tet_distortion(num_nodes, coordinates); } //check for overflow if(metrics_request_flag & V_TET_ASPECT_BETA ) { if( metric_vals->aspect_beta > 0 ) metric_vals->aspect_beta = (VERDICT_REAL) VERDICT_MIN( metric_vals->aspect_beta, VERDICT_DBL_MAX ); metric_vals->aspect_beta = (VERDICT_REAL) VERDICT_MAX( metric_vals->aspect_beta, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_ASPECT_GAMMA) { if( metric_vals->aspect_gamma > 0 ) metric_vals->aspect_gamma = (VERDICT_REAL) VERDICT_MIN( metric_vals->aspect_gamma, VERDICT_DBL_MAX ); metric_vals->aspect_gamma = (VERDICT_REAL) VERDICT_MAX( metric_vals->aspect_gamma, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_VOLUME) { if( metric_vals->volume > 0 ) metric_vals->volume = (VERDICT_REAL) VERDICT_MIN( metric_vals->volume, VERDICT_DBL_MAX ); metric_vals->volume = (VERDICT_REAL) VERDICT_MAX( metric_vals->volume, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_CONDITION) { if( metric_vals->condition > 0 ) metric_vals->condition = (VERDICT_REAL) VERDICT_MIN( metric_vals->condition, VERDICT_DBL_MAX ); metric_vals->condition = (VERDICT_REAL) VERDICT_MAX( metric_vals->condition, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_JACOBIAN) { if( metric_vals->jacobian > 0 ) metric_vals->jacobian = (VERDICT_REAL) VERDICT_MIN( metric_vals->jacobian, VERDICT_DBL_MAX ); metric_vals->jacobian = (VERDICT_REAL) VERDICT_MAX( metric_vals->jacobian, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_SCALED_JACOBIAN) { if( metric_vals->scaled_jacobian > 0 ) metric_vals->scaled_jacobian = (VERDICT_REAL) VERDICT_MIN( metric_vals->scaled_jacobian, VERDICT_DBL_MAX ); metric_vals->scaled_jacobian = (VERDICT_REAL) VERDICT_MAX( metric_vals->scaled_jacobian, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_SHAPE) { if( metric_vals->shape > 0 ) metric_vals->shape = (VERDICT_REAL) VERDICT_MIN( metric_vals->shape, VERDICT_DBL_MAX ); metric_vals->shape = (VERDICT_REAL) VERDICT_MAX( metric_vals->shape, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_RELATIVE_SIZE_SQUARED) { if( metric_vals->relative_size_squared > 0 ) metric_vals->relative_size_squared = (VERDICT_REAL) VERDICT_MIN( metric_vals->relative_size_squared, VERDICT_DBL_MAX ); metric_vals->relative_size_squared = (VERDICT_REAL) VERDICT_MAX( metric_vals->relative_size_squared, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_SHAPE_AND_SIZE) { if( metric_vals->shape_and_size > 0 ) metric_vals->shape_and_size = (VERDICT_REAL) VERDICT_MIN( metric_vals->shape_and_size, VERDICT_DBL_MAX ); metric_vals->shape_and_size = (VERDICT_REAL) VERDICT_MAX( metric_vals->shape_and_size, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_TET_DISTORTION) { if( metric_vals->distortion > 0 ) metric_vals->distortion = (VERDICT_REAL) VERDICT_MIN( metric_vals->distortion, VERDICT_DBL_MAX ); metric_vals->distortion = (VERDICT_REAL) VERDICT_MAX( metric_vals->distortion, -VERDICT_DBL_MAX ); } }
inline void product( VerdictVector& a1, VerdictVector& a2, VerdictVector& a3, VerdictVector& b1, VerdictVector& b2, VerdictVector& b3, VerdictVector& c1, VerdictVector& c2, VerdictVector& c3 ) { VerdictVector x1, x2, x3; x1.set( a1.x(), a2.x(), a3.x() ); x2.set( a1.y(), a2.y(), a3.y() ); x3.set( a1.z(), a2.z(), a3.z() ); c1.set( x1 % b1, x2 % b1, x3 % b1 ); c2.set( x1 % b2, x2 % b2, x3 % b2 ); c3.set( x1 % b3, x2 % b3, x3 % b3 ); }
/*! tri_quality for calculating multiple tri functions at once using this method is generally faster than using the individual method multiple times. */ C_FUNC_DEF void v_tri_quality( int num_nodes, double coordinates[][3], unsigned int metrics_request_flag, TriMetricVals *metric_vals ) { memset( metric_vals, 0, sizeof(TriMetricVals) ); // for starts, lets set up some basic and common information /* node numbers and side numbers used below 2 ++ / \ 2 / \ 1 / \ / \ 0 ---------+ 1 0 */ // vectors for each side VerdictVector sides[3]; sides[0].set( coordinates[1][0] - coordinates[0][0], coordinates[1][1] - coordinates[0][1], coordinates[1][2] - coordinates[0][2] ); sides[1].set( coordinates[2][0] - coordinates[1][0], coordinates[2][1] - coordinates[1][1], coordinates[2][2] - coordinates[1][2] ); sides[2].set( coordinates[2][0] - coordinates[0][0], coordinates[2][1] - coordinates[0][1], coordinates[2][2] - coordinates[0][2] ); VerdictVector tri_normal = sides[0] * sides[2]; //if we have access to normal information, check to see if the //element is inverted. If we don't have the normal information //that we need for this, assume the element is not inverted. //This flag will be used for condition number, jacobian, shape, //and size and shape. bool is_inverted = false; if( compute_normal ) { //center of tri double point[3], surf_normal[3]; point[0] = (coordinates[0][0] + coordinates[1][0] + coordinates[2][0]) / 3; point[1] = (coordinates[0][1] + coordinates[1][1] + coordinates[2][1]) / 3; point[2] = (coordinates[0][2] + coordinates[1][2] + coordinates[2][2]) / 3; //dot product compute_normal( point, surf_normal ); if( (tri_normal.x()*surf_normal[0] + tri_normal.y()*surf_normal[1] + tri_normal.z()*surf_normal[2] ) < 0 ) is_inverted=true; } // lengths squared of each side double sides_lengths_squared[3]; sides_lengths_squared[0] = sides[0].length_squared(); sides_lengths_squared[1] = sides[1].length_squared(); sides_lengths_squared[2] = sides[2].length_squared(); // if we are doing angle calcuations if( metrics_request_flag & (V_TRI_MINIMUM_ANGLE | V_TRI_MAXIMUM_ANGLE) ) { // which is short and long side int short_side=0, long_side=0; if(sides_lengths_squared[1] < sides_lengths_squared[0]) short_side = 1; if(sides_lengths_squared[2] < sides_lengths_squared[short_side]) short_side = 2; if(sides_lengths_squared[1] > sides_lengths_squared[0]) long_side = 1; if(sides_lengths_squared[2] > sides_lengths_squared[long_side]) long_side = 2; // calculate the minimum angle of the tri if( metrics_request_flag & V_TRI_MINIMUM_ANGLE ) { if(sides_lengths_squared[0] == 0.0 || sides_lengths_squared[1] == 0.0 || sides_lengths_squared[2] == 0.0) { metric_vals->minimum_angle = 0.0; } else if(short_side == 0) metric_vals->minimum_angle = (double)sides[2].interior_angle(sides[1]); else if(short_side == 1) metric_vals->minimum_angle = (double)sides[0].interior_angle(sides[2]); else metric_vals->minimum_angle = (double)sides[0].interior_angle(-sides[1]); } // calculate the maximum angle of the tri if( metrics_request_flag & V_TRI_MAXIMUM_ANGLE ) { if(sides_lengths_squared[0] == 0.0 || sides_lengths_squared[1] == 0.0 || sides_lengths_squared[2] == 0.0) { metric_vals->maximum_angle = 0.0; } else if(long_side == 0) metric_vals->maximum_angle = (double)sides[2].interior_angle(sides[1]); else if(long_side == 1) metric_vals->maximum_angle = (double)sides[0].interior_angle(sides[2]); else metric_vals->maximum_angle = (double)sides[0].interior_angle(-sides[1]); } } // calculate the area of the tri // the following functions depend on area if( metrics_request_flag & (V_TRI_AREA | V_TRI_SCALED_JACOBIAN | V_TRI_SHAPE | V_TRI_RELATIVE_SIZE_SQUARED | V_TRI_SHAPE_AND_SIZE ) ) { metric_vals->area = (double)((sides[0] * sides[2]).length() * 0.5); } // calculate the aspect ratio if(metrics_request_flag & V_TRI_ASPECT_FROBENIUS) { // sum the lengths squared double srms = sides_lengths_squared[0] + sides_lengths_squared[1] + sides_lengths_squared[2] ; // calculate once and reuse static const double twoTimesRootOf3 = 2*sqrt(3.0); double div = (twoTimesRootOf3 * ( (sides[0] * sides[2]).length() )); if(div == 0.0) metric_vals->aspect_frobenius = (double)VERDICT_DBL_MAX; else metric_vals->aspect_frobenius = (double)(srms / div); } // calculate the radius ratio of the triangle if( metrics_request_flag & V_TRI_RADIUS_RATIO ) { double a1 = sqrt( sides_lengths_squared[0] ); double b1 = sqrt( sides_lengths_squared[1] ); double c1 = sqrt( sides_lengths_squared[2] ); VerdictVector ab = sides[0] * sides[1]; metric_vals->radius_ratio = (double) .25 * a1 * b1 * c1 * ( a1 + b1 + c1 ) / ab.length_squared(); } // calculate the scaled jacobian if(metrics_request_flag & V_TRI_SCALED_JACOBIAN) { // calculate once and reuse static const double twoOverRootOf3 = 2/sqrt(3.0); // use the area from above double tmp = tri_normal.length() * twoOverRootOf3; // now scale it by the lengths of the sides double min_scaled_jac = VERDICT_DBL_MAX; double temp_scaled_jac; for(int i=0; i<3; i++) { if(sides_lengths_squared[i%3] == 0.0 || sides_lengths_squared[(i+2)%3] == 0.0) temp_scaled_jac = 0.0; else temp_scaled_jac = tmp / sqrt(sides_lengths_squared[i%3]) / sqrt(sides_lengths_squared[(i+2)%3]); if( temp_scaled_jac < min_scaled_jac ) min_scaled_jac = temp_scaled_jac; } //multiply by -1 if the normals are in opposite directions if( is_inverted ) { min_scaled_jac *= -1; } metric_vals->scaled_jacobian = (double)min_scaled_jac; } // calculate the condition number if(metrics_request_flag & V_TRI_CONDITION) { // calculate once and reuse static const double rootOf3 = sqrt(3.0); //if it is inverted, the condition number is considered to be infinity. if(is_inverted){ metric_vals->condition = VERDICT_DBL_MAX; } else{ double area2x = (sides[0] * sides[2]).length(); if(area2x == 0.0 ) metric_vals->condition = (double)(VERDICT_DBL_MAX); else metric_vals->condition = (double) ( (sides[0]%sides[0] + sides[2]%sides[2] - sides[0]%sides[2]) / (area2x*rootOf3) ); } } // calculate the shape if(metrics_request_flag & V_TRI_SHAPE || metrics_request_flag & V_TRI_SHAPE_AND_SIZE) { //if element is inverted, shape is zero. We don't need to //calculate anything. if(is_inverted ){ metric_vals->shape = 0.0; } else{//otherwise, we calculate the shape // calculate once and reuse static const double rootOf3 = sqrt(3.0); // reuse area from before double area2x = metric_vals->area * 2; // dot products double dots[3] = { sides[0] % sides[0], sides[2] % sides[2], sides[0] % sides[2] }; // add the dots double sum_dots = dots[0] + dots[1] - dots[2]; // then the finale if( sum_dots == 0.0 ) metric_vals->shape = 0.0; else metric_vals->shape = (double)(rootOf3 * area2x / sum_dots); } } // calculate relative size squared if(metrics_request_flag & V_TRI_RELATIVE_SIZE_SQUARED || metrics_request_flag & V_TRI_SHAPE_AND_SIZE) { // get weights double w11, w21, w12, w22; v_tri_get_weight(w11,w21,w12,w22); // get the determinant double detw = v_determinant(w11,w21,w12,w22); // use the area from above and divide with the determinant if( metric_vals->area == 0.0 || detw == 0.0 ) metric_vals->relative_size_squared = 0.0; else { double size = metric_vals->area * 2.0 / detw; // square the size size *= size; // value ranges between 0 to 1 metric_vals->relative_size_squared = (double)VERDICT_MIN(size, 1.0/size ); } } // calculate shape and size if(metrics_request_flag & V_TRI_SHAPE_AND_SIZE) { metric_vals->shape_and_size = metric_vals->relative_size_squared * metric_vals->shape; } // calculate distortion if(metrics_request_flag & V_TRI_DISTORTION) metric_vals->distortion = v_tri_distortion(num_nodes, coordinates); //take care of any over-flow problems if( metric_vals->aspect_frobenius > 0 ) metric_vals->aspect_frobenius = (double) VERDICT_MIN( metric_vals->aspect_frobenius, VERDICT_DBL_MAX );\ else metric_vals->aspect_frobenius = (double) VERDICT_MAX( metric_vals->aspect_frobenius, -VERDICT_DBL_MAX ); if( metric_vals->area > 0 ) metric_vals->area = (double) VERDICT_MIN( metric_vals->area, VERDICT_DBL_MAX ); else metric_vals->area = (double) VERDICT_MAX( metric_vals->area, -VERDICT_DBL_MAX ); if( metric_vals->minimum_angle > 0 ) metric_vals->minimum_angle = (double) VERDICT_MIN( metric_vals->minimum_angle, VERDICT_DBL_MAX ); else metric_vals->minimum_angle = (double) VERDICT_MAX( metric_vals->minimum_angle, -VERDICT_DBL_MAX ); if( metric_vals->maximum_angle > 0 ) metric_vals->maximum_angle = (double) VERDICT_MIN( metric_vals->maximum_angle, VERDICT_DBL_MAX ); else metric_vals->maximum_angle = (double) VERDICT_MAX( metric_vals->maximum_angle , -VERDICT_DBL_MAX ); if( metric_vals->condition > 0 ) metric_vals->condition = (double) VERDICT_MIN( metric_vals->condition, VERDICT_DBL_MAX ); else metric_vals->condition = (double) VERDICT_MAX( metric_vals->condition, -VERDICT_DBL_MAX ); if( metric_vals->shape > 0 ) metric_vals->shape = (double) VERDICT_MIN( metric_vals->shape, VERDICT_DBL_MAX ); else metric_vals->shape = (double) VERDICT_MAX( metric_vals->shape, -VERDICT_DBL_MAX ); if( metric_vals->radius_ratio > 0 ) metric_vals->radius_ratio = (double) VERDICT_MIN( metric_vals->radius_ratio, VERDICT_DBL_MAX );\ else metric_vals->radius_ratio = (double) VERDICT_MAX( metric_vals->radius_ratio, -VERDICT_DBL_MAX ); if( metric_vals->scaled_jacobian > 0 ) metric_vals->scaled_jacobian = (double) VERDICT_MIN( metric_vals->scaled_jacobian, VERDICT_DBL_MAX ); else metric_vals->scaled_jacobian = (double) VERDICT_MAX( metric_vals->scaled_jacobian, -VERDICT_DBL_MAX ); if( metric_vals->relative_size_squared > 0 ) metric_vals->relative_size_squared = (double) VERDICT_MIN( metric_vals->relative_size_squared, VERDICT_DBL_MAX ); else metric_vals->relative_size_squared = (double) VERDICT_MAX( metric_vals->relative_size_squared, -VERDICT_DBL_MAX ); if( metric_vals->shape_and_size > 0 ) metric_vals->shape_and_size = (double) VERDICT_MIN( metric_vals->shape_and_size, VERDICT_DBL_MAX ); else metric_vals->shape_and_size = (double) VERDICT_MAX( metric_vals->shape_and_size, -VERDICT_DBL_MAX ); if( metric_vals->distortion > 0 ) metric_vals->distortion = (double) VERDICT_MIN( metric_vals->distortion, VERDICT_DBL_MAX ); else metric_vals->distortion = (double) VERDICT_MAX( metric_vals->distortion, -VERDICT_DBL_MAX ); }
/*! multiple quality measures of a quad */ C_FUNC_DEF void v_quad_quality( int num_nodes, VERDICT_REAL coordinates[][3], unsigned int metrics_request_flag, QuadMetricVals *metric_vals ) { memset( metric_vals, 0, sizeof(QuadMetricVals) ); // for starts, lets set up some basic and common information /* node numbers and side numbers used below 2 3 +--------- 2 / + / | 3 / | 1 / | + | 0 -------------+ 1 0 */ // vectors for each side VerdictVector edges[4]; make_quad_edges( edges, coordinates ); double areas[4]; signed_corner_areas( areas, coordinates ); double lengths[4]; lengths[0] = edges[0].length(); lengths[1] = edges[1].length(); lengths[2] = edges[2].length(); lengths[3] = edges[3].length(); VerdictBoolean is_collapsed = is_collapsed_quad(coordinates); // handle collapsed quads metrics here if(is_collapsed == VERDICT_TRUE && metrics_request_flag & ( V_QUAD_MINIMUM_ANGLE | V_QUAD_MAXIMUM_ANGLE | V_QUAD_JACOBIAN | V_QUAD_SCALED_JACOBIAN )) { if(metrics_request_flag & V_QUAD_MINIMUM_ANGLE) metric_vals->minimum_angle = v_tri_minimum_angle(3, coordinates); if(metrics_request_flag & V_QUAD_MAXIMUM_ANGLE) metric_vals->maximum_angle = v_tri_maximum_angle(3, coordinates); if(metrics_request_flag & V_QUAD_JACOBIAN) metric_vals->jacobian = (VERDICT_REAL)(v_tri_area(3, coordinates) * 2.0); if(metrics_request_flag & V_QUAD_SCALED_JACOBIAN) metric_vals->jacobian = (VERDICT_REAL)(v_tri_scaled_jacobian(3, coordinates) * 2.0); } // calculate both largest and smallest angles if(metrics_request_flag & (V_QUAD_MINIMUM_ANGLE | V_QUAD_MAXIMUM_ANGLE) && is_collapsed == VERDICT_FALSE ) { // gather the angles double angles[4]; angles[0] = acos( -(edges[0] % edges[1])/(lengths[0]*lengths[1]) ); angles[1] = acos( -(edges[1] % edges[2])/(lengths[1]*lengths[2]) ); angles[2] = acos( -(edges[2] % edges[3])/(lengths[2]*lengths[3]) ); angles[3] = acos( -(edges[3] % edges[0])/(lengths[3]*lengths[0]) ); if( lengths[0] <= VERDICT_DBL_MIN || lengths[1] <= VERDICT_DBL_MIN || lengths[2] <= VERDICT_DBL_MIN || lengths[3] <= VERDICT_DBL_MIN ) { metric_vals->minimum_angle = 360.0; metric_vals->maximum_angle = 0.0; } else { // if smallest angle, find the smallest angle if(metrics_request_flag & V_QUAD_MINIMUM_ANGLE) { metric_vals->minimum_angle = VERDICT_DBL_MAX; for(int i = 0; i<4; i++) metric_vals->minimum_angle = VERDICT_MIN(angles[i], metric_vals->minimum_angle); metric_vals->minimum_angle *= 180.0 / VERDICT_PI; } // if largest angle, find the largest angle if(metrics_request_flag & V_QUAD_MAXIMUM_ANGLE) { metric_vals->maximum_angle = 0.0; for(int i = 0; i<4; i++) metric_vals->maximum_angle = VERDICT_MAX(angles[i], metric_vals->maximum_angle); metric_vals->maximum_angle *= 180.0 / VERDICT_PI; if( areas[0] < 0 || areas[1] < 0 || areas[2] < 0 || areas[3] < 0 ) metric_vals->maximum_angle = 360 - metric_vals->maximum_angle; } } } // handle aspect, skew, taper, and area together if( metrics_request_flag & ( V_QUAD_ASPECT | V_QUAD_SKEW | V_QUAD_TAPER ) ) { //get principle axes VerdictVector principal_axes[2]; principal_axes[0] = edges[0] - edges[2]; principal_axes[1] = edges[1] - edges[3]; if(metrics_request_flag & (V_QUAD_ASPECT | V_QUAD_SKEW | V_QUAD_TAPER)) { double len1 = principal_axes[0].length(); double len2 = principal_axes[1].length(); // calculate the aspect ratio if(metrics_request_flag & V_QUAD_ASPECT) { if( len1 < VERDICT_DBL_MIN || len2 < VERDICT_DBL_MIN ) metric_vals->aspect = VERDICT_DBL_MAX; else metric_vals->aspect = VERDICT_MAX( len1 / len2, len2 / len1 ); } // calculate the taper if(metrics_request_flag & V_QUAD_TAPER) { double min_length = VERDICT_MIN( len1, len2 ); VerdictVector cross_derivative = edges[1] + edges[3]; if( min_length < VERDICT_DBL_MIN ) metric_vals->taper = VERDICT_DBL_MAX; else metric_vals->taper = cross_derivative.length()/ min_length; } // calculate the skew if(metrics_request_flag & V_QUAD_SKEW) { if( principal_axes[0].normalize() < VERDICT_DBL_MIN || principal_axes[1].normalize() < VERDICT_DBL_MIN ) metric_vals->skew = 0.0; else metric_vals->skew = fabs( principal_axes[0] % principal_axes[1] ); } } } // calculate the area if(metrics_request_flag & (V_QUAD_AREA | V_QUAD_RELATIVE_SIZE_SQUARED) ) { metric_vals->area = 0.25 * (areas[0] + areas[1] + areas[2] + areas[3]); } // calculate the relative size if(metrics_request_flag & (V_QUAD_RELATIVE_SIZE_SQUARED | V_QUAD_SHAPE_AND_SIZE | V_QUAD_SHEAR_AND_SIZE ) ) { double quad_area = v_quad_area (4, coordinates); v_set_quad_size( quad_area ); double w11,w21,w12,w22; get_weight(w11,w21,w12,w22); double avg_area = determinant(w11,w21,w12,w22); if( avg_area < VERDICT_DBL_MIN ) metric_vals->relative_size_squared = 0.0; else metric_vals->relative_size_squared = pow( VERDICT_MIN( metric_vals->area/avg_area, avg_area/metric_vals->area ), 2 ); } // calculate the jacobian if(metrics_request_flag & V_QUAD_JACOBIAN) { metric_vals->jacobian = VERDICT_MIN( VERDICT_MIN( areas[0], areas[1] ), VERDICT_MIN( areas[2], areas[3] ) ); } if( metrics_request_flag & ( V_QUAD_SCALED_JACOBIAN | V_QUAD_SHEAR | V_QUAD_SHEAR_AND_SIZE ) ) { double scaled_jac, min_scaled_jac = VERDICT_DBL_MAX; if( lengths[0] < VERDICT_DBL_MIN || lengths[1] < VERDICT_DBL_MIN || lengths[2] < VERDICT_DBL_MIN || lengths[3] < VERDICT_DBL_MIN ) { metric_vals->scaled_jacobian = 0.0; metric_vals->shear = 0.0; } else { scaled_jac = areas[0] / (lengths[0] * lengths[3]); min_scaled_jac = VERDICT_MIN( scaled_jac, min_scaled_jac ); scaled_jac = areas[1] / (lengths[1] * lengths[0]); min_scaled_jac = VERDICT_MIN( scaled_jac, min_scaled_jac ); scaled_jac = areas[2] / (lengths[2] * lengths[1]); min_scaled_jac = VERDICT_MIN( scaled_jac, min_scaled_jac ); scaled_jac = areas[3] / (lengths[3] * lengths[2]); min_scaled_jac = VERDICT_MIN( scaled_jac, min_scaled_jac ); metric_vals->scaled_jacobian = min_scaled_jac; //what the heck...set shear as well if( min_scaled_jac <= VERDICT_DBL_MIN ) metric_vals->shear = 0.0; else metric_vals->shear = min_scaled_jac; } } if( metrics_request_flag & (V_QUAD_WARPAGE | V_QUAD_ODDY) ) { VerdictVector corner_normals[4]; corner_normals[0] = edges[3] * edges[0]; corner_normals[1] = edges[0] * edges[1]; corner_normals[2] = edges[1] * edges[2]; corner_normals[3] = edges[2] * edges[3]; if( metrics_request_flag & V_QUAD_ODDY ) { double oddy, max_oddy = 0.0; double diff, dot_prod; double length_squared[4]; length_squared[0] = corner_normals[0].length_squared(); length_squared[1] = corner_normals[1].length_squared(); length_squared[2] = corner_normals[2].length_squared(); length_squared[3] = corner_normals[3].length_squared(); if( length_squared[0] < VERDICT_DBL_MIN || length_squared[1] < VERDICT_DBL_MIN || length_squared[2] < VERDICT_DBL_MIN || length_squared[3] < VERDICT_DBL_MIN ) metric_vals->oddy = VERDICT_DBL_MAX; else { diff = (lengths[0]*lengths[0]) - (lengths[1]*lengths[1]); dot_prod = edges[0]%edges[1]; oddy = ((diff*diff) + 4*dot_prod*dot_prod ) / (2*length_squared[1]); max_oddy = VERDICT_MAX( oddy, max_oddy ); diff = (lengths[1]*lengths[1]) - (lengths[2]*lengths[2]); dot_prod = edges[1]%edges[2]; oddy = ((diff*diff) + 4*dot_prod*dot_prod ) / (2*length_squared[2]); max_oddy = VERDICT_MAX( oddy, max_oddy ); diff = (lengths[2]*lengths[2]) - (lengths[3]*lengths[3]); dot_prod = edges[2]%edges[3]; oddy = ((diff*diff) + 4*dot_prod*dot_prod ) / (2*length_squared[3]); max_oddy = VERDICT_MAX( oddy, max_oddy ); diff = (lengths[3]*lengths[3]) - (lengths[0]*lengths[0]); dot_prod = edges[3]%edges[0]; oddy = ((diff*diff) + 4*dot_prod*dot_prod ) / (2*length_squared[0]); max_oddy = VERDICT_MAX( oddy, max_oddy ); metric_vals->oddy = max_oddy; } } if( metrics_request_flag & V_QUAD_WARPAGE ) { if( corner_normals[0].normalize() < VERDICT_DBL_MIN || corner_normals[1].normalize() < VERDICT_DBL_MIN || corner_normals[2].normalize() < VERDICT_DBL_MIN || corner_normals[3].normalize() < VERDICT_DBL_MIN ) metric_vals->warpage = VERDICT_DBL_MAX; else { metric_vals->warpage = pow( VERDICT_MIN( corner_normals[0]%corner_normals[2], corner_normals[1]%corner_normals[3]), 3 ); } } } if( metrics_request_flag & V_QUAD_STRETCH ) { VerdictVector temp; temp.set( coordinates[2][0] - coordinates[0][0], coordinates[2][1] - coordinates[0][1], coordinates[2][2] - coordinates[0][2]); double diag02 = temp.length_squared(); temp.set( coordinates[3][0] - coordinates[1][0], coordinates[3][1] - coordinates[1][1], coordinates[3][2] - coordinates[1][2]); double diag13 = temp.length_squared(); static const double QUAD_STRETCH_FACTOR = sqrt(2.0); // 'diag02' is now the max diagonal of the quad diag02 = VERDICT_MAX( diag02, diag13 ); if( diag02 < VERDICT_DBL_MIN ) metric_vals->stretch = VERDICT_DBL_MAX; else metric_vals->stretch = QUAD_STRETCH_FACTOR * VERDICT_MIN( VERDICT_MIN( lengths[0], lengths[1] ), VERDICT_MIN( lengths[2], lengths[3] ) ) / sqrt(diag02); } if(metrics_request_flag & (V_QUAD_CONDITION | V_QUAD_SHAPE | V_QUAD_SHAPE_AND_SIZE ) ) { double lengths_squared[4]; lengths_squared[0] = edges[0].length_squared(); lengths_squared[1] = edges[1].length_squared(); lengths_squared[2] = edges[2].length_squared(); lengths_squared[3] = edges[3].length_squared(); if( areas[0] < VERDICT_DBL_MIN || areas[1] < VERDICT_DBL_MIN || areas[2] < VERDICT_DBL_MIN || areas[3] < VERDICT_DBL_MIN ) { metric_vals->condition = VERDICT_DBL_MAX; metric_vals->shape= VERDICT_DBL_MAX; } else { double max_condition = 0.0, condition; condition = (lengths_squared[0] + lengths_squared[3])/areas[0]; max_condition = VERDICT_MAX( max_condition, condition ); condition = (lengths_squared[1] + lengths_squared[0])/areas[1]; max_condition = VERDICT_MAX( max_condition, condition ); condition = (lengths_squared[2] + lengths_squared[1])/areas[2]; max_condition = VERDICT_MAX( max_condition, condition ); condition = (lengths_squared[3] + lengths_squared[2])/areas[3]; max_condition = VERDICT_MAX( max_condition, condition ); metric_vals->condition = 0.5*max_condition; metric_vals->shape = 2/max_condition; } } if(metrics_request_flag & V_QUAD_AREA ) { if( metric_vals->area > 0 ) metric_vals->area = (VERDICT_REAL) VERDICT_MIN( metric_vals->area, VERDICT_DBL_MAX ); metric_vals->area = (VERDICT_REAL) VERDICT_MAX( metric_vals->area, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_ASPECT ) { if( metric_vals->aspect > 0 ) metric_vals->aspect = (VERDICT_REAL) VERDICT_MIN( metric_vals->aspect, VERDICT_DBL_MAX ); metric_vals->aspect = (VERDICT_REAL) VERDICT_MAX( metric_vals->aspect, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_CONDITION ) { if( metric_vals->condition > 0 ) metric_vals->condition = (VERDICT_REAL) VERDICT_MIN( metric_vals->condition, VERDICT_DBL_MAX ); metric_vals->condition = (VERDICT_REAL) VERDICT_MAX( metric_vals->condition, -VERDICT_DBL_MAX ); } // calculate distortion if(metrics_request_flag & V_QUAD_DISTORTION) { metric_vals->distortion = v_quad_distortion(num_nodes, coordinates); if( metric_vals->distortion > 0 ) metric_vals->distortion = (VERDICT_REAL) VERDICT_MIN( metric_vals->distortion, VERDICT_DBL_MAX ); metric_vals->distortion = (VERDICT_REAL) VERDICT_MAX( metric_vals->distortion, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_JACOBIAN ) { if( metric_vals->jacobian > 0 ) metric_vals->jacobian = (VERDICT_REAL) VERDICT_MIN( metric_vals->jacobian, VERDICT_DBL_MAX ); metric_vals->jacobian = (VERDICT_REAL) VERDICT_MAX( metric_vals->jacobian, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_MAXIMUM_ANGLE ) { if( metric_vals->maximum_angle > 0 ) metric_vals->maximum_angle = (VERDICT_REAL) VERDICT_MIN( metric_vals->maximum_angle, VERDICT_DBL_MAX ); metric_vals->maximum_angle = (VERDICT_REAL) VERDICT_MAX( metric_vals->maximum_angle, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_MINIMUM_ANGLE ) { if( metric_vals->minimum_angle > 0 ) metric_vals->minimum_angle = (VERDICT_REAL) VERDICT_MIN( metric_vals->minimum_angle, VERDICT_DBL_MAX ); metric_vals->minimum_angle = (VERDICT_REAL) VERDICT_MAX( metric_vals->minimum_angle, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_ODDY ) { if( metric_vals->oddy > 0 ) metric_vals->oddy = (VERDICT_REAL) VERDICT_MIN( metric_vals->oddy, VERDICT_DBL_MAX ); metric_vals->oddy = (VERDICT_REAL) VERDICT_MAX( metric_vals->oddy, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_RELATIVE_SIZE_SQUARED ) { if( metric_vals->relative_size_squared> 0 ) metric_vals->relative_size_squared = (VERDICT_REAL) VERDICT_MIN( metric_vals->relative_size_squared, VERDICT_DBL_MAX ); metric_vals->relative_size_squared = (VERDICT_REAL) VERDICT_MAX( metric_vals->relative_size_squared, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_SCALED_JACOBIAN ) { if( metric_vals->scaled_jacobian> 0 ) metric_vals->scaled_jacobian = (VERDICT_REAL) VERDICT_MIN( metric_vals->scaled_jacobian, VERDICT_DBL_MAX ); metric_vals->scaled_jacobian = (VERDICT_REAL) VERDICT_MAX( metric_vals->scaled_jacobian, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_SHEAR ) { if( metric_vals->shear > 0 ) metric_vals->shear = (VERDICT_REAL) VERDICT_MIN( metric_vals->shear, VERDICT_DBL_MAX ); metric_vals->shear = (VERDICT_REAL) VERDICT_MAX( metric_vals->shear, -VERDICT_DBL_MAX ); } // calculate shear and size // reuse values from above if(metrics_request_flag & V_QUAD_SHEAR_AND_SIZE) { metric_vals->shear_and_size = metric_vals->shear * metric_vals->relative_size_squared; if( metric_vals->shear_and_size > 0 ) metric_vals->shear_and_size = (VERDICT_REAL) VERDICT_MIN( metric_vals->shear_and_size, VERDICT_DBL_MAX ); metric_vals->shear_and_size = (VERDICT_REAL) VERDICT_MAX( metric_vals->shear_and_size, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_SHAPE ) { if( metric_vals->shape > 0 ) metric_vals->shape = (VERDICT_REAL) VERDICT_MIN( metric_vals->shape, VERDICT_DBL_MAX ); metric_vals->shape = (VERDICT_REAL) VERDICT_MAX( metric_vals->shape, -VERDICT_DBL_MAX ); } // calculate shape and size // reuse values from above if(metrics_request_flag & V_QUAD_SHAPE_AND_SIZE) { metric_vals->shape_and_size = metric_vals->shape * metric_vals->relative_size_squared; if( metric_vals->shape_and_size > 0 ) metric_vals->shape_and_size = (VERDICT_REAL) VERDICT_MIN( metric_vals->shape_and_size, VERDICT_DBL_MAX ); metric_vals->shape_and_size = (VERDICT_REAL) VERDICT_MAX( metric_vals->shape_and_size, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_SKEW ) { if( metric_vals->skew > 0 ) metric_vals->skew = (VERDICT_REAL) VERDICT_MIN( metric_vals->skew, VERDICT_DBL_MAX ); metric_vals->skew = (VERDICT_REAL) VERDICT_MAX( metric_vals->skew, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_STRETCH ) { if( metric_vals->stretch > 0 ) metric_vals->stretch = (VERDICT_REAL) VERDICT_MIN( metric_vals->stretch, VERDICT_DBL_MAX ); metric_vals->stretch = (VERDICT_REAL) VERDICT_MAX( metric_vals->stretch, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_TAPER ) { if( metric_vals->taper > 0 ) metric_vals->taper = (VERDICT_REAL) VERDICT_MIN( metric_vals->taper, VERDICT_DBL_MAX ); metric_vals->taper = (VERDICT_REAL) VERDICT_MAX( metric_vals->taper, -VERDICT_DBL_MAX ); } if(metrics_request_flag & V_QUAD_WARPAGE ) { if( metric_vals->warpage > 0 ) metric_vals->warpage = (VERDICT_REAL) VERDICT_MIN( metric_vals->warpage, VERDICT_DBL_MAX ); metric_vals->warpage = (VERDICT_REAL) VERDICT_MAX( metric_vals->warpage, -VERDICT_DBL_MAX ); } }