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 ); }
/*! 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 ); } }
/*! 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 ); }
/*! 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 ); } }