Welcome to Fractal Forums

Fractal Math, Chaos Theory & Research => (new) Theories & Research => Topic started by: M Benesi on December 18, 2015, 09:15:30 PM




Title: Escapetime (not L-System) tree or plant fractals
Post by: M Benesi on December 18, 2015, 09:15:30 PM
  Any decent algorithms? 


Title: Re: Escapetime (not L-System) tree or plant fractals
Post by: claude on December 18, 2015, 09:34:30 PM
You can use escape time with IFS using some caching tricks to speed it up:

http://mathr.co.uk/blog/2011-12-31_the_sky_cracked_open.html

And then you can convert to distance estimate:

http://mathr.co.uk/blog/2013-06-30_distance_estimation_for_ifs_fractals.html

(http://mathr.co.uk/blog/2013-06-30_distance_estimation_for_ifs_fractals.jpg)


Title: Re: Escapetime (not L-System) tree or plant fractals
Post by: M Benesi on December 19, 2015, 08:49:12 AM
Thanks Claude-

 I'll re-look at your pages while I'm trying to figure out the formulas for plants. 


Title: Re: Escapetime (not L-System) tree or plant fractals
Post by: knighty on December 20, 2015, 12:17:11 PM
If you want to burn your GPU: link (http://www.fractalforums.com/ifs-iterated-function-systems/dragonkifs-promising-formula-but-help-needed/msg68853/#msg68853). :D
On CPU there is a better method that uses a priority queue. Here is my (cumbersome) implementation:
Code:
//IFS distance estimation proof of concept.
//It's derived from Hart's well known algorithm.
//Instead of intersecting a ray with bounding volumes we compute distance to them
//The algorithm needs a priority queue.
//The one implemented here is cartainly the most stupid of all: just arrays. Insertions are O(n) (very bad) and deletions O(1) (optimal)
//A better priority queue would be heap based: O(log(n)) insertion and deletion.
//It's better also because we do much more insertions than deletions.
//An even priority queue would be the one where insertions are even less costly.
//This script shows that for our purpose a (very short) stack is sufficient.
//There should be a relation between number of transformations and 'optimal' queue size and scales of the transformations.
(x,y){
   static binit=1;
   static frm=-1,mx,my,mr2;
   if(frm<numframes){
      frm=numframes;
      mx=(mousx-0.5*xres)*0.01;
      my=(0.5*yres-mousy)*0.01;
      mr2=deifs(mx,my);mr2*=mr2;
   }
   if((x-mx)^2+(y-my)^2<mr2) return 0;
   //x=abs(x);
   if(binit){//initialization of the transformations. Called once.
      binit=0;
      settrns(0,-1+0.25,-0.04,-0.25*PI,sqrt(2));
      settrns(1, 0.25,-0.04,1.25*PI,sqrt(2));
      //settrns(2, 0+0.44,   1+0.05,0.5,1.7);
   }
   deifs(x,y)^0.25
}
enum {Ntrns=2};//number of transformations.
enum {tcx=0,tcy=1,trr=2,tir=3,tis=4};//names for transformations entries.
static trns[Ntrns][8];//transformations array
settrns(i,scx,scy,sa,ss){//transformation set up function
   if(i>=Ntrns) return;//should be an error
   trns[i][tcx]=scx;//x center
   trns[i][tcy]=scy;//y center
   trns[i][trr]=ss*cos(sa);//x similarity
   trns[i][tir]=ss*sin(sa);//y similarity
   trns[i][tis]=abs(1/ss);//inverse of scaling factor
}
enum {Npq=16};//Size of the priority queue

deifs(x,y){
   //priority queue
      auto pqx[Npq];//x
      auto pqy[Npq];//y
      auto pqs[Npq];//1/accumulated_scale
      auto pqd[Npq];//distance
   
   stptr=0;//pointer in the priority queue
   bvr=1.27;//bounding volume (a disc centered at 0,0) radius
   bvrm=(4*bvr)^2;//bailout: controls the smoothness of the DE field (lower values faster, higher values smoother)
   mm=0;//to compute mean movement number in the priority queu
   {//push first instance
      pqx[stptr]=x; pqy[stptr]=y;
      pqs[stptr]=1; pqd[stptr]=sqrt(x*x+y*y)-bvr;
      stptr++;
   }
   bNoExit=(x*x+y*y<bvrm);i=0;ii=0;
   while(bNoExit && i<30){
      //extract nearest element
      {
         stptr--;
         x=pqx[stptr]; y=pqy[stptr];s=pqs[stptr];ad=pqd[stptr];
      }
      //generate and insert its children
      for(j=0;j<Ntrns;j++){
         //generate child j
         pqx[stptr]=(x-trns[j][tcx])*trns[j][trr]-(y-trns[j][tcy])*trns[j][tir]+trns[j][tcx];
         pqy[stptr]=(x-trns[j][tcx])*trns[j][tir]+(y-trns[j][tcy])*trns[j][trr]+trns[j][tcy];
         pqs[stptr]=s*trns[j][tis];
         pqd[stptr]=(sqrt((pqx[stptr])^2+(pqy[stptr])^2)-bvr)*pqs[stptr];
         pqd[stptr]=max(ad,pqd[stptr]);//this removes discontinuities when the bounding volume doesn't cover it's images

         //insert it at its place
         ptr=stptr;
         while(ptr>0){
            if(pqd[ptr]>pqd[ptr-1]){//swap
               k=pqx[ptr]; pqx[ptr]=pqx[ptr-1]; pqx[ptr-1]=k;
               k=pqy[ptr]; pqy[ptr]=pqy[ptr-1]; pqy[ptr-1]=k;
               k=pqs[ptr]; pqs[ptr]=pqs[ptr-1]; pqs[ptr-1]=k;
               k=pqd[ptr]; pqd[ptr]=pqd[ptr-1]; pqd[ptr-1]=k;
               //k=pqh[ptr]; pqh[ptr]=pqh[ptr-1]; pqh[ptr-1]=k;
               ptr--;
            } else break;
         }
         stptr++;
         if(stptr>=Npq){//remove last elements... The dumb way ;)
            n2r=1;//number of elements to remove, usually 1 but here it's equal to number of transformations
            for(ii=0;ii<Npq-n2r;ii++){
               pqx[ii]=pqx[ii+n2r];
               pqy[ii]=pqy[ii+n2r];
               pqs[ii]=pqs[ii+n2r];
               pqd[ii]=pqd[ii+n2r];
            }
            stptr-=n2r;
         }
      }
      bNoExit=(x*x+y*y<bvrm);
      i++;
   };
   pqd[stptr-1]
}
It works with evaldraw. A c++11 implementation would be much shorter!

This one uses orbit trap:
Code:
(x,y,t){
   static binit=1;
   if(binit){
      //binit=0;
      settrns(0,-0.4,0.25,0.2*t*0.5*pi,2);
      settrns(1, 0.4,0.25,0.2*t*0.5*pi,2);
      settrns(2, 0+0.1,   1+0.0,0.5,1.7);
   }
   //rect(x,y,1,1)
   deifs(x,y)^0.25
}
static recta=0.62,rectb=0.15;
rect(x,y,a,b){
   x=abs(x)-a;y=abs(y)-b;
   if(min(x,y)>0 && max(x,y)>0) return sqrt(x*x+y*y);
   max(x,y)
}
enum {Ntrns=2};
enum {tcx=0,tcy=1,trr=2,tir=3,tis=4};
static trns[Ntrns][8];
settrns(i,scx,scy,sa,ss){
   if(i>=Ntrns) return;
   trns[i][tcx]=scx;
   trns[i][tcy]=scy;
   trns[i][trr]=ss*cos(sa);
   trns[i][tir]=ss*sin(sa);
   trns[i][tis]=abs(1/ss);
}
enum {Npq=256};
//enum {cx=0,cy=1,cs=2,cd=3};
deifs(x,y){
   maxdepth=5;
   //priority queu
   auto pqx[Npq];//x
   auto pqy[Npq];//y
   auto pqs[Npq];//1/accumulated_scale
   auto pqd[Npq];//distance
   auto pqh[Npq];//depth
   stptr=0;//pointer in the priority queu
   bvr=1.2;//bounding volume (a disc centered at 0,0 for now) radius
   bvrm=100;//bailout. it controls the smoothness of the DE field (lower values faster, higher values smoother)
   mm=0;//to compute mean movement number in the priority queu
   {//first instance
      pqx[stptr]=x; pqy[stptr]=y;
      pqs[stptr]=1; pqd[stptr]=sqrt(x*x+y*y)-bvr;
      pqh[stptr]=0;
      stptr++;
   }
   bNoExit=(x*x+y*y<bvrm*bvr*bvr);i=0;ii=0;
   while(bNoExit && i<100){
      //extract nearest element
      {
         depth=pqh[stptr-1];
         if(depth>maxdepth+1) break;
         stptr--;
         if(depth>maxdepth) if(stptr==0) break; else continue;
         x=pqx[stptr]; y=pqy[stptr];s=pqs[stptr];ad=pqd[stptr];
      }
      //generate and insert its children
      for(j=0;j<Ntrns;j++){
         //generate child j
         pqx[stptr]=(x-trns[j][tcx])*trns[j][trr]-(y-trns[j][tcy])*trns[j][tir]+trns[j][tcx];
         pqy[stptr]=(x-trns[j][tcx])*trns[j][tir]+(y-trns[j][tcy])*trns[j][trr]+trns[j][tcy];
         pqs[stptr]=s*trns[j][tis];
         pqd[stptr]=(sqrt((pqx[stptr])^2+(pqy[stptr])^2)-bvr)*pqs[stptr];
         pqd[stptr]=max(ad,pqd[stptr]);//this removes discontinuities when the bounding volume doesn't cover it's images
         pqh[stptr]=depth+1;
         
         //insert it at its place in the priority queue
         ptr=stptr;
         imm=0;
         while(ptr>0){
            if(pqd[ptr]>pqd[ptr-1]){//swap
               k=pqx[ptr]; pqx[ptr]=pqx[ptr-1]; pqx[ptr-1]=k;
               k=pqy[ptr]; pqy[ptr]=pqy[ptr-1]; pqy[ptr-1]=k;
               k=pqs[ptr]; pqs[ptr]=pqs[ptr-1]; pqs[ptr-1]=k;
               k=pqd[ptr]; pqd[ptr]=pqd[ptr-1]; pqd[ptr-1]=k;
               k=pqh[ptr]; pqh[ptr]=pqh[ptr-1]; pqh[ptr-1]=k;
               ptr--;imm++;
            } else break;
         }
         stptr++;
      }
         //generate internal chape
         pqd[stptr]=max(ad,s*rect(x,y,recta,rectb));
         pqh[stptr]=maxdepth+2;
         //insert it at its place in the priority queue
         ptr=stptr;
         imm=0;
         while(ptr>0){
            if(pqd[ptr]>pqd[ptr-1]){//swap
               k=pqx[ptr]; pqx[ptr]=pqx[ptr-1]; pqx[ptr-1]=k;
               k=pqy[ptr]; pqy[ptr]=pqy[ptr-1]; pqy[ptr-1]=k;
               k=pqs[ptr]; pqs[ptr]=pqs[ptr-1]; pqs[ptr-1]=k;
               k=pqd[ptr]; pqd[ptr]=pqd[ptr-1]; pqd[ptr-1]=k;
               k=pqh[ptr]; pqh[ptr]=pqh[ptr-1]; pqh[ptr-1]=k;
               ptr--;imm++;
            } else break;
         }
         mm=mm+imm;ii++;
         stptr++;

      //d=pqd[stptr-1];x=pqx[stptr-1];y=pqy[stptr-1];s=pqs[stptr-1];
      bNoExit=(x*x+y*y<bvrm*bvr*bvr);//(d<0.01*cs*bvr);//||((d>0)&&(d-ad>0.01*ad));//
      i++;
   };
   pqd[stptr-1]//minde//
}

It should be possible to convert L-systems (or at least a subset of them) to language restricted IFSs. Then using orbit traps (branches, leaves...etc.) one could draw plant like structures.
In those examples, the transformations are similarities. It is possible to use general affine transformations but it would require to compute the distance to ellipses/ellipsoid which is not cheap. An approximate ellipse-DE would also do the job.


Title: Re: Escapetime (not L-System) tree or plant fractals
Post by: M Benesi on January 03, 2016, 09:22:45 PM
  Thanks Knighty, I saw your post and intended to do something with it and reply, then got caught up in other stuff!


  For some of the stuff I'd (most likely we'd) like to do we're going to need distance estimation versions of various plant type IFSes, unless we use pre existing depth-mapped plants.  Need grasses, ferns, conifers, leafy plants, etc...


  The stuff that 3dickulus and I are working on (that the plant IFSes for), is sort of essential to the idea (of an interactive fractal world sandbox... which is way down the road, unless we're in one already) so that's being cranked out first.


  I'll focus on plant IFSes after current project is done and I clean up some coloring scheme code and heightmap functions so that there is only one type of that code out there.  :D