# include # include # include # include # include # include # include "triangulation.h" /******************************************************************************/ void alpha_measure ( int n, double z[], int triangle_order, int triangle_num, int triangle_node[], double *alpha_min, double *alpha_ave, double *alpha_area ) /******************************************************************************/ /* Purpose: ALPHA_MEASURE determines the triangulated pointset quality measure ALPHA. Discusion: The ALPHA measure evaluates the uniformity of the shapes of the triangles defined by a triangulated pointset. We compute the minimum angle among all the triangles in the triangulated dataset and divide by the maximum possible value (which, in degrees, is 60). The best possible value is 1, and the worst 0. A good triangulation should have an ALPHA score close to 1. The code has been modified to 'allow' 6-node triangulations. However, no effort is made to actually process the midside nodes. Only information from the vertices is used. Licensing: This code is distributed under the MIT license. Modified: 21 June 2009 Author: John Burkardt Parameters: Input, int N, the number of points. Input, real ( kind = 8 ) Z(2,N), the points. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE(TRIANGLE_ORDER,TRIANGLE_NUM), the triangulation. Output, double *ALPHA_MIN, the minimum value of ALPHA over all triangles. Output, double *ALPHA_AVE, the value of ALPHA averaged over all triangles. Output, double *ALPHA_AREA, the value of ALPHA averaged over all triangles and weighted by area. */ { double a_angle; int a_index; double a_x; double a_y; double ab_len; double area; double area_total; double b_angle; int b_index; double b_x; double b_y; double bc_len; double c_angle; int c_index; double c_x; double c_y; double ca_len; double pi = 3.141592653589793; int triangle; *alpha_min = HUGE_VAL; *alpha_ave = 0.0; *alpha_area = 0.0; area_total = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { a_index = triangle_node[0+triangle*triangle_order]; b_index = triangle_node[1+triangle*triangle_order]; c_index = triangle_node[2+triangle*triangle_order]; a_x = z[0+(a_index-1)*2]; a_y = z[1+(a_index-1)*2]; b_x = z[0+(b_index-1)*2]; b_y = z[1+(b_index-1)*2]; c_x = z[0+(c_index-1)*2]; c_y = z[1+(c_index-1)*2]; area = 0.5 * fabs ( a_x * ( b_y - c_y ) + b_x * ( c_y - a_y ) + c_x * ( a_y - b_y ) ); ab_len = sqrt ( pow ( a_x - b_x, 2 ) + pow ( a_y - b_y, 2 ) ); bc_len = sqrt ( pow ( b_x - c_x, 2 ) + pow ( b_y - c_y, 2 ) ); ca_len = sqrt ( pow ( c_x - a_x, 2 ) + pow ( c_y - a_y, 2 ) ); /* Take care of a ridiculous special case. */ if ( ab_len == 0.0 && bc_len == 0.0 && ca_len == 0.0 ) { a_angle = 2.0 * pi / 3.0; b_angle = 2.0 * pi / 3.0; c_angle = 2.0 * pi / 3.0; } else { if ( ca_len == 0.0 || ab_len == 0.0 ) { a_angle = pi; } else { a_angle = r8_acos ( ( ca_len * ca_len + ab_len * ab_len - bc_len * bc_len ) / ( 2.0 * ca_len * ab_len ) ); } if ( ab_len == 0.0 || bc_len == 0.0 ) { b_angle = pi; } else { b_angle = r8_acos ( ( ab_len * ab_len + bc_len * bc_len - ca_len * ca_len ) / ( 2.0 * ab_len * bc_len ) ); } if ( bc_len == 0.0 || ca_len == 0.0 ) { c_angle = pi; } else { c_angle = r8_acos ( ( bc_len * bc_len + ca_len * ca_len - ab_len * ab_len ) / ( 2.0 * bc_len * ca_len ) ); } } *alpha_min = fmin ( *alpha_min, a_angle ); *alpha_min = fmin ( *alpha_min, b_angle ); *alpha_min = fmin ( *alpha_min, c_angle ); *alpha_ave = *alpha_ave + *alpha_min; *alpha_area = *alpha_area + area * *alpha_min; area_total = area_total + area; } *alpha_ave = *alpha_ave / ( double ) ( triangle_num ); *alpha_area = *alpha_area / area_total; /* Normalize angles from [0,pi/3] radians into qualities in [0,1]. */ *alpha_min = *alpha_min * 3.0 / pi; *alpha_ave = *alpha_ave * 3.0 / pi; *alpha_area = *alpha_area * 3.0 / pi; return; } /******************************************************************************/ double angle_rad_2d ( double p1[2], double p2[2], double p3[2] ) /******************************************************************************/ /* Purpose: ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. Discussion: ANGLE_RAD_2D ( P1, P2, P3 ) + ANGLE_RAD_2D ( P3, P2, P1 ) = 2 * PI P1 / / / / P2--------->P3 Licensing: This code is distributed under the MIT license. Modified: 24 June 2005 Author: John Burkardt Parameters: Input, double P1[2], P2[2], P3[2], define the rays P1 - P2 and P3 - P2 which define the angle. Output, double ANGLE_RAD_3D, the angle between the two rays, in radians. This value will always be between 0 and 2*PI. If either ray has zero length, then the angle is returned as zero. */ { # define DIM_NUM 2 double p[DIM_NUM]; double pi = 3.141592653589793; double value; p[0] = ( p3[0] - p2[0] ) * ( p1[0] - p2[0] ) + ( p3[1] - p2[1] ) * ( p1[1] - p2[1] ); p[1] = ( p3[0] - p2[0] ) * ( p1[1] - p2[1] ) - ( p3[1] - p2[1] ) * ( p1[0] - p2[0] ); if ( p[0] == 0.0 && p[1] == 0.0 ) { value = 0.0; return value; } value = atan2 ( p[1], p[0] ); if ( value < 0.0 ) { value = value + 2.0 * pi; } return value; # undef DIM_NUM } /******************************************************************************/ void area_measure ( int n, double z[], int triangle_order, int triangle_num, int triangle_node[], double *area_min, double *area_max, double *area_ratio, double *area_ave, double *area_std ) /******************************************************************************/ /* Purpose: AREA_MEASURE determines the area ratio quality measure. Discusion: This measure computes the area of every triangle, and returns the ratio of the minimum to the maximum triangle. A value of 1 is "perfect", indicating that all triangles have the same area. A value of 0 is the worst possible result. The code has been modified to 'allow' 6-node triangulations. However, no effort is made to actually process the midside nodes. Only information from the vertices is used. Licensing: This code is distributed under the MIT license. Modified: 21 June 2009 Author: John Burkardt Parameters: Input, int N, the number of points. Input, double Z[2*N], the points. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the triangulation. Output, double *AREA_MIN, *AREA_MAX, the minimum and maximum areas. Output, double *AREA_RATIO, the ratio of the minimum to the maximum area. Output, double *AREA_AVE, the average area. Output, double *AREA_STD, the standard deviation of the areas. */ { double area; int triangle; double x1; double x2; double x3; double y1; double y2; double y3; *area_max = 0.0; *area_min = HUGE_VAL; *area_ave = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { x1 = z[0+(triangle_node[0+triangle*triangle_order]-1)*2]; y1 = z[1+(triangle_node[0+triangle*triangle_order]-1)*2]; x2 = z[0+(triangle_node[1+triangle*triangle_order]-1)*2]; y2 = z[1+(triangle_node[1+triangle*triangle_order]-1)*2]; x3 = z[0+(triangle_node[2+triangle*triangle_order]-1)*2]; y3 = z[1+(triangle_node[2+triangle*triangle_order]-1)*2]; area = 0.5 * fabs ( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ) ); *area_min = fmin ( *area_min, area ); *area_max = fmax ( *area_max, area ); *area_ave = *area_ave + area; } *area_ave = *area_ave / ( double ) ( triangle_num ); *area_std = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { x1 = z[0+(triangle_node[0+triangle*triangle_order]-1)*2]; y1 = z[1+(triangle_node[0+triangle*triangle_order]-1)*2]; x2 = z[0+(triangle_node[1+triangle*triangle_order]-1)*2]; y2 = z[1+(triangle_node[1+triangle*triangle_order]-1)*2]; x3 = z[0+(triangle_node[2+triangle*triangle_order]-1)*2]; y3 = z[1+(triangle_node[2+triangle*triangle_order]-1)*2]; area = 0.5 * fabs ( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ) ); *area_std = *area_std + pow ( area - *area_ave, 2 ); } *area_std = sqrt ( *area_std / ( double ) ( triangle_num ) ); if ( 0.0 < *area_max ) { *area_ratio = *area_min / *area_max; } else { *area_ratio = 0.0; } return; } /******************************************************************************/ void bandwidth ( int element_order, int element_num, int element_node[], int *ml, int *mu, int *m ) /******************************************************************************/ /* Purpose: BANDWIDTH determines the bandwidth associated with the finite element mesh. Discussion: The quantity computed here is the "geometric" bandwidth determined by the finite element mesh alone. If a single finite element variable is associated with each node of the mesh, and if the nodes and variables are numbered in the same way, then the geometric bandwidth is the same as the bandwidth of a typical finite element matrix. The bandwidth M is defined in terms of the lower and upper bandwidths: M = ML + 1 + MU where ML = maximum distance from any diagonal entry to a nonzero entry in the same row, but earlier column, MU = maximum distance from any diagonal entry to a nonzero entry in the same row, but later column. Because the finite element node adjacency relationship is symmetric, we are guaranteed that ML = MU. Licensing: This code is distributed under the MIT license. Modified: 23 September 2006 Author: John Burkardt Parameters: Input, int ELEMENT_ORDER, the order of the elements. Input, int ELEMENT_NUM, the number of elements. Input, int ELEMENT_NODE[ELEMENT_ORDER*ELEMENT_NUM]; ELEMENT_NODE(I,J) is the global index of local node I in element J. Output, int *ML, *MU, the lower and upper bandwidths of the matrix. Output, int *M, the bandwidth of the matrix. */ { int element; int global_i; int global_j; int local_i; int local_j; *ml = 0; *mu = 0; for ( element = 0; element < element_num; element++ ) { for ( local_i = 0; local_i < element_order; local_i++ ) { global_i = element_node[local_i+element*element_order]; for ( local_j = 0; local_j < element_order; local_j++ ) { global_j = element_node[local_j+element*element_order]; *mu = i4_max ( *mu, global_j - global_i ); *ml = i4_max ( *ml, global_i - global_j ); } } } *m = *ml + 1 + *mu; return; } /******************************************************************************/ int delaunay_swap_test ( double xy[] ) /******************************************************************************/ /* Purpose: DELAUNAY_SWAP_TEST performs the Delaunay swap test. Discussion: The current triangles are formed by nodes [0+2,3) and [0+3,4). if a swap is recommended, the new triangles will be [0+2,4) and [1+3,4). 4 ? 4 / \ /|\ 1---3 ==> 1 | 3 \ / \|/ 2 2 Licensing: This code is distributed under the MIT license. Modified: 26 June 2009 Author: John Burkardt Reference: Graham Carey, Computational Grids: Generation, Adaptation and Solution Strategies, Taylor and Francis, 1997, ISBN13: 978-1560326359, LC: QA377.C32. Parameters: Input, double XY[2*4], the coordinates of four points. Output, int SWAP, is TRUE if the triangles [0+2,4) and [1+3,4) are to replace triangles [0+2,3) and [0+3,4). */ { double a; double b; double c; double d; int swap; double x13; double x14; double x23; double x24; double y13; double y14; double y23; double y24; x13 = xy[0+0*2] - xy[0+2*2]; x14 = xy[0+0*2] - xy[0+3*2]; x23 = xy[0+1*2] - xy[0+2*2]; x24 = xy[0+1*2] - xy[0+3*2]; y13 = xy[1+0*2] - xy[1+2*2]; y14 = xy[1+0*2] - xy[1+3*2]; y23 = xy[1+1*2] - xy[1+2*2]; y24 = xy[1+1*2] - xy[1+3*2]; a = x13 * x23 + y13 * y23; b = x24 * y14 - x14 * y24; c = x23 * y13 - x13 * y23; d = x24 * x14 + y14 * y24; /* The reference gives two initial tests before the main one. However, there seems to be an error in at least one of these tests. Since they are intended to avoid error in borderline cases, but instead cause real error in common cases, they are omitted for now. if ( 0.0 <= a && 0.0 <= d ) { swap = 1; } else if ( a < d && d < 0.0 ) { swap = 1; } else if ... */ if ( a * b < c * d ) { swap = 1; } else { swap = 0; } return swap; } /******************************************************************************/ int diaedg ( double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3 ) /******************************************************************************/ /* Purpose: DIAEDG chooses a diagonal edge. Discussion: The routine determines whether 0--2 or 1--3 is the diagonal edge that should be chosen, based on the circumcircle criterion, where (X0,Y0), (X1,Y1), (X2,Y2), (X3,Y3) are the vertices of a simple quadrilateral in counterclockwise order. Licensing: This code is distributed under the MIT license. Modified: 28 August 2003 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, double X0, Y0, X1, Y1, X2, Y2, X3, Y3, the coordinates of the vertices of a quadrilateral, given in counter clockwise order. Output, int DIAEDG, chooses a diagonal: +1, if diagonal edge 02 is chosen; -1, if diagonal edge 13 is chosen; 0, if the four vertices are cocircular. */ { double ca; double cb; double dx10; double dx12; double dx30; double dx32; double dy10; double dy12; double dy30; double dy32; double s; double tol; double tola; double tolb; int value; tol = 100.0 * DBL_EPSILON; dx10 = x1 - x0; dy10 = y1 - y0; dx12 = x1 - x2; dy12 = y1 - y2; dx30 = x3 - x0; dy30 = y3 - y0; dx32 = x3 - x2; dy32 = y3 - y2; tola = tol * fmax ( fabs ( dx10 ), fmax ( fabs ( dy10 ), fmax ( fabs ( dx30 ), fabs ( dy30 ) ) ) ); tolb = tol * fmax ( fabs ( dx12 ), fmax ( fabs ( dy12 ), fmax ( fabs ( dx32 ), fabs ( dy32 ) ) ) ); ca = dx10 * dx30 + dy10 * dy30; cb = dx12 * dx32 + dy12 * dy32; if ( tola < ca && tolb < cb ) { value = -1; } else if ( ca < -tola && cb < -tolb ) { value = 1; } else { tola = fmax ( tola, tolb ); s = ( dx10 * dy30 - dx30 * dy10 ) * cb + ( dx32 * dy12 - dx12 * dy32 ) * ca; if ( tola < s ) { value = -1; } else if ( s < -tola ) { value = 1; } else { value = 0; } } return value; } /******************************************************************************/ int get_seed ( void ) /******************************************************************************/ /* Purpose: GET_SEED returns a random seed for the random number generator. Licensing: This code is distributed under the MIT license. Modified: 17 November 2004 Author: John Burkardt Parameters: Output, int GET_SEED, a random seed value. */ { time_t clock; int i4_huge = 2147483647; int ihour; int imin; int isec; int seed; struct tm *lt; time_t tloc; /* If the internal seed is 0, generate a value based on the time. */ clock = time ( &tloc ); lt = localtime ( &clock ); /* Hours is 1, 2, ..., 12. */ ihour = lt->tm_hour; if ( 12 < ihour ) { ihour = ihour - 12; } /* Move Hours to 0, 1, ..., 11 */ ihour = ihour - 1; imin = lt->tm_min; isec = lt->tm_sec; seed = isec + 60 * ( imin + 60 * ihour ); /* We want values in [1,43200], not [0,43199]. */ seed = seed + 1; /* Remap SEED from [1,43200] to [1,HUGE]. */ seed = ( int ) ( ( ( double ) seed ) * ( ( double ) i4_huge ) / ( 60.0 * 60.0 * 12.0 ) ); /* Never use a seed of 0. */ if ( seed == 0 ) { seed = 1; } return seed; } /******************************************************************************/ int i4_max ( int i1, int i2 ) /******************************************************************************/ /* Purpose: I4_MAX returns the maximum of two I4's. Licensing: This code is distributed under the MIT license. Modified: 29 August 2006 Author: John Burkardt Parameters: Input, int I1, I2, are two integers to be compared. Output, int I4_MAX, the larger of I1 and I2. */ { int value; if ( i2 < i1 ) { value = i1; } else { value = i2; } return value; } /******************************************************************************/ int i4_min ( int i1, int i2 ) /******************************************************************************/ /* Purpose: I4_MIN returns the smaller of two I4's. Licensing: This code is distributed under the MIT license. Modified: 29 August 2006 Author: John Burkardt Parameters: Input, int I1, I2, two integers to be compared. Output, int I4_MIN, the smaller of I1 and I2. */ { int value; if ( i1 < i2 ) { value = i1; } else { value = i2; } return value; } /******************************************************************************/ int i4_modp ( int i, int j ) /******************************************************************************/ /* Purpose: I4_MODP returns the nonnegative remainder of I4 division. Discussion: If NREM = I4_MODP ( I, J ) NMULT = ( I - NREM ) / J then I = J * NMULT + NREM where NREM is always nonnegative. The MOD function computes a result with the same sign as the quantity being divided. Thus, suppose you had an angle A, and you wanted to ensure that it was between 0 and 360. Then mod(A,360) would do, if A was positive, but if A was negative, your result would be between -360 and 0. On the other hand, I4_MODP(A,360) is between 0 and 360, always. Example: I J MOD I4_MODP I4_MODP Factorization 107 50 7 7 107 = 2 * 50 + 7 107 -50 7 7 107 = -2 * -50 + 7 -107 50 -7 43 -107 = -3 * 50 + 43 -107 -50 -7 43 -107 = 3 * -50 + 43 Licensing: This code is distributed under the MIT license. Modified: 12 January 2007 Author: John Burkardt Parameters: Input, int I, the number to be divided. Input, int J, the number that divides I. Output, int I4_MODP, the nonnegative remainder when I is divided by J. */ { int value; if ( j == 0 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4_MODP - Fatal error!\n" ); fprintf ( stderr, " I4_MODP ( I, J ) called with J = %d\n", j ); exit ( 1 ); } value = i % j; if ( value < 0 ) { value = value + abs ( j ); } return value; } /******************************************************************************/ int i4_power ( int i, int j ) /******************************************************************************/ /* Purpose: I4_POWER returns the value of I^J. Licensing: This code is distributed under the MIT license. Modified: 23 October 2007 Author: John Burkardt Parameters: Input, int I, J, the base and the power. J should be nonnegative. Output, int I4_POWER, the value of I^J. */ { int k; int value; if ( j < 0 ) { if ( i == 1 ) { value = 1; } else if ( i == 0 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4_POWER - Fatal error!\n" ); fprintf ( stderr, " I^J requested, with I = 0 and J negative.\n" ); exit ( 1 ); } else { value = 0; } } else if ( j == 0 ) { if ( i == 0 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4_POWER - Fatal error!\n" ); fprintf ( stderr, " I^J requested, with I = 0 and J = 0.\n" ); exit ( 1 ); } else { value = 1; } } else if ( j == 1 ) { value = i; } else { value = 1; for ( k = 1; k <= j; k++ ) { value = value * i; } } return value; } /******************************************************************************/ int i4_sign ( int i ) /******************************************************************************/ /* Purpose: I4_SIGN returns the sign of an I4. Licensing: This code is distributed under the MIT license. Modified: 23 October 2007 Author: John Burkardt Parameters: Input, int I, the integer whose sign is desired. Output, int I4_SIGN, the sign of I. */ { int value; if ( i < 0 ) { value = -1; } else { value = 1; } return value; } /******************************************************************************/ void i4_swap ( int *i, int *j ) /******************************************************************************/ /* Purpose: I4_SWAP switches two I4's. Licensing: This code is distributed under the MIT license. Modified: 23 October 2007 Author: John Burkardt Parameters: Input/output, int *I, *J. On output, the values of I and J have been interchanged. */ { int k; k = *i; *i = *j; *j = k; return; } /******************************************************************************/ int i4_wrap ( int ival, int ilo, int ihi ) /******************************************************************************/ /* Purpose: I4_WRAP forces an I4 to lie between given limits by wrapping. Example: ILO = 4, IHI = 8 I Value -2 8 -1 4 0 5 1 6 2 7 3 8 4 4 5 5 6 6 7 7 8 8 9 4 10 5 11 6 12 7 13 8 14 4 Licensing: This code is distributed under the MIT license. Modified: 17 July 2008 Author: John Burkardt Parameters: Input, int IVAL, an integer value. Input, int ILO, IHI, the desired bounds for the integer value. Output, int I4_WRAP, a "wrapped" version of IVAL. */ { int jhi; int jlo; int value; int wide; jlo = i4_min ( ilo, ihi ); jhi = i4_max ( ilo, ihi ); wide = jhi + 1 - jlo; if ( wide == 1 ) { value = jlo; } else { value = jlo + i4_modp ( ival - jlo, wide ); } return value; } /******************************************************************************/ int i4col_compare ( int m, int n, int a[], int i, int j ) /******************************************************************************/ /* Purpose: I4COL_COMPARE compares columns I and J of an I4COL. Example: Input: M = 3, N = 4, I = 2, J = 4 A = ( 1 2 3 4 5 6 7 8 9 10 11 12 ) Output: I4COL_COMPARE = -1 Licensing: This code is distributed under the MIT license. Modified: 21 March 2009 Author: John Burkardt Parameters: Input, int M, N, the number of rows and columns. Input, int A[M*N], an array of N columns of vectors of length M. Input, int I, J, the columns to be compared. I and J must be between 1 and N. Output, int I4COL_COMPARE, the results of the comparison: -1, column I < column J, 0, column I = column J, +1, column J < column I. */ { int k; /* Check. */ if ( i < 1 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_COMPARE - Fatal error!\n" ); fprintf ( stderr, " Column index I = %d is less than 1.\n", i ); exit ( 1 ); } if ( n < i ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_COMPARE - Fatal error!\n" ); fprintf ( stderr, " N = %d is less than column index I = %d.\n", n, i ); exit ( 1 ); } if ( j < 1 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_COMPARE - Fatal error!\n" ); fprintf ( stderr, " Column index J = %d is less than 1.\n", j ); exit ( 1 ); } if ( n < j ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_COMPARE - Fatal error!\n" ); fprintf ( stderr, " N = %d is less than column index J = %d.\n", n, j ); exit ( 1 ); } if ( i == j ) { return 0; } k = 1; while ( k <= m ) { if ( a[k-1+(i-1)*m] < a[k-1+(j-1)*m] ) { return (-1); } else if ( a[k-1+(j-1)*m] < a[k-1+(i-1)*m] ) { return 1; } k = k + 1; } return 0; } /******************************************************************************/ void i4col_sort_a ( int m, int n, int a[] ) /******************************************************************************/ /* Purpose: I4COL_SORT_A ascending sorts the columns of an I4COL. Discussion: In lexicographic order, the statement "X < Y", applied to two vectors X and Y of length M, means that there is some index I, with 1 <= I <= M, with the property that X(J) = Y(J) for J < I, and X(I) < Y(I). In other words, X is less than Y if, at the first index where they differ, the X value is less than the Y value. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int M, the number of rows of A. Input, int N, the number of columns of A. Input/output, int A[M*N]. On input, the array of N columns of M vectors; On output, the columns of A have been sorted in ascending lexicographic order. */ { int i; int indx; int isgn; int j; /* Initialize. */ i = 0; indx = 0; isgn = 0; j = 0; /* Call the external heap sorter. */ for ( ; ; ) { sort_heap_external ( n, &indx, &i, &j, isgn ); /* Interchange the I and J objects. */ if ( 0 < indx ) { i4col_swap ( m, n, a, i, j ); } /* Compare the I and J objects. */ else if ( indx < 0 ) { isgn = i4col_compare ( m, n, a, i, j ); } else if ( indx == 0 ) { break; } } return; } /******************************************************************************/ int i4col_sorted_unique_count ( int m, int n, int a[] ) /******************************************************************************/ /* Purpose: I4COL_SORTED_UNIQUE_COUNT counts unique elements in an I4COL. Discussion: The columns of the array may be ascending or descending sorted. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int M, N, the number of rows and columns. Input, int A[M*N], a sorted array, containing N columns of data. Output, int I4COL_SORTED_UNIQUE_COUNT, the number of unique columns. */ { int i; int j1; int j2; int unique_num; if ( n <= 0 ) { unique_num = 0; return unique_num; } unique_num = 1; j1 = 0; for ( j2 = 1; j2 < n; j2++ ) { for ( i = 0; i < m; i++ ) { if ( a[i+j1*m] != a[i+j2*m] ) { unique_num = unique_num + 1; j1 = j2; break; } } } return unique_num; } /******************************************************************************/ void i4col_swap ( int m, int n, int a[], int icol1, int icol2 ) /******************************************************************************/ /* Purpose: I4COL_SWAP swaps two columns of an I4COL. Discussion: The two dimensional information is stored as a one dimensional array, by columns. The row indices are 1 based, NOT 0 based. However, a preprocessor variable, called OFFSET, can be reset from 1 to 0 if you wish to use 0-based indices. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int M, N, the number of rows and columns. Input/output, int A[M*N], an array of data. Input, int ICOL1, ICOL2, the two columns to swap. These indices should be between 1 and N. */ { # define OFFSET 1 int i; int t; /* Check. */ if ( icol1 - OFFSET < 0 || n-1 < icol1 - OFFSET ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_SWAP - Fatal error!\n" ); fprintf ( stderr, " ICOL1 is out of range.\n" ); exit ( 1 ); } if ( icol2 - OFFSET < 0 || n-1 < icol2 - OFFSET ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "I4COL_SWAP - Fatal error!\n" ); fprintf ( stderr, " ICOL2 is out of range.\n" ); exit ( 1 ); } if ( icol1 == icol2 ) { return; } for ( i = 0; i < m; i++ ) { t = a[i+(icol1-OFFSET)*m]; a[i+(icol1-OFFSET)*m] = a[i+(icol2-OFFSET)*m]; a[i+(icol2-OFFSET)*m] = t; } return; # undef OFFSET } /******************************************************************************/ void i4mat_transpose_print ( int m, int n, int a[], char *title ) /******************************************************************************/ /* Purpose: I4MAT_TRANSPOSE_PRINT prints an I4MAT, transposed. Discussion: An I4MAT is an MxN array of I4's, stored by (I,J) -> [I+J*M]. Licensing: This code is distributed under the MIT license. Modified: 31 January 2005 Author: John Burkardt Parameters: Input, int M, the number of rows in A. Input, int N, the number of columns in A. Input, int A[M*N], the M by N matrix. Input, char *TITLE, a title. */ { i4mat_transpose_print_some ( m, n, a, 1, 1, m, n, title ); return; } /******************************************************************************/ void i4mat_transpose_print_some ( int m, int n, int a[], int ilo, int jlo, int ihi, int jhi, char *title ) /******************************************************************************/ /* Purpose: I4MAT_TRANSPOSE_PRINT_SOME prints some of an I4MAT, transposed. Discussion: An I4MAT is an MxN array of I4's, stored by (I,J) -> [I+J*M]. Licensing: This code is distributed under the MIT license. Modified: 20 August 2010 Author: John Burkardt Parameters: Input, int M, the number of rows of the matrix. M must be positive. Input, int N, the number of columns of the matrix. N must be positive. Input, int A[M*N], the matrix. Input, int ILO, JLO, IHI, JHI, designate the first row and column, and the last row and column to be printed. Input, char *TITLE, a title. */ { # define INCX 10 int i; int i2hi; int i2lo; int j; int j2hi; int j2lo; fprintf ( stdout, "\n" ); fprintf ( stdout, "%s\n", title ); if ( m <= 0 || n <= 0 ) { fprintf ( stdout, "\n" ); fprintf ( stdout, " (None)\n" ); return; } /* Print the columns of the matrix, in strips of INCX. */ for ( i2lo = ilo; i2lo <= ihi; i2lo = i2lo + INCX ) { i2hi = i2lo + INCX - 1; i2hi = i4_min ( i2hi, m ); i2hi = i4_min ( i2hi, ihi ); fprintf ( stdout, "\n" ); /* For each row I in the current range... Write the header. */ fprintf ( stdout, " Row: " ); for ( i = i2lo; i <= i2hi; i++ ) { fprintf ( stdout, "%6d ", i - 1 ); } fprintf ( stdout, "\n" ); fprintf ( stdout, " Col\n" ); fprintf ( stdout, "\n" ); /* Determine the range of the rows in this strip. */ j2lo = i4_max ( jlo, 1 ); j2hi = i4_min ( jhi, n ); for ( j = j2lo; j <= j2hi; j++ ) { /* Print out (up to INCX) entries in column J, that lie in the current strip. */ fprintf ( stdout, "%5d: ", j - 1 ); for ( i = i2lo; i <= i2hi; i++ ) { fprintf ( stdout, "%6d ", a[i-1+(j-1)*m] ); } fprintf ( stdout, "\n" ); } } return; # undef INCX } /******************************************************************************/ void i4vec_heap_d ( int n, int a[] ) /******************************************************************************/ /* Purpose: I4VEC_HEAP_D reorders an I4VEC into a descending heap. Discussion: An I4VEC is a vector of I4's. A heap is an array A with the property that, for every index J, A[J] >= A[2*J+1] and A[J] >= A[2*J+2], (as long as the indices 2*J+1 and 2*J+2 are legal). Diagram: A(0) / \ A(1) A(2) / \ / \ A(3) A(4) A(5) A(6) / \ / \ A(7) A(8) A(9) A(10) Licensing: This code is distributed under the MIT license. Modified: 30 April 1999 Author: John Burkardt Reference: Albert Nijenhuis, Herbert Wilf, Combinatorial Algorithms, Academic Press, 1978, second edition, ISBN 0-12-519260-6. Parameters: Input, int N, the size of the input array. Input/output, int A[N]. On input, an unsorted array. On output, the array has been reordered into a heap. */ { int i; int ifree; int key; int m; /* Only nodes (N/2)-1 down to 0 can be "parent" nodes. */ for ( i = ( n / 2 ) - 1; 0 <= i; i-- ) { /* Copy the value out of the parent node. Position IFREE is now "open". */ key = a[i]; ifree = i; for ( ; ; ) { /* Positions 2*IFREE + 1 and 2*IFREE + 2 are the descendants of position IFREE. (One or both may not exist because they equal or exceed N.) */ m = 2 * ifree + 1; /* Does the first position exist? */ if ( n <= m ) { break; } else { /* Does the second position exist? */ if ( m + 1 < n ) { /* If both positions exist, take the larger of the two values, and update M if necessary. */ if ( a[m] < a[m+1] ) { m = m + 1; } } /* If the large descendant is larger than KEY, move it up, and update IFREE, the location of the free position, and consider the descendants of THIS position. */ if ( key < a[m] ) { a[ifree] = a[m]; ifree = m; } else { break; } } } /* When you have stopped shifting items up, return the item you pulled out back to the heap. */ a[ifree] = key; } return; } /******************************************************************************/ int *i4vec_indicator_new ( int n ) /******************************************************************************/ /* Purpose: I4VEC_INDICATOR_NEW sets an I4VEC to the indicator vector. Discussion: An I4VEC is a vector of I4's. Licensing: This code is distributed under the MIT license. Modified: 26 August 2008 Author: John Burkardt Parameters: Input, int N, the number of elements of A. Output, int I4VEC_INDICATOR_NEW[N], the array. */ { int *a; int i; a = ( int * ) malloc ( n * sizeof ( int ) ); for ( i = 0; i < n; i++ ) { a[i] = i + 1; } return a; } /******************************************************************************/ int i4vec_min ( int n, int a[] ) /******************************************************************************/ /* Purpose: I4VEC_MIN returns the minimum element in an I4VEC. Discussion: An I4VEC is a vector of I4's. Licensing: This code is distributed under the MIT license. Modified: 17 May 2003 Author: John Burkardt Parameters: Input, int N, the number of entries in the array. Input, int A[N], the array to be checked. Output, int I4VEC_MIN, the value of the minimum element. This is set to 0 if N <= 0. */ { int i; int value; if ( n <= 0 ) { return 0; } value = a[0]; for ( i = 1; i < n; i++ ) { if ( a[i] < value ) { value = a[i]; } } return value; } /******************************************************************************/ void i4vec_print ( int n, int a[], char *title ) /******************************************************************************/ /* Purpose: I4VEC_PRINT prints an I4VEC. Discussion: An I4VEC is a vector of I4's. Licensing: This code is distributed under the MIT license. Modified: 14 November 2003 Author: John Burkardt Parameters: Input, int N, the number of components of the vector. Input, int A[N], the vector to be printed. Input, char *TITLE, a title. */ { int i; fprintf ( stdout, "\n" ); fprintf ( stdout, "%s\n", title ); fprintf ( stdout, "\n" ); for ( i = 0; i < n; i++ ) { fprintf ( stdout, " %6d: %8d\n", i, a[i] ); } return; } /******************************************************************************/ void i4vec_reverse ( int n, int a[] ) /******************************************************************************/ /* Purpose: I4VEC_REVERSE reverses the elements of an I4VEC. Discussion: An I4VEC is a vector of I4's. Example: Input: N = 5, A = ( 11, 12, 13, 14, 15 ). Output: A = ( 15, 14, 13, 12, 11 ). Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of entries in the array. Input/output, int A[N], the array to be reversed. */ { int i; int j; for ( i = 0; i < n / 2; i++ ) { j = a[i]; a[i] = a[n-1-i]; a[n-1-i] = j; } return; } /******************************************************************************/ void i4vec_sort_heap_a ( int n, int a[] ) /******************************************************************************/ /* Purpose: I4VEC_SORT_HEAP_A ascending sorts an I4VEC using heap sort. Discussion: An I4VEC is a vector of I4's. Licensing: This code is distributed under the MIT license. Modified: 30 April 1999 Author: John Burkardt Reference: Albert Nijenhuis, Herbert Wilf, Combinatorial Algorithms, Academic Press, 1978, second edition, ISBN 0-12-519260-6. Parameters: Input, int N, the number of entries in the array. Input/output, int A[N]. On input, the array to be sorted; On output, the array has been sorted. */ { int n1; int temp; if ( n <= 1 ) { return; } /* 1: Put A into descending heap form. */ i4vec_heap_d ( n, a ); /* 2: Sort A. The largest object in the heap is in A[0]. Move it to position A[N-1]. */ temp = a[0]; a[0] = a[n-1]; a[n-1] = temp; /* Consider the diminished heap of size N1. */ for ( n1 = n-1; 2 <= n1; n1-- ) { /* Restore the heap structure of the initial N1 entries of A. */ i4vec_heap_d ( n1, a ); /* Take the largest object from A[0] and move it to A[N1-1]. */ temp = a[0]; a[0] = a[n1-1]; a[n1-1] = temp; } return; } /******************************************************************************/ int i4vec_sorted_unique ( int n, int a[] ) /******************************************************************************/ /* Purpose: I4VEC_SORTED_UNIQUE finds the unique elements in a sorted I4VEC. Discussion: An I4VEC is a vector of I4's. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of elements in A. Input/output, int A[N]. On input, the sorted integer array. On output, the unique elements in A. Output, int I4VEC_SORTED_UNIQUE, the number of unique elements in A. */ { int i; int unique_num; unique_num = 0; if ( n <= 0 ) { return unique_num; } unique_num = 1; for ( i = 1; i < n; i++ ) { if ( a[i] != a[unique_num-1] ) { unique_num = unique_num + 1; a[unique_num-1] = a[i]; } } return unique_num; } /******************************************************************************/ int i4vec2_compare ( int n, int a1[], int a2[], int i, int j ) /******************************************************************************/ /* Purpose: I4VEC2_COMPARE compares pairs of integers stored in two vectors. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of data items. Input, int A1[N], A2[N], contain the two components of each item. Input, int I, J, the items to be compared. These values will be 1-based indices for the arrays A1 and A2. Output, int I4VEC2_COMPARE, the results of the comparison: -1, item I < item J, 0, item I = item J, +1, item J < item I. */ { int isgn; isgn = 0; if ( a1[i-1] < a1[j-1] ) { isgn = -1; } else if ( a1[i-1] == a1[j-1] ) { if ( a2[i-1] < a2[j-1] ) { isgn = -1; } else if ( a2[i-1] < a2[j-1] ) { isgn = 0; } else if ( a2[j-1] < a2[i-1] ) { isgn = +1; } } else if ( a1[j-1] < a1[i-1] ) { isgn = +1; } return isgn; } /******************************************************************************/ void i4vec2_sort_a ( int n, int a1[], int a2[] ) /******************************************************************************/ /* Purpose: I4VEC2_SORT_A ascending sorts an I4VEC2. Discussion: Each item to be sorted is a pair of integers (I,J), with the I and J values stored in separate vectors A1 and A2. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of items of data. Input/output, int A1[N], A2[N], the data to be sorted.. */ { int i; int indx; int isgn; int j; int temp; /* Initialize. */ i = 0; indx = 0; isgn = 0; j = 0; /* Call the external heap sorter. */ for ( ; ; ) { sort_heap_external ( n, &indx, &i, &j, isgn ); /* Interchange the I and J objects. */ if ( 0 < indx ) { temp = a1[i-1]; a1[i-1] = a1[j-1]; a1[j-1] = temp; temp = a2[i-1]; a2[i-1] = a2[j-1]; a2[j-1] = temp; } /* Compare the I and J objects. */ else if ( indx < 0 ) { isgn = i4vec2_compare ( n, a1, a2, i, j ); } else if ( indx == 0 ) { break; } } return; } /******************************************************************************/ int i4vec2_sorted_unique ( int n, int a1[], int a2[] ) /******************************************************************************/ /* Purpose: I4VEC2_SORTED_UNIQUE keeps the unique elements in an I4VEC2. Discussion: Item I is stored as the pair A1(I), A2(I). The items must have been sorted, or at least it must be the case that equal items are stored in adjacent vector locations. If the items were not sorted, then this routine will only replace a string of equal values by a single representative. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of items. Input/output, int A1[N], A2[N]. On input, the array of N items. On output, an array of UNIQUE_NUM unique items. Output, int I4VEC2_SORTED_UNIQUE, the number of unique items. */ { int itest; int unique_num; unique_num = 0; if ( n <= 0 ) { return unique_num; } unique_num = 1; for ( itest = 1; itest < n; itest++ ) { if ( a1[itest] != a1[unique_num-1] || a2[itest] != a2[unique_num-1] ) { a1[unique_num] = a1[itest]; a2[unique_num] = a2[itest]; unique_num = unique_num + 1; } } return unique_num; } /******************************************************************************/ int lrline ( double xu, double yu, double xv1, double yv1, double xv2, double yv2, double dv ) /******************************************************************************/ /* Purpose: LRLINE determines where a point lies in relation to a directed line. Discussion: LRLINE determines whether a point is to the left of, right of, or on a directed line parallel to a line through given points. Licensing: This code is distributed under the MIT license. Modified: 28 August 2003 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, double XU, YU, XV1, YV1, XV2, YV2, are vertex coordinates; the directed line is parallel to and at signed distance DV to the left of the directed line from (XV1,YV1) to (XV2,YV2); (XU,YU) is the vertex for which the position relative to the directed line is to be determined. Input, double DV, the signed distance, positive for left. Output, int LRLINE, is +1, 0, or -1 depending on whether (XU,YU) is to the right of, on, or left of the directed line. LRLINE is 0 if the line degenerates to a point. */ { double dx; double dxu; double dy; double dyu; double t; double tol = 0.0000001; double tolabs; int value; dx = xv2 - xv1; dy = yv2 - yv1; dxu = xu - xv1; dyu = yu - yv1; tolabs = tol * fmax ( fabs ( dx ), fmax ( fabs ( dy ), fmax ( fabs ( dxu ), fmax ( fabs ( dyu ), fabs ( dv ) ) ) ) ); t = dy * dxu - dx * dyu + dv * sqrt ( dx * dx + dy * dy ); if ( tolabs < t ) { value = 1; } else if ( -tolabs <= t ) { value = 0; } else if ( t < -tolabs ) { value = -1; } return value; } /******************************************************************************/ void lvec_print ( int n, int a[], char *title ) /******************************************************************************/ /* Purpose: LVEC_PRINT prints a logical vector. Licensing: This code is distributed under the MIT license. Modified: 19 May 2010 Author: John Burkardt Parameters: Input, int N, the number of components of the vector. Input, int A[N], the vector to be printed. Input, char *TITLE, a title. */ { int i; fprintf ( stdout, "\n" ); fprintf ( stdout, "%s\n", title ); fprintf ( stdout, "\n" ); for ( i = 0; i <= n-1; i++ ) { fprintf ( stdout, "%6d: %1d\n", i, a[i] ); } return; } /******************************************************************************/ void mesh_base_one ( int node_num, int element_order, int element_num, int element_node[] ) /******************************************************************************/ /* Purpose: MESH_BASE_ONE ensures that the element definition is one-based. Discussion: The ELEMENT_NODE array contains nodes indices that form elements. The convention for node indexing might start at 0 or at 1. If this function detects 0-based indexing, it converts to 1-based. Licensing: This code is distributed under the MIT license. Modified: 18 October 2014 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int ELEMENT_ORDER, the order of the elements. Input, int ELEMENT_NUM, the number of elements. Input/output, int ELEMENT_NODE[ELEMENT_ORDER*ELEMENT_NUM], the element definitions. */ { int element; const int i4_huge = 2147483647; int node; int node_max; int node_min; int order; node_min = + i4_huge; node_max = - i4_huge; for ( element = 0; element < element_num; element++ ) { for ( order = 0; order < element_order; order++ ) { node = element_node[order+element*element_order]; if ( node < node_min ) { node_min = node; } if ( node_max < node ) { node_max = node; } } } if ( node_min == 0 && node_max == node_num - 1 ) { printf ( "\n" ); printf ( "MESH_BASE_ONE:\n" ); printf ( " The element indexing appears to be 0-based!\n" ); printf ( " This will be converted to 1-based.\n" ); for ( element = 0; element < element_num; element++ ) { for ( order = 0; order < element_order; order++ ) { element_node[order+element*element_order] = element_node[order+element*element_order] + 1; } } } else if ( node_min == 1 && node_max == node_num ) { printf ( "\n" ); printf ( "MESH_BASE_ONE:\n" ); printf ( " The element indexing appears to be 1-based!\n" ); printf ( " No conversion is necessary.\n" ); } else { printf ( "\n" ); printf ( "MESH_BASE_ONE - Warning!\n" ); printf ( " The element indexing is not of a recognized type.\n" ); printf ( " NODE_MIN = %d\n", node_min ); printf ( " NODE_MAX = %d\n", node_max ); printf ( " NODE_NUM = %d\n", node_num ); } return; } /******************************************************************************/ void mesh_base_zero ( int node_num, int element_order, int element_num, int element_node[] ) /******************************************************************************/ /* Purpose: MESH_BASE_ZERO ensures that the element definition is zero-based. Discussion: The ELEMENT_NODE array contains nodes indices that form elements. The convention for node indexing might start at 0 or at 1. Since a C program will naturally assume a 0-based indexing, it is necessary to check a given element definition and, if it is actually 1-based, to convert it. This function attempts to detect 1-based node indexing and correct it. Licensing: This code is distributed under the MIT license. Modified: 18 October 2014 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int ELEMENT_ORDER, the order of the elements. Input, int ELEMENT_NUM, the number of elements. Input/output, int ELEMENT_NODE[ELEMENT_ORDER*ELEMENT_NUM], the element definitions. */ { int element; const int i4_huge = 2147483647; int node; int node_max; int node_min; int order; node_min = + i4_huge; node_max = - i4_huge; for ( element = 0; element < element_num; element++ ) { for ( order = 0; order < element_order; order++ ) { node = element_node[order+element*element_order]; if ( node < node_min ) { node_min = node; } if ( node_max < node ) { node_max = node; } } } if ( node_min == 1 && node_max == node_num ) { printf ( "\n" ); printf ( "MESH_BASE_ZERO:\n" ); printf ( " The element indexing appears to be 1-based!\n" ); printf ( " This will be converted to 0-based.\n" ); for ( element = 0; element < element_num; element++ ) { for ( order = 0; order < element_order; order++ ) { element_node[order+element*element_order] = element_node[order+element*element_order] - 1; } } } else if ( node_min == 0 && node_max == node_num - 1 ) { printf ( "\n" ); printf ( "MESH_BASE_ZERO:\n" ); printf ( " The element indexing appears to be 0-based!\n" ); printf ( " No conversion is necessary.\n" ); } else { printf ( "\n" ); printf ( "MESH_BASE_ZERO - Warning!\n" ); printf ( " The element indexing is not of a recognized type.\n" ); printf ( " NODE_MIN = %d\n", node_min ); printf ( " NODE_MAX = %d\n", node_max ); printf ( " NODE_NUM = %d\n", node_num ); } return; } /******************************************************************************/ void node_merge ( int dim_num, int node_num, double node_xy[], double tolerance, int node_rep[] ) /******************************************************************************/ /* Purpose: NODE_MERGE detects nodes that should be merged. Discussion: Two nodes "should" be merged if they are within TOLERANCE distance of each other. With a tolerance of 0, only exactly equal nodes are counted. With a positive tolerance, a pair of nodes inside a circle of radius TOLERANCE result in a count of 1 duplicate. However, what do we do if nodes A, B and C are arranged in a line,! with A and B just within TOLERANCE of each other, and B and C just within tolerance of each other? What we do here is make a choice that can be defended consistently. A and B define an equivalence class because they are closer than TOLERANCE. C is then added to this equivalence class, because it is within TOLERANCE of at least on thing in that equivalence class. Thus, if 100 nodes are separated pairwise by slightly less than TOLERANCE, a total of 99 duplicates will be counted. The program starts out by giving each node its own label. If it finds that two nodes should be merged, then the index of one node is used as the label for both. This process continues until all nodes have been considered. The number of unique nodes is the number of unique values in the output quantity NODE_REP. Licensing: This code is distributed under the MIT license. Modified: 24 August 2006 Author: John Burkardt Parameters: Input, int DIM_NUM, the spatial dimension. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[DIM_NUM*NODE_NUM], the nodes. Input, double TOLERANCE, the maximum distance between two nodes regarded as duplicate. Output, int NODE_REP[NODE_NUM], the "representative" of each node. NODE_REP(NODE) is the index of a node which is within TOLERANCE of node NODE, or for which a chain of nodes can be found, all having the same representative, and all of which are pairwise closer than TOLERANCE. */ { double dist; int i; int j; int node1; int node2; int rep; double *rep_dist; rep_dist = ( double * ) malloc ( node_num * sizeof ( double ) ); for ( node1 = 0; node1 < node_num; node1++ ) { node_rep[node1] = node1; } for ( node1 = 0; node1 < node_num; node1++ ) { for ( j = 0; j < node_num; j++ ) { rep_dist[j] = HUGE_VAL; } for ( node2 = 0; node2 < node_num; node2++ ) { dist = 0.0; for ( i = 0; i < dim_num; i++ ) { dist = dist + pow ( node_xy[i+node1*dim_num] - node_xy[i+node2*dim_num], 2 ); } dist = sqrt ( dist ); rep = node_rep[node2]; if ( dist < rep_dist[rep] ) { rep_dist[rep] = dist; } } for ( node2 = 0; node2 < node_num; node2++ ) { rep = node_rep[node2]; if ( rep_dist[rep] <= tolerance ) { node_rep[node2] = node1; } } } free ( rep_dist ); return; } /******************************************************************************/ int ns_adj_col_set ( int node_num, int triangle_num, int variable_num, int triangle_node[], int triangle_neighbor[], int node_u_variable[], int node_v_variable[], int node_p_variable[], int adj_col[] ) /******************************************************************************/ /* Purpose: NS_ADJ_COL_SET sets the COL array in a Navier Stokes triangulation. Discussion: This routine also counts the the value and returns the value of ADJ_NUM, the number of Navier-Stokes variable adjacencies, which should be identical to the value that would have been computed by calling NS_ADJ_COUNT. This routine is called to set up the ADJ_COL array, which indicates the number of entries needed to store each column in the sparse compressed column storage to be used for the adjancency matrix. The triangulation is assumed to involve 6-node triangles. Variables for the horizontal and vertical velocities are associated with every node. Variables for the pressure are associated only with the vertex nodes. We are interested in determining the number of nonzero entries in the stiffness matrix of the Stokes equations, or the jacobian matrix of the Navier Stokes equations. To this end, we will say, somewhat too broadly, that two variables are "adjacent" if their associated nodes both occur in some common element. This adjacency of variables I and J is taken to be equivalent to the possible nonzeroness of matrix entries A(I,J) and A(J,I). A sparse compressed column format is used to store the counts for the nonzeroes. In other words, while the value ADJ_NUM reports the number of adjacencies, the vector ADJ_COL is sufficient to allow us to properly set up a sparse compressed matrix for the actual storage of the sparse matrix, if we desire to proceed. Local Node Numbering: 3 s |\ i | \ d | \ e 6 5 side 2 | \ 3 | \ | \ 1---4---2 side 1 Variable Diagram: UVP |\ | \ | \ UV UV | \ | \ | \ UVP--UV--UVP Licensing: This code is distributed under the MIT license. Modified: 26 September 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int VARIABLE_NUM, the number of variables. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int NODE_U_VARIABLE[NODE_NUM], NODE_V_VARIABLE[NODE_NUM], NODE_P_VARIABLE[NODE_NUM], the index of the horizontal velocity, vertical velocity and pressure variables associated with a node, or -1 if no such variable is associated with the node. Output, int ADJ_COL[VARIABLE_NUM+1]. Information about variable J is stored in entries ADJ_COL[J] through ADJ_COL[J+1]-1 of ADJ. Output, int NS_ADJ_COL_SET, the number of Navier Stokes variable adjacencies. */ { int adj_num; int n1; int n2; int n3; int n4; int n5; int n6; int node; int p1; int p2; int p3; int triangle; int triangle_order = 6; int triangle2; int u1; int u2; int u3; int u4; int u5; int u6; int v1; int v2; int v3; int v4; int v5; int v6; int variable; adj_num = 0; /* Set every variable to be adjacent to itself. */ for ( variable = 0; variable < variable_num; variable++ ) { adj_col[variable] = 1; } /* Set every variable to be adjacent to the other variables associated with that node. U <=> V U <=> P (if there is a P variable) V <=> P (if there is a P variable) */ for ( node = 0; node < node_num; node++ ) { u1 = node_u_variable[node] - 1; v1 = node_v_variable[node] - 1; p1 = node_p_variable[node] - 1; adj_col[u1] = adj_col[u1] + 1; adj_col[v1] = adj_col[v1] + 1 ; if ( 0 <= p1 ) { adj_col[u1] = adj_col[u1] + 1; adj_col[v1] = adj_col[v1] + 1; adj_col[p1] = adj_col[p1] + 2; } } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order] - 1; n2 = triangle_node[1+triangle*triangle_order] - 1; n3 = triangle_node[2+triangle*triangle_order] - 1; n4 = triangle_node[3+triangle*triangle_order] - 1; n5 = triangle_node[4+triangle*triangle_order] - 1; n6 = triangle_node[5+triangle*triangle_order] - 1; u1 = node_u_variable[n1] - 1; v1 = node_v_variable[n1] - 1; p1 = node_p_variable[n1] - 1; u2 = node_u_variable[n2] - 1; v2 = node_v_variable[n2] - 1; p2 = node_p_variable[n2] - 1; u3 = node_u_variable[n3] - 1; v3 = node_v_variable[n3] - 1; p3 = node_p_variable[n3] - 1; u4 = node_u_variable[n4] - 1; v4 = node_v_variable[n4] - 1; u5 = node_u_variable[n5] - 1; v5 = node_v_variable[n5] - 1; u6 = node_u_variable[n6] - 1; v6 = node_v_variable[n6] - 1; /* For sure, we add the new adjacencies: U5 V5 <=> U1 V1 P1 U6 V6 <=> U2 V2 P2 U4 V4 <=> U3 V3 P3 U5 V5 <=> U4 V4 U6 V6 <=> U4 V4 U6 V6 <=> U5 V5 */ adj_col[u1] = adj_col[u1] + 2; adj_col[v1] = adj_col[v1] + 2; adj_col[p1] = adj_col[p1] + 2; adj_col[u2] = adj_col[u2] + 2; adj_col[v2] = adj_col[v2] + 2; adj_col[p2] = adj_col[p2] + 2; adj_col[u3] = adj_col[u3] + 2; adj_col[v3] = adj_col[v3] + 2; adj_col[p3] = adj_col[p3] + 2; adj_col[u4] = adj_col[u4] + 7; adj_col[v4] = adj_col[v4] + 7; adj_col[u5] = adj_col[u5] + 7; adj_col[v5] = adj_col[v5] + 7; adj_col[u6] = adj_col[u6] + 7; adj_col[v6] = adj_col[v6] + 7; /* Add edges (1,2), (1,4), (2,4) if this is the first occurrence, that is, if the edge (1,4,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). Maybe add U1 V1 P1 <=> U2 V2 P2 U1 V1 P1 <=> U4 V4 U2 V2 P2 <=> U4 V4 */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[u1] = adj_col[u1] + 5; adj_col[v1] = adj_col[v1] + 5; adj_col[p1] = adj_col[p1] + 5; adj_col[u2] = adj_col[u2] + 5; adj_col[v2] = adj_col[v2] + 5; adj_col[p2] = adj_col[p2] + 5; adj_col[u4] = adj_col[u4] + 6; adj_col[v4] = adj_col[v4] + 6; } /* Maybe add U2 V2 P2 <=> U3 V3 P3 U2 V2 P2 <=> U5 V5 U3 V3 P3 <=> U5 V5 */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[u2] = adj_col[u2] + 5; adj_col[v2] = adj_col[v2] + 5; adj_col[p2] = adj_col[p2] + 5; adj_col[u3] = adj_col[u3] + 5; adj_col[v3] = adj_col[v3] + 5; adj_col[p3] = adj_col[p3] + 5; adj_col[u5] = adj_col[u5] + 6; adj_col[v5] = adj_col[v5] + 6; } /* Maybe add U1 V1 P1 <=> U3 V3 P3 U1 V1 P1 <=> U6 V6 U3 V3 P3 <=> U6 V6 */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[u1] = adj_col[u1] + 5; adj_col[v1] = adj_col[v1] + 5; adj_col[p1] = adj_col[p1] + 5; adj_col[u3] = adj_col[u3] + 5; adj_col[v3] = adj_col[v3] + 5; adj_col[p3] = adj_col[p3] + 5; adj_col[u6] = adj_col[u6] + 6; adj_col[v6] = adj_col[v6] + 6; } } /* We used ADJ_COL to count the number of entries in each column. Convert it to pointers into the ADJ array. */ for ( variable = variable_num; 0 < variable; variable-- ) { adj_col[variable] = adj_col[variable-1]; } adj_col[0] = 1; for ( variable = 1; variable <= variable_num; variable++ ) { adj_col[variable] = adj_col[variable-1] + adj_col[variable]; } adj_num = adj_col[variable_num] - 1; return adj_num; } /******************************************************************************/ int ns_adj_count ( int node_num, int triangle_num, int variable_num, int triangle_node[], int triangle_neighbor[], int node_u_variable[], int node_v_variable[], int node_p_variable[] ) /******************************************************************************/ /* Purpose: NS_ADJ_COUNT counts adjacencies in a Navier Stokes triangulation. Discussion: This routine is called to count the adjacencies, so that the appropriate amount of memory can be set aside for storage when the adjacency structure is created. The value of ADJ_NUM computed and returned by this routine should be identical to the value computed by NS_ADJ_COL_SET. The triangulation is assumed to involve 6-node triangles. Variables for the horizontal and vertical velocities are associated with every node. Variables for the pressure are associated only with the vertex nodes. We are interested in determining the number of nonzero entries in the stiffness matrix of the Stokes equations, or the jacobian matrix of the Navier Stokes equations. To this end, we will say, somewhat too broadly, that two variables are "adjacent" if their associated nodes both occur in some common element. This adjacency of variables I and J is taken to be equivalent to the possible nonzeroness of matrix entries A(I,J) and A(J,I). A sparse compressed column format is used to store the counts for the nonzeroes. In other words, while the value ADJ_NUM reports the number of adjacencies, the vector ADJ_COL is sufficient to allow us to properly set up a sparse compressed matrix for the actual storage of the sparse matrix, if we desire to proceed. Local Node Numbering: 3 s |\ i | \ d | \ e 6 5 side 2 | \ 3 | \ | \ 1---4---2 side 1 Variable Diagram: UVP |\ | \ | \ UV UV | \ | \ | \ UVP--UV--UVP Licensing: This code is distributed under the MIT license. Modified: 26 September 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int VARIABLE_NUM, the number of variables. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int NODE_U_VARIABLE[NODE_NUM], NODE_V_VARIABLE[NODE_NUM], NODE_P_VARIABLE[NODE_NUM], the index of the horizontal velocity, vertical velocity and pressure variables associated with a node, or -1 if no such variable is associated with the node. Output, int NS_ADJ_COUNT, the value of ADJ_NUM, the number of Navier Stokes variable adjacencies. */ { int adj_num; int node; int p1; int triangle; int triangle2; adj_num = 0; /* Set every variable to be adjacent to itself. */ adj_num = variable_num; /* Set every variable to be adjacent to the other variables associated with that node. U <=> V U <=> P (if there is a P variable) V <=> P (if there is a P variable) */ for ( node = 0; node < node_num; node++ ) { adj_num = adj_num + 2; p1 = node_p_variable[node] - 1; if ( 0 <= p1 ) { adj_num = adj_num + 4; } } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { /* For sure, we add the new adjacencies: U5 V5 <=> U1 V1 P1 U6 V6 <=> U2 V2 P2 U4 V4 <=> U3 V3 P3 U5 V5 <=> U4 V4 U6 V6 <=> U4 V4 U6 V6 <=> U5 V5 */ adj_num = adj_num + 60; /* Add edges (1,2), (1,4), (2,4) if this is the first occurrence, that is, if the edge (1,4,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). Maybe add U1 V1 P1 <=> U2 V2 P2 U1 V1 P1 <=> U4 V4 U2 V2 P2 <=> U4 V4 */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_num = adj_num + 42; } /* Maybe add U2 V2 P2 <=> U3 V3 P3 U2 V2 P2 <=> U5 V5 U3 V3 P3 <=> U5 V5 */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_num = adj_num + 42; } /* Maybe add U1 V1 P1 <=> U3 V3 P3 U1 V1 P1 <=> U6 V6 U3 V3 P3 <=> U6 V6 */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_num = adj_num + 42; } } return adj_num; } /******************************************************************************/ void ns_adj_insert ( int v1, int v2, int variable_num, int adj_num, int adj_col_free[], int adj_row[] ) /******************************************************************************/ /* Purpose: NS_ADJ_INSERT inserts an adjacency into a compressed column adjacency matrix. Licensing: This code is distributed under the MIT license. Modified: 23 September 2006 Author: John Burkardt Parameters: Input, int V1, V2, the indices of two items which are adjacent. Input, int VARIABLE_NUM, the number of items. Input, int ADJ_NUM, the number of entries available in ADJ_ROW. Input/output, int ADJ_COL_FREE[VARIABLE_NUM], contains the next free location in which an entry for a given column can be stored. On output, two pointers have been updated. Input/output, int ADJ_ROW[ADJ_NUM], the row indices of the Navier Stokes variable adjacency matrix. On output, two new entries have been added. */ { adj_row[adj_col_free[v1-1]-1] = v2; adj_col_free[v1-1] = adj_col_free[v1-1] + 1; if ( v1 == v2 ) { return; } adj_row[adj_col_free[v2-1]-1] = v1; adj_col_free[v2-1] = adj_col_free[v2-1] + 1; return; } /******************************************************************************/ void ns_adj_row_set ( int node_num, int triangle_num, int variable_num, int triangle_node[], int triangle_neighbor[], int node_u_variable[], int node_v_variable[], int node_p_variable[], int adj_num, int adj_col[], int adj_row[] ) /******************************************************************************/ /* Purpose: NS_ADJ_ROW_SET sets the Navier Stokes sparse compressed column row indices. Discussion: After NS_ADJ_COUNT has been called to count ADJ_NUM, the number of variable adjacencies and to set up ADJ_COL, the compressed column pointer, this routine can be called to assign values to ADJ_ROW, the row indices of the sparse compressed column adjacency matrix. Licensing: This code is distributed under the MIT license. Modified: 24 September 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int VARIABLE_NUM, the number of variables. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int NODE_U_VARIABLE[NODE_NUM], NODE_V_VARIABLE[NODE_NUM], NODE_P_VARIABLE[NODE_NUM], the index of the horizontal velocity, vertical velocity and pressure variables associated with a node, or -1 if no such variable is associated with the node. Input, int ADJ_NUM, the number of Navier Stokes variable adjacencies. Input, int ADJ_COL[VARIABLE_NUM+1]. Information about variable J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. Output, int ADJ_ROW[ADJ_NUM], the row indices of the Navier Stokes variable adjacency matrix. Local Parameters: Local, int ADJ_COL_FREE[VARIABLE_NUM], for each column, the location in ADJ_ROW which can store the next row index. */ { int *adj_col_free; int k1; int k2; int n1; int n2; int n3; int n4; int n5; int n6; int node; int p1; int p2; int p3; int triangle; int triangle2; int u1; int u2; int u3; int u4; int u5; int u6; int v; int v1; int v2; int v3; int v4; int v5; int v6; for ( v = 0; v < adj_num; v++ ) { adj_row[v] = -1; } adj_col_free = ( int * ) malloc ( variable_num * sizeof ( int ) ); for ( v = 0; v < variable_num; v++ ) { adj_col_free[v] = adj_col[v]; } /* Set every variable to be adjacent to itself. Here, we have to be careful to start at index 1. */ for ( v = 1; v <= variable_num; v++ ) { ns_adj_insert ( v, v, variable_num, adj_num, adj_col_free, adj_row ); } /* Set every variable to be adjacent to the other variables associated with that node. U <=> V U <=> P (if there is a P variable) V <=> P (if there is a P variable) */ for ( node = 0; node < node_num; node++ ) { u1 = node_u_variable[node]; v1 = node_v_variable[node]; p1 = node_p_variable[node]; ns_adj_insert ( u1, v1, variable_num, adj_num, adj_col_free, adj_row ); if ( 0 < p1 ) { ns_adj_insert ( u1, p1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, p1, variable_num, adj_num, adj_col_free, adj_row ); } } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*6]; n2 = triangle_node[1+triangle*6]; n3 = triangle_node[2+triangle*6]; n4 = triangle_node[3+triangle*6]; n5 = triangle_node[4+triangle*6]; n6 = triangle_node[5+triangle*6]; u1 = node_u_variable[n1-1]; v1 = node_v_variable[n1-1]; p1 = node_p_variable[n1-1]; u2 = node_u_variable[n2-1]; v2 = node_v_variable[n2-1]; p2 = node_p_variable[n2-1]; u3 = node_u_variable[n3-1]; v3 = node_v_variable[n3-1]; p3 = node_p_variable[n3-1]; u4 = node_u_variable[n4-1]; v4 = node_v_variable[n4-1]; u5 = node_u_variable[n5-1]; v5 = node_v_variable[n5-1]; u6 = node_u_variable[n6-1]; v6 = node_v_variable[n6-1]; /* For sure, we add the new adjacencies: U5 V5 <=> U1 V1 P1 U6 V6 <=> U2 V2 P2 U4 V4 <=> U3 V3 P3 U5 V5 <=> U4 V4 U6 V6 <=> U4 V4 U6 V6 <=> U5 V5 */ ns_adj_insert ( u5, u1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u5, v1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u5, p1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v5, u1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v5, v1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v5, p1, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, u2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, v2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, p2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, u2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, v2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, p2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u4, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u4, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u4, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v4, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v4, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v4, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u5, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u5, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v5, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v5, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u6, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v6, v5, variable_num, adj_num, adj_col_free, adj_row ); /* Add edges (1,2), (1,4), (2,4) if this is the first occurrence, that is, if the edge (1,4,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). Maybe add U1 V1 P1 <=> U2 V2 P2 U1 V1 P1 <=> U4 V4 U2 V2 P2 <=> U4 V4 */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ns_adj_insert ( u1, u2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, v2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, p2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, u2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, v2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, p2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, u2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, v2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, p2, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, v4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, u4, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, v4, variable_num, adj_num, adj_col_free, adj_row ); } /* Maybe add U2 V2 P2 <=> U3 V3 P3 U2 V2 P2 <=> U5 V5 U3 V3 P3 <=> U5 V5 */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ns_adj_insert ( u2, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u2, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v2, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p2, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u3, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u3, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v3, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v3, v5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p3, u5, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p3, v5, variable_num, adj_num, adj_col_free, adj_row ); } /* Maybe add U1 V1 P1 <=> U3 V3 P3 U1 V1 P1 <=> U6 V6 U3 V3 P3 <=> U6 V6 */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ns_adj_insert ( u1, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, u3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, v3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, p3, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u1, v6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v1, v6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p1, v6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u3, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( u3, v6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v3, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( v3, v6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p3, u6, variable_num, adj_num, adj_col_free, adj_row ); ns_adj_insert ( p3, v6, variable_num, adj_num, adj_col_free, adj_row ); } } /* Ascending sort the entries for each variable. */ for ( v = 0; v < variable_num; v++ ) { k1 = adj_col[v]; k2 = adj_col[v+1]-1; i4vec_sort_heap_a ( k2+1-k1, adj_row+k1-1 ); } return; } /******************************************************************************/ void q_measure ( int n, double z[], int triangle_order, int triangle_num, int triangle_node[], double *q_min, double *q_max, double *q_ave, double *q_area ) /******************************************************************************/ /* Purpose: Q_MEASURE determines the triangulated pointset quality measure Q. Discussion: The Q measure evaluates the uniformity of the shapes of the triangles defined by a triangulated pointset. For a single triangle T, the value of Q(T) is defined as follows: TAU_IN = radius of the inscribed circle, TAU_OUT = radius of the circumscribed circle, Q(T) = 2 * TAU_IN / TAU_OUT = ( B + C - A ) * ( C + A - B ) * ( A + B - C ) / ( A * B * C ) where A, B and C are the lengths of the sides of the triangle T. The Q measure computes the value of Q(T) for every triangle T in the triangulation, and then computes the minimum of this set of values: Q_MEASURE = min ( all T in triangulation ) Q(T) In an ideally regular mesh, all triangles would have the same equilateral shape, for which Q = 1. A good mesh would have 0.5 < Q. Given the 2D coordinates of a set of N nodes, stored as Z(1:2,1:N), a triangulation is a list of TRIANGLE_NUM triples of node indices that form triangles. Generally, a maximal triangulation is expected, namely, a triangulation whose image is a planar graph, but for which the addition of any new triangle would mean the graph was no longer planar. A Delaunay triangulation is a maximal triangulation which maximizes the minimum angle that occurs in any triangle. The code has been modified to 'allow' 6-node triangulations. However, no effort is made to actually process the midside nodes. Only information from the vertices is used. Licensing: This code is distributed under the MIT license. Modified: 21 June 2009 Author: John Burkardt Reference: Max Gunzburger and John Burkardt, Uniformity Measures for Point Samples in Hypercubes. Per-Olof Persson and Gilbert Strang, A Simple Mesh Generator in MATLAB, SIAM Review, Volume 46, Number 2, pages 329-345, June 2004. Parameters: Input, int N, the number of points. Input, double Z[2*N], the points. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the triangulation. Output, double *Q_MIN, *Q_MAX, the minimum and maximum values of Q over all triangles. Output, double *Q_AVE, the average value of Q. Output, double *Q_AREA, the average value of Q, weighted by the area of each triangle. */ { int a_index; double ab_length; double area; double area_total; int b_index; double bc_length; int c_index; double ca_length; double q; int triangle; double x1; double x2; double x3; double y1; double y2; double y3; *q_min = HUGE_VAL; *q_max = - HUGE_VAL; *q_ave = 0.0; *q_area = 0.0; area_total = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { a_index = triangle_node[0+triangle*triangle_order]; b_index = triangle_node[1+triangle*triangle_order]; c_index = triangle_node[2+triangle*triangle_order]; ab_length = sqrt ( pow ( z[0+(a_index-1)*2] - z[0+(b_index-1)*2], 2 ) + pow ( z[1+(a_index-1)*2] - z[1+(b_index-1)*2], 2 ) ); bc_length = sqrt ( pow ( z[0+(b_index-1)*2] - z[0+(c_index-1)*2], 2 ) + pow ( z[1+(b_index-1)*2] - z[1+(c_index-1)*2], 2 ) ); ca_length = sqrt ( pow ( z[0+(c_index-1)*2] - z[0+(a_index-1)*2], 2 ) + pow ( z[1+(c_index-1)*2] - z[1+(a_index-1)*2], 2 ) ); q = ( bc_length + ca_length - ab_length ) * ( ca_length + ab_length - bc_length ) * ( ab_length + bc_length - ca_length ) / ( ab_length * bc_length * ca_length ); x1 = z[0+(triangle_node[0+triangle*triangle_order]-1)*2]; y1 = z[1+(triangle_node[0+triangle*triangle_order]-1)*2]; x2 = z[0+(triangle_node[1+triangle*triangle_order]-1)*2]; y2 = z[1+(triangle_node[1+triangle*triangle_order]-1)*2]; x3 = z[0+(triangle_node[2+triangle*triangle_order]-1)*2]; y3 = z[1+(triangle_node[2+triangle*triangle_order]-1)*2]; area = 0.5 * fabs ( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ) ); *q_min = fmin ( *q_min, q ); *q_max = fmax ( *q_max, q ); *q_ave = *q_ave + q; *q_area = *q_area + q * area; area_total = area_total + area; } *q_ave = *q_ave / ( double ) ( triangle_num ); if ( 0.0 < area_total ) { *q_area = *q_area / area_total; } else { *q_area = 0.0; } return; } /******************************************************************************/ void quad_convex_random ( int *seed, double xy[] ) /******************************************************************************/ /* Purpose: QUAD_CONVEX_RANDOM returns a random convex quadrilateral. Description: The quadrilateral is constrained in that the vertices must all lie with the unit square. Licensing: This code is distributed under the MIT license. Modified: 26 June 2009 Author: John Burkardt Parameters: Input/output, int *SEED, a seed for the random number generator. Output, double XY[2*NODE_NUM], the coordinates of the nodes of the quadrilateral, given in counterclockwise order. */ { int hull[4]; int hull_num; int i; int j; double xy_random[2*4]; for ( ; ; ) { /* Generate 4 random points. */ r8mat_uniform_01 ( 2, 4, seed, xy_random ); /* Determine the convex hull. */ points_hull_2d ( 4, xy_random, &hull_num, hull ); /* If HULL_NUM < 4, then our convex hull is a triangle. Try again. */ if ( hull_num == 4 ) { break; } } /* Make an ordered copy of the random points. */ for ( j = 0; j < 4; j++ ) { for ( i = 0; i < 2; i++ ) { xy[i+j*2] = xy_random[i+(hull[j]-1)*2]; } } return; } /******************************************************************************/ int perm_check2 ( int n, int p[], int base ) /******************************************************************************/ /* Purpose: PERM_CHECK2 checks that a vector represents a permutation. Discussion: The routine verifies that each of the integers from 1 to N occurs among the N entries of the permutation. Set the input quantity BASE to 0, if P is a 0-based permutation, or to 1 if P is a 1-based permutation. Licensing: This code is distributed under the MIT license. Modified: 18 October 2012 Author: John Burkardt Parameters: Input, int N, the number of entries. Input, int P[N], the permutation, in standard index form. Input, int BASE, the index base. Output, int PERM_CHECK, is 1 if the array is NOT a permutation. */ { int error; int ifind; int iseek; error = 0; for ( iseek = base; iseek < base + n; iseek++ ) { error = 1; for ( ifind = 1; ifind <= n; ifind++ ) { if ( p[ifind-1] == iseek ) { error = 0; break; } } if ( error ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "PERM_CHECK2 - Fatal error!\n" ); fprintf ( stderr, " Could not find occurrence of value %d\n", iseek ); return 1; } } return 0; } /******************************************************************************/ void perm_inverse ( int n, int p[] ) /******************************************************************************/ /* Purpose: PERM_INVERSE inverts a permutation "in place". Licensing: This code is distributed under the MIT license. Modified: 28 March 2009 Author: John Burkardt Parameters: Input, int N, the number of objects being permuted. Input/output, int P[N], the permutation, in standard index form. On output, P describes the inverse permutation */ { int base; int error; int i; int i0; int i1; int i2; int is; int p_min; if ( n <= 0 ) { printf ( "\n" ); printf ( "PERM_INVERSE - Fatal error!\n" ); printf ( " Input value of N = %d\n", n ); exit ( 1 ); } /* Find the least value, and shift data so it begins at 1. */ p_min = i4vec_min ( n, p ); base = 1; for ( i = 0; i < n; i++ ) { p[i] = p[i] - p_min + base; } /* Check the permutation. */ error = perm_check2 ( n, p, base ); if ( error ) { printf ( "\n" ); printf ( "PERM_INVERSE - Fatal error!\n" ); printf ( " The input array does not represent\n" ); printf ( " a proper permutation.\n" ); exit ( 1 ); } is = 1; for ( i = 1; i <= n; i++ ) { i1 = p[i-1]; while ( i < i1 ) { i2 = p[i1-1]; p[i1-1] = -i2; i1 = i2; } is = - i4_sign ( p[i-1] ); p[i-1] = abs ( p[i-1] ) * i4_sign ( is ); } for ( i = 1; i <= n; i++ ) { i1 = -p[i-1]; if ( 0 <= i1 ) { i0 = i; for ( ; ; ) { i2 = p[i1-1]; p[i1-1] = i0; if ( i2 < 0 ) { break; } i0 = i1; i1 = i2; } } } /* Now we can restore the permutation. */ for ( i = 0; i < n; i++ ) { p[i] = p[i] + p_min - base; } return; } /******************************************************************************/ int *points_delaunay_naive_2d ( int node_num, double node_xy[], int *triangle_num ) /******************************************************************************/ /* Purpose: POINTS_DELAUNAY_NAIVE_2D computes the Delaunay triangulation in 2D. Discussion: A naive and inefficient (but extremely simple) method is used. This routine is only suitable as a demonstration code for small problems. Its running time is of order NODE_NUM^4. Much faster algorithms are available. Given a set of nodes in the plane, a triangulation is a set of triples of distinct nodes, forming triangles, so that every point with the convex hull of the set of nodes is either one of the nodes, or lies on an edge of one or more triangles, or lies within exactly one triangle. The number of nodes must be at least 3. Licensing: This code is distributed under the MIT license. Modified: 13 June 2005 Author: John Burkardt Reference: Joseph ORourke, Computational Geometry, Cambridge University Press, Second Edition, 1998, page 187. Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int *TRIANGLE_NUM, the number of triangles. Output, int POINTS_DELAUNAY_NAIVE_2D[3*TRIANGLE_NUM], the indices of the nodes making each triangle. */ { int count; int flag; int i; int j; int k; int m; int pass; int *tri; double xn; double yn; double zn; double *z; count = 0; z = ( double * ) malloc ( node_num * sizeof ( double ) ); for ( i = 0; i < node_num; i++ ) { z[i] = node_xy[0+i*2] * node_xy[0+i*2] + node_xy[1+i*2] * node_xy[1+i*2]; } /* First pass counts triangles, Second pass allocates triangles and sets them. */ for ( pass = 1; pass <= 2; pass++ ) { if ( pass == 2 ) { tri = ( int * ) malloc ( 3 * count * sizeof ( int ) ); } count = 0; /* For each triple (I,J,K): */ for ( i = 0; i < node_num - 2; i++ ) { for ( j = i+1; j < node_num; j++ ) { for ( k = i+1; k < node_num; k++ ) { if ( j != k ) { xn = ( node_xy[1+j*2] - node_xy[1+i*2] ) * ( z[k] - z[i] ) - ( node_xy[1+k*2] - node_xy[1+i*2] ) * ( z[j] - z[i] ); yn = ( node_xy[0+k*2] - node_xy[0+i*2] ) * ( z[j] - z[i] ) - ( node_xy[0+j*2] - node_xy[0+i*2] ) * ( z[k] - z[i] ); zn = ( node_xy[0+j*2] - node_xy[0+i*2] ) * ( node_xy[1+k*2] - node_xy[1+i*2] ) - ( node_xy[0+k*2] - node_xy[0+i*2] ) * ( node_xy[1+j*2] - node_xy[1+i*2] ); flag = ( zn < 0 ); if ( flag ) { for ( m = 0; m < node_num; m++ ) { flag = flag && ( ( node_xy[0+m*2] - node_xy[0+i*2] ) * xn + ( node_xy[1+m*2] - node_xy[1+i*2] ) * yn + ( z[m] - z[i] ) * zn <= 0 ); } } if ( flag ) { if ( pass == 2 ) { tri[0+count*3] = i + 1; tri[1+count*3] = j + 1; tri[2+count*3] = k + 1; } count = count + 1; } } } } } } *triangle_num = count; free ( z ); return tri; } /******************************************************************************/ void points_hull_2d ( int node_num, double node_xy[], int *hull_num, int hull[] ) /******************************************************************************/ /* Purpose: POINTS_HULL_2D computes the convex hull of a set of nodes in 2D. Discussion: The work involved is N*log(H), where N is the number of points, and H is the number of points that are on the hull. Licensing: This code is distributed under the MIT license. Modified: 20 May 2010 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int *HULL_NUM, the number of nodes that lie on the convex hull. Output, int HULL[NODE_NUM]. The first HULL_NUM entries contain the indices of the nodes that form the convex hull, in order. These indices are 1-based, not 0-based! */ { double angle; double angle_max; double di; double dr; int first; int i; double p_xy[2]; int q; double q_xy[2]; int r; double r_xy[2]; *hull_num = 0; if ( node_num < 1 ) { return; } /* If NODE_NUM = 1, the hull is the node. */ if ( node_num == 1 ) { hull[*hull_num] = 1; *hull_num = *hull_num + 1; return; } /* If NODE_NUM = 2, then the convex hull is either the two distinct nodes, or possibly a single (repeated) node. */ if ( node_num == 2 ) { hull[*hull_num] = 1; *hull_num = *hull_num + 1; if ( node_xy[0+0*2] != node_xy[0+1*2] || node_xy[1+0*2] != node_xy[1+1*2] ) { hull[*hull_num] = 2; *hull_num = *hull_num + 1; } return; } /* Find the leftmost point, and take the bottom-most in a tie. Call it "Q". */ q = 1; for ( i = 2; i <= node_num; i++ ) { if ( node_xy[0+(i-1)*2] < node_xy[0+(q-1)*2] || ( node_xy[0+(i-1)*2] == node_xy[0+(q-1)*2] && node_xy[1+(i-1)*2] < node_xy[1+(q-1)*2] ) ) { q = i; } } q_xy[0] = node_xy[0+(q-1)*2]; q_xy[1] = node_xy[1+(q-1)*2]; /* Remember the starting point. */ first = q; hull[*hull_num] = q; *hull_num = *hull_num + 1; /* For the first point, make a dummy previous point, 1 unit south, and call it "P". */ p_xy[0] = q_xy[0]; p_xy[1] = q_xy[1] - 1.0; /* Now, having old point P, and current point Q, find the new point R so the angle PQR is maximal. Watch out for the possibility that the two nodes are identical. */ for ( ; ; ) { r = 0; angle_max = 0.0; for ( i = 1; i <= node_num; i++ ) { if ( i != q && ( node_xy[0+(i-1)*2] != q_xy[0] || node_xy[1+(i-1)*2] != q_xy[1] ) ) { angle = angle_rad_2d ( p_xy, q_xy, node_xy+(i-1)*2 ); if ( r == 0 || angle_max < angle ) { r = i; r_xy[0] = node_xy[0+(r-1)*2]; r_xy[1] = node_xy[1+(r-1)*2]; angle_max = angle; } /* In case of ties, choose the nearer point. */ else if ( r != 0 && angle == angle_max ) { di = sqrt ( pow ( node_xy[0+(i-1)*2] - q_xy[0], 2 ) + pow ( node_xy[1+(i-1)*2] - q_xy[1], 2 ) ); dr = sqrt ( pow ( r_xy[0] - q_xy[0], 2 ) + pow ( r_xy[1] - q_xy[1], 2 ) ); if ( di < dr ) { r = i; r_xy[0] = node_xy[0+(r-1)*2]; r_xy[1] = node_xy[1+(r-1)*2]; angle_max = angle; } } } } /* If we've returned to our starting node, exit. */ if ( r == first ) { break; } if ( node_num < *hull_num + 1 ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "POINTS_HULL_2D - Fatal error!\n" ); fprintf ( stderr, " The algorithm failed.\n" ); exit ( 1 ); } /* Add point R to the convex hull. */ hull[*hull_num] = r; *hull_num = *hull_num + 1; /* Set Q := P, P := R, and repeat. */ q = r; p_xy[0] = q_xy[0]; p_xy[1] = q_xy[1]; q_xy[0] = r_xy[0]; q_xy[1] = r_xy[1]; } return; } /******************************************************************************/ int points_point_near_naive_nd ( int dim_num, int nset, double pset[], double ptest[], double *d_min ) /******************************************************************************/ /* Purpose: POINTS_POINT_NEAR_NAIVE_ND finds the nearest point to a given point in ND. Discussion: A naive algorithm is used. The distance to every point is calculated, in order to determine the smallest. Licensing: This code is distributed under the MIT license. Modified: 20 May 2010 Author: John Burkardt Parameters: Input, int NSET, the number of points in the set. Input, double PSET[DIM_NUM*NSET], the coordinates of the points in the set. Input, double PTEST[DIM_NUM], the point whose nearest neighbor is sought. Output, double *D_MIN, the distance between P and PSET(*,I_MIN). Output, int POINTS_POINT_NEAR_NAIVE_ND, the index of the nearest point in PSET to P. */ { double d; int i; int j; int p_min; *d_min = HUGE_VAL; p_min = 0; for ( j = 0; j < nset; j++ ) { d = 0.0; for ( i = 0; i < dim_num; i++ ) { d = d + pow ( ptest[i] - pset[i+j*dim_num], 2 ); } if ( d < *d_min ) { *d_min = d; p_min = j; } } *d_min = sqrt ( *d_min ); return p_min; } /******************************************************************************/ double r8_acos ( double c ) /******************************************************************************/ /* Purpose: R8_ACOS computes the arc cosine function, with argument truncation. Discussion: If you call your system ACOS routine with an input argument that is outside the range [-1.0, 1.0 ], you may get an unpleasant surprise. This routine truncates arguments outside the range. Licensing: This code is distributed under the MIT license. Modified: 13 June 2002 Author: John Burkardt Parameters: Input, double C, the argument, the cosine of an angle. Output, double R8_ACOS, an angle whose cosine is C. */ { double angle; double pi = 3.141592653589793; if ( c <= -1.0 ) { angle = pi; } else if ( 1.0 <= c ) { angle = 0.0; } else { angle = acos ( c ); } return angle; } /******************************************************************************/ int r8_nint ( double x ) /******************************************************************************/ /* Purpose: R8_NINT returns the nearest integer to an R8. Example: X R8_NINT 1.3 1 1.4 1 1.5 1 or 2 1.6 2 0.0 0 -0.7 -1 -1.1 -1 -1.6 -2 Licensing: This code is distributed under the MIT license. Modified: 05 May 2006 Author: John Burkardt Parameters: Input, double X, the value. Output, int R8_NINT, the nearest integer to X. */ { int s; int value; if ( x < 0.0 ) { s = - 1; } else { s = + 1; } value = s * ( int ) ( fabs ( x ) + 0.5 ); return value; } /******************************************************************************/ double r8_uniform_01 ( int *seed ) /******************************************************************************/ /* Purpose: R8_UNIFORM_01 returns a unit pseudorandom R8. Discussion: This routine implements the recursion seed = 16807 * seed mod ( 2^31 - 1 ) r8_uniform_01 = seed / ( 2^31 - 1 ) The integer arithmetic never requires more than 32 bits, including a sign bit. If the initial seed is 12345, then the first three computations are Input Output R8_UNIFORM_01 SEED SEED 12345 207482415 0.096616 207482415 1790989824 0.833995 1790989824 2035175616 0.947702 Licensing: This code is distributed under the MIT license. Modified: 11 August 2004 Author: John Burkardt Reference: Paul Bratley, Bennett Fox, Linus Schrage, A Guide to Simulation, Springer Verlag, pages 201-202, 1983. Pierre L'Ecuyer, Random Number Generation, in Handbook of Simulation edited by Jerry Banks, Wiley Interscience, page 95, 1998. Bennett Fox, Algorithm 647: Implementation and Relative Efficiency of Quasirandom Sequence Generators, ACM Transactions on Mathematical Software, Volume 12, Number 4, pages 362-376, 1986. P A Lewis, A S Goodman, J M Miller, A Pseudo-Random Number Generator for the System/360, IBM Systems Journal, Volume 8, pages 136-143, 1969. Parameters: Input/output, int *SEED, the "seed" value. Normally, this value should not be 0. On output, SEED has been updated. Output, double R8_UNIFORM_01, a new pseudorandom variate, strictly between 0 and 1. */ { int k; double r; k = *seed / 127773; *seed = 16807 * ( *seed - k * 127773 ) - k * 2836; if ( *seed < 0 ) { *seed = *seed + 2147483647; } r = ( ( double ) ( *seed ) ) * 4.656612875E-10; return r; } /******************************************************************************/ void r82vec_permute ( int n, int p[], int base, double a[] ) /******************************************************************************/ /* Purpose: R82VEC_PERMUTE permutes an R82VEC in place. Discussion: An R82VEC is a vector whose entries are R82's. An R82 is a vector of R8's with two entries. An R82VEC may be stored as a 2 by N array. This routine permutes an array of real "objects", but the same logic can be used to permute an array of objects of any arithmetic type, or an array of objects of any complexity. The only temporary storage required is enough to store a single object. The number of data movements made is N + the number of cycles of order 2 or more, which is never more than N + N/2. Example: Input: N = 5 P = ( 2, 4, 5, 1, 3 ) A = ( 1.0, 2.0, 3.0, 4.0, 5.0 ) (11.0, 22.0, 33.0, 44.0, 55.0 ) Output: A = ( 2.0, 4.0, 5.0, 1.0, 3.0 ) ( 22.0, 44.0, 55.0, 11.0, 33.0 ). Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of objects. Input, int P[N], the permutation. P(I) = J means that the I-th element of the output array should be the J-th element of the input array. Input, int BASE, is 0 for a 0-based permutation and 1 for a 1-based permutation. Input/output, double A[2*N], the array to be permuted. */ { double a_temp[2]; int i; int iget; int iput; int istart; if ( perm_check2 ( n, p, base ) ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "R82VEC_PERMUTE - Fatal error!\n" ); fprintf ( stderr, " PERM_CHECK2 rejects this permutation.\n" ); exit ( 1 ); } /* In order for the sign negation trick to work, we need to assume that the entries of P are strictly positive. Presumably, the lowest number is BASE. So temporarily add 1-BASE to each entry to force positivity. */ for ( i = 0; i < n; i++ ) { p[i] = p[i] + 1 - base; } /* Search for the next element of the permutation that has not been used. */ for ( istart = 1; istart <= n; istart++ ) { if ( p[istart-1] < 0 ) { continue; } else if ( p[istart-1] == istart ) { p[istart-1] = - p[istart-1]; continue; } else { a_temp[0] = a[0+(istart-1)*2]; a_temp[1] = a[1+(istart-1)*2]; iget = istart; /* Copy the new value into the vacated entry. */ for ( ; ; ) { iput = iget; iget = p[iget-1]; p[iput-1] = - p[iput-1]; if ( iget < 1 || n < iget ) { fprintf ( stderr, "\n" ); fprintf ( stderr, "R82VEC_PERMUTE - Fatal error!\n" ); fprintf ( stderr, " Entry IPUT = %d of the permutation has\n", iput ); fprintf ( stderr, " an illegal value IGET = %d.\n", iget ); exit ( 1 ); } if ( iget == istart ) { a[0+(iput-1)*2] = a_temp[0]; a[1+(iput-1)*2] = a_temp[1]; break; } a[0+(iput-1)*2] = a[0+(iget-1)*2]; a[1+(iput-1)*2] = a[1+(iget-1)*2]; } } } /* Restore the signs of the entries. */ for ( i = 0; i < n; i++ ) { p[i] = - p[i]; } /* Restore the base of the entries. */ for ( i = 0; i < n; i++ ) { p[i] = p[i] - 1 + base; } return; } /******************************************************************************/ int *r82vec_sort_heap_index_a ( int n, int base, double a[] ) /******************************************************************************/ /* Purpose: R82VEC_SORT_HEAP_INDEX_A does an indexed heap ascending sort of an R82VEC. Discussion: An R82VEC is a vector whose entries are R82's. An R82 is a vector of R8's with two entries. An R82VEC may be stored as a 2 by N array. The sorting is not actually carried out. Rather an index array is created which defines the sorting. This array may be used to sort or index the array, or to sort or index related arrays keyed on the original array. Once the index array is computed, the sorting can be carried out "implicitly: a(*,indx(*)) or explicitly, by the call r82vec_permute ( n, indx, base, a ) after which a(*,*) is sorted. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Parameters: Input, int N, the number of entries in the array. Input, int BASE, the desired indexing for the sort index: 0 for 0-based indexing, 1 for 1-based indexing. Input, double A[2*N], an array to be index-sorted. Output, int R82VEC_SORT_HEAP_INDEX_A[N], the sort index. The I-th element of the sorted array is A(0:1,R82VEC_SORT_HEAP_INDEX_A(I)). */ { double aval[2]; int i; int *indx; int indxt; int ir; int j; int l; if ( n < 1 ) { return NULL; } indx = ( int * ) malloc ( n * sizeof ( int ) ); for ( i = 0; i < n; i++ ) { indx[i] = i; } if ( n == 1 ) { indx[0] = indx[0] + base; return indx; } l = n / 2 + 1; ir = n; for ( ; ; ) { if ( 1 < l ) { l = l - 1; indxt = indx[l-1]; aval[0] = a[0+indxt*2]; aval[1] = a[1+indxt*2]; } else { indxt = indx[ir-1]; aval[0] = a[0+indxt*2]; aval[1] = a[1+indxt*2]; indx[ir-1] = indx[0]; ir = ir - 1; if ( ir == 1 ) { indx[0] = indxt; break; } } i = l; j = l + l; while ( j <= ir ) { if ( j < ir ) { if ( a[0+indx[j-1]*2] < a[0+indx[j]*2] || ( a[0+indx[j-1]*2] == a[0+indx[j]*2] && a[1+indx[j-1]*2] < a[1+indx[j]*2] ) ) { j = j + 1; } } if ( aval[0] < a[0+indx[j-1]*2] || ( aval[0] == a[0+indx[j-1]*2] && aval[1] < a[1+indx[j-1]*2] ) ) { indx[i-1] = indx[j-1]; i = j; j = j + j; } else { j = ir + 1; } } indx[i-1] = indxt; } /* Take care of the base. */ for ( i = 0; i < n; i++ ) { indx[i] = indx[i] + base; } return indx; } /******************************************************************************/ void r8mat_print ( int m, int n, double a[], char *title ) /******************************************************************************/ /* Purpose: R8MAT_PRINT prints an R8MAT. Discussion: An R8MAT is a doubly dimensioned array of R8's, which may be stored as a vector in column-major order. Entry A(I,J) is stored as A[I+J*M] Licensing: This code is distributed under the MIT license. Modified: 28 May 2008 Author: John Burkardt Parameters: Input, int M, the number of rows in A. Input, int N, the number of columns in A. Input, double A[M*N], the M by N matrix. Input, char *TITLE, a title. */ { r8mat_print_some ( m, n, a, 1, 1, m, n, title ); return; } /******************************************************************************/ void r8mat_print_some ( int m, int n, double a[], int ilo, int jlo, int ihi, int jhi, char *title ) /******************************************************************************/ /* Purpose: R8MAT_PRINT_SOME prints some of an R8MAT. Discussion: An R8MAT is a doubly dimensioned array of R8's, which may be stored as a vector in column-major order. Licensing: This code is distributed under the MIT license. Modified: 20 August 2010 Author: John Burkardt Parameters: Input, int M, the number of rows of the matrix. M must be positive. Input, int N, the number of columns of the matrix. N must be positive. Input, double A[M*N], the matrix. Input, int ILO, JLO, IHI, JHI, designate the first row and column, and the last row and column to be printed. Input, char *TITLE, a title. */ { # define INCX 5 int i; int i2hi; int i2lo; int j; int j2hi; int j2lo; fprintf ( stdout, "\n" ); fprintf ( stdout, "%s\n", title ); if ( m <= 0 || n <= 0 ) { fprintf ( stdout, "\n" ); fprintf ( stdout, " (None)\n" ); return; } /* Print the columns of the matrix, in strips of 5. */ for ( j2lo = jlo; j2lo <= jhi; j2lo = j2lo + INCX ) { j2hi = j2lo + INCX - 1; j2hi = i4_min ( j2hi, n ); j2hi = i4_min ( j2hi, jhi ); fprintf ( stdout, "\n" ); /* For each column J in the current range... Write the header. */ fprintf ( stdout, " Col: "); for ( j = j2lo; j <= j2hi; j++ ) { fprintf ( stdout, " %7d ", j - 1 ); } fprintf ( stdout, "\n" ); fprintf ( stdout, " Row\n" ); fprintf ( stdout, "\n" ); /* Determine the range of the rows in this strip. */ i2lo = i4_max ( ilo, 1 ); i2hi = i4_min ( ihi, m ); for ( i = i2lo; i <= i2hi; i++ ) { /* Print out (up to) 5 entries in row I, that lie in the current strip. */ fprintf ( stdout, "%5d:", i - 1 ); for ( j = j2lo; j <= j2hi; j++ ) { fprintf ( stdout, " %14f", a[i-1+(j-1)*m] ); } fprintf ( stdout, "\n" ); } } return; # undef INCX } /******************************************************************************/ void r8mat_transpose_print ( int m, int n, double a[], char *title ) /******************************************************************************/ /* Purpose: R8MAT_TRANSPOSE_PRINT prints an R8MAT, transposed. Licensing: This code is distributed under the MIT license. Modified: 28 May 2008 Author: John Burkardt Parameters: Input, int M, N, the number of rows and columns. Input, double A[M*N], an M by N matrix to be printed. Input, char *TITLE, a title. */ { r8mat_transpose_print_some ( m, n, a, 1, 1, m, n, title ); return; } /******************************************************************************/ void r8mat_transpose_print_some ( int m, int n, double a[], int ilo, int jlo, int ihi, int jhi, char *title ) /******************************************************************************/ /* Purpose: R8MAT_TRANSPOSE_PRINT_SOME prints some of an R8MAT, transposed. Licensing: This code is distributed under the MIT license. Modified: 20 August 2010 Author: John Burkardt Parameters: Input, int M, N, the number of rows and columns. Input, double A[M*N], an M by N matrix to be printed. Input, int ILO, JLO, the first row and column to print. Input, int IHI, JHI, the last row and column to print. Input, char *TITLE, a title. */ { # define INCX 5 int i; int i2; int i2hi; int i2lo; int inc; int j; int j2hi; int j2lo; fprintf ( stdout, "\n" ); fprintf ( stdout, "%s\n", title ); if ( m <= 0 || n <= 0 ) { fprintf ( stdout, "\n" ); fprintf ( stdout, " (None)\n" ); return; } for ( i2lo = i4_max ( ilo, 1 ); i2lo <= i4_min ( ihi, m ); i2lo = i2lo + INCX ) { i2hi = i2lo + INCX - 1; i2hi = i4_min ( i2hi, m ); i2hi = i4_min ( i2hi, ihi ); inc = i2hi + 1 - i2lo; fprintf ( stdout, "\n" ); fprintf ( stdout, " Row:" ); for ( i = i2lo; i <= i2hi; i++ ) { fprintf ( stdout, " %7d ", i - 1 ); } fprintf ( stdout, "\n" ); fprintf ( stdout, " Col\n" ); fprintf ( stdout, "\n" ); j2lo = i4_max ( jlo, 1 ); j2hi = i4_min ( jhi, n ); for ( j = j2lo; j <= j2hi; j++ ) { fprintf ( stdout, "%5d:", j - 1 ); for ( i2 = 1; i2 <= inc; i2++ ) { i = i2lo - 1 + i2; fprintf ( stdout, " %14f", a[(i-1)+(j-1)*m] ); } fprintf ( stdout, "\n" ); } } return; # undef INCX } /******************************************************************************/ void r8mat_uniform_01 ( int m, int n, int *seed, double r[] ) /******************************************************************************/ /* Purpose: R8MAT_UNIFORM_01 fills an R8MAT with unit pseudorandom values. Discussion: An R8MAT is a doubly dimensioned array of R8 values, stored as a vector in column-major order. This routine implements the recursion seed = 16807 * seed mod ( 2^31 - 1 ) unif = seed / ( 2^31 - 1 ) The integer arithmetic never requires more than 32 bits, including a sign bit. Licensing: This code is distributed under the MIT license. Modified: 30 June 2009 Author: John Burkardt Reference: Paul Bratley, Bennett Fox, Linus Schrage, A Guide to Simulation, Springer Verlag, pages 201-202, 1983. Bennett Fox, Algorithm 647: Implementation and Relative Efficiency of Quasirandom Sequence Generators, ACM Transactions on Mathematical Software, Volume 12, Number 4, pages 362-376, 1986. Philip Lewis, Allen Goodman, James Miller, A Pseudo-Random Number Generator for the System/360, IBM Systems Journal, Volume 8, pages 136-143, 1969. Parameters: Input, int M, N, the number of rows and columns. Input/output, int *SEED, the "seed" value. Normally, this value should not be 0, otherwise the output value of SEED will still be 0, and R8_UNIFORM will be 0. On output, SEED has been updated. Output, double R[M*N], a matrix of pseudorandom values. */ { int i; int j; int k; for ( j = 0; j < n; j++ ) { for ( i = 0; i < m; i++ ) { k = *seed / 127773; *seed = 16807 * ( *seed - k * 127773 ) - k * 2836; if ( *seed < 0 ) { *seed = *seed + 2147483647; } r[i+j*m] = ( double ) ( *seed ) * 4.656612875E-10; } } return; } /******************************************************************************/ int r8tris2 ( int node_num, double node_xy[], int *triangle_num, int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: R8TRIS2 constructs a Delaunay triangulation of 2D vertices. Discussion: The routine constructs the Delaunay triangulation of a set of 2D vertices using an incremental approach and diagonal edge swaps. Vertices are first sorted in lexicographically increasing (X,Y) order, and then are inserted one at a time from outside the convex hull. Licensing: This code is distributed under the MIT license. Modified: 15 January 2004 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, int NODE_NUM, the number of nodes. Input/output, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. On output, the coordinates have been sorted into dictionary order. Output, int *TRIANGLE_NUM, the number of triangles in the triangulation; TRIANGLE_NUM is equal to 2*node_num - NB - 2, where NB is the number of boundary vertices. Output, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up each triangle. The elements are indices of NODE_XY. The vertices of the triangles are in counterclockwise order. Output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbor list. Positive elements are indices of TIL; negative elements are used for links of a counter clockwise linked list of boundary edges; LINK = -(3*I + J-1) where I, J = triangle, edge index; TRIANGLE_NEIGHBOR[I,J] refers to the neighbor along edge from vertex J to J+1 (mod 3). Output, int R8TRIS2, is 0 for no error. */ { int base; double cmax; int e; int error; int i; int *indx; int j; int k; int l; int ledg; int lr; int ltri; int m; int m1; int m2; int n; int redg; int rtri; int *stack; int t; double tol; int top; stack = ( int * ) malloc ( node_num * sizeof ( int ) ); tol = 100.0 * DBL_EPSILON; /* Sort the vertices by increasing (x,y). */ base = 0; indx = r82vec_sort_heap_index_a ( node_num, base, node_xy ); r82vec_permute ( node_num, indx, base, node_xy ); /* Make sure that the nodes are "reasonably" distinct. */ m1 = 1; for ( i = 2; i <= node_num; i++ ) { m = m1; m1 = i; k = -1; for ( j = 0; j <= 1; j++ ) { cmax = fmax ( fabs ( node_xy[2*(m-1)+j] ), fabs ( node_xy[2*(m1-1)+j] ) ); if ( tol * ( cmax + 1.0 ) < fabs ( node_xy[2*(m-1)+j] - node_xy[2*(m1-1)+j] ) ) { k = j; break; } } if ( k == -1 ) { printf ( "\n" ); printf ( "R8TRIS2 - Fatal error!\n" ); printf ( " Fails for point number I = %d\n", i ); printf ( " M = %d\n", m ); printf ( " M1 = %d\n", m1 ); printf ( " X,Y(M) = %g %g\n", node_xy[2*(m-1)+0], node_xy[2*(m-1)+1] ); printf ( " X,Y(M1) = %g %g\n", node_xy[2*(m1-1)+0], node_xy[2*(m1-1)+1] ); exit ( 1 ); } } /* Starting from nodes M1 and M2, search for a third point M that makes a "healthy" triangle (M1,M2,M) */ m1 = 1; m2 = 2; j = 3; for ( ; ; ) { if ( node_num < j ) { printf ( "\n" ); printf ( "R8TRIS2 - Fatal error!\n" ); free ( stack ); return 225; } m = j; lr = lrline ( node_xy[2*(m-1)+0], node_xy[2*(m-1)+1], node_xy[2*(m1-1)+0], node_xy[2*(m1-1)+1], node_xy[2*(m2-1)+0], node_xy[2*(m2-1)+1], 0.0 ); if ( lr != 0 ) { break; } j = j + 1; } /* Set up the triangle information for (M1,M2,M), and for any other triangles you created because nodes were collinear with M1, M2. */ *triangle_num = j - 2; if ( lr == -1 ) { triangle_node[3*0+0] = m1; triangle_node[3*0+1] = m2; triangle_node[3*0+2] = m; triangle_neighbor[3*0+2] = -3; for ( i = 2; i <= *triangle_num; i++ ) { m1 = m2; m2 = i+1; triangle_node[3*(i-1)+0] = m1; triangle_node[3*(i-1)+1] = m2; triangle_node[3*(i-1)+2] = m; triangle_neighbor[3*(i-1)+0] = -3 * i; triangle_neighbor[3*(i-1)+1] = i; triangle_neighbor[3*(i-1)+2] = i - 1; } triangle_neighbor[3*(*triangle_num-1)+0] = -3 * (*triangle_num) - 1; triangle_neighbor[3*(*triangle_num-1)+1] = -5; ledg = 2; ltri = *triangle_num; } else { triangle_node[3*0+0] = m2; triangle_node[3*0+1] = m1; triangle_node[3*0+2] = m; triangle_neighbor[3*0+0] = -4; for ( i = 2; i <= *triangle_num; i++ ) { m1 = m2; m2 = i+1; triangle_node[3*(i-1)+0] = m2; triangle_node[3*(i-1)+1] = m1; triangle_node[3*(i-1)+2] = m; triangle_neighbor[3*(i-2)+2] = i; triangle_neighbor[3*(i-1)+0] = -3 * i - 3; triangle_neighbor[3*(i-1)+1] = i - 1; } triangle_neighbor[3*(*triangle_num-1)+2] = -3 * (*triangle_num); triangle_neighbor[3*0+1] = -3 * (*triangle_num) - 2; ledg = 2; ltri = 1; } /* Insert the vertices one at a time from outside the convex hull, determine visible boundary edges, and apply diagonal edge swaps until Delaunay triangulation of vertices (so far) is obtained. */ top = 0; for ( i = j+1; i <= node_num; i++ ) { m = i; m1 = triangle_node[3*(ltri-1)+ledg-1]; if ( ledg <= 2 ) { m2 = triangle_node[3*(ltri-1)+ledg]; } else { m2 = triangle_node[3*(ltri-1)+0]; } lr = lrline ( node_xy[2*(m-1)+0], node_xy[2*(m-1)+1], node_xy[2*(m1-1)+0], node_xy[2*(m1-1)+1], node_xy[2*(m2-1)+0], node_xy[2*(m2-1)+1], 0.0 ); if ( 0 < lr ) { rtri = ltri; redg = ledg; ltri = 0; } else { l = -triangle_neighbor[3*(ltri-1)+ledg-1]; rtri = l / 3; redg = (l % 3) + 1; } vbedg ( node_xy[2*(m-1)+0], node_xy[2*(m-1)+1], node_num, node_xy, *triangle_num, triangle_node, triangle_neighbor, <ri, &ledg, &rtri, &redg ); n = *triangle_num + 1; l = -triangle_neighbor[3*(ltri-1)+ledg-1]; for ( ; ; ) { t = l / 3; e = ( l % 3 ) + 1; l = -triangle_neighbor[3*(t-1)+e-1]; m2 = triangle_node[3*(t-1)+e-1]; if ( e <= 2 ) { m1 = triangle_node[3*(t-1)+e]; } else { m1 = triangle_node[3*(t-1)+0]; } *triangle_num = *triangle_num + 1; triangle_neighbor[3*(t-1)+e-1] = *triangle_num; triangle_node[3*(*triangle_num-1)+0] = m1; triangle_node[3*(*triangle_num-1)+1] = m2; triangle_node[3*(*triangle_num-1)+2] = m; triangle_neighbor[3*(*triangle_num-1)+0] = t; triangle_neighbor[3*(*triangle_num-1)+1] = *triangle_num - 1; triangle_neighbor[3*(*triangle_num-1)+2] = *triangle_num + 1; top = top + 1; if ( node_num < top ) { printf ( "\n" ); printf ( "R8TRIS2 - Fatal error!\n" ); printf ( " Stack overflow.\n" ); free ( stack ); return 8; } stack[top-1] = *triangle_num; if ( t == rtri && e == redg ) { break; } } triangle_neighbor[3*(ltri-1)+ledg-1] = -3 * n - 1; triangle_neighbor[3*(n-1)+1] = -3 * (*triangle_num) - 2; triangle_neighbor[3*(*triangle_num-1)+2] = -l; ltri = n; ledg = 2; error = swapec ( m, &top, <ri, &ledg, node_num, node_xy, *triangle_num, triangle_node, triangle_neighbor, stack ); if ( error != 0 ) { printf ( "\n" ); printf ( "R8TRIS2 - Fatal error!\n" ); printf ( " Error return from SWAPEC.\n" ); free ( stack ); return error; } } /* Now account for the sorting that we did. */ for ( i = 0; i < 3; i++ ) { for ( j = 0; j < *triangle_num; j++ ) { triangle_node[i+j*3] = indx [ triangle_node[i+j*3] - 1 ]; } } perm_inverse ( node_num, indx ); r82vec_permute ( node_num, indx, base, node_xy ); free ( indx ); free ( stack ); return 0; } /******************************************************************************/ void r8vec_bracket ( int n, double x[], double xval, int *left, int *right ) /******************************************************************************/ /* Purpose: R8VEC_BRACKET searches a sorted array for successive brackets of a value. Discussion: An R8VEC is a vector of R8's. If the values in the vector are thought of as defining intervals on the real line, then this routine searches for the interval nearest to or containing the given value. It is always true that RIGHT = LEFT+1. If XVAL < X[0], then LEFT = 1, RIGHT = 2, and XVAL < X[0] < X[1]; If X(1) <= XVAL < X[N-1], then X[LEFT-1] <= XVAL < X[RIGHT-1]; If X[N-1] <= XVAL, then LEFT = N-1, RIGHT = N, and X[LEFT-1] <= X[RIGHT-1] <= XVAL. For consistency, this routine computes indices RIGHT and LEFT that are 1-based, although it would be more natural in C and C++ to use 0-based values. Licensing: This code is distributed under the MIT license. Modified: 31 May 2009 Author: John Burkardt Parameters: Input, int N, length of input array. Input, double X[N], an array that has been sorted into ascending order. Input, double XVAL, a value to be bracketed. Output, int *LEFT, *RIGHT, the results of the search. */ { int i; for ( i = 2; i <= n - 1; i++ ) { if ( xval < x[i-1] ) { *left = i - 1; *right = i; return; } } *left = n - 1; *right = n; return; } /******************************************************************************/ double r8vec_max ( int n, double r8vec[] ) /******************************************************************************/ /* Purpose: R8VEC_MAX returns the value of the maximum element in a R8VEC. Licensing: This code is distributed under the MIT license. Modified: 05 May 2006 Author: John Burkardt Parameters: Input, int N, the number of entries in the array. Input, double R8VEC[N], a pointer to the first entry of the array. Output, double R8VEC_MAX, the value of the maximum element. This is set to 0.0 if N <= 0. */ { int i; double value; if ( n <= 0 ) { value = 0.0; return value; } value = r8vec[0]; for ( i = 1; i < n; i++ ) { if ( value < r8vec[i] ) { value = r8vec[i]; } } return value; } /******************************************************************************/ double r8vec_min ( int n, double r8vec[] ) /******************************************************************************/ /* Purpose: R8VEC_MIN returns the value of the minimum element in a R8VEC. Licensing: This code is distributed under the MIT license. Modified: 05 May 2006 Author: John Burkardt Parameters: Input, int N, the number of entries in the array. Input, double R8VEC[N], the array to be checked. Output, double R8VEC_MIN, the value of the minimum element. */ { int i; double value; value = r8vec[0]; for ( i = 1; i < n; i++ ) { if ( r8vec[i] < value ) { value = r8vec[i]; } } return value; } /******************************************************************************/ int s_len_trim ( char *s ) /******************************************************************************/ /* Purpose: S_LEN_TRIM returns the length of a string to the last nonblank. Discussion: It turns out that I also want to ignore the '\n' character! Licensing: This code is distributed under the MIT license. Modified: 05 October 2014 Author: John Burkardt Parameters: Input, char *S, a pointer to a string. Output, int S_LEN_TRIM, the length of the string to the last nonblank. If S_LEN_TRIM is 0, then the string is entirely blank. */ { int n; char *t; n = strlen ( s ); t = s + strlen ( s ) - 1; while ( 0 < n ) { if ( *t != ' ' && *t != '\n' ) { return n; } t--; n--; } return n; } /******************************************************************************/ void sort_heap_external ( int n, int *indx, int *i, int *j, int isgn ) /******************************************************************************/ /* Purpose: SORT_HEAP_EXTERNAL externally sorts a list of items into ascending order. Discussion: The actual list is not passed to the routine. Hence it may consist of integers, reals, numbers, names, etc. The user, after each return from the routine, will be asked to compare or interchange two items. The current version of this code mimics the FORTRAN version, so the values of I and J, in particular, are FORTRAN indices. Licensing: This code is distributed under the MIT license. Modified: 05 February 2004 Author: Original FORTRAN77 version by Albert Nijenhuis, Herbert Wilf. C version by John Burkardt. Reference: Albert Nijenhuis, Herbert Wilf, Combinatorial Algorithms, Academic Press, 1978, second edition, ISBN 0-12-519260-6. Parameters: Input, int N, the length of the input list. Input/output, int *INDX. The user must set INDX to 0 before the first call. On return, if INDX is greater than 0, the user must interchange items I and J and recall the routine. If INDX is less than 0, the user is to compare items I and J and return in ISGN a negative value if I is to precede J, and a positive value otherwise. If INDX is 0, the sorting is done. Output, int *I, *J. On return with INDX positive, elements I and J of the user's list should be interchanged. On return with INDX negative, elements I and J are to be compared by the user. Input, int ISGN. On return with INDX negative, the user should compare elements I and J of the list. If item I is to precede item J, set ISGN negative, otherwise set ISGN positive. */ { static int i_save = 0; static int j_save = 0; static int k = 0; static int k1 = 0; static int n1 = 0; /* INDX = 0: This is the first call. */ if ( *indx == 0 ) { i_save = 0; j_save = 0; k = n / 2; k1 = k; n1 = n; } /* INDX < 0: The user is returning the results of a comparison. */ else if ( *indx < 0 ) { if ( *indx == -2 ) { if ( isgn < 0 ) { i_save = i_save + 1; } j_save = k1; k1 = i_save; *indx = -1; *i = i_save; *j = j_save; return; } if ( 0 < isgn ) { *indx = 2; *i = i_save; *j = j_save; return; } if ( k <= 1 ) { if ( n1 == 1 ) { i_save = 0; j_save = 0; *indx = 0; } else { i_save = n1; j_save = 1; n1 = n1 - 1; *indx = 1; } *i = i_save; *j = j_save; return; } k = k - 1; k1 = k; } /* 0 < INDX: the user was asked to make an interchange. */ else if ( *indx == 1 ) { k1 = k; } for ( ; ; ) { i_save = 2 * k1; if ( i_save == n1 ) { j_save = k1; k1 = i_save; *indx = -1; *i = i_save; *j = j_save; return; } else if ( i_save <= n1 ) { j_save = i_save + 1; *indx = -2; *i = i_save; *j = j_save; return; } if ( k <= 1 ) { break; } k = k - 1; k1 = k; } if ( n1 == 1 ) { i_save = 0; j_save = 0; *indx = 0; *i = i_save; *j = j_save; } else { i_save = n1; j_save = 1; n1 = n1 - 1; *indx = 1; *i = i_save; *j = j_save; } return; } /******************************************************************************/ int swapec ( int i, int *top, int *btri, int *bedg, int node_num, double node_xy[], int triangle_num, int triangle_node[], int triangle_neighbor[], int stack[] ) /******************************************************************************/ /* Purpose: SWAPEC swaps diagonal edges until all triangles are Delaunay. Discussion: The routine swaps diagonal edges in a 2D triangulation, based on the empty circumcircle criterion, until all triangles are Delaunay, given that I is the index of the new vertex added to the triangulation. Licensing: This code is distributed under the MIT license. Modified: 03 September 2003 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, int I, the index of the new vertex. Input/output, int *TOP, the index of the top of the stack. On output, TOP is zero. Input/output, int *BTRI, *BEDG; on input, if positive, are the triangle and edge indices of a boundary edge whose updated indices must be recorded. On output, these may be updated because of swaps. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input/output, int TRIANGLE_NODE[3*TRIANGLE_NUM], the triangle incidence list. May be updated on output because of swaps. Input/output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbor list; negative values are used for links of the counter-clockwise linked list of boundary edges; May be updated on output because of swaps. LINK = -(3*I + J-1) where I, J = triangle, edge index. Workspace, int STACK[MAXST]; on input, entries 1 through TOP contain the indices of initial triangles (involving vertex I) put in stack; the edges opposite I should be in interior; entries TOP+1 through MAXST are used as a stack. Output, int SWAPEC, is set to 8 for abnormal return. */ { int a; int b; int c; int e; int ee; int em1; int ep1; int f; int fm1; int fp1; int l; int r; int s; int swap; int t; int tt; int u; double x; double y; /* Determine whether triangles in stack are Delaunay, and swap diagonal edge of convex quadrilateral if not. */ x = node_xy[2*(i-1)+0]; y = node_xy[2*(i-1)+1]; for ( ; ; ) { if ( *top <= 0 ) { break; } t = stack[(*top)-1]; *top = *top - 1; if ( triangle_node[3*(t-1)+0] == i ) { e = 2; b = triangle_node[3*(t-1)+2]; } else if ( triangle_node[3*(t-1)+1] == i ) { e = 3; b = triangle_node[3*(t-1)+0]; } else { e = 1; b = triangle_node[3*(t-1)+1]; } a = triangle_node[3*(t-1)+e-1]; u = triangle_neighbor[3*(t-1)+e-1]; if ( triangle_neighbor[3*(u-1)+0] == t ) { f = 1; c = triangle_node[3*(u-1)+2]; } else if ( triangle_neighbor[3*(u-1)+1] == t ) { f = 2; c = triangle_node[3*(u-1)+0]; } else { f = 3; c = triangle_node[3*(u-1)+1]; } swap = diaedg ( x, y, node_xy[2*(a-1)+0], node_xy[2*(a-1)+1], node_xy[2*(c-1)+0], node_xy[2*(c-1)+1], node_xy[2*(b-1)+0], node_xy[2*(b-1)+1] ); if ( swap == 1 ) { em1 = i4_wrap ( e - 1, 1, 3 ); ep1 = i4_wrap ( e + 1, 1, 3 ); fm1 = i4_wrap ( f - 1, 1, 3 ); fp1 = i4_wrap ( f + 1, 1, 3 ); triangle_node[3*(t-1)+ep1-1] = c; triangle_node[3*(u-1)+fp1-1] = i; r = triangle_neighbor[3*(t-1)+ep1-1]; s = triangle_neighbor[3*(u-1)+fp1-1]; triangle_neighbor[3*(t-1)+ep1-1] = u; triangle_neighbor[3*(u-1)+fp1-1] = t; triangle_neighbor[3*(t-1)+e-1] = s; triangle_neighbor[3*(u-1)+f-1] = r; if ( 0 < triangle_neighbor[3*(u-1)+fm1-1] ) { *top = *top + 1; stack[(*top)-1] = u; } if ( 0 < s ) { if ( triangle_neighbor[3*(s-1)+0] == u ) { triangle_neighbor[3*(s-1)+0] = t; } else if ( triangle_neighbor[3*(s-1)+1] == u ) { triangle_neighbor[3*(s-1)+1] = t; } else { triangle_neighbor[3*(s-1)+2] = t; } *top = *top + 1; if ( node_num < *top ) { return 8; } stack[(*top)-1] = t; } else { if ( u == *btri && fp1 == *bedg ) { *btri = t; *bedg = e; } l = - ( 3 * t + e - 1 ); tt = t; ee = em1; while ( 0 < triangle_neighbor[3*(tt-1)+ee-1] ) { tt = triangle_neighbor[3*(tt-1)+ee-1]; if ( triangle_node[3*(tt-1)+0] == a ) { ee = 3; } else if ( triangle_node[3*(tt-1)+1] == a ) { ee = 1; } else { ee = 2; } } triangle_neighbor[3*(tt-1)+ee-1] = l; } if ( 0 < r ) { if ( triangle_neighbor[3*(r-1)+0] == t ) { triangle_neighbor[3*(r-1)+0] = u; } else if ( triangle_neighbor[3*(r-1)+1] == t ) { triangle_neighbor[3*(r-1)+1] = u; } else { triangle_neighbor[3*(r-1)+2] = u; } } else { if ( t == *btri && ep1 == *bedg ) { *btri = u; *bedg = f; } l = - ( 3 * u + f - 1 ); tt = u; ee = fm1; while ( 0 < triangle_neighbor[3*(tt-1)+ee-1] ) { tt = triangle_neighbor[3*(tt-1)+ee-1]; if ( triangle_node[3*(tt-1)+0] == b ) { ee = 3; } else if ( triangle_node[3*(tt-1)+1] == b ) { ee = 1; } else { ee = 2; } } triangle_neighbor[3*(tt-1)+ee-1] = l; } } } return 0; } /******************************************************************************/ void timestamp ( ) /******************************************************************************/ /* Purpose: TIMESTAMP prints the current YMDHMS date as a time stamp. Example: 31 May 2001 09:45:54 AM Licensing: This code is distributed under the MIT license. Modified: 24 September 2003 Author: John Burkardt Parameters: None */ { # define TIME_SIZE 40 static char time_buffer[TIME_SIZE]; const struct tm *tm; time_t now; now = time ( NULL ); tm = localtime ( &now ); strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm ); fprintf ( stdout, "%s\n", time_buffer ); return; # undef TIME_SIZE } /******************************************************************************/ double *triangle_angles_2d_new ( double t[2*3] ) /******************************************************************************/ /* Purpose: TRIANGLE_ANGLES_2D_NEW computes the angles of a triangle in 2D. Discussion: The law of cosines is used: C * C = A * A + B * B - 2 * A * B * COS ( GAMMA ) where GAMMA is the angle opposite side C. Licensing: This code is distributed under the MIT license. Modified: 11 September 2009 Author: John Burkardt Parameters: Input, double T[2*3], the triangle vertices. Output, double TRIANGLE_ANGLES_2D_NEW[3], the angles opposite sides P1-P2, P2-P3 and P3-P1, in radians. */ { double a; double *angle; double b; double c; double pi = 3.141592653589793; angle = ( double * ) malloc ( 3 * sizeof ( double ) ); a = sqrt ( pow ( t[0+1*2] - t[0+0*2], 2 ) + pow ( t[1+1*2] - t[1+0*2], 2 ) ); b = sqrt ( pow ( t[0+2*2] - t[0+1*2], 2 ) + pow ( t[1+2*2] - t[1+1*2], 2 ) ); c = sqrt ( pow ( t[0+0*2] - t[0+2*2], 2 ) + pow ( t[1+0*2] - t[1+2*2], 2 ) ); /* Take care of a ridiculous special case. */ if ( a == 0.0 && b == 0.0 && c == 0.0 ) { angle[0] = 2.0 * pi / 3.0; angle[1] = 2.0 * pi / 3.0; angle[2] = 2.0 * pi / 3.0; return angle; } if ( c == 0.0 || a == 0.0 ) { angle[0] = pi; } else { angle[0] = r8_acos ( ( c * c + a * a - b * b ) / ( 2.0 * c * a ) ); } if ( a == 0.0 || b == 0.0 ) { angle[1] = pi; } else { angle[1] = r8_acos ( ( a * a + b * b - c * c ) / ( 2.0 * a * b ) ); } if ( b == 0.0 || c == 0.0 ) { angle[2] = pi; } else { angle[2] = r8_acos ( ( b * b + c * c - a * a ) / ( 2.0 * b * c ) ); } return angle; } /******************************************************************************/ double triangle_area_2d ( double t[2*3] ) /******************************************************************************/ /* Purpose: TRIANGLE_AREA_2D computes the area of a triangle in 2D. Discussion: If the triangle's vertices are given in counter clockwise order, the area will be positive. If the triangle's vertices are given in clockwise order, the area will be negative! An earlier version of this routine always returned the absolute value of the computed area. I am convinced now that that is a less useful result! For instance, by returning the signed area of a triangle, it is possible to easily compute the area of a nonconvex polygon as the sum of the (possibly negative) areas of triangles formed by node 1 and successive pairs of vertices. Licensing: This code is distributed under the MIT license. Modified: 17 October 2005 Author: John Burkardt Parameters: Input, double T[2*3], the vertices of the triangle. Output, double TRIANGLE_AREA_2D, the area of the triangle. */ { double area; area = 0.5 * ( t[0+0*2] * ( t[1+1*2] - t[1+2*2] ) + t[0+1*2] * ( t[1+2*2] - t[1+0*2] ) + t[0+2*2] * ( t[1+0*2] - t[1+1*2] ) ); return area; } /******************************************************************************/ double *triangle_circumcenter_2d ( double t[2*3] ) /******************************************************************************/ /* Purpose: TRIANGLE_CIRCUMCENTER_2D computes the circumcenter of a triangle in 2D. Discussion: The circumcenter of a triangle is the center of the circumcircle, the circle that passes through the three vertices of the triangle. The circumcircle contains the triangle, but it is not necessarily the smallest triangle to do so. If all angles of the triangle are no greater than 90 degrees, then the center of the circumscribed circle will lie inside the triangle. Otherwise, the center will lie outside the triangle. The circumcenter is the intersection of the perpendicular bisectors of the sides of the triangle. In geometry, the circumcenter of a triangle is often symbolized by "O". Licensing: This code is distributed under the MIT license. Modified: 09 February 2005 Author: John Burkardt Parameters: Input, double T[2*3], the triangle vertices. Output, double *TRIANGLE_CIRCUMCENTER_2D[2], the circumcenter of the triangle. */ { # define DIM_NUM 2 double asq; double bot; double *pc; double csq; double top1; double top2; pc = ( double * ) malloc ( DIM_NUM * sizeof ( double ) ); asq = ( t[0+1*2] - t[0+0*2] ) * ( t[0+1*2] - t[0+0*2] ) + ( t[1+1*2] - t[1+0*2] ) * ( t[1+1*2] - t[1+0*2] ); csq = ( t[0+2*2] - t[0+0*2] ) * ( t[0+2*2] - t[0+0*2] ) + ( t[1+2*2] - t[1+0*2] ) * ( t[1+2*2] - t[1+0*2] ); top1 = ( t[1+1*2] - t[1+0*2] ) * csq - ( t[1+2*2] - t[1+0*2] ) * asq; top2 = - ( t[0+1*2] - t[0+0*2] ) * csq + ( t[0+2*2] - t[0+0*2] ) * asq; bot = ( t[1+1*2] - t[1+0*2] ) * ( t[0+2*2] - t[0+0*2] ) - ( t[1+2*2] - t[1+0*2] ) * ( t[0+1*2] - t[0+0*2] ); pc[0] = t[0+0*2] + 0.5 * top1 / bot; pc[1] = t[1+0*2] + 0.5 * top2 / bot; return pc; # undef DIM_NUM } /******************************************************************************/ void triangle_order3_physical_to_reference ( double t[], int n, double phy[], double ref[] ) /******************************************************************************/ /* Purpose: TRIANGLE_ORDER3_PHYSICAL_TO_REFERENCE maps physical points to reference points. Discussion: Given the vertices of an order 3 physical triangle and a point (X,Y) in the physical triangle, the routine computes the value of the corresponding image point (XSI,ETA) in reference space. Note that this routine may also be appropriate for an order 6 triangle, if the mapping between reference and physical space is linear. This implies, in particular, that the sides of the image triangle are straight and that the "midside" nodes in the physical triangle are halfway along the sides of the physical triangle. Reference Element T3: | 1 3 | |\ | | \ S | \ | | \ | | \ 0 1-----2 | +--0--R--1--> Licensing: This code is distributed under the MIT license. Modified: 24 June 2005 Author: John Burkardt Parameters: Input, double T[2*3], the X and Y coordinates of the vertices. The vertices are assumed to be the images of (0,0), (1,0) and (0,1) respectively. Input, int N, the number of points to transform. Input, double PHY[2*N], the coordinates of physical points to be transformed. Output, double REF[2*N], the coordinates of the corresponding points in the reference space. */ { int j; for ( j = 0; j < n; j++ ) { ref[0+j*2] = ( ( t[1+2*2] - t[1+0*2] ) * ( phy[0+j*2] - t[0+0*2] ) - ( t[0+2*2] - t[0+0*2] ) * ( phy[1+j*2] - t[1+0*2] ) ) / ( ( t[1+2*2] - t[1+0*2] ) * ( t[0+1*2] - t[0+0*2] ) - ( t[0+2*2] - t[0+0*2] ) * ( t[1+1*2] - t[1+0*2] ) ); ref[1+j*2] = ( ( t[0+1*2] - t[0+0*2] ) * ( phy[1+j*2] - t[1+0*2] ) - ( t[1+1*2] - t[1+0*2] ) * ( phy[0+j*2] - t[0+0*2] ) ) / ( ( t[1+2*2] - t[1+0*2] ) * ( t[0+1*2] - t[0+0*2] ) - ( t[0+2*2] - t[0+0*2] ) * ( t[1+1*2] - t[1+0*2] ) ); } return; } /******************************************************************************/ void triangle_order3_reference_to_physical ( double t[], int n, double ref[], double phy[] ) /******************************************************************************/ /* Purpose: TRIANGLE_ORDER3_REFERENCE_TO_PHYSICAL maps reference points to physical points. Discussion: Given the vertices of an order 3 physical triangle and a point (XSI,ETA) in the reference triangle, the routine computes the value of the corresponding image point (X,Y) in physical space. Note that this routine may also be appropriate for an order 6 triangle, if the mapping between reference and physical space is linear. This implies, in particular, that the sides of the image triangle are straight and that the "midside" nodes in the physical triangle are halfway along the sides of the physical triangle. Reference Element T3: | 1 3 | |\ | | \ S | \ | | \ | | \ 0 1-----2 | +--0--R--1--> Licensing: This code is distributed under the MIT license. Modified: 24 June 2005 Author: John Burkardt Parameters: Input, double T[2*3], the coordinates of the vertices. The vertices are assumed to be the images of (0,0), (1,0) and (0,1) respectively. Input, int N, the number of points to transform. Input, double REF[2*N], points in the reference triangle. Output, double PHY[2*N], corresponding points in the physical triangle. */ { int i; int j; for ( i = 0; i < 2; i++ ) { for ( j = 0; j < n; j++ ) { phy[i+j*2] = t[i+0*2] * ( 1.0 - ref[0+j*2] - ref[1+j*2] ) + t[i+1*2] * + ref[0+j*2] + t[i+2*2] * + ref[1+j*2]; } } return; } /******************************************************************************/ void triangle_order6_physical_to_reference ( double t[2*6], int n, double phy[], double ref[] ) /******************************************************************************/ /* Purpose: TRIANGLE_ORDER6_PHYSICAL_TO_REFERENCE maps a physical point to a reference point. Discussion: Given the vertices of an order 6 physical triangle and a point (X,Y) in the physical triangle, the routine computes the value of the corresponding image point (R,S) in reference space. The mapping from (R,S) to (X,Y) has the form: X(R,S) = A1 * R * R + B1 * R * S + C1 * S * S + D1 * R + E1 * S + F1 Y(R,S) = A2 * R * R + B2 * R * S + C2 * S * S + D2 * R + E2 * S + F2 Reference Element T3: | 1 3 | |\ | | \ S 6 5 | | \ | | \ 0 1--4--2 | +--0--R--1--> Licensing: This code is distributed under the MIT license. Modified: 07 December 2006 Author: John Burkardt Parameters: Input, double T(2,6), the coordinates of the vertices. The vertices are assumed to be the images of (0,0), (1,0), (0,1), (1/2,0), (1/2,1/2) and (0,1/2), in that order. Input, int N, the number of points to transform. Input, double PHY(2,N), the coordinates of points in the physical space. Output, double REF(2,N), the coordinates of the corresponding points in the reference space. */ { double a[2]; double b[2]; double c[2]; double d[2]; double det; double dx[2]; double e[2]; double f[2]; double fun[2]; double fun_norm; int i; int it; int j; double jac[2*2]; int it_max = 10; double it_tol = 0.000001; /* Set iteration parameters. */ for ( i = 0; i < 2; i++ ) { a[i] = 2.0 * t[i+0*2] + 2.0 * t[i+1*2] - 4.0 * t[i+3*2]; b[i] = 4.0 * t[i+0*2] - 4.0 * t[i+3*2] + 4.0 * t[i+4*2] - 4.0 * t[i+5*2]; c[i] = 2.0 * t[i+0*2] + 2.0 * t[i+2*2] - 4.0 * t[i+5*2]; d[i] = - 3.0 * t[i+0*2] - t[i+1*2] + 4.0 * t[i+3*2]; e[i] = - 3.0 * t[i+0*2] - t[i+2*2] + 4.0 * t[i+5*2]; f[i] = t[i+0*2]; } /* Initialize the points by inverting the linear map. */ triangle_order3_physical_to_reference ( t, n, phy, ref ); /* Carry out the Newton iteration. */ for ( j = 0; j < n; j++ ) { for ( it = 0; it < it_max; it++ ) { for ( i = 0; i < 2; i++ ) { fun[i] = a[i] * ref[0+j*2] * ref[0+j*2] + b[i] * ref[0+j*2] * ref[1+j*2] + c[i] * ref[1+j*2] * ref[1+j*2] + d[i] * ref[0+j*2] + e[i] * ref[1+j*2] + f[i] - phy[i+j*2]; } fun_norm = sqrt ( pow ( fun[0], 2 ) + pow ( fun[1], 2 ) ); if ( fun_norm <= it_tol ) { break; } jac[0+0*2] = 2.0 * a[0] * ref[0+j*2] + b[0] * ref[1+j*2] + d[0]; jac[1+0*2] = 2.0 * a[1] * ref[0+j*2] + b[1] * ref[1+j*2] + d[1]; jac[0+1*2] = b[0] * ref[0+j*2] + 2.0 * c[0] * ref[1+j*2] + e[0]; jac[1+1*2] = b[1] * ref[0+j*2] + 2.0 * c[1] * ref[1+j*2] + e[1]; det = jac[0+0*2] * jac[1+1*2] - jac[0+1*2] * jac[1+0*2]; if ( det == 0.0 ) { printf ( "\n" ); printf ( "TRIANGLE_ORDER6_PHYSICAL_TO_REFERENCE - Fatal error!\n" ); printf ( " The jacobian of the mapping is singular.\n" ); } dx[0] = ( jac[1+1*2] * fun[0] - jac[0+1*2] * fun[1] ) / det; dx[1] = ( -jac[1+0*2] * fun[0] + jac[0+0*2] * fun[1] ) / det; ref[0+j*2] = ref[0+j*2] - dx[0]; ref[1+j*2] = ref[1+j*2] - dx[1]; } } return; } /******************************************************************************/ void triangle_order6_reference_to_physical ( double t[], int n, double ref[], double phy[] ) /******************************************************************************/ /* Purpose: TRIANGLE_ORDER6_REFERENCE_TO_PHYSICAL maps reference points to physical points. Discussion: Given the vertices of an order 6 physical triangle and a point (XSI,ETA) in the reference triangle, the routine computes the value of the corresponding image point (X,Y) in physical space. The mapping from (XSI,ETA) to (X,Y) has the form: X(ETA,XSI) = A1 * XSI**2 + B1 * XSI*ETA + C1 * ETA**2 + D1 * XSI + E1 * ETA + F1 Y(ETA,XSI) = A2 * XSI**2 + B2 * XSI*ETA + C2 * ETA**2 + D2 * XSI + E2 * ETA + F2 Reference Element T6: | 1 3 | |\ | | \ S 6 5 | | \ | | \ 0 1--4--2 | +--0--R--1--> Licensing: This code is distributed under the MIT license. Modified: 25 June 2005 Author: John Burkardt Parameters: Input, double T[2*6], the coordinates of the vertices. The vertices are assumed to be the images of (0,0), (1,0), (0,1),(1/2,0), (1/2,1/2) and (0,1/2) respectively. Input, integer N, the number of points to transform. Input, double REF[2*N], points in the reference triangle. Output, double PHY[2*N], corresponding points in the physical triangle. */ { double a[2]; double b[2]; double c[2]; double d[2]; double e[2]; double f[2]; int i; int j; for ( i = 0; i < 2; i++ ) { a[i] = 2.0 * t[i+0*2] + 2.0 * t[i+1*2] - 4.0 * t[i+3*2]; b[i] = 4.0 * t[i+0*2] - 4.0 * t[i+3*2] + 4.0 * t[i+4*2] - 4.0 * t[i+5*2]; c[i] = 2.0 * t[i+0*2] + 2.0 * t[i+2*2] - 4.0 * t[i+5*2]; d[i] = - 3.0 * t[i+0*2] - t[i+1*2] + 4.0 * t[i+3*2]; e[i] = - 3.0 * t[i+0*2] - t[i+2*2] + 4.0 * t[i+5*2]; f[i] = t[i+0*2]; } for ( i = 0; i < 2; i++ ) { for ( j = 0; j < n; j++ ) { phy[i+j*2] = a[i] * ref[0+j*2] * ref[0+j*2] + b[i] * ref[0+j*2] * ref[1+j*2] + c[i] * ref[1+j*2] * ref[1+j*2] + d[i] * ref[0+j*2] + e[i] * ref[1+j*2] + f[i]; } } return; } /******************************************************************************/ void triangle_reference_sample ( int n, int *seed, double p[] ) /******************************************************************************/ /* Purpose: TRIANGLE_REFERENCE_SAMPLE returns random points in the reference triangle. Diagram: 3 s |\ i | \ d | \ e | \ side 2 | \ 3 | \ | \ 1-------2 side 1 Licensing: This code is distributed under the MIT license. Modified: 07 December 2006 Author: John Burkardt Parameters: Input, integer N, the number of points to sample. Input/output, int *SEED, a seed for the random number generator. Output, double P[2*N], a random point in the triangle. */ { # define DIM_NUM 2 double alpha; double beta; int j; double r; for ( j = 0; j < n; j++ ) { r = r8_uniform_01 ( seed ); /* Interpret R as a percentage of the triangle's area. Imagine a line L, parallel to side 1, so that the area between vertex 1 and line L is R percent of the full triangle's area. The line L will intersect sides 2 and 3 at a fraction ALPHA = SQRT ( R ) of the distance from vertex 1 to vertices 2 and 3. */ alpha = sqrt ( r ); /* Now choose, uniformly at random, a point on the line L. */ beta = r8_uniform_01 ( seed ); p[0+j*2] = ( 1.0 - beta ) * alpha; p[1+j*2] = beta * alpha; } return; # undef DIM_NUM } /******************************************************************************/ void triangle_sample ( double t[2*3], int n, int *seed, double p[] ) /******************************************************************************/ /* Purpose: TRIANGLE_SAMPLE returns random points in a triangle. Licensing: This code is distributed under the MIT license. Modified: 06 December 2006 Author: John Burkardt Parameters: Input, double T[2*3], the triangle vertices. Input, integer N, the number of points to sample. Input/output, int *SEED, a seed for the random number generator. Output, double P[2*N], a random point in the triangle. */ { # define DIM_NUM 2 double alpha; double beta; int j; double r; double p12[DIM_NUM]; double p13[DIM_NUM]; for ( j = 0; j < n; j++ ) { r = r8_uniform_01 ( seed ); /* Interpret R as a percentage of the triangle's area. Imagine a line L, parallel to side 1, so that the area between vertex 1 and line L is R percent of the full triangle's area. The line L will intersect sides 2 and 3 at a fraction ALPHA = SQRT ( R ) of the distance from vertex 1 to vertices 2 and 3. */ alpha = sqrt ( r ); /* Determine the coordinates of the points on sides 2 and 3 intersected by line L. */ p12[0] = ( 1.0 - alpha ) * t[0+0*2] + alpha * t[0+1*2]; p12[1] = ( 1.0 - alpha ) * t[1+0*2] + alpha * t[1+1*2]; p13[0] = ( 1.0 - alpha ) * t[0+0*2] + alpha * t[0+2*2];; p13[1] = ( 1.0 - alpha ) * t[1+0*2] + alpha * t[1+2*2];; /* Now choose, uniformly at random, a point on the line L. */ beta = r8_uniform_01 ( seed ); p[0+j*2] = ( 1.0 - beta ) * p12[0] + beta * p13[0]; p[1+j*2] = ( 1.0 - beta ) * p12[1] + beta * p13[1]; } return; # undef DIM_NUM } /******************************************************************************/ double triangulation_area ( int node_num, double node_xy[], int element_order, int element_num, int element_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_AREA computes the area of a triangulation. Licensing: This code is distributed under the MIT license. Modified: 26 December 2011 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int ELEMENT_ORDER, the order of the triangles. Input, int ELEMENT_NUM, the number of triangles. Input, int ELEMENT_NODE[ELEMENT_ORDER*ELEMENT_NUM], the nodes making up each triangle. Output, double TRIANGULATION_AREA, the area. */ { int element; double element_area; double element_xy[2*3]; int j; int nj; double value; value = 0.0; for ( element = 0; element < element_num; element++ ) { for ( j = 0; j < 3; j++ ) { nj = element_node[j+element*element_order]; element_xy[0+j*2] = node_xy[0+nj*2]; element_xy[1+j*2] = node_xy[1+nj*2]; } element_area = 0.5 * ( element_xy[0+0*2] * ( element_xy[1+1*2] - element_xy[1+2*2] ) + element_xy[0+1*2] * ( element_xy[1+2*2] - element_xy[1+0*2] ) + element_xy[0+2*2] * ( element_xy[1+0*2] - element_xy[1+1*2] ) ); value = value + element_area; } return value; } /******************************************************************************/ double triangulation_areas ( int node_num, double node_xy[], int triangle_order, int triangle_num, int triangle_node[], double triangle_area[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_AREAS computes triangle and triangulation areas. Licensing: This code is distributed under the MIT license. Modified: 25 September 2009 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes in the triangulation. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_ORDER, the order of triangles in the triangulation. Input, int TRIANGLE_NUM, the number of triangles in the triangulation. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes making up each triangle. Output, double TRIANGLE_AREA[TRIANGLE_NUM], the area of the triangles. Output, double TRIANGULATION_AREAS, the area of the triangulation. */ { int j; int nj; double t_xy[2*3]; int triangle; double triangulation_area; triangulation_area = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { for ( j = 0; j < 3; j++ ) { nj = triangle_node[j+triangle*triangle_order]; t_xy[0+j*2] = node_xy[0+nj*2]; t_xy[1+j*2] = node_xy[1+nj*2]; } triangle_area[triangle] = 0.5 * ( t_xy[0+0*2] * ( t_xy[1+1*2] - t_xy[1+2*2] ) + t_xy[0+1*2] * ( t_xy[1+2*2] - t_xy[1+0*2] ) + t_xy[0+2*2] * ( t_xy[1+0*2] - t_xy[1+1*2] ) ); triangulation_area = triangulation_area + triangle_area[triangle]; } return triangulation_area; } /******************************************************************************/ double triangulation_delaunay_discrepancy_compute ( int node_num, double node_xy[], int triangle_order, int triangle_num, int triangle_node[], int triangle_neighbor[], double *angle_min, int *angle_min_triangle, double *angle_max, int *angle_max_triangle ) /******************************************************************************/ /* Purpose: TRIANGULATION_DELAUNAY_DISCREPANCY_COMPUTE reports if a triangulation is Delaunay. Discussion: A (maximal) triangulation is Delaunay if and only if it is locally Delaunay. A triangulation is Delaunay if the minimum angle over all triangles in the triangulation is maximized. That is, there is no other triangulation of the points which has a larger minimum angle. A triangulation is locally Delaunay if, for every pair of triangles that share an edge, the minimum angle in the two triangles is larger than the minimum angle in the two triangles formed by removing the common edge and joining the two opposing vertices. This function examines the question of whether a given triangulation is locally Delaunay. It does this by looking at every pair of neighboring triangles and comparing the minimum angle attained for the current triangle pair and the alternative triangle pair. Let A(i,j) be the minimum angle formed by triangles T(i) and T(j), which are two triangles in the triangulation which share a common edge. Let B(I,J) be the minimum angle formed by triangles S(i) and S(j), where S(i) and S(j) are formed by removing the common edge of T(i) and T(j), and joining the opposing vertices. Then the triangulation is Delaunay if B(i,j) <= A(i,j) for every pair of neighbors T(i) and T(j). If A(i,j) < B(i,j) for at least one pair of neighbors, the triangulation is not a Delaunay triangulation. This program returns VALUE = min ( A(i,j) - B(i,j) ) over all triangle neighbors. VALUE is scaled to be in degrees, for comprehensibility. If VALUE is negative, then at least one pair of triangles violates the Delaunay condition, and so the entire triangulation is not a Delaunay triangulation. If VALUE is nonnegative, then the triangulation is a Delaunay triangulation. It is useful to return VALUE, rather than a simple True/False value, because there can be cases where the Delaunay condition is only "slightly" violated. A simple example is a triangulation formed by starting with a mesh of squares and dividing each square into two triangles by choosing one of the diagonals of the square. The Delaunay discrepancy for this mesh, if computed exactly, is 0, but roundoff could easily result in discrepancies that were very slightly negative. If VALUE is positive, and not very small in magnitude, then every pair of triangles in the triangulation satisfies the local Delaunay condition, and so the triangulation is a Delaunay triangulation. If VALUE is negative, and not very small in magnitude, then at least one pair of triangles violates the Delaunay condition, and to a significant degree. The triangulation is not a Delaunay triangulation. If the magnitude of VALUE is very close to zero, then the triangulation is numerically ambiguous. At least one pair of triangles violates or almost violates the condition, but no triangle violates the condition to a great extent. The user must judge whether the violation is significant or not. Licensing: This code is distributed under the MIT license. Modified: 10 September 2009 Author: John Burkardt. Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles in the triangulation. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes that make up each triangle. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbor list. Output, double *ANGLE_MIN, the minimum angle that occurred in the triangulation. Output, int *ANGLE_MIN_TRIANGLE, the triangle in which the minimum angle occurred. Output, double *ANGLE_MAX, the maximum angle that occurred in the triangulation. Output, int *ANGLE_MAX_TRIANGLE, the triangle in which the maximum angle occurred. Output, double TRIANGULATION_DELAUNAY_DISCREPANCY, the minimum value of ( A(i,j) - B(i,j) ). POSITIVE indicates the triangulation is Delaunay. VERY NEAR ZERO is a numerically ambiguous case. NEGATIVE indicates the triangulation is not Delaunay. */ { double angle_min1; double angle_min2; double *angles1; double *angles2; int i; int i1; int i2; int i3; int i4; int n1; int n2; int n3; int n4; int neighbor; double pi = 3.141592653589793; double t[2*3]; int triangle1; int triangle2; double value; *angle_max = 0.0; *angle_max_triangle = - 1; *angle_min = pi; *angle_min_triangle = -1; value = 0.0; /* Consider triangle TRIANGLE1 */ for ( triangle1 = 0; triangle1 < triangle_num; triangle1++ ) { /* Consider the side opposite vertex NEIGHBOR. */ for ( neighbor = 0; neighbor < 3; neighbor++ ) { triangle2 = triangle_neighbor[neighbor+triangle1*3]; /* There might be no neighbor on side NEIGHBOR. */ if ( triangle2 < 0 ) { continue; } /* We only need to check a pair of triangles once. */ if ( triangle2 < triangle1 ) { continue; } /* List the vertices of the quadrilateral in such a way that the nodes of triangle 1 come first. We rely on a property of the TRIANGLE_NEIGHBOR array, namely, that neighbor #1 is on the side opposite to vertex #1, and so on. */ i1 = i4_wrap ( neighbor + 2, 0, 2 ); i2 = i4_wrap ( neighbor, 0, 2 ); i3 = i4_wrap ( neighbor + 1, 0, 2 ); n1 = triangle_node[i1+triangle1*triangle_order]; n2 = triangle_node[i2+triangle1*triangle_order]; n3 = triangle_node[i3+triangle1*triangle_order]; /* The "odd" or "opposing" node of the neighboring triangle is the one which follows common node I3. */ n4 = -1; for ( i = 0; i < 3; i++ ) { if ( triangle_node[i+triangle2*triangle_order] == n3 ) { i4 = i + 1; i4 = i4_wrap ( i4, 0, 2 ); n4 = triangle_node[i4+triangle2*triangle_order]; break; } } if ( n4 == -1 ) { printf ( "\n" ); printf ( "TRIANGULATION_DELAUNAY_DISCREPANCY_COMPUTE - Fatal error!\n" ); printf ( " Could not identify the fourth node.\n" ); printf ( "\n" ); printf ( " Triangle1 = %d\n", triangle1 ); printf ( " Nodes = " ); for ( i = 0; i < 3; i++ ) { printf ( " %d", triangle_node[i+triangle1*triangle_order] ); } printf ( "\n" ); printf ( " Neighbors = " ); for ( i = 0; i < 3; i++ ) { printf ( " %d", triangle_neighbor[i+triangle1*3] ); } printf ( "\n" ); printf ( "\n" ); printf ( " Neighbor index = %d\n", neighbor ); printf ( "\n" ); printf ( " Triangle2 = %d\n", triangle2 ); printf ( " Nodes = " ); for ( i = 0; i < 3; i++ ) { printf ( " %d", triangle_node[i+triangle2*triangle_order] ); } printf ( "\n" ); printf ( " Neighbors = " ); for ( i = 0; i < 3; i++ ) { printf ( " %d", triangle_neighbor[i+triangle2*3] ); } printf ( "\n" ); exit ( 1 ); } /* Compute the minimum angle for (I1,I2,I3) and (I1,I3,I4). */ t[0+0*2] = node_xy[0+n1*2]; t[1+0*2] = node_xy[1+n1*2]; t[0+1*2] = node_xy[0+n2*2]; t[1+1*2] = node_xy[1+n2*2]; t[0+2*2] = node_xy[0+n3*2]; t[1+2*2] = node_xy[1+n3*2]; angles1 = triangle_angles_2d_new ( t ); t[0+0*2] = node_xy[0+n1*2]; t[1+0*2] = node_xy[1+n1*2]; t[0+1*2] = node_xy[0+n3*2]; t[1+1*2] = node_xy[1+n3*2]; t[0+2*2] = node_xy[0+n4*2]; t[1+2*2] = node_xy[1+n4*2]; angles2 = triangle_angles_2d_new ( t ); angle_min1 = fmin ( r8vec_min ( 3, angles1 ), r8vec_min ( 3, angles2 ) ); if ( *angle_max < r8vec_max ( 3, angles1 ) ) { *angle_max = r8vec_max ( 3, angles1 ); *angle_max_triangle = triangle1; } if ( *angle_max < r8vec_max ( 3, angles2 ) ) { *angle_max = r8vec_max ( 3, angles2 ); *angle_max_triangle = triangle2; } if ( r8vec_min ( 3, angles1 ) < *angle_min ) { *angle_min = r8vec_min ( 3, angles1 ); *angle_min_triangle = triangle1; } if ( r8vec_min ( 3, angles2 ) < *angle_min ) { *angle_min = r8vec_min ( 3, angles2 ); *angle_min_triangle = triangle2; } free ( angles1 ); free ( angles2 ); /* Compute the minimum angle for (I1,I2,I4) and (I2,I3,I4). */ t[0+0*2] = node_xy[0+n1*2]; t[1+0*2] = node_xy[1+n1*2]; t[0+1*2] = node_xy[0+n2*2]; t[1+1*2] = node_xy[1+n2*2]; t[0+2*2] = node_xy[0+n4*2]; t[1+2*2] = node_xy[1+n4*2]; angles1 = triangle_angles_2d_new ( t ); t[0+0*2] = node_xy[0+n3*2]; t[1+0*2] = node_xy[1+n3*2]; t[0+1*2] = node_xy[0+n3*2]; t[1+1*2] = node_xy[1+n3*2]; t[0+2*2] = node_xy[0+n4*2]; t[1+2*2] = node_xy[1+n4*2]; angles2 = triangle_angles_2d_new ( t ); angle_min2 = fmin ( r8vec_min ( 3, angles1 ), r8vec_min ( 3, angles2 ) ); free ( angles1 ); free ( angles2 ); /* Compare this value to the current minimum. */ value = fmin ( value, angle_min1 - angle_min2 ); } } /* Scale the results to degrees. */ value = value * 180.0 / pi; *angle_max = *angle_max * 180.0 / pi; *angle_min = *angle_min * 180.0 / pi; return value; } /******************************************************************************/ int *triangulation_neighbor_elements ( int triangle_order, int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_NEIGHBOR_ELEMENTS determines element neighbors. Discussion: A triangulation of a set of nodes can be completely described by the coordinates of the nodes, and the list of nodes that make up each triangle. However, in some cases, it is necessary to know triangle adjacency information, that is, which triangle, if any, is adjacent to a given triangle on a particular side. This routine creates a data structure recording this information. The primary amount of work occurs in sorting a list of 3 * TRIANGLE_NUM data items. This routine was modified to work with columns rather than rows. Example: The input information from TRIANGLE_NODE: Triangle Nodes -------- --------------- 1 3 4 1 2 3 1 2 3 3 2 8 4 2 1 5 5 8 2 13 6 8 13 9 7 3 8 9 8 13 2 5 9 9 13 7 10 7 13 5 11 6 7 5 12 9 7 6 13 10 9 6 14 6 5 12 15 11 6 12 16 10 6 11 The output information in TRIANGLE_NEIGHBOR: Triangle Neighboring Triangles -------- --------------------- 1 -1 -1 2 2 1 4 3 3 2 5 7 4 2 -1 8 5 3 8 6 6 5 9 7 7 3 6 -1 8 5 4 10 9 6 10 12 10 9 8 11 11 12 10 14 12 9 11 13 13 -1 12 16 14 11 -1 15 15 16 14 -1 16 13 15 -1 Licensing: This code is distributed under the MIT license. Modified: 07 September 2009 Author: John Burkardt Parameters: Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes that make up each triangle. Output, int TRIANGLE_ORDER3_NEIGHBOR_TRIANGLES[3*TRIANGLE_NUM], the three triangles that are direct neighbors of a given triangle. TRIANGLE_NEIGHBOR(1,I) is the index of the triangle which touches side 1, defined by nodes 2 and 3, and so on. TRIANGLE_NEIGHBOR(1,I) is negative if there is no neighbor on that side. In this case, that side of the triangle lies on the boundary of the triangulation. */ { int *col; int i; int icol; int j; int k; int side1; int side2; int tri; int tri1; int tri2; int *triangle_neighbor; triangle_neighbor = ( int * ) malloc ( 3 * triangle_num * sizeof ( int ) ); col = ( int * ) malloc ( 4 * 3 * triangle_num * sizeof ( int ) ); /* Step 1. From the list of nodes for triangle T, of the form: (I,J,K) construct the three neighbor relations: (I,J,3,T) or (J,I,3,T), (J,K,1,T) or (K,J,1,T), (K,I,2,T) or (I,K,2,T) where we choose (I,J,3,T) if I < J, or else (J,I,3,T) */ for ( tri = 0; tri < triangle_num; tri++ ) { i = triangle_node[0+tri*triangle_order]; j = triangle_node[1+tri*triangle_order]; k = triangle_node[2+tri*triangle_order]; if ( i < j ) { col[0+(3*tri+0)*4] = i; col[1+(3*tri+0)*4] = j; col[2+(3*tri+0)*4] = 3; col[3+(3*tri+0)*4] = tri + 1; } else { col[0+(3*tri+0)*4] = j; col[1+(3*tri+0)*4] = i; col[2+(3*tri+0)*4] = 3; col[3+(3*tri+0)*4] = tri + 1; } if ( j < k ) { col[0+(3*tri+1)*4] = j; col[1+(3*tri+1)*4] = k; col[2+(3*tri+1)*4] = 1; col[3+(3*tri+1)*4] = tri + 1; } else { col[0+(3*tri+1)*4] = k; col[1+(3*tri+1)*4] = j; col[2+(3*tri+1)*4] = 1; col[3+(3*tri+1)*4] = tri + 1; } if ( k < i ) { col[0+(3*tri+2)*4] = k; col[1+(3*tri+2)*4] = i; col[2+(3*tri+2)*4] = 2; col[3+(3*tri+2)*4] = tri + 1; } else { col[0+(3*tri+2)*4] = i; col[1+(3*tri+2)*4] = k; col[2+(3*tri+2)*4] = 2; col[3+(3*tri+2)*4] = tri + 1; } } /* Step 2. Perform an ascending dictionary sort on the neighbor relations. We only intend to sort on rows 1 and 2; the routine we call here sorts on rows 1 through 4 but that won't hurt us. What we need is to find cases where two triangles share an edge. Say they share an edge defined by the nodes I and J. Then there are two columns of COL that start out ( I, J, ?, ? ). By sorting COL, we make sure that these two columns occur consecutively. That will make it easy to notice that the triangles are neighbors. */ i4col_sort_a ( 4, 3*triangle_num, col ); /* Step 3. Neighboring triangles show up as consecutive columns with identical first two entries. Whenever you spot this happening, make the appropriate entries in TRIANGLE_NEIGHBOR. */ for ( j = 0; j < triangle_num; j++ ) { for ( i = 0; i < 3; i++ ) { triangle_neighbor[i+j*3] = -1; } } icol = 1; for ( ; ; ) { if ( 3 * triangle_num <= icol ) { break; } if ( col[0+(icol-1)*4] != col[0+icol*4] || col[1+(icol-1)*4] != col[1+icol*4] ) { icol = icol + 1; continue; } side1 = col[2+(icol-1)*4]; tri1 = col[3+(icol-1)*4]; side2 = col[2+ icol *4]; tri2 = col[3+ icol *4]; triangle_neighbor[side1-1+(tri1-1)*3] = tri2; triangle_neighbor[side2-1+(tri2-1)*3] = tri1; icol = icol + 2; } free ( col ); return triangle_neighbor; } /******************************************************************************/ int *triangulation_node_order ( int triangle_order, int triangle_num, int triangle_node[], int node_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_NODE_ORDER determines the order of nodes in a triangulation. Discussion: The order of a node is the number of triangles that use that node as a vertex. Licensing: This code is distributed under the MIT license. Modified: 29 August 2005 Author: John Burkardt Parameters: Input, integer TRIANGLE_ORDER, the order of the triangulation. Input, integer TRIANGLE_NUM, the number of triangles. Input, integer TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes that make up the triangles. Input, integer NODE_NUM, the number of nodes. Output, integer TRIANGULATION_NODE_ORDER[NODE_NUM], the order of each node. */ { int i; int node; int *node_order; int triangle; node_order = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( node = 0; node < node_num; node++ ) { node_order[node] = 0; } for ( triangle = 0; triangle < triangle_num; triangle++ ) { for ( i = 0; i < triangle_order; i++ ) { node = triangle_node[i+triangle*triangle_order]; if ( node < 1 || node_num < node ) { printf ( "\n" ); printf ( "TRIANGULATION_NODE_ORDER - Fatal error!\n" ); printf ( " Illegal entry in TRIANGLE_NODE.\n" ); node_order = NULL; exit ( 1 ); } else { node_order[node-1] = node_order[node-1] + 1; } } } return node_order; } /******************************************************************************/ int triangulation_order3_adj_count ( int node_num, int triangle_num, int triangle_node[], int triangle_neighbor[], int adj_col[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_ADJ_COUNT counts adjacencies in a triangulation. Discussion: This routine is called to count the adjacencies, so that the appropriate amount of memory can be set aside for storage when the adjacency structure is created. The triangulation is assumed to involve 3-node triangles. Two nodes are "adjacent" if they are both nodes in some triangle. Also, a node is considered to be adjacent to itself. Diagram: 3 s |\ i | \ d | \ e | \ side 2 | \ 3 | \ | \ 1-------2 side 1 The local node numbering 21-22-23-24-25 |\ |\ |\ |\ | | \| \| \| \| 16-17-18-19-20 |\ |\ |\ |\ | | \| \| \| \| 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 A sample grid. Below, we have a chart that summarizes the adjacency relationships in the sample grid. On the left, we list the node, and its neighbors, with an asterisk to indicate the adjacency of the node to itself (in some cases, you want to count this self adjacency and in some you don't). On the right, we list the number of adjancencies to lower-indexed nodes, to the node itself, to higher-indexed nodes, the total number of adjacencies for this node, and the location of the first and last entries required to list this set of adjacencies in a single list of all the adjacencies. N Adjacencies Below Self Above Total First Last -- -- -- -- -- -- -- -- -- -- -- -- --- 0 1: * 2 6 0 1 2 3 1 3 2: 1 * 3 6 7 1 1 3 5 4 8 3: 2 * 4 7 8 1 1 3 5 9 13 4: 3 * 5 8 9 1 1 3 5 14 18 5: 4 * 9 10 1 1 2 4 19 22 6: 1 2 * 7 11 2 1 2 5 23 27 7: 2 3 6 * 8 11 12 3 1 3 7 28 34 8: 3 4 7 * 9 12 13 3 1 3 7 35 41 9: 4 5 8 * 10 13 14 3 1 3 7 42 48 10: 5 9 * 14 15 2 1 2 5 49 53 11: 6 7 * 12 16 2 1 2 5 54 58 12: 7 8 11 * 13 16 17 3 1 3 7 59 65 13: 8 9 12 * 14 17 18 3 1 3 7 66 72 14: 9 10 13 * 15 18 19 3 1 3 7 73 79 15: 10 14 * 19 20 2 1 2 5 80 84 16: 11 12 * 17 21 2 1 2 5 85 89 17: 12 13 16 * 18 21 22 3 1 3 7 90 96 18: 13 14 17 * 19 22 23 3 1 3 7 97 103 19: 14 15 18 * 20 23 24 3 1 3 7 104 110 20: 15 19 * 24 25 2 1 2 5 111 115 21: 16 17 * 22 2 1 1 4 116 119 22: 17 18 21 * 23 3 1 1 5 120 124 23: 18 19 22 * 24 3 1 1 5 125 129 24: 19 20 23 * 25 3 1 1 5 130 134 25: 20 24 * 2 1 0 3 135 137 -- -- -- -- -- -- -- -- -- -- -- -- 138 --- Licensing: This code is distributed under the MIT license. Modified: 25 August 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], lists the nodes that make up each triangle, in counterclockwise order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Output, TRIANGULATION_ORDER3_ADJ_COUNT, the number of adjacencies. Output, int ADJ_COL[NODE_NUM+1]. Information about column J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. */ { int adj_num; int i; int n1; int n2; int n3; int node; int triangle; int triangle_order = 3; int triangle2; adj_num = 0; /* Set every node to be adjacent to itself. */ for ( node = 0; node < node_num; node++ ) { adj_col[node] = 1; } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order]; n2 = triangle_node[1+triangle*triangle_order]; n3 = triangle_node[2+triangle*triangle_order]; /* Add edge (1,2) if this is the first occurrence, that is, if the edge (1,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n2-1] = adj_col[n2-1] + 1; } /* Add edge (2,3). */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; } /* Add edge (3,1). */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; } } /* We used ADJ_COL to count the number of entries in each column. Convert it to pointers into the ADJ array. */ for ( node = node_num; 1 <= node; node-- ) { adj_col[node] = adj_col[node-1]; } adj_col[0] = 1; for ( i = 1; i <= node_num; i++ ) { adj_col[i]= adj_col[i-1] + adj_col[i]; } adj_num = adj_col[node_num] - 1; return adj_num; } /******************************************************************************/ int *triangulation_order3_adj_set ( int node_num, int triangle_num, int triangle_node[], int triangle_neighbor[], int adj_num, int adj_col[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_ADJ_SET sets adjacencies in a triangulation. Discussion: This routine is called to set the adjacencies, after the appropriate amount of memory has been set aside for storage. The triangulation is assumed to involve 3-node triangles. Two nodes are "adjacent" if they are both nodes in some triangle. Also, a node is considered to be adjacent to itself. This routine can be used to create the compressed column storage for a linear triangle finite element discretization of Poisson's equation in two dimensions. Diagram: 3 s |\ i | \ d | \ e | \ side 2 | \ 3 | \ | \ 1-------2 side 1 The local node numbering 21-22-23-24-25 |\ |\ |\ |\ | | \| \| \| \| 16-17-18-19-20 |\ |\ |\ |\ | | \| \| \| \| 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 A sample grid Below, we have a chart that summarizes the adjacency relationships in the sample grid. On the left, we list the node, and its neighbors, with an asterisk to indicate the adjacency of the node to itself (in some cases, you want to count this self adjacency and in some you don't). On the right, we list the number of adjancencies to lower-indexed nodes, to the node itself, to higher-indexed nodes, the total number of adjacencies for this node, and the location of the first and last entries required to list this set of adjacencies in a single list of all the adjacencies. N Adjacencies Below Self Above Total First Last -- -- -- -- -- -- -- -- -- -- -- -- --- 0 1: * 2 6 0 1 2 3 1 3 2: 1 * 3 6 7 1 1 3 5 4 8 3: 2 * 4 7 8 1 1 3 5 9 13 4: 3 * 5 8 9 1 1 3 5 14 18 5: 4 * 9 10 1 1 2 4 19 22 6: 1 2 * 7 11 2 1 2 5 23 27 7: 2 3 6 * 8 11 12 3 1 3 7 28 34 8: 3 4 7 * 9 12 13 3 1 3 7 35 41 9: 4 5 8 * 10 13 14 3 1 3 7 42 48 10: 5 9 * 14 15 2 1 2 5 49 53 11: 6 7 * 12 16 2 1 2 5 54 58 12: 7 8 11 * 13 16 17 3 1 3 7 59 65 13: 8 9 12 * 14 17 18 3 1 3 7 66 72 14: 9 10 13 * 15 18 19 3 1 3 7 73 79 15: 10 14 * 19 20 2 1 2 5 80 84 16: 11 12 * 17 21 2 1 2 5 85 89 17: 12 13 16 * 18 21 22 3 1 3 7 90 96 18: 13 14 17 * 19 22 23 3 1 3 7 97 103 19: 14 15 18 * 20 23 24 3 1 3 7 104 110 20: 15 19 * 24 25 2 1 2 5 111 115 21: 16 17 * 22 2 1 1 4 116 119 22: 17 18 21 * 23 3 1 1 5 120 124 23: 18 19 22 * 24 3 1 1 5 125 129 24: 19 20 23 * 25 3 1 1 5 130 134 25: 20 24 * 2 1 0 3 135 137 -- -- -- -- -- -- -- -- -- -- -- -- 138 --- Licensing: This code is distributed under the MIT license. Modified: 25 August 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], lists the nodes that make up each triangle in counterclockwise order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int ADJ_NUM, the number of adjacencies. Input, int ADJ_COL[NODE_NUM+1]. Information about column J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. Output, int TRIANGULATION_ORDER3_ADJ_SET[ADJ_NUM], the adjacency information. */ { int *adj; int *adj_copy; int k; int k1; int k2; int n1; int n2; int n3; int node; int triangle; int triangle2; int triangle_order = 3; adj = ( int * ) malloc ( adj_num * sizeof ( int ) ); for ( k = 0; k < adj_num; k++ ) { adj[k] = -1; } adj_copy = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( node = 0; node < node_num; node++ ) { adj_copy[node] = adj_col[node]; } /* Set every node to be adjacent to itself. */ for ( node = 1; node <= node_num; node++ ) { adj[adj_copy[node-1]-1] = node; adj_copy[node-1] = adj_copy[node-1] + 1; } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order]; n2 = triangle_node[1+triangle*triangle_order]; n3 = triangle_node[2+triangle*triangle_order]; /* Add edge (1,2) if this is the first occurrence, that is, if the edge (1,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n1-1]-1] = n2; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n2-1]-1] = n1; adj_copy[n2-1] = adj_copy[n2-1] + 1; } /* Add edge (2,3). */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n2-1]-1] = n3; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n3-1]-1] = n2; adj_copy[n3-1] = adj_copy[n3-1] + 1; } /* Add edge (3,1). */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n1-1]-1] = n3; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n3-1]-1] = n1; adj_copy[n3-1] = adj_copy[n3-1] + 1; } } /* Ascending sort the entries for each node. */ for ( node = 1; node <= node_num; node++ ) { k1 = adj_col[node-1]; k2 = adj_col[node]-1; i4vec_sort_heap_a ( k2+1-k1, adj+k1-1 ); } free ( adj_copy ); return adj; } /******************************************************************************/ void triangulation_order3_adj_set2 ( int node_num, int triangle_num, int triangle_node[], int triangle_neighbor[], int adj_num, int adj_col[], int ia[], int ja[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_ADJ_SET2 sets adjacencies in a triangulation. Discussion: This routine is called to set up the arrays IA and JA that record which nodes are adjacent in a triangulation. The triangulation is assumed to involve 3-node triangles. Two nodes are "adjacent" if they are both nodes in some triangle. Also, a node is considered to be adjacent to itself. This routine can be used to create the compressed column storage for a linear triangle finite element discretization of Poisson's equation in two dimensions. Diagram: 3 s |\ i | \ d | \ e | \ side 2 | \ 3 | \ | \ 1-------2 side 1 The local node numbering 21-22-23-24-25 |\ |\ |\ |\ | | \| \| \| \| 16-17-18-19-20 |\ |\ |\ |\ | | \| \| \| \| 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 A sample grid Below, we have a chart that summarizes the adjacency relationships in the sample grid. On the left, we list the node, and its neighbors, with an asterisk to indicate the adjacency of the node to itself (in some cases, you want to count this self adjacency and in some you don't). On the right, we list the number of adjancencies to lower-indexed nodes, to the node itself, to higher-indexed nodes, the total number of adjacencies for this node, and the location of the first and last entries required to list this set of adjacencies in a single list of all the adjacencies. N Adjacencies Below Self Above Total First Last -- -- -- -- -- -- -- -- -- -- -- -- --- 0 1: * 2 6 0 1 2 3 1 3 2: 1 * 3 6 7 1 1 3 5 4 8 3: 2 * 4 7 8 1 1 3 5 9 13 4: 3 * 5 8 9 1 1 3 5 14 18 5: 4 * 9 10 1 1 2 4 19 22 6: 1 2 * 7 11 2 1 2 5 23 27 7: 2 3 6 * 8 11 12 3 1 3 7 28 34 8: 3 4 7 * 9 12 13 3 1 3 7 35 41 9: 4 5 8 * 10 13 14 3 1 3 7 42 48 10: 5 9 * 14 15 2 1 2 5 49 53 11: 6 7 * 12 16 2 1 2 5 54 58 12: 7 8 11 * 13 16 17 3 1 3 7 59 65 13: 8 9 12 * 14 17 18 3 1 3 7 66 72 14: 9 10 13 * 15 18 19 3 1 3 7 73 79 15: 10 14 * 19 20 2 1 2 5 80 84 16: 11 12 * 17 21 2 1 2 5 85 89 17: 12 13 16 * 18 21 22 3 1 3 7 90 96 18: 13 14 17 * 19 22 23 3 1 3 7 97 103 19: 14 15 18 * 20 23 24 3 1 3 7 104 110 20: 15 19 * 24 25 2 1 2 5 111 115 21: 16 17 * 22 2 1 1 4 116 119 22: 17 18 21 * 23 3 1 1 5 120 124 23: 18 19 22 * 24 3 1 1 5 125 129 24: 19 20 23 * 25 3 1 1 5 130 134 25: 20 24 * 2 1 0 3 135 137 -- -- -- -- -- -- -- -- -- -- -- -- 138 --- For this example, the initial portion of the IA and JA arrays will be: (1,1), (1,2), (1,6), (2,1), (2,2), (2,3), (2,6), (2,7), (3,2), (3,3), (3,4), (3,7), (3,8), ... (25,20), (25,24), (25,25) for a total of 137 pairs of values. Licensing: This code is distributed under the MIT license. Modified: 15 July 2007 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], lists the nodes that make up each triangle in counterclockwise order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int ADJ_NUM, the number of adjacencies. Input, int ADJ_COL[NODE_NUM+1]. Information about column J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. Output, int IA[ADJ_NUM], JA[ADJ_NUM], the adjacency information. */ { int adj; int *adj_copy; int n1; int n2; int n3; int node; int triangle; int triangle2; int triangle_order = 3; for ( adj = 0; adj < adj_num; adj++ ) { ia[adj] = -1; } for ( adj = 0; adj < adj_num; adj++ ) { ja[adj] = -1; } adj_copy = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( node = 0; node < node_num; node++ ) { adj_copy[node] = adj_col[node]; } /* Set every node to be adjacent to itself. */ for ( node = 1; node <= node_num; node++ ) { ia[adj_copy[node-1]-1] = node; ja[adj_copy[node-1]-1] = node; adj_copy[node-1] = adj_copy[node-1] + 1; } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order]; n2 = triangle_node[1+triangle*triangle_order]; n3 = triangle_node[2+triangle*triangle_order]; /* Add edge (1,2) if this is the first occurrence, that is, if the edge (1,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ia[adj_copy[n1-1]-1] = n1; ja[adj_copy[n1-1]-1] = n2; adj_copy[n1-1] = adj_copy[n1-1] + 1; ia[adj_copy[n2-1]-1] = n2; ja[adj_copy[n2-1]-1] = n1; adj_copy[n2-1] = adj_copy[n2-1] + 1; } /* Add edge (2,3). */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ia[adj_copy[n2-1]-1] = n2; ja[adj_copy[n2-1]-1] = n3; adj_copy[n2-1] = adj_copy[n2-1] + 1; ia[adj_copy[n3-1]-1] = n3; ja[adj_copy[n3-1]-1] = n2; adj_copy[n3-1] = adj_copy[n3-1] + 1; } /* Add edge (3,1). */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { ia[adj_copy[n1-1]-1] = n1; ja[adj_copy[n1-1]-1] = n3; adj_copy[n1-1] = adj_copy[n1-1] + 1; ia[adj_copy[n3-1]-1] = n3; ja[adj_copy[n3-1]-1] = n1; adj_copy[n3-1] = adj_copy[n3-1] + 1; } } /* Lexically sort the IA, JA values. */ i4vec2_sort_a ( adj_num, ia, ja ); free ( adj_copy ); return; } /******************************************************************************/ int *triangulation_order3_adjacency ( int node_num, int element_num, int element_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_ADJACENCY computes the full adjacency matrix Licensing: This code is distributed under the MIT license. Modified: 01 March 2014 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes in the triangulation. Input, int ELEMENT_NUM, the number of triangles in the triangulation. Input, int ELEMENT_NODE[3*ELEMENT_NUM], the nodes making up each triangle. Output, int TRIANGULATION_ORDER3_ADJACENCY[NODE_NUM*NODE_NUM], the adjacency matrix. ADJ(I,J) is 1 if nodes I and J are adjacent, that is, they are immediate neighbors on an edge of the triangulation. */ { int *adj; int element; int i; int j; int k; adj = ( int * ) malloc ( node_num * node_num * sizeof ( int ) ); for ( j = 0; j < node_num; j++ ) { for ( i = 0; i < node_num; i++ ) { adj[i+j*node_num] = 0; } } for ( element = 0; element < element_num; element++ ) { i = element_node[0+element*3]; j = element_node[1+element*3]; k = element_node[2+element*3]; adj[i+j*node_num] = 1; adj[i+k*node_num] = 1; adj[j+i*node_num] = 1; adj[j+k*node_num] = 1; adj[k+i*node_num] = 1; adj[k+j*node_num] = 1; } return adj; } /******************************************************************************/ int triangulation_order3_boundary_edge_count ( int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_BOUNDARY_EDGE_COUNT counts the boundary edges. Discussion: This routine is given a triangulation, an abstract list of triples of nodes. It is assumed that the nodes in each triangle are listed in a counterclockwise order, although the routine should work if the nodes are consistently listed in a clockwise order as well. It is assumed that each edge of the triangulation is either * an INTERIOR edge, which is listed twice, once with positive orientation and once with negative orientation, or; * a BOUNDARY edge, which will occur only once. This routine should work even if the region has holes - as long as the boundary of the hole comprises more than 3 edges! Licensing: This code is distributed under the MIT license. Modified: 12 June 2005 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. These should be listed in counterclockwise order. Output, integer TRIANGULATION_ORDER3_BOUNDARY_EDGE_COUNT, the number of boundary edges. */ { int boundary_edge_num; int e1; int e2; int *edge; int interior_edge_num; int j; int m; int n; int unique_num; m = 2; n = 3 * triangle_num; /* Set up the edge array. */ edge = ( int * ) malloc ( m * n * sizeof ( int ) ); for ( j = 0; j < triangle_num; j++ ) { edge[0+(j )*m] = triangle_node[0+j*3]; edge[1+(j )*m] = triangle_node[1+j*3]; edge[0+(j+ triangle_num)*m] = triangle_node[1+j*3]; edge[1+(j+ triangle_num)*m] = triangle_node[2+j*3]; edge[0+(j+2*triangle_num)*m] = triangle_node[2+j*3]; edge[1+(j+2*triangle_num)*m] = triangle_node[0+j*3]; } /* In each column, force the smaller entry to appear first. */ for ( j = 0; j < n; j++ ) { e1 = i4_min ( edge[0+j*m], edge[1+j*m] ); e2 = i4_max ( edge[0+j*m], edge[1+j*m] ); edge[0+j*m] = e1; edge[1+j*m] = e2; } /* Ascending sort the column array. */ i4col_sort_a ( m, n, edge ); /* Get the number of unique columns in EDGE. */ unique_num = i4col_sorted_unique_count ( m, n, edge ); interior_edge_num = 3 * triangle_num - unique_num; boundary_edge_num = 3 * triangle_num - 2 * interior_edge_num; free ( edge ); return boundary_edge_num; } /******************************************************************************/ int triangulation_order3_boundary_edge_count_euler ( int node_num, int triangle_num, int hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_BOUNDARY_EDGE_COUNT_EULER counts boundary edges. Discussion: We assume we are given information about a triangulation of a set of nodes in the plane. Given the number of nodes and triangles, we are going to apply Euler's formula to determine the number of edges that lie on the boundary of the set of nodes. The number of faces, including the infinite face and internal holes, is TRIANGLE_NUM + HOLE_NUM + 1. Let BOUNDARY_NUM denote the number of edges on the boundary. Each of the TRIANGLE_NUM triangles uses three edges. Every edge occurs in two different faces, so the number of edges must be ( 3 * TRIANGLE_NUM + BOUNDARY_NUM ) / 2. The number of nodes used in the triangulation is NODE_NUM. Euler's formula asserts that, for a simple connected figure in the plane with no edge crossings, NODE_NUM nodes, EDGE_NUM edges and FACE_NUM faces: NODE_NUM - EDGE_NUM + FACE_NUM = 2 In our context, this becomes NODE_NUM - ( 3 * TRIANGLE_NUM + BOUNDARY_NUM ) / 2 + TRIANGLE_NUM + HOLE_NUM + 1 = 2 or BOUNDARY_NUM = 2 * NODE_NUM + 2 * HOLE_NUM - TRIANGLE_NUM - 2 Licensing: This code is distributed under the MIT license. Modified: 11 June 2005 Author: John Burkardt Reference: Marc deBerg, Marc Krevald, Mark Overmars, Otfried Schwarzkopf, Computational Geometry, Springer, 2000, ISBN: 3-540-65620-0. Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int HOLE_NUM, the number of holes. Output, int TRIANGULATION_BOUNDARY_COUNT, the number of edges that lie on the convex hull of the triangulation. */ { return ( 2 * node_num + 2 * hole_num - triangle_num - 2 ); } /******************************************************************************/ int *triangulation_order3_boundary_node ( int node_num, int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_BOUNDARY_NODE indicates nodes on the boundary. Discussion: This routine is given a triangulation, an abstract list of triples of nodes. It is assumed that the nodes in each triangle are listed in a counterclockwise order, although the routine should work if the nodes are consistently listed in a clockwise order as well. It is assumed that each edge of the triangulation is either * an INTERIOR edge, which is listed twice, once with positive orientation and once with negative orientation, or; * a BOUNDARY edge, which will occur only once. This routine should work even if the region has holes - as long as the boundary of the hole comprises more than 3 edges! Licensing: This code is distributed under the MIT license. Modified: 25 January 2013 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. These should be listed in counterclockwise order. Output, int TRIANGULATION_ORDER3_BOUNDARY_NODE[NODE_NUM], is TRUE if the node is on a boundary edge. */ { int e1; int e2; int *edge; int equal; int i; int j; int m; int n; int *node_boundary; m = 2; n = 3 * triangle_num; /* Set up the edge array. */ edge = ( int * ) malloc ( m * n * sizeof ( int ) ); for ( j = 0; j < triangle_num; j++ ) { edge[0+(j )*m] = triangle_node[0+j*3]; edge[1+(j )*m] = triangle_node[1+j*3]; edge[0+(j+ triangle_num)*m] = triangle_node[1+j*3]; edge[1+(j+ triangle_num)*m] = triangle_node[2+j*3]; edge[0+(j+2*triangle_num)*m] = triangle_node[2+j*3]; edge[1+(j+2*triangle_num)*m] = triangle_node[0+j*3]; } /* In each column, force the smaller entry to appear first. */ for ( j = 0; j < n; j++ ) { e1 = i4_min ( edge[0+j*m], edge[1+j*m] ); e2 = i4_max ( edge[0+j*m], edge[1+j*m] ); edge[0+j*m] = e1; edge[1+j*m] = e2; } /* Ascending sort the column array. */ i4col_sort_a ( m, n, edge ); /* Records which appear twice are internal edges and can be ignored. */ node_boundary = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( i = 0; i < node_num; i++ ) { node_boundary[i] = 0; } j = 0; while ( j < 3 * triangle_num ) { j = j + 1; if ( j == 3 * triangle_num ) { for ( i = 0; i < m; i++ ) { node_boundary[edge[i+(j-1)*m]-1] = 1; } break; } equal = 1; for ( i = 0; i < m; i++ ) { if ( edge[i+(j-1)*m] != edge[i+j*m] ) { equal = 0; } } if ( equal ) { j = j + 1; } else { for ( i = 0; i < m; i++ ) { node_boundary[edge[i+(j-1)*m]-1] = 1; } } } free ( edge ); return node_boundary; } /******************************************************************************/ int triangulation_order3_check ( int node_num, int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_CHECK makes some simple checks on a triangulation. Discussion: Because this routine does not receive the physical coordinates of the nodes, it cannot ensure that the triangulation is maximal, that is, that no more triangles can be created. Licensing: This code is distributed under the MIT license. Modified: 14 June 2005 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. These should be listed in counterclockwise order. Output, int TRIANGULATION_CHECK, error flag. 0, no error occurred. nonzero, an error occurred, the triangulation is not valid. */ { int boundary_num; int euler; int i; int j; int *used; /* Checks 1 and 2: node_num must be at least 3. TRIANGLE_NUM must be at least 1. */ if ( node_num < 3 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " The number of nodes is less than 3!\n" ); return 1; } if ( triangle_num < 1 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " The number of triangles is less than 1!\n" ); return 2; } /* Checks 3 and 4: Verify that all node values are greater than or equal to 1 and less than or equal to node_num. */ for ( j = 0; j < triangle_num; j++ ) { for ( i = 0; i < 3; i++ ) { if ( triangle_node[i+j*3] < 1 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " Some vertices are less than 1!\n" ); return 3; } } } for ( j = 0; j < triangle_num; j++ ) { for ( i = 0; i < 3; i++ ) { if ( node_num < triangle_node[i+j*3] ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " Some vertices are greater than node_num!\n" ); return 4; } } } /* Check 5: Verify that every node is used at least once. */ used = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( i = 0; i < node_num; i++ ) { used[i] = 0; } for ( j = 0; j < triangle_num; j++ ) { for ( i = 0; i < 3; i++ ) { used[triangle_node[i+j*3]-1] = used[triangle_node[i+j*3]-1] + 1; } } for ( i = 0; i < node_num; i++ ) { if ( used[i] == 0 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " Some nodes are never used as triangle vertices!\n" ); printf ( " First example is node %d\n", i + 1 ); free ( used ); return 5; } } free ( used ); /* Check 6: Verify that no node is repeated in a triangle. */ for ( j = 0; j < triangle_num; j++ ) { if ( triangle_node[0+j*3] == triangle_node[1+j*3] || triangle_node[1+j*3] == triangle_node[2+j*3] || triangle_node[2+j*3] == triangle_node[0+j*3] ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " A triangle contains a null edge!\n" ); return 6; } } /* Check 7: Verify that no edge is repeated, and that repeated edges occur in negated pairs. */ boundary_num = triangulation_order3_edge_check ( triangle_num, triangle_node ); if ( boundary_num < 0 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " Some edges are repeated or given in the wrong direction!\n" ); return 7; } /* Check 8: Does the triangulation satisfy Euler's criterion? If not, then the triangulation is not proper. (For instance, there might be a hole in the interior.) */ euler = boundary_num + triangle_num + 2 - 2 * node_num; if ( euler != 0 ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_CHECK - Fatal error!\n" ); printf ( " The triangulation does not satisfy Euler's criterion!\n" ); return 8; } return 0; } /******************************************************************************/ int triangulation_order3_edge_check ( int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_EDGE_CHECK checks the edges of a triangulation. Discussion: Converted from a row-based to a column-based calculation. Licensing: This code is distributed under the MIT license. Modified: 12 February 2007 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up each triangle. Output, int TRIANGULATION_EDGE_CHECK is negative if an error was detected; otherwise, it is the number of edges that lie on the boundary. */ { int boundary_num; int i; int j; int k; int *col; int tri; int triangle_order = 3; /* Step 1. From the list of nodes for triangle T, of the form: (I,J,K) construct the three neighbor relations: (I,J,+1) or (J,I,-1), (J,K,+1) or (K,J,-1), (K,I,+1) or (I,K,-1) where we choose (I,J,+1) if I < J, or else (J,I,-1) and so on. */ col = ( int * ) malloc ( 3 * 3 * triangle_num * sizeof ( int ) ); for ( tri = 0; tri < triangle_num; tri++ ) { i = triangle_node[0+tri*triangle_order]; j = triangle_node[1+tri*triangle_order]; k = triangle_node[2+tri*triangle_order]; if ( i < j ) { col[0+(3*tri+0)*3] = i; col[1+(3*tri+0)*3] = j; col[2+(3*tri+0)*3] = +1; } else { col[0+(3*tri+0)*3] = j; col[1+(3*tri+0)*3] = i; col[2+(3*tri+0)*3] = -1; } if ( j < k ) { col[0+(3*tri+1)*3] = j; col[1+(3*tri+1)*3] = k; col[2+(3*tri+1)*3] = +1; } else { col[0+(3*tri+1)*3] = k; col[1+(3*tri+1)*3] = j; col[2+(3*tri+1)*3] = -1; } if ( k < i ) { col[0+(3*tri+2)*3] = k; col[1+(3*tri+2)*3] = i; col[2+(3*tri+2)*3] = +1; } else { col[0+(3*tri+2)*3] = i; col[1+(3*tri+2)*3] = k; col[2+(3*tri+2)*3] = -1; } } /* Step 2. Perform an ascending dictionary sort on the neighbor relations. */ i4col_sort_a ( 3, 3*triangle_num, col ); /* Step 3. If any record occurs twice, we have an error. Unpaired records lie on the convex hull. */ i = 0; boundary_num = 0; while ( i < 3 * triangle_num ) { i = i + 1; if ( i == 3 * triangle_num ) { boundary_num = boundary_num + 1; } else { if ( col[0+(i-1)*3] == col[0+i*3] && col[1+(i-1)*3] == col[1+i*3] ) { if ( col[2+(i-1)*3] == col[2+i*3] ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_EDGE_CHECK - Warning!\n" ); printf ( " An edge occurs twice.\n" ); free ( col ); boundary_num = -1; return boundary_num; } else { i = i + 1; } } else { boundary_num = boundary_num + 1; } } } free ( col ); return boundary_num; } /******************************************************************************/ void triangulation_order3_example1 ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_EXAMPLE1 sets up a sample triangulation. Discussion: This triangulation is actually a Delaunay triangulation. The appropriate input values of NODE_NUM and TRIANGLE_NUM can be determined by calling TRIANGULATION_ORDER3_EXAMPLE1_SIZE first. Licensing: This code is distributed under the MIT license. Modified: 03 January 2007 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Output, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. Output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. Negative values indicate edges that lie on the exterior. */ { # define DIM_NUM 2 # define NODE_NUM 13 # define TRIANGLE_NUM 16 # define TRIANGLE_ORDER 3 int i; static int triangle_neighbor_save[3*TRIANGLE_NUM] = { -4, -13, 2, 1, 4, 3, 2, 5, 7, 2, -43, 8, 3, 8, 6, 5, 9, 7, 3, 6, -3, 5, 4, 10, 6, 10, 12, 9, 8, 11, 12, 10, 14, 9, 11, 13, -23, 12, 16, 11, -47, 15, 16, 14, -50, 13, 15, -39 }; static int triangle_node_save[TRIANGLE_ORDER*TRIANGLE_NUM] = { 3, 4, 1, 3, 1, 2, 3, 2, 8, 2, 1, 5, 8, 2, 13, 8, 13, 9, 3, 8, 9, 13, 2, 5, 9, 13, 7, 7, 13, 5, 6, 7, 5, 9, 7, 6, 10, 9, 6, 6, 5, 12, 11, 6, 12, 10, 6, 11 }; static double node_xy_save[DIM_NUM*NODE_NUM] = { 0.0, 0.0, 2.0, 2.0, -1.0, 3.0, -2.0, 2.0, 8.0, 2.0, 9.0, 5.0, 7.0, 4.0, 5.0, 6.0, 6.0, 7.0, 8.0, 8.0, 11.0, 7.0, 10.0, 4.0, 6.0, 4.0 }; for ( i = 0; i < 3 * TRIANGLE_NUM; i++ ) { triangle_neighbor[i] = triangle_neighbor_save[i]; } for ( i = 0; i < TRIANGLE_ORDER * TRIANGLE_NUM; i++ ) { triangle_node[i] = triangle_node_save[i]; } for ( i = 0; i < DIM_NUM * NODE_NUM; i++ ) { node_xy[i] = node_xy_save[i]; } return; # undef DIM_NUM # undef NODE_NUM # undef TRIANGLE_NUM # undef TRIANGLE_ORDER } /******************************************************************************/ void triangulation_order3_example1_size ( int *node_num, int *triangle_num, int *hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_EXAMPLE1_SIZE sets sizes for a sample triangulation. Licensing: This code is distributed under the MIT license. Modified: 13 June 2005 Author: John Burkardt Parameters: Output, int *NODE_NUM, the number of nodes. Output, int *TRIANGLE_NUM, the number of triangles. Output, int *HOLE_NUM, the number of holes. */ { *node_num = 13; *triangle_num = 16; *hole_num = 0; return; } /******************************************************************************/ void triangulation_order3_example2 ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_EXAMPLE2 sets up a sample triangulation. Discussion: This triangulation is actually a Delaunay triangulation. The appropriate input values of NODE_NUM and TRIANGLE_NUM can be determined by calling TRIANGULATION_ORDER3_EXAMPLE2_SIZE first. Diagram: 21-22-23-24-25 |\ |\ |\ |\ | | \| \| \| \| 16-17-18-19-20 |\ |\ |\ |\ | | \| \| \| \| 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 Licensing: This code is distributed under the MIT license. Modified: 03 January 2007 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Output, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. Output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. Negative values indicate edges that lie on the exterior. */ { # define DIM_NUM 2 # define NODE_NUM 25 # define TRIANGLE_NUM 32 # define TRIANGLE_ORDER 3 int i; static int triangle_neighbor_save[3*TRIANGLE_NUM] = { -1, 2, -1, 9, 1, 3, -1, 4, 2, 11, 3, 5, -1, 6, 4, 13, 5, 7, -1, 8, 6, 15, 7, -1, 2, 10, -1, 17, 9, 11, 4, 12, 10, 19, 11, 13, 6, 14, 12, 21, 13, 15, 8, 16, 14, 23, 15, -1, 10, 18, -1, 25, 17, 19, 12, 20, 18, 27, 19, 21, 14, 22, 20, 29, 21, 23, 16, 24, 22, 31, 23, -1, 18, 26, -1, -1, 25, 27, 20, 28, 26, -1, 27, 29, 22, 30, 28, -1, 29, 31, 24, 32, 30, -1, 31, -1 }; static int triangle_node_save[TRIANGLE_ORDER*TRIANGLE_NUM] = { 1, 2, 6, 7, 6, 2, 2, 3, 7, 8, 7, 3, 3, 4, 8, 9, 8, 4, 4, 5, 9, 10, 9, 5, 6, 7, 11, 12, 11, 7, 7, 8, 12, 13, 12, 8, 8, 9, 13, 14, 13, 9, 9, 10, 14, 15, 14, 10, 11, 12, 16, 17, 16, 12, 12, 13, 17, 18, 17, 13, 13, 14, 18, 19, 18, 14, 14, 15, 19, 20, 19, 15, 16, 17, 21, 22, 21, 17, 17, 18, 22, 23, 22, 18, 18, 19, 23, 24, 23, 19, 19, 20, 24, 25, 24, 20 }; static double node_xy_save[DIM_NUM*NODE_NUM] = { 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0, 3.0, 1.0, 4.0, 1.0, 0.0, 2.0, 1.0, 2.0, 2.0, 2.0, 3.0, 2.0, 4.0, 2.0, 0.0, 3.0, 1.0, 3.0, 2.0, 3.0, 3.0, 3.0, 4.0, 3.0, 0.0, 4.0, 1.0, 4.0, 2.0, 4.0, 3.0, 4.0, 4.0, 4.0 }; for ( i = 0; i < 3 * TRIANGLE_NUM; i++ ) { triangle_neighbor[i] = triangle_neighbor_save[i]; } for ( i = 0; i < TRIANGLE_ORDER * TRIANGLE_NUM; i++ ) { triangle_node[i] = triangle_node_save[i]; } for ( i = 0; i < DIM_NUM * NODE_NUM; i++ ) { node_xy[i] = node_xy_save[i]; } return; # undef DIM_NUM # undef NODE_NUM # undef TRIANGLE_NUM # undef TRIANGLE_ORDER } /******************************************************************************/ void triangulation_order3_example2_size ( int *node_num, int *triangle_num, int *hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_EXAMPLE2_SIZE sets sizes for a sample triangulation. Diagram: 21-22-23-24-25 |\ |\ |\ |\ | | \| \| \| \| 16-17-18-19-20 |\ |\ |\ |\ | | \| \| \| \| 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 Licensing: This code is distributed under the MIT license. Modified: 03 January 2007 Author: John Burkardt Parameters: Output, int *NODE_NUM, the number of nodes. Output, int *TRIANGLE_NUM, the number of triangles. Output, int *HOLE_NUM, the number of holes. */ { *node_num = 25; *triangle_num = 32; *hole_num = 0; return; } /******************************************************************************/ void triangulation_order3_neighbor ( int triangle_num, int triangle_node[], int t1, int s1, int *t2, int *s2 ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_NEIGHBOR determines a neighbor of a given triangle. Discussion: A set of nodes is given. A triangulation of the nodes has been defined and recorded in TRIANGLE_NODE. The TRIANGLE_NODE data structure records triangles as sets of three nodes, N1, N2, N3, that implicitly define three sides, being the line segments N1-N2, N2-N3, and N3-N1. The nodes of the triangle are listed in counterclockwise order. This means that if two triangles share a side, then the nodes defining that side occur in the order (N1,N2) for one triangle, and (N2,N1) for the other. The routine is given a triangle and a side, and asked to find another triangle (if any) that shares that side. The routine simply searches the TRIANGLE_NODE structure for an occurrence of the nodes in the opposite order. Licensing: This code is distributed under the MIT license. Modified: 14 October 2003 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM, the number of triangles. Input/output, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that define each triangle. Input, int T1, the index of the triangle. Input, int S1, the index of the triangle side. Output, int *T2, the index of the triangle which is the neighbor to T1 on side S1, or -1 if there is no such neighbor. Output, int *S2, the index of the side of triangle T2 which is shared with triangle T1, or -1 if there is no such neighbor. */ { int n1; int n2; int s; int ss; int t; n1 = triangle_node[s1-1+(t1-1)*3]; ss = i4_wrap ( s1+1, 1, 3 ); n2 = triangle_node[ss-1+(t1-1)*3]; for ( t = 0; t < triangle_num; t++ ) { for ( s = 0; s < 3; s++ ) { if ( triangle_node[s+t*3] == n1 ) { ss = i4_wrap ( s-1, 0, 2 ); if ( triangle_node[ss+t*3] == n2 ) { *t2 = t + 1; *s2 = ss + 1; return; } } } } *t2 = -1; *s2 = -1; return; } /******************************************************************************/ void triangulation_order3_neighbor_nodes ( int node_num, int triangle_num, int triangle_node[], int nabes_first[], int nabes_num[], int nabes_max, int *nabes_dim, int nabes[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_NEIGHBOR_NODES determines node neighbors. Example: On input, the triangle data structure is: Triangle Nodes -------- ---------- 1 3, 4, 1 2 3, 1, 2 3 3, 2, 6 4 2, 1, 5 5 6, 2, 5 On output, the auxilliary neighbor arrays are: Node Num First ---- --- ----- 1 4 1 2 4 5 3 4 9 4 2 13 5 3 15 6 3 18 and the neighbor array is: Position Node -------- ---- 1 2 2 3 3 4 4 5 ----------- 5 1 6 3 7 5 8 6 ----------- 9 1 10 2 11 4 12 6 ----------- 13 1 14 3 ----------- 15 1 16 2 17 6 ----------- 18 2 19 3 20 5 Licensing: This code is distributed under the MIT license. Modified: 10 November 2003 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up each triangle. Output, int NABES_FIRST[NODE_NUM], the index in NABES of the first neighbor in the list for each node. Output, int NABES_NUM[NODE_NUM], the number of neighbors of each node. Input, int NABES_MAX, the maximum dimension of NABES. Output, int *NABES_DIM, the dimension of NABES. Output, int NABES[*NABES_DIM], a list of the neighbors of all the nodes. Neighbors of node 1 are listed first, and so on. */ { int i; int i_current; int j; int k; int n; int nabe; int *nabes1; int tri; nabes = ( int * ) malloc ( nabes_max * sizeof ( int ) ); nabes1 = ( int * ) malloc ( nabes_max * sizeof ( int ) ); /* Step 1. From the triangle list (I,J,K) construct the neighbor relations: (I,J), (J,K), (K,I), (J,I), (K,J), (I,K). */ n = 0; for ( tri = 0; tri < triangle_num; tri++ ) { i = triangle_node[0+tri*3]; j = triangle_node[1+tri*3]; k = triangle_node[2+tri*3]; nabes1[n] = i; nabes1[n+1] = i; nabes1[n+2] = j; nabes1[n+3] = j; nabes1[n+4] = k; nabes1[n+5] = k; nabes[n] = j; nabes[n+1] = k; nabes[n+2] = i; nabes[n+3] = k; nabes[n+4] = i; nabes[n+5] = j; n = n + 6; } /* Step 2. Dictionary sort the neighbor relations. */ i4vec2_sort_a ( n, nabes1, nabes ); /* Step 3. Remove duplicate entries. */ n = i4vec2_sorted_unique ( n, nabes1, nabes ); /* Step 4. Construct the NABES_NUM and NABES_FIRST data. */ for ( i = 0; i < node_num; i++ ) { nabes_num[i] = 0; } for ( i = 0; i < node_num; i++ ) { nabes_first[i] = 0; } i_current = 0; for ( nabe = 1; nabe <= n; nabe++ ) { i = nabes1[nabe-1]; if ( i == i_current ) { nabes_num[i-1] = nabes_num[i-1] + 1; } else { i_current = i; nabes_first[i-1] = nabe; nabes_num[i-1] = 1; } } *nabes_dim = n; free ( nabes1 ); return; } /******************************************************************************/ void triangulation_order3_neighbor_nodes_print ( int node_num, int nabes_first[], int nabes_num[], int nabes_dim, int nabes[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_NEIGHBOR_NODES_PRINT prints a node neighbor array. Licensing: This code is distributed under the MIT license. Modified: 11 July 2001 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int NABES_FIRST[NODE_NUM], the index in NABES of the first neighbor in the list for each node. Input, int NABES_NUM[NODE_NUM], the number of neighbors of each node. Input, int NABES_DIM, the dimension of NABES. Input, int NABES[NABES_DIM], a list of the neighbors of all the nodes. Neighbors of node 1 are listed first, and so on. */ { int i; int j; int k; printf ( "\n" ); printf ( " Node Nabes Index List\n" ); printf ( "\n" ); for ( i = 0; i < node_num; i++ ) { printf ( "%4d %4d %4d\n", i, nabes_num[i], nabes_first[i] ); k = 0; for ( j = nabes_first[i] - 1; j < nabes_first[i] + nabes_num[i]; j++ ) { if ( k == 10 ) { printf ( "\n" ); printf ( " " ); k = 0; } printf ( "%4d ", nabes[j] ); k = k + 1; } } printf ( "\n" ); return; } /******************************************************************************/ void triangulation_order3_plot ( char *file_name, int node_num, double node_xy[], int triangle_num, int triangle_node[], int node_show, int triangle_show ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_PLOT plots a triangulation of a set of nodes. Discussion: The triangulation is most usually a Delaunay triangulation, but this is not necessary. Licensing: This code is distributed under the MIT license. Modified: 08 June 2009 Author: John Burkardt Parameters: Input, char *FILE_NAME, the name of the output file. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], lists, for each triangle, the indices of the nodes that form the vertices of the triangle. Input, int NODE_SHOW: 0, do not show nodes; 1, show nodes; 2, show nodes and label them. Input, int TRIANGLE_SHOW: 0, do not show triangles; 1, show triangles; 2, show triangles and label them. */ { double ave_x; double ave_y; int circle_size; int delta; int e; FILE *file_unit; int i; int node; int triangle; double x_max; double x_min; int x_ps; int x_ps_max = 576; int x_ps_max_clip = 594; int x_ps_min = 36; int x_ps_min_clip = 18; double x_scale; double y_max; double y_min; int y_ps; int y_ps_max = 666; int y_ps_max_clip = 684; int y_ps_min = 126; int y_ps_min_clip = 108; double y_scale; /* We need to do some figuring here, so that we can determine the range of the data, and hence the height and width of the piece of paper. */ x_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( x_max < node_xy[0+node*2] ) { x_max = node_xy[0+node*2]; } } x_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[0+node*2] < x_min ) { x_min = node_xy[0+node*2]; } } x_scale = x_max - x_min; x_max = x_max + 0.05 * x_scale; x_min = x_min - 0.05 * x_scale; x_scale = x_max - x_min; y_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( y_max < node_xy[1+node*2] ) { y_max = node_xy[1+node*2]; } } y_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[1+node*2] < y_min ) { y_min = node_xy[1+node*2]; } } y_scale = y_max - y_min; y_max = y_max + 0.05 * y_scale; y_min = y_min - 0.05 * y_scale; y_scale = y_max - y_min; if ( x_scale < y_scale ) { delta = r8_nint ( ( double ) ( x_ps_max - x_ps_min ) * ( y_scale - x_scale ) / ( 2.0 * y_scale ) ); x_ps_max = x_ps_max - delta; x_ps_min = x_ps_min + delta; x_ps_max_clip = x_ps_max_clip - delta; x_ps_min_clip = x_ps_min_clip + delta; x_scale = y_scale; } else if ( y_scale < x_scale ) { delta = r8_nint ( ( double ) ( y_ps_max - y_ps_min ) * ( x_scale - y_scale ) / ( 2.0 * x_scale ) ); y_ps_max = y_ps_max - delta; y_ps_min = y_ps_min + delta; y_ps_max_clip = y_ps_max_clip - delta; y_ps_min_clip = y_ps_min_clip + delta; y_scale = x_scale; } file_unit = fopen ( file_name, "wt" ); if ( !file_unit ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_PLOT - Fatal error!\n" ); printf ( " Could not open the output EPS file.\n" ); exit ( 1 ); } fprintf ( file_unit, "%%!PS-Adobe-3.0 EPSF-3.0\n" ); fprintf ( file_unit, "%%%%Creator: triangulation_order3_plot.C\n" ); fprintf ( file_unit, "%%%%Title: %s\n", file_name ); fprintf ( file_unit, "%%%%Pages: 1\n" ); fprintf ( file_unit, "%%%%BoundingBox: %d %d %d %d\n", x_ps_min, y_ps_min, x_ps_max, y_ps_max ); fprintf ( file_unit, "%%%%Document-Fonts: Times-Roman\n" ); fprintf ( file_unit, "%%%%LanguageLevel: 1\n" ); fprintf ( file_unit, "%%%%EndComments\n" ); fprintf ( file_unit, "%%%%BeginProlog\n" ); fprintf ( file_unit, "/inch {72 mul} def\n" ); fprintf ( file_unit, "%%%%EndProlog\n" ); fprintf ( file_unit, "%%%%Page: 1 1\n" ); fprintf ( file_unit, "save\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Increase line width from default 0.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "2 setlinewidth\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB line color to very light gray.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.9000 0.9000 0.9000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw a gray border around the page.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "stroke\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set RGB line color to black.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.0000 0.0000 0.0000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the font and its size:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.50 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Print a title:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 210 702 moveto\n" ); fprintf ( file_unit, "(Pointset) show\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Define a clipping polygon\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "clip newpath\n" ); /* Draw the nodes. */ if ( node_num <= 200 ) { circle_size = 5; } else if ( node_num <= 500 ) { circle_size = 4; } else if ( node_num <= 1000 ) { circle_size = 3; } else if ( node_num <= 5000 ) { circle_size = 2; } else { circle_size = 1; } if ( 1 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw filled dots at each node:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.150 0.750 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d %d 0 360 arc closepath fill\n", x_ps, y_ps, circle_size ); } } /* Label the nodes. */ if ( 2 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the nodes:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to darker blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.250 0.850 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d moveto (%d) show\n", x_ps, y_ps + 5, node + 1 ); } } /* Draw the triangles. */ if ( 1 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.900 0.200 0.100 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw the triangles.\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { fprintf ( file_unit, "newpath\n" ); for ( i = 1; i <= 4; i++ ) { e = i4_wrap ( i, 1, 3 ); node = triangle_node[e-1+triangle*3] - 1; x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); if ( i == 1 ) { fprintf ( file_unit, "%d %d moveto\n", x_ps, y_ps ); } else { fprintf ( file_unit, "%d %d lineto\n", x_ps, y_ps ); } } fprintf ( file_unit, "stroke\n" ); } } /* Label the triangles. */ if ( 2 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the triangles.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to darker red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.950 0.250 0.150 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { ave_x = 0.0; ave_y = 0.0; for ( i = 1; i <= 3; i++ ) { node = triangle_node[i-1+triangle*3] - 1; ave_x = ave_x + node_xy[0+node*2]; ave_y = ave_y + node_xy[1+node*2]; } ave_x = ave_x / 3.0; ave_y = ave_y / 3.0; x_ps = ( int ) ( ( ( x_max - ave_x ) * ( double ) ( x_ps_min ) + ( + ave_x - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - ave_y ) * ( double ) ( y_ps_min ) + ( ave_y - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "%d %d moveto (%d) show\n", x_ps, y_ps, triangle + 1 ); } } fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "restore showpage\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% End of page.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%%%%Trailer\n" ); fprintf ( file_unit, "%%%%EOF\n" ); fclose ( file_unit ); return; } /******************************************************************************/ void triangulation_order3_print ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_PRINT prints information defining a triangulation. Discussion: Triangulations created by R8TRIS2 include extra information encoded in the negative values of TRIANGLE_NEIGHBOR. Because some of the nodes counted in NODE_NUM may not actually be used in the triangulation, I needed to compute the true number of vertices. I added this calculation on 13 October 2001. Ernest Fasse pointed out an error in the indexing of VERTEX_LIST, which was corrected on 19 February 2004. Licensing: This code is distributed under the MIT license. Modified: 11 June 2005 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. If there is no triangle neighbor on a particular side, the value of TRIANGLE_NEIGHBOR should be negative. If the triangulation data was created by R8TRIS2, then there is more information encoded in the negative values. */ { # define DIM_NUM 2 int boundary_num; int i; int j; int k; int n1; int n2; int s; int s1; int s2; int skip; int t; int *vertex_list; int vertex_num; printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_PRINT\n" ); printf ( " Information defining a triangulation.\n" ); printf ( "\n" ); printf ( " The number of nodes is %d\n", node_num ); r8mat_transpose_print ( DIM_NUM, node_num, node_xy, " Node coordinates" ); printf ( "\n" ); printf ( " The number of triangles is %d\n", triangle_num ); printf ( "\n" ); printf ( " Sets of three nodes are used as vertices of\n" ); printf ( " the triangles. For each triangle, the nodes\n" ); printf ( " are listed in counterclockwise order.\n" ); i4mat_transpose_print ( 3, triangle_num, triangle_node, " Triangle nodes" ); printf ( "\n" ); printf ( " On each side of a given triangle, there is either\n" ); printf ( " another triangle, or a piece of the convex hull.\n" ); printf ( " For each triangle, we list the indices of the three\n" ); printf ( " neighbors, or (if negative) the codes of the\n" ); printf ( " segments of the convex hull.\n" ); i4mat_transpose_print ( 3, triangle_num, triangle_neighbor, " Triangle neighbors" ); /* Determine VERTEX_NUM, the number of vertices. */ vertex_list = ( int * ) malloc ( 3 * triangle_num * sizeof ( int ) ); k = 0; for ( t = 0; t < triangle_num; t++ ) { for ( s = 0; s < 3; s++ ) { vertex_list[k] = triangle_node[s+t*3]; k = k + 1; } } i4vec_sort_heap_a ( 3*triangle_num, vertex_list ); vertex_num = i4vec_sorted_unique ( 3*triangle_num, vertex_list ); free ( vertex_list ); /* Determine the number of boundary points. */ boundary_num = 2 * vertex_num - triangle_num - 2; printf ( "\n" ); printf ( " The number of boundary points is %d\n", boundary_num ); printf ( "\n" ); printf ( " The segments that make up the convex hull can be\n" ); printf ( " determined from the negative entries of the triangle\n" ); printf ( " neighbor list.\n" ); printf ( "\n" ); printf ( " # Tri Side N1 N2\n" ); printf ( "\n" ); skip = 0; k = 0; for ( i = 0; i < triangle_num; i++ ) { for ( j = 0; j < 3; j++ ) { if ( triangle_neighbor[j+i*3] < 0 ) { s = -triangle_neighbor[j+i*3]; t = s / 3; if ( t < 1 || triangle_num < t ) { printf ( "\n" ); printf ( " Sorry, this data does not use the R8TRIS2\n" ); printf ( " convention for convex hull segments.\n" ); skip = 1; break; } s1 = ( s % 3 ) + 1; s2 = i4_wrap ( s1+1, 1, 3 ); k = k + 1; n1 = triangle_node[s1-1+(t-1)*3]; n2 = triangle_node[s2-1+(t-1)*3]; printf ( " %4d %4d %4d %4d %4d\n", k, t, s1, n1, n2 ); } } if ( skip ) { break; } } return; # undef DIM_NUM } /******************************************************************************/ void triangulation_order3_quad ( int node_num, double node_xy[], int triangle_order, int triangle_num, int triangle_node[], void quad_fun ( int n, double xy_vec[], double f_vec[] ), int quad_num, double quad_xy[], double quad_w[], double *quad_value, double *region_area ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_QUAD approximates an integral over a triangulation. Discussion: The routine will accept triangulations of order higher than 3. However, only the first three nodes (the vertices) of each triangle will be used. This will still produce correct results for higher order triangulations, as long as the sides of the triangle are straight. We assume that the vertices of each triangle are listed first in the description of higher order triangles, and we assume that the vertices are listed in counterclockwise order. The approximation of the integral is made using a quadrature rule defined on the unit triangle, and supplied by the user. The user also supplies the name of a subroutine, here called "QUAD_FUN", which evaluates the integrand at a set of points. The form is: void quad_fun ( int n, double xy_vec[], double f_vec[] ) Licensing: This code is distributed under the MIT license. Modified: 22 January 2007 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes in the triangulation. Input, double NODE_XY(2,NODE_NUM), the coordinates of the nodes. Input, int TRIANGLE_ORDER, the order of triangles in the triangulation. Input, int TRIANGLE_NUM, the number of triangles in the triangulation. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes making up each triangle. Input, void QUAD_FUN ( int N, double XY_VEC[], double F_VEC[] ), the name of the function that evaluates the integrand. Input, int QUAD_NUM, the order of the quadrature rule. Input, double QUAD_XY(2,QUAD_NUM), the abscissas of the quadrature rule, in the unit triangle. Input, double QUAD_W(QUAD_NUM), the weights of the quadrature rule. Output, double *QUAD_VALUE, the estimate of the integral of F(X,Y) over the region covered by the triangulation. Output, double *REGION_AREA, the area of the region. */ { int i; int j; int quad; double *quad_f; double *quad2_xy; double temp; int triangle; double triangle_area; double triangle_xy[2*3]; quad_f = ( double * ) malloc ( quad_num * sizeof ( double ) ); quad2_xy = ( double * ) malloc ( 2*quad_num * sizeof ( double ) ); *quad_value = 0.0; *region_area = 0.0; for ( triangle = 0; triangle < triangle_num; triangle++ ) { for ( j = 0; j < 3; j++ ) { for ( i = 0; i < 2; i++ ) { triangle_xy[i+j*2] = node_xy[i+(triangle_node[j+triangle*3]-1)*2]; } } triangle_area = triangle_area_2d ( triangle_xy ); triangle_order3_reference_to_physical ( triangle_xy, quad_num, quad_xy, quad2_xy ); quad_fun ( quad_num, quad2_xy, quad_f ); temp = 0.0; for ( quad = 0; quad < quad_num; quad++ ) { temp = temp + quad_w[quad] * quad_f[quad]; } *quad_value = *quad_value + triangle_area * temp; *region_area = *region_area + triangle_area; } free ( quad_f ); free ( quad2_xy ); return; } /******************************************************************************/ void triangulation_order3_refine_compute ( int node_num1, int triangle_num1, double node_xy1[], int triangle_node1[], int node_num2, int triangle_num2, int edge_data[], double node_xy2[], int triangle_node2[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_REFINE_COMPUTE computes a refined order 3 triangulation. Discussion: Given a triangle defined by nodes 1, 2, 3, we need to generate nodes 12, 23, and 13, and create 4 new subtriangles, T1, T2, T3 and T4. The task is more complicated by the fact that we are working with a mesh of triangles, so that we want to create a node only once, even though it may be shared by other triangles. 3 / \ /T3 \ 13----23 / \T4 / \ /T1 \ /T2 \ 1----12-----2 Licensing: This code is distributed under the MIT license. Modified: 07 June 2009 Author: John Burkardt Parameters: Input, int NODE_NUM1, the number of nodes. Input, int TRIANGLE_NUM1, the number of triangles. Input, double NODE_XY1[2*NODE_NUM1], the nodes. Input, int TRIANGLE_NODE1[3*TRIANGLE_NUM1], the nodes that make up the triangles. These should be listed in counterclockwise order. Input, int NODE_NUM2, the number of nodes in the refined mesh. Input, int TRIANGLE_NUM2, the number of triangles in the refined mesh. Input, int EDGE_DATA[5*(3*TRIANGLE_NUM1)], edge information computed by TRIANGULATION_ORDER3_REFINE_SIZE. Output, double NODE_XY2[2*NODE_NUM2], the refined nodes. Output, int TRIANGLE_NODE2[3*TRIANGLE_NUM2], the nodes that make up the triangles in the refined mesh. */ { int edge; int i; int j; int n1; int n1_old; int n2; int n2_old; int node; int triangle1; int v1; int v2; /* Copy the old nodes. */ for ( j = 0; j < node_num1; j++ ) { for ( i = 0; i < 2; i++ ) { node_xy2[i+j*2] = node_xy1[i+j*2]; } } for ( j = 0; j < triangle_num2; j++ ) { for ( i = 0; i < 3; i++ ) { triangle_node2[i+j*3] = -1; } } /* We can assign the existing nodes to the new triangles. */ for ( triangle1 = 0; triangle1 < triangle_num1; triangle1++ ) { triangle_node2[0+(triangle1*4+0)*3] = triangle_node1[0+triangle1*3]; triangle_node2[1+(triangle1*4+1)*3] = triangle_node1[1+triangle1*3]; triangle_node2[2+(triangle1*4+2)*3] = triangle_node1[2+triangle1*3]; } node = node_num1; n1_old = -1; n2_old = -1; for ( edge = 0; edge < 3 * triangle_num1; edge++ ) { n1 = edge_data[0+edge*5] - 1; n2 = edge_data[1+edge*5] - 1; /* If this edge is new, create the coordinates and index for this node. */ if ( n1 != n1_old || n2 != n2_old ) { if ( node_num2 < node ) { printf ( "\n" ); printf ( "TRIANGLE_MESH_ORDER3_REFINE - Fatal error!\n" ); printf ( " Node index exceeds NODE_NUM2.\n" ); exit ( 1 ); } for ( i = 0; i < 2; i++ ) { node_xy2[i+node*2] = ( node_xy2[i+n1*2] + node_xy2[i+n2*2] ) / 2.0; } node = node + 1; n1_old = n1; n2_old = n2; } /* Assign the node to triangles. */ v1 = edge_data[2+edge*5]; v2 = edge_data[3+edge*5]; triangle1 = edge_data[4+edge*5]; if ( v1 == 1 && v2 == 2 ) { triangle_node2[0+(triangle1*4+1)*3] = node; triangle_node2[1+(triangle1*4+0)*3] = node; triangle_node2[2+(triangle1*4+3)*3] = node; } else if ( v1 == 1 && v2 == 3 ) { triangle_node2[0+(triangle1*4+2)*3] = node; triangle_node2[1+(triangle1*4+3)*3] = node; triangle_node2[2+(triangle1*4+0)*3] = node; } else if ( v1 == 2 && v2 == 3 ) { triangle_node2[0+(triangle1*4+3)*3] = node; triangle_node2[1+(triangle1*4+2)*3] = node; triangle_node2[2+(triangle1*4+1)*3] = node; } } return; } /******************************************************************************/ void triangulation_order3_refine_size ( int node_num1, int triangle_num1, int triangle_node1[], int *node_num2, int *triangle_num2, int edge_data[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_REFINE_SIZE sizes a refined order 3 triangulation. Discussion: Given a triangle defined by nodes 1, 2, 3, we need to generate nodes 12, 23, and 13, and create 4 new subtriangles, T1, T2, T3 and T4. The task is more complicated by the fact that we are working with a mesh of triangles, so that we want to create a node only once, even though it may be shared by other triangles. 3 / \ /T3 \ 13----23 / \T4 / \ /T1 \ /T2 \ 1----12-----2 This routine simply determines the sizes of the resulting node and triangle arrays. The primary amount of work occurs in sorting a list of 3 * TRIANGLE_NUM data items, one item for every edge of every triangle. Each data item records, for a given edge, the global indices of the two endpoints, the local indices of the two endpoints, and the index of the triangle. Through careful sorting, it is possible to arrange this data in a way that allows the proper generation of the interpolated nodes. Licensing: This code is distributed under the MIT license. Modified: 28 January 2007 Author: John Burkardt Parameters: Input, int NODE_NUM1, the number of nodes in the original mesh. Input, int TRIANGLE_NUM1, the number of triangles in the original mesh. Input, int TRIANGLE_NODE1[3*TRIANGLE_NUM1], the indices of the nodes that form the triangles in the input mesh. Output, int *NODE_NUM2, the number of nodes in the refined mesh. Output, int *TRIANGLE_NUM2, the number of triangles in the refined mesh. Output, int EDGE_DATA[5*(3*TRIANGLE_NUM1)], edge data that will be needed by TRIANGULATION_ORDER3_REFINE_COMPUTE. */ { int a; int b; int edge; int i; int j; int k; int n1; int n1_old; int n2; int n2_old; int triangle; /* Step 1. From the list of nodes for triangle T, of the form: (I,J,K) construct the edge relations: (I,J,1,2,T) (I,K,1,3,T) (J,K,2,3,T) In order to make matching easier, we reorder each pair of nodes into ascending order. */ for ( triangle = 0; triangle < triangle_num1; triangle++ ) { i = triangle_node1[0+triangle*3]; j = triangle_node1[1+triangle*3]; k = triangle_node1[2+triangle*3]; a = i4_min ( i, j ); b = i4_max ( i, j ); edge_data[0+5*(3*triangle+0)] = a; edge_data[1+5*(3*triangle+0)] = b; edge_data[2+5*(3*triangle+0)] = 1; edge_data[3+5*(3*triangle+0)] = 2; edge_data[4+5*(3*triangle+0)] = triangle; a = i4_min ( i, k ); b = i4_max ( i, k ); edge_data[0+5*(3*triangle+1)] = a; edge_data[1+5*(3*triangle+1)] = b; edge_data[2+5*(3*triangle+1)] = 1; edge_data[3+5*(3*triangle+1)] = 3; edge_data[4+5*(3*triangle+1)] = triangle; a = i4_min ( j, k ); b = i4_max ( j, k ); edge_data[0+5*(3*triangle+2)] = a; edge_data[1+5*(3*triangle+2)] = b; edge_data[2+5*(3*triangle+2)] = 2; edge_data[3+5*(3*triangle+2)] = 3; edge_data[4+5*(3*triangle+2)] = triangle; } /* Step 2. Perform an ascending dictionary sort on the neighbor relations. We only intend to sort on rows 1:2; the routine we call here sorts on the full column but that won't hurt us. What we need is to find all cases where triangles share an edge. By sorting the columns of the EDGE_DATA array, we will put shared edges next to each other. */ i4col_sort_a ( 5, 3*triangle_num1, edge_data ); /* Step 3. All the triangles which share an edge show up as consecutive columns with identical first two entries. Figure out how many new nodes there are, and allocate space for their coordinates. */ *node_num2 = node_num1; n1_old = -1; n2_old = -1; for ( edge = 0; edge < 3 * triangle_num1; edge++ ) { n1 = edge_data[0+edge*5]; n2 = edge_data[1+edge*5]; if ( n1 != n1_old || n2 != n2_old ) { *node_num2 = *node_num2 + 1; n1_old = n1; n2_old = n2; } } *triangle_num2 = 4 * triangle_num1; return; } /******************************************************************************/ void triangulation_order3_sample ( int node_num, double node_xy[], int triangle_num, int triangle_node[], int num_ran, int *seed, double xd[], int td[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER3_SAMPLE returns random points in a triangulation. Discussion: It is assumed that the triangulation consists of a set of non-overlapping triangles. The point is chosen uniformly in the area covered by the triangulation. Licensing: This code is distributed under the MIT license. Modified: 07 June 2009 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the nodes that make up the triangles. Input, int NUM_RAN, the number of points to sample. Input/output, int *SEED, a seed for the random number generator. Output, double XD[2*NUM_RAN], the sample points. Output, int TD[NUM_RAN], the triangle to which each sample point belongs. */ { double *area_cum; double area_total; int i; int i1; int i2; int i3; int left; double r; int right; double t[2*3]; /* Compute the areas of the triangles. Build a cumulative area vector. Convert it to a relative cumulative area vector. */ area_cum = ( double * ) malloc ( ( triangle_num + 1 ) * sizeof ( double ) ); area_cum[0] = 0.0; for ( i = 0; i < triangle_num; i++ ) { i1 = triangle_node[0+i*3]; t[0+0*2] = node_xy[0+i1*2]; t[1+0*2] = node_xy[1+i1*2]; i2 = triangle_node[1+i*3]; t[0+1*2] = node_xy[0+i2*2]; t[1+1*2] = node_xy[1+i2*2]; i3 = triangle_node[2+i*3]; t[0+2*2] = node_xy[0+i3*2]; t[1+2*2] = node_xy[1+i3*2]; area_cum[i+1] = area_cum[i] + triangle_area_2d ( t ); } area_total = area_cum[triangle_num]; for ( i = 0; i <= triangle_num; i++ ) { area_cum[i] = area_cum[i] / area_total; } /* Pick random values. A random value R indicates the corresponding triangle whose cumulative relative area contains R. Bracket the random value in the cumulative relative areas, indicating a triangle. Pick a random point in the triangle. */ for ( i = 0; i < num_ran; i++ ) { r = r8_uniform_01 ( seed ); r8vec_bracket ( triangle_num+1, area_cum, r, &left, &right ); td[i] = right - 1; i1 = triangle_node[0+(td[i]-1)*3]; t[0+0*2] = node_xy[0+i1*2]; t[1+0*2] = node_xy[1+i1*2]; i2 = triangle_node[1+(td[i]-1)*3]; t[0+1*2] = node_xy[0+i2*2]; t[1+1*2] = node_xy[1+i2*2]; i3 = triangle_node[2+(td[i]-1)*3]; t[0+2*2] = node_xy[0+i3*2]; t[1+2*2] = node_xy[1+i3*2]; triangle_sample ( t, 1, seed, xd+i*2 ); } free ( area_cum ); return; } /******************************************************************************/ void triangulation_order4_plot ( char *plot_filename, int node_num, double node_xy[], int triangle_num, int triangle_node[], int node_show, int triangle_show ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER4_PLOT plots a 4-node triangulation. Licensing: This code is distributed under the MIT license. Modified: 08 June 2009 Author: John Burkardt Parameters: Input, char *PLOT_FILENAME, the name of the output file. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[4*TRIANGLE_NUM], lists, for each triangle, the indices of the nodes that form the vertices of the triangle, and the centroid. Input, int NODE_SHOW: 0, do not show nodes; 1, show nodes; 2, show nodes and label them. Input, int TRIANGLE_SHOW: 0, do not show triangles; 1, show triangles; 2, show triangles and label them. */ { double ave_x; double ave_y; int circle_size; int delta; int e; FILE *file_unit; int i; int node; int triangle; double x_max; double x_min; int x_ps; int x_ps_max = 576; int x_ps_max_clip = 594; int x_ps_min = 36; int x_ps_min_clip = 18; double x_scale; double y_max; double y_min; int y_ps; int y_ps_max = 666; int y_ps_max_clip = 684; int y_ps_min = 126; int y_ps_min_clip = 108; double y_scale; /* We need to do some figuring here, so that we can determine the range of the data, and hence the height and width of the piece of paper. */ x_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( x_max < node_xy[0+node*2] ) { x_max = node_xy[0+node*2]; } } x_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[0+node*2] < x_min ) { x_min = node_xy[0+node*2]; } } x_scale = x_max - x_min; x_max = x_max + 0.05 * x_scale; x_min = x_min - 0.05 * x_scale; x_scale = x_max - x_min; y_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( y_max < node_xy[1+node*2] ) { y_max = node_xy[1+node*2]; } } y_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[1+node*2] < y_min ) { y_min = node_xy[1+node*2]; } } y_scale = y_max - y_min; y_max = y_max + 0.05 * y_scale; y_min = y_min - 0.05 * y_scale; y_scale = y_max - y_min; if ( x_scale < y_scale ) { delta = r8_nint ( ( double ) ( x_ps_max - x_ps_min ) * ( y_scale - x_scale ) / ( 2.0 * y_scale ) ); x_ps_max = x_ps_max - delta; x_ps_min = x_ps_min + delta; x_ps_max_clip = x_ps_max_clip - delta; x_ps_min_clip = x_ps_min_clip + delta; x_scale = y_scale; } else if ( y_scale < x_scale ) { delta = r8_nint ( ( double ) ( y_ps_max - y_ps_min ) * ( x_scale - y_scale ) / ( 2.0 * x_scale ) ); y_ps_max = y_ps_max - delta; y_ps_min = y_ps_min + delta; y_ps_max_clip = y_ps_max_clip - delta; y_ps_min_clip = y_ps_min_clip + delta; y_scale = x_scale; } file_unit = fopen ( plot_filename, "wt" ); if ( !file_unit ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER4_PLOT - Fatal error!\n" ); printf ( " Could not open the output EPS file.\n" ); exit ( 1 ); } fprintf ( file_unit, "%%!PS-Adobe-3.0 EPSF-3.0\n" ); fprintf ( file_unit, "%%%%Creator: triangulation_order4_plot.C\n" ); fprintf ( file_unit, "%%%%Title: %s\n", plot_filename ); fprintf ( file_unit, "%%%%Pages: 1\n" ); fprintf ( file_unit, "%%%%BoundingBox: %d %d %d %d\n", x_ps_min, y_ps_min, x_ps_max, y_ps_max ); fprintf ( file_unit, "%%%%Document-Fonts: Times-Roman\n" ); fprintf ( file_unit, "%%%%LanguageLevel: 1\n" ); fprintf ( file_unit, "%%%%EndComments\n" ); fprintf ( file_unit, "%%%%BeginProlog\n" ); fprintf ( file_unit, "/inch {72 mul} def\n" ); fprintf ( file_unit, "%%%%EndProlog\n" ); fprintf ( file_unit, "%%%%Page: 1 1\n" ); fprintf ( file_unit, "save\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Increase line width from default 0.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "2 setlinewidth\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB line color to very light gray.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.9000 0.9000 0.9000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw a gray border around the page.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "stroke\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set RGB line color to black.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.0000 0.0000 0.0000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the font and its size:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.50 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Print a title:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 210 702 moveto\n" ); fprintf ( file_unit, "(Pointset) show\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Define a clipping polygon\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "clip newpath\n" ); /* Draw the nodes. */ if ( node_num <= 200 ) { circle_size = 5; } else if ( node_num <= 500 ) { circle_size = 4; } else if ( node_num <= 1000 ) { circle_size = 3; } else if ( node_num <= 5000 ) { circle_size = 2; } else { circle_size = 1; } if ( 1 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw filled dots at each node:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.150 0.750 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d %d 0 360 arc closepath fill\n", x_ps, y_ps, circle_size ); } } /* Label the nodes. */ if ( 2 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the nodes:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to darker blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.250 0.850 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d moveto (%d) show\n", x_ps, y_ps + 5, node + 1 ); } } /* Draw the triangles. */ if ( 1 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.900 0.200 0.100 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw the triangles.\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { fprintf ( file_unit, "newpath\n" ); for ( i = 1; i <= 4; i++ ) { e = i4_wrap ( i, 1, 3 ); node = triangle_node[e-1+triangle*4] - 1; x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); if ( i == 1 ) { fprintf ( file_unit, "%d %d moveto\n", x_ps, y_ps ); } else { fprintf ( file_unit, "%d %d lineto\n", x_ps, y_ps ); } } fprintf ( file_unit, "stroke\n" ); } } /* Label the triangles. */ if ( 2 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the triangles.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to darker red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.950 0.250 0.150 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { ave_x = 0.0; ave_y = 0.0; for ( i = 1; i <= 3; i++ ) { node = triangle_node[i-1+triangle*4] - 1; ave_x = ave_x + node_xy[0+node*2]; ave_y = ave_y + node_xy[1+node*2]; } ave_x = ave_x / 3.0; ave_y = ave_y / 3.0; x_ps = ( int ) ( ( ( x_max - ave_x ) * ( double ) ( x_ps_min ) + ( + ave_x - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - ave_y ) * ( double ) ( y_ps_min ) + ( ave_y - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "%d %d moveto (%d) show\n", x_ps, y_ps, triangle + 1 ); } } fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "restore showpage\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% End of page.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%%%%Trailer\n" ); fprintf ( file_unit, "%%%%EOF\n" ); fclose ( file_unit ); return; } /******************************************************************************/ int triangulation_order6_adj_count ( int node_num, int triangle_num, int triangle_node[], int triangle_neighbor[], int adj_col[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_ADJ_COUNT counts adjacencies in a triangulation. Discussion: This routine is called to count the adjacencies, so that the appropriate amount of memory can be set aside for storage when the adjacency structure is created. The triangulation is assumed to involve 6-node triangles. Two nodes are "adjacent" if they are both nodes in some triangle. Also, a node is considered to be adjacent to itself. Diagram: 3 s |\ i | \ d | \ e 6 5 side 2 | \ 3 | \ | \ 1---4---2 side 1 The local node numbering 21-22-23-24-25 |\ |\ | | \ | \ | 16 17 18 19 20 | \ | \ | | \| \| 11-12-13-14-15 |\ |\ | | \ | \ | 6 7 8 9 10 | \ | \ | | \| \| 1--2--3--4--5 A sample grid. Below, we have a chart that lists the nodes adjacent to each node, with an asterisk to indicate the adjacency of the node to itself (in some cases, you want to count this self adjacency and in some you don't). N Adjacencies 1: * 2 3 6 7 11 2: 1 * 3 6 7 11 3: 1 2 * 4 5 6 7 8 9 11 12 13 4: 3 * 5 8 9 13 5: 3 4 * 8 9 10 13 14 15 6: 1 2 3 * 7 11 7: 1 2 3 6 * 8 11 12 13 8: 3 4 5 7 * 9 11 12 13 9: 3 4 5 8 * 10 13 14 15 10: 5 9 * 13 14 15 11: 1 2 3 6 7 8 * 12 13 16 17 21 12: 3 7 8 11 * 13 16 17 21 13: 3 4 5 7 8 9 10 11 12 * 14 15 16 17 18 19 21 22 23 14: 5 9 10 13 * 15 18 19 23 15: 5 9 10 13 14 * 18 19 20 23 24 25 16: 11 12 13 * 17 21 17: 11 12 13 16 * 18 21 22 23 18: 13 14 15 17 * 19 21 22 23 19: 13 14 15 18 * 20 23 24 25 20: 15 19 * 23 24 25 21: 11 12 13 16 17 18 * 22 23 22: 13 17 18 21 * 23 23: 13 14 15 17 18 19 20 21 22 * 24 25 24: 15 19 20 23 * 25 25: 15 19 20 23 24 * Below, we list the number of adjancencies to lower-indexed nodes, to the node itself, to higher-indexed nodes, the total number of adjacencies for this node, and the location of the first and last entries required to list this set of adjacencies in a single list of all the adjacencies. N Below Self Above Total First Last -- -- -- -- -- --- 0 1: 0 1 5 6 1 6 2: 1 1 4 6 7 12 3: 2 1 9 12 13 24 4: 1 1 4 6 25 30 5: 2 1 6 9 31 39 6: 3 1 2 6 40 45 7: 4 1 4 9 46 54 8: 4 1 4 9 55 63 9: 4 1 4 9 62 72 10: 2 1 3 6 73 78 11: 6 1 5 12 79 90 12: 4 1 4 9 91 99 13: 9 1 9 19 100 118 14: 4 1 4 9 119 127 15: 5 1 6 12 128 139 16: 3 1 2 6 140 145 17: 4 1 4 9 146 154 18: 4 1 4 9 155 163 19: 4 1 4 9 164 172 20: 2 1 3 6 173 178 21: 6 1 2 9 179 187 22: 4 1 1 6 188 193 23: 9 1 2 12 194 205 24: 4 1 1 6 206 211 25: 5 1 0 6 212 217 -- -- -- -- -- 218 --- Licensing: This code is distributed under the MIT license. Modified: 25 August 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Output, int TRIANGULATION_ORDER6_ADJ_COUNT, the number of adjacencies. Output, int ADJ_COL[NODE_NUM+1]. Information about column J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. */ { int adj_num; int i; int n1; int n2; int n3; int n4; int n5; int n6; int node; int triangle; int triangle_order = 6; int triangle2; adj_num = 0; /* Set every node to be adjacent to itself. */ for ( node = 0; node < node_num; node++ ) { adj_col[node] = 1; } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order]; n2 = triangle_node[1+triangle*triangle_order]; n3 = triangle_node[2+triangle*triangle_order]; n4 = triangle_node[3+triangle*triangle_order]; n5 = triangle_node[4+triangle*triangle_order]; n6 = triangle_node[5+triangle*triangle_order]; /* For sure, we add the adjacencies: 43 / (34) 51 / (15) 54 / (45) 62 / (26) 64 / (46) 65 / (56) */ adj_col[n3-1] = adj_col[n3-1] + 1; adj_col[n4-1] = adj_col[n4-1] + 1; adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n5-1] = adj_col[n5-1] + 1; adj_col[n4-1] = adj_col[n4-1] + 1; adj_col[n5-1] = adj_col[n5-1] + 1; adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n6-1] = adj_col[n6-1] + 1; adj_col[n4-1] = adj_col[n4-1] + 1; adj_col[n6-1] = adj_col[n6-1] + 1; adj_col[n5-1] = adj_col[n5-1] + 1; adj_col[n6-1] = adj_col[n6-1] + 1; /* Add edges (1,2), (1,4), (2,4) if this is the first occurrence, that is, if the edge (1,4,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). Maybe add 21 / 12 41 / 14 42 / 24 */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n4-1] = adj_col[n4-1] + 1; adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n4-1] = adj_col[n4-1] + 1; } /* Maybe add 32 / 23 52 / 25 53 / 35 */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; adj_col[n2-1] = adj_col[n2-1] + 1; adj_col[n5-1] = adj_col[n5-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; adj_col[n5-1] = adj_col[n5-1] + 1; } /* Maybe add 31 / 13 61 / 16 63 / 36 */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; adj_col[n1-1] = adj_col[n1-1] + 1; adj_col[n6-1] = adj_col[n6-1] + 1; adj_col[n3-1] = adj_col[n3-1] + 1; adj_col[n6-1] = adj_col[n6-1] + 1; } } /* We used ADJ_COL to count the number of entries in each column. Convert it to pointers into the ADJ array. */ for ( node = node_num; 1 <= node; node-- ) { adj_col[node] = adj_col[node-1]; } adj_col[0] = 1; for ( i = 1; i <= node_num; i++ ) { adj_col[i]= adj_col[i-1] + adj_col[i]; } adj_num = adj_col[node_num] - 1; return adj_num; } /******************************************************************************/ int *triangulation_order6_adj_set ( int node_num, int triangle_num, int triangle_node[], int triangle_neighbor[], int adj_num, int adj_col[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_ADJ_SET sets adjacencies in a triangulation. Discussion: This routine is called to count the adjacencies, so that the appropriate amount of memory can be set aside for storage when the adjacency structure is created. The triangulation is assumed to involve 6-node triangles. Two nodes are "adjacent" if they are both nodes in some triangle. Also, a node is considered to be adjacent to itself. This routine can be used to create the compressed column storage for a quadratic triangle finite element discretization of Poisson's equation in two dimensions. Diagram: 3 s |\ i | \ d | \ e 6 5 side 2 | \ 3 | \ | \ 1---4---2 side 1 The local node numbering 21-22-23-24-25 |\ |\ | | \ | \ | 16 17 18 19 20 | \ | \ | | \| \| 11-12-13-14-15 |\ |\ | | \ | \ | 6 7 8 9 10 | \ | \ | | \| \| 1--2--3--4--5 A sample grid. Below, we have a chart that lists the nodes adjacent to each node, with an asterisk to indicate the adjacency of the node to itself (in some cases, you want to count this self adjacency and in some you don't). N Adjacencies 1: * 2 3 6 7 11 2: 1 * 3 6 7 11 3: 1 2 * 4 5 6 7 8 9 11 12 13 4: 3 * 5 8 9 13 5: 3 4 * 8 9 10 13 14 15 6: 1 2 3 * 7 11 7: 1 2 3 6 * 8 11 12 13 8: 3 4 5 7 * 9 11 12 13 9: 3 4 5 8 * 10 13 14 15 10: 5 9 * 13 14 15 11: 1 2 3 6 7 8 * 12 13 16 17 21 12: 3 7 8 11 * 13 16 17 21 13: 3 4 5 7 8 9 10 11 12 * 14 15 16 17 18 19 21 22 23 14: 5 9 10 13 * 15 18 19 23 15: 5 9 10 13 14 * 18 19 20 23 24 25 16: 11 12 13 * 17 21 17: 11 12 13 16 * 18 21 22 23 18: 13 14 15 17 * 19 21 22 23 19: 13 14 15 18 * 20 23 24 25 20: 15 19 * 23 24 25 21: 11 12 13 16 17 18 * 22 23 22: 13 17 18 21 * 23 23: 13 14 15 17 18 19 20 21 22 * 24 25 24: 15 19 20 23 * 25 25: 15 19 20 23 24 * Below, we list the number of adjancencies to lower-indexed nodes, to the node itself, to higher-indexed nodes, the total number of adjacencies for this node, and the location of the first and last entries required to list this set of adjacencies in a single list of all the adjacencies. N Below Self Above Total First Last -- -- -- -- -- --- 0 1: 0 1 5 6 1 6 2: 1 1 4 6 7 12 3: 2 1 9 12 13 24 4: 1 1 4 6 25 30 5: 2 1 6 9 31 39 6: 3 1 2 6 40 45 7: 4 1 4 9 46 54 8: 4 1 4 9 55 63 9: 4 1 4 9 62 72 10: 2 1 3 6 73 78 11: 6 1 5 12 79 90 12: 4 1 4 9 91 99 13: 9 1 9 19 100 118 14: 4 1 4 9 119 127 15: 5 1 6 12 128 139 16: 3 1 2 6 140 145 17: 4 1 4 9 146 154 18: 4 1 4 9 155 163 19: 4 1 4 9 164 172 20: 2 1 3 6 173 178 21: 6 1 2 9 179 187 22: 4 1 1 6 188 193 23: 9 1 2 12 194 205 24: 4 1 1 6 206 211 25: 5 1 0 6 212 217 -- -- -- -- -- 218 --- Licensing: This code is distributed under the MIT license. Modified: 25 August 2006 Author: John Burkardt Parameters Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], for each side of a triangle, lists the neighboring triangle, or -1 if there is no neighbor. Input, int ADJ_NUM, the number of adjacencies. Input, int ADJ_COL[NODE_NUM+1]. Information about column J is stored in entries ADJ_COL(J) through ADJ_COL(J+1)-1 of ADJ. Output, int TRIANGULATION_ORDER6_ADJ_SET[ADJ_NUM], the adjacency information. */ { int *adj; int *adj_copy; int k; int k1; int k2; int n1; int n2; int n3; int n4; int n5; int n6; int node; int triangle; int triangle2; int triangle_order = 6; adj = ( int * ) malloc ( adj_num * sizeof ( int ) ); for ( k = 0; k < adj_num; k++ ) { adj[k] = -1; } adj_copy = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( node = 0; node < node_num; node++ ) { adj_copy[node] = adj_col[node]; } /* Set every node to be adjacent to itself. */ for ( node = 1; node <= node_num; node++ ) { adj[adj_copy[node-1]-1] = node; adj_copy[node-1] = adj_copy[node-1] + 1; } /* Examine each triangle. */ for ( triangle = 0; triangle < triangle_num; triangle++ ) { n1 = triangle_node[0+triangle*triangle_order]; n2 = triangle_node[1+triangle*triangle_order]; n3 = triangle_node[2+triangle*triangle_order]; n4 = triangle_node[3+triangle*triangle_order]; n5 = triangle_node[4+triangle*triangle_order]; n6 = triangle_node[5+triangle*triangle_order]; /* For sure, we add the adjacencies: 43 / (34) 51 / (15) 54 / (45) 62 / (26) 64 / (46) 65 / (56) */ adj[adj_copy[n3-1]-1] = n4; adj_copy[n3-1] = adj_copy[n3-1] + 1; adj[adj_copy[n4-1]-1] = n3; adj_copy[n4-1] = adj_copy[n4-1] + 1; adj[adj_copy[n1-1]-1] = n5; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n5-1]-1] = n1; adj_copy[n5-1] = adj_copy[n5-1] + 1; adj[adj_copy[n4-1]-1] = n5; adj_copy[n4-1] = adj_copy[n4-1] + 1; adj[adj_copy[n5-1]-1] = n4; adj_copy[n5-1] = adj_copy[n5-1] + 1; adj[adj_copy[n2-1]-1] = n6; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n6-1]-1] = n2; adj_copy[n6-1] = adj_copy[n6-1] + 1; adj[adj_copy[n4-1]-1] = n6; adj_copy[n4-1] = adj_copy[n4-1] + 1; adj[adj_copy[n6-1]-1] = n4; adj_copy[n6-1] = adj_copy[n6-1] + 1; adj[adj_copy[n5-1]-1] = n6; adj_copy[n5-1] = adj_copy[n5-1] + 1; adj[adj_copy[n6-1]-1] = n5; adj_copy[n6-1] = adj_copy[n6-1] + 1; /* Add edges (1,2), (1,4), (2,4) if this is the first occurrence, that is, if the edge (1,4,2) is on a boundary (TRIANGLE2 <= 0) or if this triangle is the first of the pair in which the edge occurs (TRIANGLE < TRIANGLE2). Maybe add 21 / 12 41 / 14 42 / 24 */ triangle2 = triangle_neighbor[0+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n1-1]-1] = n2; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n2-1]-1] = n1; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n1-1]-1] = n4; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n4-1]-1] = n1; adj_copy[n4-1] = adj_copy[n4-1] + 1; adj[adj_copy[n2-1]-1] = n4; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n4-1]-1] = n2; adj_copy[n4-1] = adj_copy[n4-1] + 1; } /* Maybe add 32 / 23 52 / 25 53 / 35 */ triangle2 = triangle_neighbor[1+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n2-1]-1] = n3; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n3-1]-1] = n2; adj_copy[n3-1] = adj_copy[n3-1] + 1; adj[adj_copy[n2-1]-1] = n5; adj_copy[n2-1] = adj_copy[n2-1] + 1; adj[adj_copy[n5-1]-1] = n2; adj_copy[n5-1] = adj_copy[n5-1] + 1; adj[adj_copy[n3-1]-1] = n5; adj_copy[n3-1] = adj_copy[n3-1] + 1; adj[adj_copy[n5-1]-1] = n3; adj_copy[n5-1] = adj_copy[n5-1] + 1; } /* Maybe add 31 / 13 61 / 16 63 / 36 */ triangle2 = triangle_neighbor[2+triangle*3]; if ( triangle2 < 0 || triangle < triangle2 ) { adj[adj_copy[n1-1]-1] = n3; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n3-1]-1] = n1; adj_copy[n3-1] = adj_copy[n3-1] + 1; adj[adj_copy[n1-1]-1] = n6; adj_copy[n1-1] = adj_copy[n1-1] + 1; adj[adj_copy[n6-1]-1] = n1; adj_copy[n6-1] = adj_copy[n6-1] + 1; adj[adj_copy[n3-1]-1] = n6; adj_copy[n3-1] = adj_copy[n3-1] + 1; adj[adj_copy[n6-1]-1] = n3; adj_copy[n6-1] = adj_copy[n6-1] + 1; } } /* Ascending sort the entries for each node. */ for ( node = 1; node <= node_num; node++ ) { k1 = adj_col[node-1]; k2 = adj_col[node]-1; i4vec_sort_heap_a ( k2+1-k1, adj+k1-1 ); } free ( adj_copy ); return adj; } /******************************************************************************/ int triangulation_order6_boundary_edge_count ( int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_BOUNDARY_EDGE_COUNT counts the boundary edges. Discussion: This routine is given a triangulation, a set of 6-node triangles. It is assumed that, in each list of 6 nodes, the vertices are listed first, in counterclockwise order, followed by the three midside nodes, in counterclockwise order, starting with the node between vertices 1 and 2. It is assumed that each edge of the triangulation is either * an INTERIOR edge, which is listed twice, once with positive orientation and once with negative orientation, or; * a BOUNDARY edge, which will occur only once. This routine should work even if the region has holes - as long as the boundary of the hole comprises more than 3 edges! Except for the dimension of TRIANGLE, this routine is identical to the routine for the order 3 case. Licensing: This code is distributed under the MIT license. Modified: 14 June 2005 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], the nodes that make up the triangles. These should be listed in counterclockwise order. Output, integer TRIANGULATION_ORDER6_BOUNDARY_EDGE_COUNT, the number of boundary edges. */ { int boundary_edge_num; int e1; int e2; int *edge; int interior_edge_num; int j; int m; int n; int unique_num; m = 2; n = 3 * triangle_num; /* Set up the edge array. */ edge = ( int * ) malloc ( m * n * sizeof ( int ) ); for ( j = 0; j < triangle_num; j++ ) { edge[0+(j )*m] = triangle_node[0+j*6]; edge[1+(j )*m] = triangle_node[1+j*6]; edge[0+(j+ triangle_num)*m] = triangle_node[1+j*6]; edge[1+(j+ triangle_num)*m] = triangle_node[2+j*6]; edge[0+(j+2*triangle_num)*m] = triangle_node[2+j*6]; edge[1+(j+2*triangle_num)*m] = triangle_node[0+j*6]; } /* In each column, force the smaller entry to appear first. */ for ( j = 0; j < n; j++ ) { e1 = i4_min ( edge[0+j*m], edge[1+j*m] ); e2 = i4_max ( edge[0+j*m], edge[1+j*m] ); edge[0+j*m] = e1; edge[1+j*m] = e2; } /* Ascending sort the column array. */ i4col_sort_a ( m, n, edge ); /* Get the number of unique columns in EDGE. */ unique_num = i4col_sorted_unique_count ( m, n, edge ); interior_edge_num = 3 * triangle_num - unique_num; boundary_edge_num = 3 * triangle_num - 2 * interior_edge_num; free ( edge ); return boundary_edge_num; } /******************************************************************************/ int triangulation_order6_boundary_edge_count_euler ( int node_num, int triangle_num, int hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_BOUNDARY_EDGE_COUNT_EULER counts boundary edges. Discussion: We assume we are given information about an order 6 triangulation of a set of nodes in the plane. By ignoring the midside nodes, we can determine the corresponding information for an order 3 triangulation, and apply Euler's formula to determine the number of edges that lie on the boundary of the set of nodes. Thus, if we have TRIANGLE_NUM triangles, and NODE_NUM nodes, we imagine that each triangle is replaced by 4 triangles, created by adding the edges created by joining the midside nodes. Thus, for 4 * TRIANGLE_NUM triangles, we can apply Euler's formula to compute the number of boundary edges. Now, to adjust the data to our order 6 triangles, we divide the number of boundary edges by 2. Licensing: This code is distributed under the MIT license. Modified: 11 June 2005 Author: John Burkardt Reference: Marc deBerg, Marc Krevald, Mark Overmars, Otfried Schwarzkopf, Computational Geometry, Springer, 2000, ISBN: 3-540-65620-0. Parameters: Input, integer NODE_NUM, the number of nodes. Input, integer TRIANGLE_NUM, the number of triangles. Input, integer HOLE_NUM, the number of internal nodes. Output, int TRIANGULATION_ORDER6_BOUNDARY_EDGE_COUNT, the number of edges that lie on the boundary of the triangulation. */ { int boundary_num; boundary_num = ( 2 * node_num + 2 * hole_num - 4 * triangle_num - 2 ) / 2; return boundary_num; } /******************************************************************************/ int *triangulation_order6_boundary_node ( int node_num, int triangle_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_BOUNDARY_NODE indicates nodes on the boundary. Discussion: This routine is given an order 6 triangulation, an abstract list of sets of six nodes. The vertices are listed clockwise, then the midside nodes. It is assumed that each edge of the triangulation is either * an INTERIOR edge, which is listed twice, once with positive orientation and once with negative orientation, or; * a BOUNDARY edge, which will occur only once. This routine should work even if the region has holes - as long as the boundary of the hole comprises more than 3 edges! Licensing: This code is distributed under the MIT license. Modified: 25 January 2013 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], the nodes that make up the triangles. Output, int TRIANGULATION_ORDER6_BOUNDARY_NODE[NODE_NUM], is TRUE if the node is on a boundary edge. */ { int e1; int e2; int *edge; int equal; int i; int j; int m; int n; int *node_boundary; m = 3; n = 3 * triangle_num; /* Set up the edge array. */ edge = ( int * ) malloc ( m * n * sizeof ( int ) ); for ( j = 0; j < triangle_num; j++ ) { edge[0+(j )*m] = triangle_node[0+j*6]; edge[1+(j )*m] = triangle_node[3+j*6]; edge[2+(j )*m] = triangle_node[1+j*6]; edge[0+(j+ triangle_num)*m] = triangle_node[1+j*6]; edge[1+(j+ triangle_num)*m] = triangle_node[4+j*6]; edge[2+(j+ triangle_num)*m] = triangle_node[2+j*6]; edge[0+(j+2*triangle_num)*m] = triangle_node[2+j*6]; edge[1+(j+2*triangle_num)*m] = triangle_node[5+j*6]; edge[2+(j+2*triangle_num)*m] = triangle_node[0+j*6]; } /* In each column, force the smaller entry to appear first. */ for ( j = 0; j < n; j++ ) { e1 = i4_min ( edge[0+j*m], edge[2+j*m] ); e2 = i4_max ( edge[0+j*m], edge[2+j*m] ); edge[0+j*m] = e1; edge[2+j*m] = e2; } /* Ascending sort the column array. */ i4col_sort_a ( m, n, edge ); /* Records which appear twice are internal edges and can be ignored. */ node_boundary = ( int * ) malloc ( node_num * sizeof ( int ) ); for ( i = 0; i < node_num; i++ ) { node_boundary[i] = 0; } j = 0; while ( j < 3 * triangle_num ) { j = j + 1; if ( j == 3 * triangle_num ) { for ( i = 0; i < m; i++ ) { node_boundary[edge[i+(j-1)*m]-1] = 1; } break; } equal = 1; for ( i = 0; i < m; i++ ) { if ( edge[i+(j-1)*m] != edge[i+j*m] ) { equal = 0; } } if ( equal ) { j = j + 1; } else { for ( i = 0; i < m; i++ ) { node_boundary[edge[i+(j-1)*m]-1] = 1; } } } free ( edge ); return node_boundary; } /******************************************************************************/ void triangulation_order6_example1 ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_EXAMPLE1 sets up a sample triangulation. Discussion: This triangulation is actually a Delaunay triangulation. The appropriate input values of NODE_NUM and TRIANGLE_NUM can be determined by calling TRIANGULATION_ORDER6_EXAMPLE1_SIZE first. Licensing: This code is distributed under the MIT license. Modified: 11 June 2005 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Output, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int TRIANGLE_ORDER[6*TRIANGLE_NUM], the nodes that make up the triangles. Output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. Negative values indicate edges that lie on the exterior. */ { # define DIM_NUM 2 # define NODE_NUM 48 # define TRIANGLE_NUM 16 # define TRIANGLE_ORDER 6 int i; int j; static double node_xy_save[DIM_NUM*NODE_NUM] = { 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0, 5.0, 0.0, 6.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0, 3.0, 1.0, 4.0, 1.0, 5.0, 1.0, 6.0, 1.0, 0.0, 2.0, 1.0, 2.0, 2.0, 2.0, 3.0, 2.0, 4.0, 2.0, 5.0, 2.0, 6.0, 2.0, 0.0, 3.0, 1.0, 3.0, 2.0, 3.0, 3.0, 3.0, 5.0, 3.0, 6.0, 3.0, 0.0, 4.0, 1.0, 4.0, 2.0, 4.0, 3.0, 4.0, 4.0, 4.0, 5.0, 4.0, 6.0, 4.0, 0.0, 5.0, 1.0, 5.0, 2.0, 5.0, 3.0, 5.0, 4.0, 5.0, 5.0, 5.0, 6.0, 5.0, 0.0, 6.0, 1.0, 6.0, 2.0, 6.0, 3.0, 6.0, 4.0, 6.0, 5.0, 6.0, 6.0, 6.0 }; static int triangle_node_save[TRIANGLE_ORDER*TRIANGLE_NUM] = { 1, 3, 15, 2, 9, 8, 17, 15, 3, 16, 9, 10, 5, 19, 3, 12, 11, 4, 17, 3, 19, 10, 11, 18, 7, 21, 5, 14, 13, 6, 19, 5, 21, 12, 13, 20, 17, 30, 15, 24, 23, 16, 28, 15, 30, 22, 23, 29, 30, 17, 32, 24, 25, 31, 21, 34, 19, 27, 26, 20, 30, 44, 28, 37, 36, 29, 42, 28, 44, 35, 36, 43, 32, 46, 30, 39, 38, 31, 44, 30, 46, 37, 38, 45, 32, 34, 46, 33, 40, 39, 48, 46, 34, 47, 40, 41 }; static int triangle_neighbor_save[3*TRIANGLE_NUM] = { -3, 2, -5, 7, 1, 4, 6, 4, -11, 2, 3, -14, -15, 6, -17, 3, 5, 10, 9, 8, 2, -24, 7, 11, 7, -28, 13, 27, -31, 6, 8, 14, 12, -36, 11, -38, 15, 14, 9, 11, 13, -44, -45, 16, 13, -48, 15, -50 }; for ( j = 0; j < NODE_NUM; j++ ) { for ( i = 0; i < DIM_NUM; i++ ) { node_xy[i+j*DIM_NUM] = node_xy_save[i+j*DIM_NUM]; } } for ( j = 0; j < TRIANGLE_NUM; j++ ) { for ( i = 0; i < TRIANGLE_ORDER; i++ ) { triangle_node[i+j*TRIANGLE_ORDER] = triangle_node_save[i+j*TRIANGLE_ORDER]; } } for ( j = 0; j < TRIANGLE_NUM; j++ ) { for ( i = 0; i < 3; i++ ) { triangle_neighbor[i+j*3] = triangle_neighbor_save[i+j*3]; } } return; # undef DIM_NUM # undef NODE_NUM # undef TRIANGLE_NUM # undef TRIANGLE_ORDER } /******************************************************************************/ void triangulation_order6_example1_size ( int *node_num, int *triangle_num, int *hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_EXAMPLE1_SIZE sets sizes for a sample triangulation. Licensing: This code is distributed under the MIT license. Modified: 13 June 2004 Author: John Burkardt Parameters: Output, int *NODE_NUM, the number of nodes. Output, int *TRIANGLE_NUM, the number of triangles. Output, int *HOLE_NUM, the number of holes. */ { *node_num = 48; *triangle_num = 16; *hole_num = 1; return; } /******************************************************************************/ void triangulation_order6_example2 ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_EXAMPLE2 sets up a sample triangulation. Discussion: This triangulation is actually a Delaunay triangulation. The appropriate input values of NODE_NUM and TRIANGLE_NUM can be determined by calling TRIANGULATION_ORDER6_EXAMPLE2_SIZE first. Diagram: 21-22-23-24-25 |\ 6 |\ 8 | | \ | \ | 16 17 18 19 20 | \ | \ | | 5 \| 7 \| 11-12-13-14-15 |\ 2 |\ 4 | | \ | \ | 6 7 8 9 10 | 1 \ | 3 \ | | \| \| 1--2--3--4--5 Licensing: This code is distributed under the MIT license. Modified: 03 January 2007 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Output, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, int TRIANGLE_ORDER[6*TRIANGLE_NUM], the nodes that make up the triangles. Output, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. Negative values indicate edges that lie on the exterior. */ { # define DIM_NUM 2 # define NODE_NUM 48 # define TRIANGLE_NUM 16 # define TRIANGLE_ORDER 6 int i; int j; static double node_xy_save[DIM_NUM*NODE_NUM] = { 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 3.0, 0.0, 4.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0, 3.0, 1.0, 4.0, 1.0, 0.0, 2.0, 1.0, 2.0, 2.0, 2.0, 3.0, 2.0, 4.0, 2.0, 0.0, 3.0, 1.0, 3.0, 2.0, 3.0, 3.0, 3.0, 4.0, 3.0, 0.0, 4.0, 1.0, 4.0, 2.0, 4.0, 3.0, 4.0, 4.0, 4.0 }; static int triangle_node_save[TRIANGLE_ORDER*TRIANGLE_NUM] = { 1, 3, 11, 2, 7, 6, 13, 11, 3, 12, 7, 8, 3, 5, 13, 4, 9, 8, 15, 13, 5, 14, 9, 10, 11, 13, 21, 12, 17, 16, 23, 21, 13, 22, 17, 18, 13, 15, 23, 14, 19, 18, 25, 23, 15, 24, 19, 20 }; static int triangle_neighbor_save[3*TRIANGLE_NUM] = { -1, 2, -1, 5, 1, 3, -1, 4, 2, 7, 3, -1, 2, 6, -1, -1, 5, 7, 4, 8, 6, -1, 7, -1 }; for ( j = 0; j < NODE_NUM; j++ ) { for ( i = 0; i < DIM_NUM; i++ ) { node_xy[i+j*DIM_NUM] = node_xy_save[i+j*DIM_NUM]; } } for ( j = 0; j < TRIANGLE_NUM; j++ ) { for ( i = 0; i < TRIANGLE_ORDER; i++ ) { triangle_node[i+j*TRIANGLE_ORDER] = triangle_node_save[i+j*TRIANGLE_ORDER]; } } for ( j = 0; j < TRIANGLE_NUM; j++ ) { for ( i = 0; i < 3; i++ ) { triangle_neighbor[i+j*3] = triangle_neighbor_save[i+j*3]; } } return; # undef DIM_NUM # undef NODE_NUM # undef TRIANGLE_NUM # undef TRIANGLE_ORDER } /******************************************************************************/ void triangulation_order6_example2_size ( int *node_num, int *triangle_num, int *hole_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_EXAMPLE2_SIZE sets sizes for a sample triangulation. Diagram: 21-22-23-24-25 |\ 6 |\ 8 | | \ | \ | 16 17 18 19 20 | \ | \ | | 5 \| 7 \| 11-12-13-14-15 |\ 2 |\ 4 | | \ | \ | 6 7 8 9 10 | 1 \ | 3 \ | | \| \| 1--2--3--4--5 Licensing: This code is distributed under the MIT license. Modified: 03 January 2007 Author: John Burkardt Parameters: Output, int *NODE_NUM, the number of nodes. Output, int *TRIANGLE_NUM, the number of triangles. Output, int *HOLE_NUM, the number of holes. */ { *node_num = 25; *triangle_num = 8; *hole_num = 0; return; } /******************************************************************************/ void triangulation_order6_neighbor ( int triangle_num, int triangle_node[], int t1, int s1, int *t2, int *s2 ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_NEIGHBOR determines a neighbor of a given triangle. Discussion: A set of nodes is given. A triangulation of the nodes has been defined and recorded in TRIANGLE_NODE. The TRIANGLE_NODE data structure records triangles as sets of six nodes, with the first three being the vertices, in counterclockwise order. The fourth node is the midside node between nodes 1 and 2, and the other two are listed logically. The nodes of the triangle are listed in counterclockwise order. This means that if two triangles share a side, then the nodes defining that side occur in the order (N1,N2,N3) for one triangle, and (N3,N2,N1) for the other. The routine is given a triangle and a side, and asked to find another triangle (if any) that shares that side. The routine simply searches the TRIANGLE_NODE structure for an occurrence of the nodes in the opposite order. Licensing: This code is distributed under the MIT license. Modified: 13 June 2005 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM, the number of triangles. Input/output, int TRIANGLE_NODE[6*TRIANGLE_NUM], the nodes that define each triangle. Input, int T1, the index of the triangle. Input, int S1, the index of the triangle side. Output, int *T2, the index of the triangle which is the neighbor to T1 on side S1, or -1 if there is no such neighbor. Output, int *S2, the index of the side of triangle T2 which is shared with triangle T1, or -1 if there is no such neighbor. */ { int n1; int n2; int s; int ss; int t; n1 = triangle_node[s1-1+(t1-1)*6]; ss = i4_wrap ( s1+1, 1, 3 ); n2 = triangle_node[ss-1+(t1-1)*6]; for ( t = 0; t < triangle_num; t++ ) { for ( s = 0; s < 3; s++ ) { if ( triangle_node[s+t*6] == n1 ) { ss = i4_wrap ( s-1, 0, 2 ); if ( triangle_node[ss+t*6] == n2 ) { *t2 = t + 1; *s2 = ss + 1; return; } } } } *t2 = -1; *s2 = -1; return; } /******************************************************************************/ void triangulation_order6_plot ( char *file_name, int node_num, double node_xy[], int triangle_num, int triangle_node[], int node_show, int triangle_show ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_PLOT plots a 6-node triangulation of a set of nodes. Discussion: The triangulation is most usually a Delaunay triangulation, but this is not necessary. This routine has been specialized to deal correctly ONLY with a mesh of 6 node elements, with the property that starting from local node 1 and traversing the edges of the element will result in encountering local nodes 1, 4, 2, 5, 3, 6 in that order. Licensing: This code is distributed under the MIT license. Modified: 08 June 2009 Author: John Burkardt Parameters: Input, char *FILE_NAME, the name of the file to create. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], lists, for each triangle, the indices of the nodes that form the vertices and midsides of the triangle. Input, int NODE_SHOW: 0, do not show nodes; 1, show nodes; 2, show nodes and label them. Input, int TRIANGLE_SHOW: 0, do not show triangles; 1, show triangles; 2, show triangles and label them. */ { double ave_x; double ave_y; int circle_size; int delta; FILE *file_unit; int i; int ip1; int node; int order[6] = { 1, 4, 2, 5, 3, 6 }; int triangle; double x_max; double x_min; int x_ps; int x_ps_max = 576; int x_ps_max_clip = 594; int x_ps_min = 36; int x_ps_min_clip = 18; double x_scale; double y_max; double y_min; int y_ps; int y_ps_max = 666; int y_ps_max_clip = 684; int y_ps_min = 126; int y_ps_min_clip = 108; double y_scale; /* We need to do some figuring here, so that we can determine the range of the data, and hence the height and width of the piece of paper. */ x_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( x_max < node_xy[0+node*2] ) { x_max = node_xy[0+node*2]; } } x_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[0+node*2] < x_min ) { x_min = node_xy[0+node*2]; } } x_scale = x_max - x_min; x_max = x_max + 0.05 * x_scale; x_min = x_min - 0.05 * x_scale; x_scale = x_max - x_min; y_max = - HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( y_max < node_xy[1+node*2] ) { y_max = node_xy[1+node*2]; } } y_min = HUGE_VAL; for ( node = 0; node < node_num; node++ ) { if ( node_xy[1+node*2] < y_min ) { y_min = node_xy[1+node*2]; } } y_scale = y_max - y_min; y_max = y_max + 0.05 * y_scale; y_min = y_min - 0.05 * y_scale; y_scale = y_max - y_min; if ( x_scale < y_scale ) { delta = r8_nint ( ( double ) ( x_ps_max - x_ps_min ) * ( y_scale - x_scale ) / ( 2.0 * y_scale ) ); x_ps_max = x_ps_max - delta; x_ps_min = x_ps_min + delta; x_ps_max_clip = x_ps_max_clip - delta; x_ps_min_clip = x_ps_min_clip + delta; x_scale = y_scale; } else if ( y_scale < x_scale ) { delta = r8_nint ( ( double ) ( y_ps_max - y_ps_min ) * ( x_scale - y_scale ) / ( 2.0 * x_scale ) ); y_ps_max = y_ps_max - delta; y_ps_min = y_ps_min + delta; y_ps_max_clip = y_ps_max_clip - delta; y_ps_min_clip = y_ps_min_clip + delta; y_scale = x_scale; } file_unit = fopen ( file_name, "wt" ); if ( !file_unit ) { printf ( "\n" ); printf ( "TRIANGULATION_ORDER6_PLOT - Fatal error!\n" ); printf ( " Could not open the output EPS file.\n" ); exit ( 1 ); } fprintf ( file_unit, "%%!PS-Adobe-3.0 EPSF-3.0\n" ); fprintf ( file_unit, "%%%%Creator: triangulation_order6_plot.C\n" ); fprintf ( file_unit, "%%%%Title: %s\n", file_name ); fprintf ( file_unit, "%%%%Pages: 1\n" ); fprintf ( file_unit, "%%%%BoundingBox: %d %d %d %d\n", x_ps_min, y_ps_min, x_ps_max, y_ps_max ); fprintf ( file_unit, "%%%%Document-Fonts: Times-Roman\n" ); fprintf ( file_unit, "%%%%LanguageLevel: 1\n" ); fprintf ( file_unit, "%%%%EndComments\n" ); fprintf ( file_unit, "%%%%BeginProlog\n" ); fprintf ( file_unit, "/inch {72 mul} def\n" ); fprintf ( file_unit, "%%%%EndProlog\n" ); fprintf ( file_unit, "%%%%Page: 1 1\n" ); fprintf ( file_unit, "save\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Increase line width from default 0.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "2 setlinewidth\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB line color to very light gray.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.9000 0.9000 0.9000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw a gray border around the page.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_min ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_max ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min, y_ps_min ); fprintf ( file_unit, "stroke\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set RGB line color to black.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 0.0000 0.0000 0.0000 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the font and its size:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.50 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Print a title:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, " 210 702 moveto\n" ); fprintf ( file_unit, "(Pointset) show\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Define a clipping polygon\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "newpath\n" ); fprintf ( file_unit, "%d %d moveto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_min_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_max_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_max_clip ); fprintf ( file_unit, "%d %d lineto\n", x_ps_min_clip, y_ps_min_clip ); fprintf ( file_unit, "clip newpath\n" ); /* Draw the nodes. */ if ( node_num <= 200 ) { circle_size = 5; } else if ( node_num <= 500 ) { circle_size = 4; } else if ( node_num <= 1000 ) { circle_size = 3; } else if ( node_num <= 5000 ) { circle_size = 2; } else { circle_size = 1; } if ( 1 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw filled dots at each node:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.150 0.750 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d %d 0 360 arc closepath fill\n", x_ps, y_ps, circle_size ); } } /* Label the nodes. */ if ( 2 <= node_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the nodes:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the color to darker blue:\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.000 0.250 0.850 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( node = 0; node < node_num; node++ ) { x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d moveto (%d) show\n", x_ps, y_ps + 5, node + 1 ); } } /* Draw the triangles. */ if ( 1 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.900 0.200 0.100 setrgbcolor\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Draw the triangles.\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { node = triangle_node[order[0]-1+triangle*6] - 1; x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "newpath %d %d moveto\n", x_ps, y_ps ); for ( i = 1; i <= 6; i++ ) { ip1 = ( i % 6 ) + 1; node = triangle_node[order[ip1-1]-1+triangle*6] - 1; x_ps = ( int ) ( ( ( x_max - node_xy[0+node*2] ) * ( double ) ( x_ps_min ) + ( + node_xy[0+node*2] - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - node_xy[1+node*2] ) * ( double ) ( y_ps_min ) + ( node_xy[1+node*2] - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "%d %d lineto\n", x_ps, y_ps ); } fprintf ( file_unit, "stroke\n" ); } } /* Label the triangles. */ if ( 2 <= triangle_show ) { fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Label the triangles.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% Set the RGB color to darker red.\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "0.950 0.250 0.150 setrgbcolor\n" ); fprintf ( file_unit, "/Times-Roman findfont\n" ); fprintf ( file_unit, "0.20 inch scalefont\n" ); fprintf ( file_unit, "setfont\n" ); fprintf ( file_unit, "%%\n" ); for ( triangle = 0; triangle < triangle_num; triangle++ ) { ave_x = 0.0; ave_y = 0.0; for ( i = 0; i < 6; i++ ) { node = triangle_node[i+triangle*6] - 1; ave_x = ave_x + node_xy[0+node*2]; ave_y = ave_y + node_xy[1+node*2]; } ave_x = ave_x / 6.0; ave_y = ave_y / 6.0; x_ps = ( int ) ( ( ( x_max - ave_x ) * ( double ) ( x_ps_min ) + ( + ave_x - x_min ) * ( double ) ( x_ps_max ) ) / ( x_max - x_min ) ); y_ps = ( int ) ( ( ( y_max - ave_y ) * ( double ) ( y_ps_min ) + ( ave_y - y_min ) * ( double ) ( y_ps_max ) ) / ( y_max - y_min ) ); fprintf ( file_unit, "%d %d moveto (%d) show\n", x_ps, y_ps, triangle + 1 ); } } fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "restore showpage\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%% End of page\n" ); fprintf ( file_unit, "%%\n" ); fprintf ( file_unit, "%%%%Trailer\n" ); fprintf ( file_unit, "%%%%EOF\n" ); fclose ( file_unit ); return; } /******************************************************************************/ void triangulation_order6_print ( int node_num, int triangle_num, double node_xy[], int triangle_node[], int triangle_neighbor[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_PRINT prints information defining a triangulation. Discussion: Triangulations created by R8TRIS2 include extra information encoded in the negative values of TRIANGLE_NEIGHBOR. Because some of the nodes counted in node_num may not actually be used in the triangulation, I needed to compute the true number of vertices. I added this calculation on 13 October 2001. Licensing: This code is distributed under the MIT license. Modified: 13 June 2005 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NODE[6*TRIANGLE_NUM], the nodes that make up the triangles. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbors on each side. If there is no triangle neighbor on a particular side, the value of TRIANGLE_NEIGHBOR should be negative. If the triangulation data was created by R8TRIS2, then there is more information encoded in the negative values. */ { # define DIM_NUM 2 int boundary_num; int i; int j; int k; int n1; int n2; int n3; int s; int s1; int s2; int skip; int t; int *vertex_list; int vertex_num; printf ( "\n" ); printf ( "TRIANGULATION_ORDER6_PRINT\n" ); printf ( " Information defining a triangulation.\n" ); printf ( "\n" ); printf ( " The number of nodes is %d\n", node_num ); r8mat_transpose_print ( DIM_NUM, node_num, node_xy, " Node coordinates" ); printf ( "\n" ); printf ( " The number of triangles is %d\n", triangle_num ); printf ( "\n" ); printf ( " Sets of six nodes are used as vertices of\n" ); printf ( " the triangles. For each triangle, the vertices are listed\n" ); printf ( " in counterclockwise order, followed by the midside nodes.\n" ); i4mat_transpose_print ( 6, triangle_num, triangle_node, " Triangle nodes" ); printf ( "\n" ); printf ( " On each side of a given triangle, there is either\n" ); printf ( " another triangle, or a piece of the convex hull.\n" ); printf ( " For each triangle, we list the indices of the three\n" ); printf ( " neighbors, or (if negative) the codes of the\n" ); printf ( " segments of the convex hull.\n" ); i4mat_transpose_print ( 3, triangle_num, triangle_neighbor, " Triangle neighbors" ); /* Determine VERTEX_NUM, the number of vertices. */ vertex_list = ( int * ) malloc ( 3 * triangle_num * sizeof ( int ) ); k = 0; for ( t = 0; t < triangle_num; t++ ) { for ( s = 0; s < 3; s++ ) { vertex_list[k] = triangle_node[s+t*6]; k = k + 1; } } i4vec_sort_heap_a ( 3*triangle_num, vertex_list ); vertex_num = i4vec_sorted_unique ( 3*triangle_num, vertex_list ); free ( vertex_list ); /* Determine the number of boundary points. */ boundary_num = 2 * vertex_num - triangle_num - 2; printf ( "\n" ); printf ( " The number of boundary points is %d\n", boundary_num ); printf ( "\n" ); printf ( " The segments that make up the convex hull can be\n" ); printf ( " determined from the negative entries of the triangle\n" ); printf ( " neighbor list.\n" ); printf ( "\n" ); printf ( " # Tri Side N1 N2 N3\n" ); printf ( "\n" ); skip = 0; k = 0; for ( i = 0; i < triangle_num; i++ ) { for ( j = 0; j < 3; j++ ) { if ( triangle_neighbor[j+i*3] < 0 ) { s = -triangle_neighbor[j+i*3]; t = s / 3; if ( t < 1 || triangle_num < t ) { printf ( "\n" ); printf ( " Sorry, this data does not use the R8TRIS2\n" ); printf ( " convention for convex hull segments.\n" ); skip = 1; break; } s1 = ( s % 3 ) + 1; s2 = i4_wrap ( s1+1, 1, 3 ); k = k + 1; n1 = triangle_node[s1-1+(t-1)*6]; n2 = triangle_node[s1+3-1+(t-1)*6]; n3 = triangle_node[s2-1+(t-1)*6]; printf ( " %4d %4d %4d %4d %4d %4d\n", k, t, s1, n1, n2, n3 ); } } if ( skip ) { break; } } return; # undef DIM_NUM } /******************************************************************************/ void triangulation_order6_refine_compute ( int node_num1, int triangle_num1, double node_xy1[], int triangle_node1[], int node_num2, int triangle_num2, int edge_data[], double node_xy2[], int triangle_node2[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_REFINE_COMPUTE computes a refined order 6 triangulation. Discussion: Given a quadratic triangle defined by nodes 1, 2, 3, 4, 5, 6, we need to generate nodes 14, 16, 24, 25, 35, 36, 45, 46, 56, and 4 new quadratic subtriangles T1, T2, T3 and T4. The task is more complicated by the fact that we are working with a mesh of triangles, so that we want to create a node only once, even though it may be shared by other triangles. (In fact, only the new nodes on the edges can be shared, and then only by at most one other triangle.) 3 / \ 36 35 / T3 \ 6--56---5 / \ T4 / \ 16 46 45 25 / T1 \ / T2 \ 1--14---4--24---2 This routine is given sorted information defining the edges, and uses it to build the new node and triangle arrays. Licensing: This code is distributed under the MIT license. Modified: 07 June 2009 Author: John Burkardt Parameters: Input, int NODE_NUM1, the number of nodes. Input, int TRIANGLE_NUM1, the number of triangles. Input, double NODE_XY1[2*NODE_NUM1], the nodes. Input, int TRIANGLE_NODE1[6*TRIANGLE_NUM1], the nodes that make up the triangles. Input, int NODE_NUM2, the number of nodes in the refined mesh. Input, int TRIANGLE_NUM2, the number of triangles in the refined mesh. Input, int EDGE_DATA[5*(3*TRIANGLE_NUM1)], edge information computed by TRIANGULATION_ORDER6_REFINE_SIZE. Output, double NODE_XY2[2*NODE_NUM2], the refined nodes. Output, int TRIANGLE_NODE2[6*TRIANGLE_NUM2], the nodes that make up the triangles in the refined mesh. */ { int edge; int i; int j; int l1; int l2; int l3; int n1; int n1_old; int n2; int n2_old; int node; int t1; int t2; int t3; int t4; int triangle1; int v1; int v2; int v3; int v4; int v5; int v6; /* Step 1: Copy the old nodes. */ for ( j = 0; j < node_num1; j++ ) { for ( i = 0; i < 2; i++ ) { node_xy2[i+j*2] = node_xy1[i+j*2]; } } for ( j = 0; j < triangle_num2; j++ ) { for ( i = 0; i < 6; i++ ) { triangle_node2[i+j*6] = -1; } } /* We can assign the existing nodes to the new triangles. */ for ( triangle1 = 0; triangle1 < triangle_num1; triangle1++ ) { t1 = triangle1 * 4 + 0; t2 = triangle1 * 4 + 1; t3 = triangle1 * 4 + 2; t4 = triangle1 * 4 + 3; triangle_node2[0+t1*6] = triangle_node1[0+triangle1*6]; triangle_node2[1+t1*6] = triangle_node1[3+triangle1*6]; triangle_node2[2+t1*6] = triangle_node1[5+triangle1*6]; triangle_node2[0+t2*6] = triangle_node1[3+triangle1*6]; triangle_node2[1+t2*6] = triangle_node1[1+triangle1*6]; triangle_node2[2+t2*6] = triangle_node1[4+triangle1*6]; triangle_node2[0+t3*6] = triangle_node1[5+triangle1*6]; triangle_node2[1+t3*6] = triangle_node1[4+triangle1*6]; triangle_node2[2+t3*6] = triangle_node1[2+triangle1*6]; triangle_node2[0+t4*6] = triangle_node1[4+triangle1*6]; triangle_node2[1+t4*6] = triangle_node1[5+triangle1*6]; triangle_node2[2+t4*6] = triangle_node1[3+triangle1*6]; } /* Step 2. Examine sorted edge information. The first time an edge is encountered, generate two new nodes, then assign them (usually) to the four subtriangles of the two triangles that share that edge. */ node = node_num1; n1_old = -1; n2_old = -1; for ( edge = 0; edge < 3 * triangle_num1; edge++ ) { n1 = edge_data[0+edge*5] - 1; n2 = edge_data[1+edge*5] - 1; l1 = edge_data[2+edge*5]; l3 = edge_data[3+edge*5]; if ( l1 == 1 && l3 == 2 ) { l2 = 4; } else if ( l1 == 1 && l3 == 3 ) { l2 = 6; } else if ( l1 == 2 && l3 == 3 ) { l2 = 5; } triangle1 = edge_data[4+edge*5]; /* If this is the first time we've encountered this edge, create the new nodes. */ if ( n1 != n1_old || n2 != n2_old ) { n1_old = n1; n2_old = n2; v1 = triangle_node1[l1-1+triangle1*6]; v2 = triangle_node1[l2-1+triangle1*6]; v3 = triangle_node1[l3-1+triangle1*6]; for ( i = 0; i < 2; i++ ) { node_xy2[i+node*2] = ( node_xy2[i+(v1-1)*2] + node_xy2[i+(v2-1)*2] ) / 2.0; } node = node + 1; v4 = node; for ( i = 0; i < 2; i++ ) { node_xy2[i+node*2] = ( node_xy2[i+(v2-1)*2] + node_xy2[i+(v3-1)*2] ) / 2.0; } node = node + 1; v5 = node; } t1 = triangle1 * 4 + 0; t2 = triangle1 * 4 + 1; t3 = triangle1 * 4 + 2; if ( l1 == 1 && l3 == 2 ) { if ( triangle_node1[0+triangle1*6] == v1 + 1 ) { triangle_node2[3+t1*6] = v4; triangle_node2[3+t2*6] = v5; } else { triangle_node2[3+t1*6] = v5; triangle_node2[3+t2*6] = v4; } } else if ( l1 == 1 && l3 == 3 ) { if ( triangle_node1[0+triangle1*6] == v1 + 1 ) { triangle_node2[5+t1*6] = v4; triangle_node2[5+t3*6] = v5; } else { triangle_node2[5+t1*6] = v5; triangle_node2[5+t3*6] = v4; } } else if ( l1 == 2 && l3 == 3 ) { if ( triangle_node1[1+triangle1*6] == v1 + 1 ) { triangle_node2[4+t3*6] = v4; triangle_node2[4+t2*6] = v5; } else { triangle_node2[4+t3*6] = v5; triangle_node2[4+t2*6] = v4; } } } /* Step 3. Each old triangle has a single central subtriangle, for which we now need to generate three new "interior" nodes. */ for ( triangle1 = 0; triangle1 < triangle_num1; triangle1++ ) { v4 = triangle_node1[3+triangle1*6]; v5 = triangle_node1[4+triangle1*6]; v6 = triangle_node1[5+triangle1*6]; t1 = triangle1 * 4 + 0; t2 = triangle1 * 4 + 1; t3 = triangle1 * 4 + 2; t4 = triangle1 * 4 + 3; node_xy2[0+node*2] = 0.5 * ( node_xy1[0+(v5-1)*2] + node_xy1[0+(v6-1)*2] ); node_xy2[1+node*2] = 0.5 * ( node_xy1[1+(v5-1)*2] + node_xy1[1+(v6-1)*2] ); node = node + 1; triangle_node2[3+t4*6] = node; triangle_node2[3+t3*6] = node; node_xy2[0+node*2] = 0.5 * ( node_xy1[0+(v6-1)*2] + node_xy1[0+(v4-1)*2] ); node_xy2[1+node*2] = 0.5 * ( node_xy1[1+(v6-1)*2] + node_xy1[1+(v4-1)*2] ); node = node + 1; triangle_node2[4+t4*6] = node; triangle_node2[4+t1*6] = node; node_xy2[0+node*2] = 0.5 * ( node_xy1[0+(v4-1)*2] + node_xy1[0+(v5-1)*2] ); node_xy2[1+node*2] = 0.5 * ( node_xy1[1+(v4-1)*2] + node_xy1[1+(v5-1)*2] ); node = node + 1; triangle_node2[5+t4*6] = node; triangle_node2[5+t2*6] = node; } return; } /******************************************************************************/ void triangulation_order6_refine_size ( int node_num1, int triangle_num1, int triangle_node1[], int *node_num2, int *triangle_num2, int edge_data[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_REFINE_SIZE sizes a refined order 6 triangulation. Discussion: Given a quadratic triangle defined by nodes 1, 2, 3, 4, 5, 6, we need to generate nodes 14, 16, 24, 25, 35, 36, 45, 46, 56, and 4 new quadratic subtriangles T1, T2, T3 and T4. The task is more complicated by the fact that we are working with a mesh of triangles, so that we want to create a node only once, even though it may be shared by other triangles. (In fact, only the new nodes on the edges can be shared, and then only by at most one other triangle.) 3 / \ 36 35 / T3 \ 6--56---5 / \ T4 / \ 16 46 45 25 / T1 \ / T2 \ 1--14---4--24---2 This routine determines the sizes of the resulting node and triangles, and constructs an edge array that can be used to properly number the new nodes. The primary work occurs in sorting a list related to the edges. Licensing: This code is distributed under the MIT license. Modified: 11 February 2007 Author: John Burkardt Parameters: Input, int NODE_NUM1, the number of nodes in the original mesh. Input, int TRIANGLE_NUM1, the number of triangles in the original mesh. Input, int TRIANGLE_NODE1[6*TRIANGLE_NUM1], the indices of the nodes that form the triangles in the input mesh. Output, int *NODE_NUM2, the number of nodes in the refined mesh. Output, int *TRIANGLE_NUM2, the number of triangles in the refined mesh. Output, int EDGE_DATA[5*(3*TRIANGLE_NUM1)], edge data that will be needed by TRIANGULATION_ORDER6_REFINE_COMPUTE. */ { int a; int b; int edge; int i; int j; int k; int n1; int n1_old; int n2; int n2_old; int triangle1; /* Step 1. From the list of nodes for triangle T, of the form: (I,J,K) construct the edge relations: (I,J,1,2,T) (I,K,1,3,T) (J,K,2,3,T) In order to make matching easier, we reorder each pair of nodes into ascending order. */ for ( triangle1 = 0; triangle1 < triangle_num1; triangle1++ ) { i = triangle_node1[0+triangle1*6]; j = triangle_node1[1+triangle1*6]; k = triangle_node1[2+triangle1*6]; a = i4_min ( i, j ); b = i4_max ( i, j ); edge_data[0+5*(3*triangle1+0)] = a; edge_data[1+5*(3*triangle1+0)] = b; edge_data[2+5*(3*triangle1+0)] = 1; edge_data[3+5*(3*triangle1+0)] = 2; edge_data[4+5*(3*triangle1+0)] = triangle1; a = i4_min ( i, k ); b = i4_max ( i, k ); edge_data[0+5*(3*triangle1+1)] = a; edge_data[1+5*(3*triangle1+1)] = b; edge_data[2+5*(3*triangle1+1)] = 1; edge_data[3+5*(3*triangle1+1)] = 3; edge_data[4+5*(3*triangle1+1)] = triangle1; a = i4_min ( j, k ); b = i4_max ( j, k ); edge_data[0+5*(3*triangle1+2)] = a; edge_data[1+5*(3*triangle1+2)] = b; edge_data[2+5*(3*triangle1+2)] = 2; edge_data[3+5*(3*triangle1+2)] = 3; edge_data[4+5*(3*triangle1+2)] = triangle1; } /* Step 2. Perform an ascending dictionary sort on the neighbor relations. We only intend to sort on rows 1:2; the routine we call here sorts on the full column but that won't hurt us. What we need is to find all cases where triangles share an edge. By sorting the columns of the EDGE_DATA array, we will put shared edges next to each other. */ i4col_sort_a ( 5, 3*triangle_num1, edge_data ); /* Step 3. All the triangles which share an edge show up as consecutive columns with identical first two entries. Figure out how many new nodes there are, and allocate space for their coordinates. */ *node_num2 = node_num1; n1_old = -1; n2_old = -1; for ( edge = 0; edge < 3 * triangle_num1; edge++ ) { n1 = edge_data[0+edge*5]; n2 = edge_data[1+edge*5]; if ( n1 != n1_old || n2 != n2_old ) { *node_num2 = *node_num2 + 2; n1_old = n1; n2_old = n2; } } *node_num2 = *node_num2 + 3 * triangle_num1; *triangle_num2 = 4 * triangle_num1; return; } /******************************************************************************/ int *triangulation_order6_to_order3 ( int triangle_num1, int triangle_node1[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_TO_ORDER3 linearizes a quadratic triangulation. Discussion: A quadratic triangulation is assumed to consist of 6-node triangles, as in the following: 11-12-13-14-15 |\ |\ | | \ | \ | 6 7 8 9 10 | \ | \ | | \| \| 1--2--3--4--5 This routine rearranges information so as to define the 3-node triangulation: 11-12-13-14-15 |\ |\ |\ |\ | | \| \| \| \| 6--7--8--9-10 |\ |\ |\ |\ | | \| \| \| \| 1--2--3--4--5 Licensing: This code is distributed under the MIT license. Modified: 24 March 2005 Author: John Burkardt Parameters: Input, int TRIANGLE_NUM1, the number of triangles in the quadratic triangulation. Input, int TRIANGLE_NODE1[6*TRIANGLE_NUM1], the quadratic triangulation. Output, int TRIANGULATION_ORDER6_TO_ORDER3[3*TRIANGLE_NUM2], the linear triangulation. Here, TRIANGLE_NUM2 = 4 * TRIANGLE_NUM1. */ { int n1; int n2; int n3; int n4; int n5; int n6; int triangle_num2; int tri1; int tri2; int *triangle_node2; triangle_num2 = 4 * triangle_num1; triangle_node2 = ( int * ) malloc ( 3 * triangle_num2 * sizeof ( int ) ); tri2 = 0; for ( tri1 = 0; tri1 < triangle_num1; tri1++ ) { n1 = triangle_node1[0+tri1*6]; n2 = triangle_node1[1+tri1*6]; n3 = triangle_node1[2+tri1*6]; n4 = triangle_node1[3+tri1*6]; n5 = triangle_node1[4+tri1*6]; n6 = triangle_node1[5+tri1*6]; triangle_node2[0+tri2*3] = n1; triangle_node2[1+tri2*3] = n4; triangle_node2[2+tri2*3] = n6; tri2 = tri2 + 1; triangle_node2[0+tri2*3] = n2; triangle_node2[1+tri2*3] = n5; triangle_node2[2+tri2*3] = n4; tri2 = tri2 + 1; triangle_node2[0+tri2*3] = n3; triangle_node2[1+tri2*3] = n6; triangle_node2[2+tri2*3] = n5; tri2 = tri2 + 1; triangle_node2[0+tri2*3] = n4; triangle_node2[1+tri2*3] = n5; triangle_node2[2+tri2*3] = n6; tri2 = tri2 + 1; } return triangle_node2; } /******************************************************************************/ int triangulation_order6_vertex_count ( int tri_num, int triangle_node[] ) /******************************************************************************/ /* Purpose: TRIANGULATION_ORDER6_VERTEX_COUNT counts vertex nodes in a triangulation. Discussion: In a triangulation of order 6, some nodes are midside nodes and some nodes are vertex nodes. Especially when an order 6 triangulation is used to handle the Navier Stokes equations, it is useful to know the number of vertex and midside nodes. Note that the number of midside nodes is simple NODE_NUM - VERTEX_NUM. Diagram: 3 s |\ i | \ d | \ e 6 5 side 2 | \ 3 | \ | \ 1---4---2 side 1 The local node numbering. Local nodes 1, 2 and 3 are "vertex nodes", while nodes 4, 5 and 6 are "midside nodes". 21-22-23-24-25 |\ |\ | | \ | \ | 16 17 18 19 20 | \ | \ | | \| \| 11-12-13-14-15 |\ |\ | | \ | \ | 6 7 8 9 10 | \ | \ | | \| \| 1--2--3--4--5 A sample grid, which contains 9 vertex nodes and 16 midside nodes. Licensing: This code is distributed under the MIT license. Modified: 24 August 2006 Author: John Burkardt Parameters Input, int TRI_NUM, the number of triangles. Input, int TRIANGLE_NODE[6*TRI_NUM], lists the nodes that make up each triangle. The first three nodes are the vertices, in counterclockwise order. The fourth value is the midside node between nodes 1 and 2; the fifth and sixth values are the other midside nodes in the logical order. Output, int TRIANGULATION_ORDER6_VERTEX_COUNT, the number of nodes which are vertices. */ { int j; int vertex_num; int *vertices; vertices = ( int * ) malloc ( 3 * tri_num * sizeof ( int ) ); for ( j = 0; j < tri_num; j++ ) { vertices[j] = triangle_node[0+j*6]; } for ( j = 0; j < tri_num; j++ ) { vertices[tri_num+j] = triangle_node[1+j*6]; } for ( j = 0; j < tri_num; j++ ) { vertices[2*tri_num+j] = triangle_node[2+j*6]; } i4vec_sort_heap_a ( 3*tri_num, vertices ); vertex_num = i4vec_sorted_unique ( 3*tri_num, vertices ); free ( vertices ); return vertex_num; } /******************************************************************************/ void triangulation_search_delaunay ( int node_num, double node_xy[], int triangle_order, int triangle_num, int triangle_node[], int triangle_neighbor[], double p[2], int *triangle_index, double *alpha, double *beta, double *gamma, int *edge, int *step_num ) /******************************************************************************/ /* Purpose: TRIANGULATION_SEARCH_DELAUNAY searches a triangulation for a point. Discussion: The algorithm "walks" from one triangle to its neighboring triangle, and so on, until a triangle is found containing point P, or P is found to be outside the convex hull. The algorithm computes the barycentric coordinates of the point with respect to the current triangle. If all three quantities are positive, the point is contained in the triangle. If the I-th coordinate is negative, then (X,Y) lies on the far side of edge I, which is opposite from vertex I. This gives a hint as to where to search next. For a Delaunay triangulation, the search is guaranteed to terminate. For other triangulations, a cycle may occur. Note the surprising fact that, even for a Delaunay triangulation of a set of nodes, the nearest point to (X,Y) need not be one of the vertices of the triangle containing (X,Y). The code can be called for triangulations of any order, but only the first three nodes in each triangle are considered. Thus, if higher order triangles are used, and the extra nodes are intended to give the triangle a polygonal shape, these will have no effect, and the results obtained here might be misleading. Licensing: This code is distributed under the MIT license. Modified: 26 October 2012 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles in the triangulation. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes of each triangle. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbor list. Input, double P[2], the coordinates of a point. Output, int *TRIANGLE_INDEX, the index of the triangle where the search ended. If a cycle occurred, then TRIANGLE_INDEX = -1. Output, double *ALPHA, *BETA, *GAMMA, the barycentric coordinates of the point relative to triangle TRIANGLE_INDEX. Output, int *EDGE, indicates the position of the point (X,Y) in triangle TRIANGLE: 0, the interior or boundary of the triangle; -1, outside the convex hull of the triangulation, past edge 1; -2, outside the convex hull of the triangulation, past edge 2; -3, outside the convex hull of the triangulation, past edge 3. Output, int *STEP_NUM, the number of steps. */ { int a; int b; int c; double det; double dxp; double dxa; double dxb; double dyp; double dya; double dyb; static int triangle_index_save = -1; *step_num = - 1; *edge = 0; if ( triangle_index_save < 0 || triangle_num <= triangle_index_save ) { *triangle_index = ( triangle_num + 1 ) / 2; } else { *triangle_index = triangle_index_save; } for ( ; ; ) { *step_num = *step_num + 1; if ( triangle_num < *step_num ) { printf ( "\n" ); printf ( "TRIANGULATION_SEARCH_DELAUNAY - Fatal error!\n" ); printf ( " The algorithm seems to be cycling.\n" ); printf ( " Current triangle is %d\n", *triangle_index ); *triangle_index = -1; *alpha = -1.0; *beta = -1.0; *gamma = -1.0; *edge = -1; return; } /* Get the vertices of triangle TRIANGLE. */ a = triangle_node[0+(*triangle_index-1)*triangle_order]; b = triangle_node[1+(*triangle_index-1)*triangle_order]; c = triangle_node[2+(*triangle_index-1)*triangle_order]; /* Using vertex C as a base, compute the distances to vertices A and B, and the point (X,Y). */ dxa = node_xy[0+a*2] - node_xy[0+c*2]; dya = node_xy[1+a*2] - node_xy[1+c*2]; dxb = node_xy[0+b*2] - node_xy[0+c*2]; dyb = node_xy[1+b*2] - node_xy[1+c*2]; dxp = p[0] - node_xy[0+c*2]; dyp = p[1] - node_xy[1+c*2]; det = dxa * dyb - dya * dxb; /* Compute the barycentric coordinates of the point (X,Y) with respect to this triangle. */ *alpha = ( dxp * dyb - dyp * dxb ) / det; *beta = ( dxa * dyp - dya * dxp ) / det; *gamma = 1.0 - *alpha - *beta; /* If the barycentric coordinates are all positive, then the point is inside the triangle and we're done. */ if ( 0.0 <= *alpha && 0.0 <= *beta && 0.0 <= *gamma ) { break; } /* At least one barycentric coordinate is negative. If there is a negative barycentric coordinate for which there exists an opposing triangle neighbor closer to the point, move to that triangle. (Two coordinates could be negative, in which case we could go for the most negative one, or the most negative one normalized by the actual distance it represents). */ if ( *alpha < 0.0 && 0 <= triangle_neighbor[1+(*triangle_index-1)*3] ) { *triangle_index = triangle_neighbor[1+(*triangle_index-1)*3]; continue; } else if ( *beta < 0.0 && 0 <= triangle_neighbor[2+(*triangle_index-1)*3] ) { *triangle_index = triangle_neighbor[2+(*triangle_index-1)*3]; continue; } else if ( *gamma < 0.0 && 0 <= triangle_neighbor[0+(*triangle_index-1)*3] ) { *triangle_index = triangle_neighbor[0+(*triangle_index-1)*3]; continue; } /* All negative barycentric coordinates correspond to vertices opposite sides on the convex hull. Note the edge and exit. */ if ( *alpha < 0.0 ) { *edge = -2; break; } else if ( *beta < 0.0 ) { *edge = -3; break; } else if ( *gamma < 0.0 ) { *edge = -1; break; } else { printf ( "\n" ); printf ( "TRIANGULATION_ORDER3_SEARCH - Fatal error!\n" ); printf ( " The algorithm seems to have reached a dead end\n" ); printf ( " after %d steps.\n", *step_num ); *triangle_index = -1; *edge = -1; return; } } triangle_index_save = *triangle_index; return; } /******************************************************************************/ int triangulation_search_naive ( int node_num, double node_xy[], int triangle_order, int triangle_num, int triangle_node[], double p[2] ) /******************************************************************************/ /* Purpose: TRIANGULATION_SEARCH_NAIVE naively searches a triangulation for a point. Discussion: The algorithm simply checks each triangle to see if point P is contained in it. Surprisingly, this is not the fastest way to do the check, at least if the triangulation is Delaunay. Licensing: This code is distributed under the MIT license. Modified: 07 June 2009 Author: John Burkardt Parameters: Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_ORDER, the order of the triangles. Input, int TRIANGLE_NUM, the number of triangles in the triangulation. Input, int TRIANGLE_NODE[TRIANGLE_ORDER*TRIANGLE_NUM], the nodes of each triangle. Input, double P[2], the coordinates of a point. Output, int TRIANGULATION_SEARCH_NAIVE, the index of the triangle containing the point, or -1 if no triangle was found containing the point. */ { int a; double alpha; int b; double beta; int c; double det; double dxp; double dxa; double dxb; double dyp; double dya; double dyb; double gamma; int triangle; int triangle_index; triangle_index = -1; for ( triangle = 0; triangle < triangle_num; triangle++ ) { /* Get the vertices of triangle TRIANGLE. */ a = triangle_node[0+triangle*triangle_order]; b = triangle_node[1+triangle*triangle_order]; c = triangle_node[2+triangle*triangle_order]; /* Using vertex C as a base, compute the distances to vertices A and B, and the point (X,Y). */ dxa = node_xy[0+a*2] - node_xy[0+c*2]; dya = node_xy[1+a*2] - node_xy[1+c*2]; dxb = node_xy[0+b*2] - node_xy[0+c*2]; dyb = node_xy[1+b*2] - node_xy[1+c*2]; dxp = p[0] - node_xy[0+c*2]; dyp = p[1] - node_xy[1+c*2]; det = dxa * dyb - dya * dxb; /* Compute the barycentric coordinates of the point (X,Y) with respect to this triangle. */ alpha = ( dxp * dyb - dyp * dxb ) / det; beta = ( dxa * dyp - dya * dxp ) / det; gamma = 1.0 - alpha - beta; /* If the barycentric coordinates are all positive, then the point is inside the triangle and we're done. */ if ( 0.0 <= alpha && 0.0 <= beta && 0.0 <= gamma ) { triangle_index = triangle + 1; break; } } return triangle_index; } /******************************************************************************/ void vbedg ( double x, double y, int node_num, double node_xy[], int triangle_num, int triangle_node[], int triangle_neighbor[], int *ltri, int *ledg, int *rtri, int *redg ) /******************************************************************************/ /* Purpose: VBEDG determines which boundary edges are visible to a point. Discussion: The point (X,Y) is assumed to be outside the convex hull of the region covered by the 2D triangulation. Licensing: This code is distributed under the MIT license. Modified: 30 September 2008 Author: Original FORTRAN77 version by Barry Joe. C version by John Burkardt. Reference: Barry Joe, GEOMPACK - a software package for the generation of meshes using geometric algorithms, Advances in Engineering Software, Volume 13, pages 325-331, 1991. Parameters: Input, double X, Y, the coordinates of a point outside the convex hull of the current triangulation. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Input, int TRIANGLE_NUM, the number of triangles. Input, int TRIANGLE_NODE[3*TRIANGLE_NUM], the triangle incidence list. Input, int TRIANGLE_NEIGHBOR[3*TRIANGLE_NUM], the triangle neighbor list; negative values are used for links of a counter clockwise linked list of boundary edges; LINK = -(3*I + J-1) where I, J = triangle, edge index. Input/output, int *LTRI, *LEDG. If LTRI != 0 then these values are assumed to be already computed and are not changed, else they are updated. On output, LTRI is the index of boundary triangle to the left of the leftmost boundary triangle visible from (X,Y), and LEDG is the boundary edge of triangle LTRI to the left of the leftmost boundary edge visible from (X,Y). 1 <= LEDG <= 3. Input/output, int *RTRI. On input, the index of the boundary triangle to begin the search at. On output, the index of the rightmost boundary triangle visible from (X,Y). Input/output, int *REDG, the edge of triangle RTRI that is visible from (X,Y). 1 <= REDG <= 3. */ { int a; double ax; double ay; int b; double bx; double by; int done; int e; int l; int lr; int t; /* Find the rightmost visible boundary edge using links, then possibly leftmost visible boundary edge using triangle neighbor information. */ if ( *ltri == 0 ) { done = 0; *ltri = *rtri; *ledg = *redg; } else { done = 1; } for ( ; ; ) { l = -triangle_neighbor[3*((*rtri)-1)+(*redg)-1]; t = l / 3; e = 1 + l % 3; a = triangle_node[3*(t-1)+e-1]; if ( e <= 2 ) { b = triangle_node[3*(t-1)+e]; } else { b = triangle_node[3*(t-1)+0]; } ax = node_xy[2*(a-1)+0]; ay = node_xy[2*(a-1)+1]; bx = node_xy[2*(b-1)+0]; by = node_xy[2*(b-1)+1]; lr = lrline ( x, y, ax, ay, bx, by, 0.0 ); if ( lr <= 0 ) { break; } *rtri = t; *redg = e; } if ( done ) { return; } t = *ltri; e = *ledg; for ( ; ; ) { b = triangle_node[3*(t-1)+e-1]; e = i4_wrap ( e-1, 1, 3 ); while ( 0 < triangle_neighbor[3*(t-1)+e-1] ) { t = triangle_neighbor[3*(t-1)+e-1]; if ( triangle_node[3*(t-1)+0] == b ) { e = 3; } else if ( triangle_node[3*(t-1)+1] == b ) { e = 1; } else { e = 2; } } a = triangle_node[3*(t-1)+e-1]; ax = node_xy[2*(a-1)+0]; ay = node_xy[2*(a-1)+1]; bx = node_xy[2*(b-1)+0]; by = node_xy[2*(b-1)+1]; lr = lrline ( x, y, ax, ay, bx, by, 0.0 ); if ( lr <= 0 ) { break; } } *ltri = t; *ledg = e; return; } /******************************************************************************/ double voronoi_polygon_area ( int node, int neighbor_num, int neighbor_index[], int node_num, double node_xy[] ) /******************************************************************************/ /* Purpose: VORONOI_POLYGON_AREA computes the area of a Voronoi polygon. Formula: It is assumed that the Voronoi polygon is finite! Every Voronoi diagram includes some regions which are infinite, and for those, this formula is not appropriate. The routine is given the indices of the nodes that are Voronoi "neighbors" of a given node. These are also the nodes that are paired to form edges of Delaunay triangles. The assumption that the polygon is a Voronoi polygon is used to determine the location of the boundaries of the polygon, which are the perpendicular bisectors of the lines connecting the center point to each of its neighbors. The finiteness assumption is employed in part in the assumption that the polygon is bounded by the finite line segments from point 1 to 2, 2 to 3, ..., M-1 to M, and M to 1, where M is the number of neighbors. It is assumed that this routine is being called by a process which has computed the Voronoi diagram of a large set of nodes, so the arrays X and Y are dimensioned by NODE_NUM, which may be much greater than the number of neighbor nodes. Licensing: This code is distributed under the MIT license. Modified: 08 February 2005 Author: John Burkardt Reference: Atsuyuki Okabe, Barry Boots, Kokichi Sugihara, Sung Nok Chiu, Spatial Tessellations: Concepts and Applications of Voronoi Diagrams, Second Edition, Wiley, 2000, page 485. Parameters: Input, int NODE, the index of the node whose Voronoi polygon is to be measured. 0 <= NODE < NODE_NUM. Input, int NEIGHBOR_NUM, the number of neighbor nodes of the given node. Input, int NEIGHBOR_INDEX[NEIGHBOR_NUM], the indices of the neighbor nodes (used to access X and Y). The neighbor nodes should be listed in the (counter-clockwise) order in which they occur as one circles the center node. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, double VORONOI_POLYGON_AREA, the area of the Voronoi polygon. */ { double a; double area; double b; double c; int i; int ip1; double ui; double uip1; double vi; double vip1; double xc; double xi; double xip1; double yc; double yi; double yip1; area = 0.0; if ( node < 0 || node_num <= node ) { printf ( "\n" ); printf ( " VORONOI_POLYGON_AREA - Fatal error!\n" ); printf ( " Illegal value of input parameter NODE.\n" ); exit ( 1 ); } xc = node_xy[0+node*2]; yc = node_xy[1+node*2]; i = neighbor_num - 1; i = neighbor_index[i]; xi = node_xy[0+i*2]; yi = node_xy[1+i*2]; ip1 = 0; ip1 = neighbor_index[ip1]; xip1 = node_xy[0+ip1*2]; yip1 = node_xy[1+ip1*2]; a = ( xi * xi + yi * yi - xc * xc - yc * yc ); b = ( xip1 * xip1 + yip1 * yip1 - xc * xc - yc * yc ); c = 2.0 * ( ( xi - xc ) * ( yip1 - yc ) - ( xip1 - xc ) * ( yi - yc ) ); uip1 = ( a * ( yip1 - yc ) - b * ( yi - yc ) ) / c; vip1 = ( a * ( xip1 - xc ) - b * ( xi - xc ) ) / c; for ( i = 0; i < neighbor_num; i++ ) { xi = xip1; yi = yip1; ui = uip1; vi = vip1; ip1 = i4_wrap ( i+1, 0, neighbor_num-1 ); ip1 = neighbor_index[ip1]; xip1 = node_xy[0+ip1*2]; yip1 = node_xy[1+ip1*2]; a = ( xi * xi + yi * yi - xc * xc - yc * yc ); b = ( xip1 * xip1 + yip1 * yip1 - xc * xc - yc * yc ); c = 2.0 * ( ( xi - xc ) * ( yip1 - yc ) - ( xip1 - xc ) * ( yi - yc ) ); uip1 = ( a * ( yip1 - yc ) - b * ( yi - yc ) ) / c; vip1 = ( a * ( xip1 - xc ) - b * ( xi - xc ) ) / c; area = area + uip1 * vi - ui * vip1; } area = 0.5 * area; return area; } /******************************************************************************/ double *voronoi_polygon_centroid ( int node, int neighbor_num, int neighbor_index[], int node_num, double node_xy[] ) /******************************************************************************/ /* Purpose: VORONOI_POLYGON_CENTROID_2D computes the centroid of a Voronoi polygon. Formula: It is assumed that the Voronoi polygon is finite! Every Voronoi diagram includes some regions which are infinite, and for those, this formula is not appropriate. The routine is given the indices of the nodes that are Voronoi "neighbors" of a given node. These are also the nodes that are paired to form edges of Delaunay triangles. The assumption that the polygon is a Voronoi polygon is used to determine the location of the boundaries of the polygon, which are the perpendicular bisectors of the lines connecting the center point to each of its neighbors. The finiteness assumption is employed in part in the assumption that the polygon is bounded by the finite line segments from point 1 to 2, 2 to 3, ..., M-1 to M, and M to 1, where M is the number of neighbors. It is assumed that this routine is being called by a process which has computed the Voronoi diagram of a large set of nodes, so the arrays X and Y are dimensioned by NODE_NUM, which may be much greater than the number of neighbor nodes. Licensing: This code is distributed under the MIT license. Modified: 08 February 2005 Author: John Burkardt Reference: Atsuyuki Okabe, Barry Boots, Kokichi Sugihara, Sung Nok Chiu, Spatial Tessellations: Concepts and Applications of Voronoi Diagrams, Second Edition, Wiley, 2000, page 490. Parameters: Input, int NODE, the index of the node whose Voronoi polygon is to be analyzed. 1 <= NODE <= NODE_NUM. Input, int NEIGHBOR_NUM, the number of neighbor nodes of the given node. Input, int NEIGHBOR_INDEX[NEIGHBOR_NUM], the indices of the neighbor nodes. These indices are used to access the X and Y arrays. The neighbor nodes should be listed in the (counter-clockwise) order in which they occur as one circles the center node. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, double *VORONOI_POLYGON_CENTROID_2D, a pointer to a 2D array containing the coordinates of the centroid of the Voronoi polygon of node NODE. */ { double a; double area; double b; double c; double *centroid; int i; int ip1; double ui; double uip1; double vi; double vip1; double xc; double xi; double xip1; double yc; double yi; double yip1; centroid = ( double * ) malloc ( 2 * sizeof ( double ) ); centroid[0] = 0.0; centroid[1] = 0.0; if ( node < 0 || node_num <= node ) { printf ( "\n" ); printf ( "VORONOI_POLYGON_CENTROID - Fatal error!\n" ); printf ( " Illegal value of input parameter NODE.\n" ); exit ( 1 ); } xc = node_xy[0+node*2]; yc = node_xy[1+node*2]; i = neighbor_num - 1; i = neighbor_index[i]; xi = node_xy[0+i*2]; yi = node_xy[1+i*2]; ip1 = 0; ip1 = neighbor_index[ip1]; xip1 = node_xy[0+ip1*2]; yip1 = node_xy[1+ip1*2]; a = ( xi * xi + yi * yi - xc * xc - yc * yc ); b = ( xip1 * xip1 + yip1 * yip1 - xc * xc - yc * yc ); c = 2.0 * ( ( xi - xc ) * ( yip1 - yc ) - ( xip1 - xc ) * ( yi - yc ) ); uip1 = ( a * ( yip1 - yc ) - b * ( yi - yc ) ) / c; vip1 = ( a * ( xip1 - xc ) - b * ( xi - xc ) ) / c; for ( i = 0; i < neighbor_num; i++ ) { xi = xip1; yi = yip1; ui = uip1; vi = vip1; ip1 = i4_wrap ( i+1, 0, neighbor_num-1 ); ip1 = neighbor_index[ip1]; xip1 = node_xy[0+ip1*2]; yip1 = node_xy[1+ip1*2]; a = ( xi * xi + yi * yi - xc * xc - yc * yc ); b = ( xip1 * xip1 + yip1 * yip1 - xc * xc - yc * yc ); c = 2.0 * ( ( xi - xc ) * ( yip1 - yc ) - ( xip1 - xc ) * ( yi - yc ) ); uip1 = ( a * ( yip1 - yc ) - b * ( yi - yc ) ) / c; vip1 = ( a * ( xip1 - xc ) - b * ( xi - xc ) ) / c; centroid[0] = centroid[0] + ( vi - vip1 ) * ( ( uip1 + ui ) * ( uip1 + ui ) - uip1 * ui ); centroid[1] = centroid[1] + ( ui - uip1 ) * ( ( vip1 + vi ) * ( vip1 + vi ) - vip1 * vi ); } area = voronoi_polygon_area ( node, neighbor_num, neighbor_index, node_num, node_xy ); centroid[0] = centroid[0] / ( 6.0 * area ); centroid[1] = centroid[1] / ( 6.0 * area ); return centroid; } /******************************************************************************/ void voronoi_polygon_vertices ( int node, int neighbor_num, int neighbor_index[], int node_num, double node_xy[], double v[] ) /******************************************************************************/ /* Purpose: VORONOI_POLYGON_VERTICES_2D computes the vertices of a Voronoi polygon. Formula: This routine is only appropriate for Voronoi polygons that are finite. The routine is given the indices of the nodes that are neighbors of a given "center" node. A node is a neighbor of the center node if the Voronoi polygons of the two nodes share an edge. The triangles of the Delaunay triangulation are formed from successive pairs of these neighbor nodes along with the center node. Given only the neighbor node information, it is possible to determine the location of the vertices of the polygonal Voronoi region by computing the circumcenters of the Delaunay triangles. It is assumed that this routine is being called by a process which has computed the Voronoi diagram of a large set of nodes, so the arrays X and Y are dimensioned by NODE_NUM, which may be much greater than the number of neighbor nodes. Licensing: This code is distributed under the MIT license. Modified: 08 February 2005 Author: John Burkardt Reference: Atsuyuki Okabe, Barry Boots, Kokichi Sugihara, Sung Nok Chiu, Spatial Tessellations: Concepts and Applications of Voronoi Diagrams, Second Edition, Wiley, 2000. Parameters: Input, int NODE, the index of the node whose Voronoi polygon is to be analyzed. 1 <= NODE <= NODE_NUM. Input, int NEIGHBOR_NUM, the number of neighbor nodes of the given node. Input, int NEIGHBOR_INDEX(NEIGHBOR_NUM), the indices of the neighbor nodes. These indices are used to access the X and Y arrays. The neighbor nodes should be listed in the (counter-clockwise) order in which they occur as one circles the center node. Input, int NODE_NUM, the number of nodes. Input, double NODE_XY[2*NODE_NUM], the coordinates of the nodes. Output, double V[2*NEIGHBOR_NUM], the vertices of the Voronoi polygon around node NODE. */ { # define DIM_NUM 2 double *center; int i; int ip1; double t[DIM_NUM*3]; if ( node < 0 || node_num <= node ) { printf ( "\n" ); printf ( "VORONOI_POLYGON_VERTICES - Fatal error!\n" ); printf ( " Illegal value of input parameter NODE.\n" ); exit ( 1 ); } t[0+0*2] = node_xy[0+node*2]; t[1+0*2] = node_xy[1+node*2]; ip1 = neighbor_index[0]; t[0+2*2] = node_xy[0+ip1*2]; t[1+2*2] = node_xy[1+ip1*2]; for ( i = 0; i < neighbor_num; i++ ) { t[0+1*2] = t[0+2*2]; t[1+1*2] = t[1+2*2]; ip1 = i4_wrap ( i+1, 0, neighbor_num-1 ); ip1 = neighbor_index[ip1]; t[0+2*2] = node_xy[0+ip1*2]; t[1+2*2] = node_xy[1+ip1*2]; center = triangle_circumcenter_2d ( t ); v[0+i*2] = center[0]; v[1+i*2] = center[1]; free ( center ); } return; # undef DIM_NUM }