Rev 7054 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/* Code for soap film smoothing. Copyright Simon Wood 2006-2012.R CMD SHLIB soap.c creates appropriate soap.so from this, canthen be loaded by dyn.load("soap.so") and called with .C() */#include <math.h>#include <stdio.h>#include <stdlib.h>#include <R.h>#include "mgcv.h"/****************************************************************************************************//* Boundary handling utilities from here on.... *//****************************************************************************************************/void boundary(int *G, double *d, double *dto, double *x0, double *y0, double *dx, double *dy,int *nx, int *ny, double *x, double *y,double *break_code, int *n, int *nb)/* Function to create solution grid definition matrix G (nx by ny).Lower left cell centre is at x0, y0. cells are dx by dy. On entrymatrices d and dto are same dimension as G.The boundary is supplied in n-arrays, `x' and `y'. Sub loops are separated by elements<= break_code. nb must have dimension of number of loops.On exit:G[i,j] < - nx * ny is outside boundary, otherwiseG[i,j] <= 0 is on boundary, and -G[i,j] indexes cell in d and g.G[i,j] > 0 indexes cell in g.On exit d contains the distances along the boundary, stored sequentially fromelement 0 (i.e. d is a 1D array). nb contains the length of each boundary loopin d (i.e. its cell count).'g' refers to the solution grid itself, which will contain only interior andboundary points.The boundary in x,y must be *strictly* within the outer grid cells.`G' is stored column-wise (R default).`dto' is a working matrix containing distances from the boundary to thecell centre. This is needed for judging which of multiple boundary segmentsshould supply the boundary value (the closest).The term `vertical' means parallel to y axis.The basic principle is that the boundaries between grid cells aregiven by a set of evenly spaced horizontal and vertical lines.It is easy to work out which lines are crossed by a boundary linesegment, and where this crossing occurs. Cells whose cell boundariesare cut are treated as boundary cells.*/{ int segi,j,j0,j1,k,kk,i,reversed,*inb,*ip,*ip1,*ip2,bnd_count,ii,out_lim;double x1,y1,x2,y2,xb0,yb0,xl,yl,xc,yc,dist,dist_to,grad=0.0,b,len2,*p1,*p2;/* first step is to mark outside points in grid */p1 = d;p2 = dto;for (x1 = *x0,i=0;i<*nx;i++,x1 += *dx) {for (y1 = *y0,j=0;j<*ny;j++,y1 += *dy,p1++,p2++) {*p1 = x1;*p2 = y1; /* cell centres */}}k = *nx * *ny; /* total size of G, d, dto */out_lim = -k;inb = (int *)CALLOC((size_t)k,sizeof(int));in_out(x,y,break_code,d,dto,inb,n,&k); /* test all cell centres for in/out */j = -(k + 10);for (ip = inb,ip1 = G,p2 = dto,ip2=G+k;ip1<ip2;ip1++,p2++,ip++) {if (!*ip) *ip1 = j; else *ip1 = 1; /* set outside/inside in G */*p2 = -1.0; /* clear dto to -ve */}FREE(inb);xb0 = *x0 - *dx/2;yb0 = *y0 - *dx/2; /* Refers to boundary lines lower left */dist = 0.0; /* distance along current boundary loop */bnd_count = 0; /* count number of boundary loops */nb[0] = 0; /* count cells in this boundary */ii = 0; /* counter for all boundary cells */for (segi=1;segi<*n;segi++) { /* loop through line segments *//* work through intersections with vertical boundary lines */if (x[segi-1]<x[segi]) { /* get current segment */x1=x[segi-1];x2=x[segi];y1=y[segi-1];y2=y[segi];reversed = 0;} else {x1=x[segi];x2=x[segi-1];y1=y[segi];y2=y[segi-1];reversed = 1;}j0 = (int) ceil( (x1 - xb0) / *dx); /* index of first vertical line after start */j1 = (int) floor( (x2 - xb0) / *dx); /* index of last vertical line before end */if (x2-x1>0) grad = (y2-y1)/(x2-x1); else j1=j0-1;for (j=j0;j<=j1;j++) { /* loop through intersected lines */xl = xb0 + j * *dx; /* line location */yl = y1 + (xl - x1)*grad; /* y intersection location */k = (int) floor(( yl - yb0)/ *dy);/* so nodes j,k and (j-1),k are boundary nodes */kk = (j-1) * *ny + k;if (G[kk]>0||G[kk]< out_lim) { /* otherwise already a boundary cell */G[kk] = -ii;ii++;nb[bnd_count]++;}kk += *ny; /* j * *ny + k */if (G[kk]>0||G[kk]< out_lim) { /* otherwise already a boundary cell */G[kk] = -ii;ii++;nb[bnd_count]++;}/* Now get the distance along/to the boundary */for (i=0;i<2;i++) { /* loop over the two cells concerned */xl = x2-x1;yl=y2-y1;xc = (j-i) * *dx + *x0;yc = k * *dy + *y0;xc -= x1;yc -= y1; /* cell centre done */len2 = yl*yl + xl*xl;b = (xc*xl + yc*yl)/len2;xl = xl*b+x1;yl = yl * b + y1; /* location of projection from node to line */if (xl < x1) {xl = x1;yl = y1;}if (xl > x2) {xl = x2;yl = y2;} /* constrained to *within* segment */dist_to = sqrt((xl-xc)*(xl-xc) + (yl-yc)*(yl-yc));kk = (j-i) * *ny + k;if (dist_to < dto[kk] || dto[kk]<0) {dto[kk] = dist_to;xl -= x1; yl -= y1;if (reversed)d[-G[kk]] = dist + sqrt(len2) - sqrt(xl*xl + yl*yl);elsed[-G[kk]] = dist + sqrt(xl*xl + yl*yl); /* distance along boundary */}}} /* end of vertical line processing *//* Now deal with horizontal lines */if (y[segi-1]<y[segi]) { /* get current segment */x1=x[segi-1];x2=x[segi];y1=y[segi-1];y2=y[segi];reversed=0;} else {x1=x[segi];x2=x[segi-1];y1=y[segi];y2=y[segi-1];reversed=1;}j0 = (int) ceil( (y1 - yb0) / *dy); /* index of first horizontal line after start */j1 = (int) floor( (y2 - yb0) / *dy); /* index of last horizontal line before end */if (y2-y1>0) grad = (x2-x1)/(y2-y1); else j1=j0-1;for (j=j0;j<=j1;j++) { /* loop through intersected lines */yl = yb0 + j * *dy; /* line location */xl = x1 + (yl - y1)*grad; /* y intersection location */k = (int) floor(( xl - xb0)/ *dx);/* so nodes k,j and k, (j-1) are boundary nodes */kk = k * *ny + j - 1;if (G[kk]>0||G[kk]< out_lim) {G[kk] = -ii;ii++;nb[bnd_count]++;} /* otherwise already a boundary cell */kk ++; /* k * *ny + j */if (G[kk]>0||G[kk]< out_lim) {G[kk] = -ii;ii++;nb[bnd_count]++;} /* otherwise already a boundary cell *//* Now get the distance along/to the boundary */for (i=0;i<2;i++) { /* loop over the two cells concerned */xl = x2-x1;yl=y2-y1;yc = (j-i) * *dy + *y0;xc = k * *dx + *x0;xc -= x1;yc -= y1; /* cell centre done */len2 = yl*yl + xl*xl;b = (xc*xl + yc*yl)/len2;xl = xl*b+x1;yl = yl * b + y1; /* location of projection from node to line */if (yl < y1) {xl = x1;yl = y1;}if (yl > y2) {xl = x2;yl = y2;} /* constrained to *within* segment */dist_to = sqrt((xl-xc)*(xl-xc) + (yl-yc)*(yl-yc));kk = k * *ny + j-i;if (dist_to < dto[kk] || dto[kk]<0) {dto[kk] = dist_to;xl -= x1; yl -= y1;if (reversed)d[-G[kk]] = dist + sqrt(len2) - sqrt(xl*xl + yl*yl);elsed[-G[kk]] = dist + sqrt(xl*xl + yl*yl); /* distance along boundary */}}} /* end of horizontal line processing *//* update `dist' */x2 = x2-x1;y2=y2-y1;dist += sqrt(x2*x2+y2*y2);/* now look ahead to see if we are at the end of a sub-loop */if (segi < *n - 1 && x[segi+1] <= *break_code) { /* reached segment end */dist = 0.0; /* reset for new loop */segi++;segi++; /* move past the break */bnd_count++; /* loop counter */if (segi < *n) nb[bnd_count] = 0; /* set cell counter for this loop */}} /* end of line segment loop *//* Clear the remainder of d to -ve */k = *nx * *ny;for (i=ii;i<k;i++) d[i] = -1;/* Set remaining elements of G */for (ip1 = G,ip2 = G + k;ip1<ip2;ip1++) if (*ip1 > 0) {*ip1 = ii;ii++;}} /* end of boundary */void pde_coeffs(int *G,double *x,int *ii,int *jj,int *n,int *nx,int *ny,double *dx,double *dy) {/* Takes nx by ny grid G produced by function boundary, and produces corresponding PDEcoefficient matrix, for soap PDEs in sparse triplet form.On entry x, ii and jj should be of length 5 times the number of cellswithin the boundary. On exit n will contain their exact required length.*/int i,j,*ip,outside,Gk0,Gk1,k0,k1;double xc,dx2,dy2,thresh=0.0;thresh = dx2= 1.0/(*dx * *dx);dy2 = 1.0/(*dy * *dy);if (dy2 < thresh) thresh = dy2;thresh *= .5;outside = - *nx * *ny - 1;*n=0;for (ip=G,i=0;i<*nx;i++) for (j=0;j<*ny;j++,ip++) if (*ip > outside){if (*ip <= 0) { /* boundary cell */*x=1.0;*jj = *ii= - *ip;x++;ii++;jj++;*n += 1;} else { /* interior */xc=0.0; /* diagonal coefficient */if (i>0&&i< *nx-1) { /* FD w.r.t. x may be possible */k0 = (i-1) * *ny + j; /* backwards diff */k1 = k0 + 2 * *ny; /* forwards diff */Gk0 = G[k0];Gk1 = G[k1];if (Gk0 > outside && Gk1 > outside) { /* difference is possible */xc += 2*dx2;if (Gk0<0) Gk0 = -Gk0;*x = -dx2;*ii = *ip;*jj = Gk0;x++;ii++;jj++;*n += 1;if (Gk1<0) Gk1 = -Gk1;*x = -dx2;*ii = *ip;*jj = Gk1;x++;ii++;jj++;*n += 1;}} /* FD in x direction finished */if (j>0&&j< *ny-1) { /* FD w.r.t. x may be possible */k0 = i * *ny + j - 1; /* backwards diff */k1 = k0 + 2; /* forwards diff */Gk0 = G[k0];Gk1 = G[k1];if (Gk0 > outside && Gk1 > outside) { /* difference is possible */xc += 2*dy2;if (Gk0<0) Gk0 = -Gk0;*x = -dy2;*ii = *ip;*jj = Gk0;x++;ii++;jj++;*n += 1;if (Gk1<0) Gk1 = -Gk1;*x = -dy2;*ii = *ip;*jj = Gk1;x++;ii++;jj++;*n += 1;}if (xc > thresh) { /* there is a difference for this cell */*x = xc;*ii = *jj = *ip;x++;ii++;jj++;*n += 1;}}} /* interior branch end*/} /* main loop end */} /* end of pde_coeffs */void gridder(double *z,double *x,double *y,int *n,double *g, int *G,int *nx, int *ny,double *x0,double *y0,double *dx,double *dy,double NA_code) {/* Takes solution g indexed by ny by nx matrix G. lower left cell of G is centred at x0, y0 andcell sizes are dx by dy.Interpolates solution to n locations in x, y, returning NA code for out of area.Does not do strict boundary testing here, since this routine is often called severaltimes with same geometry.*/int i,ix,iy,ok,Gthresh,Gk,k,ok00,ok01,ok10,ok11;double xx,yy,xx0,yy0,dmax,xa,ya,g00=0.0,g01=0.0,g10=0.0,g11=0.0,b0,b1,b2,b3,dist,d1;dmax = (*dx * *dx + *dy * *dy)*2;xx0 = *x0;yy0 = *y0;Gthresh = - *nx * *ny; /* G below with implies out of area */for (i=0;i < *n;i++) { /* loop through x,y locations */xx = x[i];yy = y[i];ix = (int) floor((xx - xx0) / *dx);iy = (int) floor((yy - yy0) / *dy);k = ix * *ny + iy;ok = 0;/* node 00... */if (ix<0||ix>=*nx||iy<0||iy>=*ny) ok00 = 0; else {Gk=G[k];if (Gk < Gthresh) ok00 = 0; else {ok00 = 1; ok++;if (Gk < 0) Gk = -Gk;g00 = g[Gk];}} /* end of node 00 */iy++;k++; /* node 01 */if (ix<0||ix>=*nx||iy<0||iy>=*ny) ok01 = 0; else {Gk=G[k];if (Gk < Gthresh) ok01 = 0; else {ok01 = 1; ok++;if (Gk < 0) Gk = -Gk;g01 = g[Gk];}} /* end of node 01 */ix++; k += *ny; /* node 11 */if (ix<0||ix>=*nx||iy<0||iy>=*ny) ok11 = 0; else {Gk=G[k];if (Gk < Gthresh) ok11 = 0; else {ok11 = 1; ok++;if (Gk < 0) Gk = -Gk;g11 = g[Gk];}} /* end of node 11 */iy--;k--; /* node 10 */if (ix<0||ix>=*nx||iy<0||iy>=*ny) ok10 = 0; else {Gk=G[k];if (Gk < Gthresh) ok10 = 0; else {ok10 = 1; ok++;if (Gk < 0) Gk = -Gk;g10 = g[Gk];}} /* end of node 10 */ix--;if (ok==4) { /* all nodes are ok, full bilinear */b0 = g00;b1 = (g10-g00) / *dx;b2 = (g01-g00) / *dy;b3 = (g11-g10-g01+g00)/( *dx * *dy);xx = xx - xx0 - ix * *dx;yy = yy - yy0 - iy * *dy;/* evaluate interpolating polynomial */z[i] = b0 + b1 * xx + b2 * yy + b3 * xx * yy;} else if (!ok) { /* no good neighbours - NA */z[i] = NA_code;} else { /* resort to nearest neighbour */xa = xx - xx0 - ix * *dx;ya = yy - yy0 - iy * *dy;dist = dmax;if (ok00) {dist = xa*xa + ya*ya;z[i] = g00;}if (ok01) {ya = *dy - ya;d1 = xa*xa + ya*ya;if (d1 < dist) {dist=d1;z[i] = g01;}}if (ok11) {xa = *dx - xa;d1 = xa*xa + ya*ya;if (d1 < dist) {dist=d1;z[i] = g11;}}if (ok10) {ya = *dy - ya;d1 = xa*xa + ya*ya;if (d1 < dist) {z[i] = g10;}}} /* end of nearest neighbour */}} /* end of gridder */