#macro Crevice(base_obj,samp_loc,samp_res,ctrl_ang,ctrl_dep,surf_off,adap_min,adap_max,adap_cnf)
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //                                                                                                                     //
  //  Crevice Finder Pattern                                                                                             //
  //  Trevor G. Quayle, 2011                                                                                             //
  //  For POV-Ray 3.7                                                                                                    //
  //                                                                                                                     //
  //  PASSED VARIABLES:                                                                                                  //
  //                                                                                                                     //
  //  base_obj    = Base object: should be declared and postioned before passing.                                        //
  //                                                                                                                     //
  //  samp_loc    = Origin of sampling rays: usually camera location, but other location may be used for other effects   //
  //                                                                                                                     //
  //  samp_res    = Resolution of sampling grid: sampling grid size = diagonal of object bounding box                    //
  //                                                                                                                     //
  //  ctrl_ang    = Sampling control angle: angle of trace samples from surface tangent (0=tangent, 90=normal), affects  //
  //                influence of crevice depth, smaller values will capture shallower crevices                           //
  //                                                                                                                     //
  //  ctrl_dep    = Sampling control depth: control depth for trace samples, affects influence of crevice size, larger   //
  //                values will capture larger crevices and open areas                                                   //
  //                                                                                                                     //
  //  surf_off    = Surface offset for traces: sample traces start offset from object surface, should be used for mesh   //
  //                objects and for edge-finding                                                                         //
  //                                                                                                                     //
  //  adap_min    = Minimum adaptive sampling level (2^value): 2 (4 samples) recommended, but may be less, non-positive  //
  //                values automatically reset to 2                                                                      //
  //                                                                                                                     //
  //  adap_max    = Maximum adaptive sampling level (2^value): values less than adap_min automatically reset to adap_min //
  //                                                                                                                     //
  //  adap_cnf    = Confidence value for adaptive sampling: adaptive sampling stopped when new value reaches confidence  //
  //                level of old value                                                                                   //
  //                                                                                                                     //
  //  Notes:                                                                                                             //
  //  - Macro returns directly as a function with values 0-1 which can be used for pigment, normal or texture mapping    //
  //  - To use as crevice finder, ctrl_ang and surf_off should be positive                                               //
  //  - To use as edge finder, ctrl_ang and surf_off should be negative                                                  //
  //  - to avoid artifacts, surface offset should be set (non zero) for meshes and for edge finder                       //
  //  - crevice data is only determined for surface points directly visible from sample_location:                        //
  //        - samp_loc should be equal to scene camera location (perspective cameras)                                    //
  //        - object should have all transforms applied before being passed                                              //
  //        - reflections of object may not display the crevice data properly                                            //
  //  - CSG objects should be merged (not unioned) for edge finder to work properly, otherwise, interior surfaces may    //
  //    interfere with proper detection                                                                                  //
  //  - temporary "CreviceData.df3" file will be created with each run of the macro                                      //
  //  - "arrays.inc" must be included (uses ARRAYS_WriteDF3())                                                           //
  //  - Warning! increasing samp_res exponentially increases parse time!                                                 //
  //                                                                                                                     //
  //    Usage:                                                                                                           //
  //      texture{                                                                                                       //
  //        Crevice(base_obj,samp_loc,samp_res,ctrl_ang,ctrl_dep,surf_off,adap_min,adap_max,adap_cnf)                    //
  //        texture_map{...}                                                                                             //
  //      }                                                                                                              //
  //                                                                                                                     //
  //      pigment{                                                                                                       //
  //        Crevice(base_obj,samp_loc,samp_res,ctrl_ang,ctrl_dep,surf_off,adap_min,adap_max,adap_cnf)                    //
  //        colour_map{...}                                                                                              //
  //      }                                                                                                              //
  //                                                                                                                     //
  //      normal{                                                                                                        //
  //        Crevice(base_obj,samp_loc,samp_res,ctrl_ang,ctrl_dep,surf_off,adap_min,adap_max,adap_cnf)                    //
  //        normal_map{...}                                                                                              //
  //      }                                                                                                              //
  //                                                                                                                     //
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  #local obj_ctr=(min_extent(base_obj)+max_extent(base_obj))/2;
  #local obj_ctr_x=obj_ctr.x;#local obj_ctr_y=obj_ctr.y;#local obj_ctr_z=obj_ctr.z;
  #local obj_siz=vlength(max_extent(base_obj)-min_extent(base_obj));
  #local samp_vec=samp_loc-obj_ctr;
  #local samp_os_x=vnormalize(<samp_vec.z,0,-samp_vec.x>);
  #local samp_os_y=vnormalize(vcross(samp_vec,samp_os_x));
  #local ang_xz=(acos(samp_vec.x/vlength(samp_vec*<1,0,1>)))*select(samp_vec.z,-1,1);
  #local ang_xy=(asin(samp_vec.y/vlength(samp_vec)));
  #local obj_norm=<0,0,0>; 
  #local trace_norm=<0,0,0>;
  #local pix_val=array[samp_res+1][samp_res+1];
  #for (i,0,samp_res,1)
    #for (j,0,samp_res,1)
      #local trace_len=0; #local pix_val[j][i]=0;
      #local pix_cur=obj_ctr+obj_siz*((i/samp_res-0.5)*samp_os_y+(j/samp_res-0.5)*samp_os_x);
      #local obj_int=trace(base_obj,samp_loc,pix_cur-samp_loc,obj_norm); 
      #if (vlength(obj_norm)!=0)
        #for (p,0,adap_max,1)
          #local adap_cur=p;
          #local PTLen=trace_len;
          #for (k,0,max(pow(2,adap_cur-1)-1,0),1)
            #local trace_ang=(k*2+1)*360/pow(2,adap_cur);
            #local trace_vec=vaxis_rotate(vaxis_rotate(obj_norm,<-obj_norm.z,0,obj_norm.x>,90-ctrl_ang),obj_norm,trace_ang);
            #local trace_int=trace(base_obj,obj_int+obj_norm*surf_off,trace_vec,trace_norm);
            #if (vlength(trace_norm)!=0)
              #local trace_len=trace_len+ctrl_dep/vlength(trace_int-obj_int);
            #end
          #end
          #if (adap_cur=adap_min)
            #if (trace_len=0) #local p=adap_max; #end
          #elseif (adap_cur>adap_min)
            #if ((1-abs(1-(trace_len/PTLen)/2))>=adap_max) #local p=adap_max; #end
          #end
        #end
      #end
      #if (trace_len=0)
        #local trace_len=1;
      #else #local
        trace_len=min(pow(2,adap_cur)/trace_len,1);
      #end
      #local pix_val[j][i]=trace_len;
      #local pix_siz=obj_siz/(2*samp_res);  
    #end
  #end
  ARRAYS_WriteDF3(pix_val, "CreviceData.df3", 16)
  #ifdef(FUNK1) #undef FUNK1 #end
  #ifdef(FUNK2) #undef FUNK2 #end
  #local FUNK1=
  function{pattern {
    density_file df3 "CreviceData.df3"
    interpolate 1  //use interpolate 0 or 1 only, for interpolate 1 enable pixel shift below
    translate -0.5
    scale obj_siz+pix_siz*2
    translate +pix_siz  //enable pixel shift for interpolate 1, disable for interpolate 0
    }
  }
  
  #local vec_len=vlength(samp_vec);
  
  #local FUNK2= //Perspective warp
  function{FUNK1(-z/(1-x/vec_len),y/(1-x/vec_len),0)}

  function{FUNK2(//rotate and translate alignment - output function
    (cos(-ang_xz)*(x-obj_ctr_x)-sin(-ang_xz)*(z-obj_ctr_z))*cos(-ang_xy)-(y-obj_ctr_y)*sin(-ang_xy),
    (cos(-ang_xz)*(x-obj_ctr_x)-sin(-ang_xz)*(z-obj_ctr_z))*sin(-ang_xy)+(y-obj_ctr_y)*cos(-ang_xy),
    (sin(-ang_xz)*(x-obj_ctr_x)+cos(-ang_xz)*(z-obj_ctr_z))
  )}
#end