Jump to content

Popcorn Fractal (Ran into a roadblock)


Recommended Posts

Hi, I'm working on translating another g'mic-qt filter to a c# filter, however it appears that I don't know how to translate it because the g'mic-qt filter use macros.

 

So far, here's the c# CodeLab version:

Spoiler


// Name:
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
IntSliderControl pts = 50; // [1,200] Points
DoubleSliderControl var_density = 1; // [0.01,2] Density
DoubleSliderControl H = 0.05; // [-5,5] H
DoubleSliderControl K = 3; // [-75,75] K
DoubleSliderControl var_zoom = 1; // [0.1,1] Zoom
AngleControl var_ang = 0; // [-180,180] Angle
PanSliderControl Origin = Pair.Create(0.0,0.0); // Origin
ListBoxControl TrigMode = 0; // Trig Mode|Trig-4|Trig-6
ListBoxControl func_x1 = 0; // X-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x2 = 0; // X-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x3 = 0; // X-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y1 = 0; // Y-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y2 = 0; // Y-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y3 = 0; // Y-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
DoubleSliderControl midpoint_shift = 0; // [-1,1] Midpoint Shift
DoubleSliderControl multiplier = 1; // [0.01,10] Multiplier
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    int w = src.Width;
    int h = src.Height;    
    bool sd_cond = w>h;
    double dw = (double)(w);
    double dh = (double)(h);
    int [,] Popcorn_Array = new int [w,h];
    double density = 1 / var_density;
    double zoom = 1 / var_zoom;
    double ang = (var_ang/180) * Math.PI;
    double origin_x = Origin.First * -1 * zoom;
    double origin_y = Origin.Second * zoom;
    double sd = Math.Max(dw,dh) / Math.Min(dw,dh);
    double sx = sd_cond ? sd : 1  ;
    double sy = sd_cond ? 1  : sd ;
    double cx = dw / 2;
    double cy = dh / 2;
    double osx = origin_x * sx ;
    double osy = origin_y * sy ;
    double cx_zoom = cx / zoom;
    double cy_zoom = cy / zoom;
    double cxsx = cx * sx;
    double cysy = cy * sy;
    double cos_ang = Math.Cos(ang);
    double sin_ang = Math.Sin(ang);
    bool angcondition = (var_ang - 360*Math.Floor(var_ang/360))>0;
    double xx,yy,xnew,ynew,xval,yval;
    for (double ix=0 ; ix < dw ; ix+=density)
    {
        if (IsCancelRequested) return;

        for (double iy=0 ; iy < dh ; iy+=density)
        {
            xx = zoom * (ix-cx) / cx;
            yy = zoom * (iy-cy) / cy;
            xx*=sx;
            yy*=sy;
            xx+=origin_x;
            yy+=origin_y;

            for (int ptn=0 ; ptn < pts ; ptn++)
            {
                if (TrigMode == 1) {}
                else {}
            }
        }
    }

    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        
        for (int x = rect.Left; x < rect.Right; x++)
        {
            //currentPixel = src[x,y];
            // TODO: Add pixel processing code here
            // Access RGBA values this way, for example:
            // currentPixel.R = primaryColor.R;
            // currentPixel.G = primaryColor.G;
            // currentPixel.B = primaryColor.B;
            // currentPixel.A = primaryColor.A;
            //dst[x,y] = currentPixel;
        }
    }
}

 

 

And the g'mic-qt version:

Spoiler


#@cli rep_pfrac : eq. to 'rep_popcorn_fractal' : (+)
rep_pfrac: rep_popcorn_fractal $*
#@cli rep_popcorn_fractal: _points>0,_density>0,_H,_K,_zoom,_rotation_angle,_origin_x,_origin_y,_mode,_f1={ 0=sin | 1=cos | 2=tan | 3=atan},...
#@cli : Generates Pickover Popcorn Fractal. Code was adapted from Paul Bourke's c code, and extended for more possibilities. Fractal is attributed to Clifford Pickover.\n
#@cli : _points defines the maximum number of points to be added on image based on pixel location.
#@cli : _density defines the frequency of points to be added along row and height of image. A value of one implies n points to be added per pixel.
#@cli : _H is the function multiplier used to subtract from the new found values from each iteration.
#@cli : _K is the inner multiplier for the inside function. See popcorn_x(a,b), and popcorn_y embedded within the code of rep_popcorn_fractal for more information.
#@cli : _zoom defines the magnification of image. A negative value will "shrink" the structure of generated fractal.
#@cli : _rotation_angle defines the function angle of fractal.
#@cli : _origin_x defines the position of fractal. Center of image row will be treated as zero, and the ranges for image row are treated as -1,1.
#@cli : _origin_y defines the position of fractal. Center of image column will be treated as zero, and the ranges for image column are treated as -1,1.
#@cli : _mode defines whether to use 4 trigonometric functions or 6 trigonometric functions. Each halves of functions are used on 2 functions used by different axis.
#@cli : _fn defines individual function used for the popcorn fractal.\n
#@cli : Default values: '_points=50','density=1','H=.05','_K=3','_rotation_angle','_origin_x=0','_origin_y=0','_mode=0',...\n
#@cli : \ \ \ \ If _mode=0: ... = '_f1=_f3=0','_f2=_f4=2'
#@cli : \ \ \ \ If _mode=1: ... = '_f1=_f4=0','_f2=_f5=1','_f3=_f6=2'\n
rep_popcorn_fractal:
skip ${1=50},${2=1},${3=.05},${4=3},${5=1},${6=0},${7=0},${8=0},${9=0},${10=},${11=},${12=},${13=},${14=},${15=}
if ($6-360*floor($6/360))?1
 fvx="((rot_x(xnew,ynew)-osx)*cx_zoom+cxsx)/sx"
 fvy="((rot_y(xnew,ynew)-osy)*cy_zoom+cysy)/sy"
else
 fvx="((xnew-osx)*cx_zoom+cxsx)/sx"
 fvy="((ynew-osy)*cy_zoom+cysy)/sy"
fi
channels. 0 f. 0
l.
 eval ${-math_lib}"
 const pts=$1;
 const density=1/abs($2);
 const H=$3;
 const K=$4;
 const zoom=1/$5;
 const ang=($6/180)*pi;
 const origin_x=$7*-1*zoom;
 const origin_y=$8*zoom;
 const sd=max(w,h)/min(w,h);
 const sx=w>h?sd:1;
 const sy=w>h?1:sd;
 const cx=w/2;
 const cy=h/2;
 const osx=origin_x*sx;
 const osy=origin_y*sy;
 const cx_zoom=cx/zoom;
 const cy_zoom=cy/zoom;
 const cxsx=cx*sx;
 const cysy=cy*sy;
 const cos_ang=cos(ang);
 const sin_ang=sin(ang);
 const angcondition=($6-360*floor($6/360))?1;
 const total_pts=wh*pts*$2;
 rot_x(a,b)=a*cos_ang-b*sin_ang;
 rot_y(a,b)=a*sin_ang+b*cos_ang;
 fvx()="$fvx";
 fvy()="$fvy";
 count=0;
 np=0;
 if($9,
  if(narg($10),
   if(($10%4)==0,func_a(a)=sin(a);,
   if(($10%4)==1,func_a(a)=cos(a);,
   if(($10%4)==2,func_a(a)=tan(a);,
   if(($10%4)==3,func_a(a)=atan(a);
   );
   );
   );
   );,
  func_a(a)=sin(a);
  );
  if(narg($11),
   if(($11%4)==0,func_b(a)=sin(a);,
   if(($11%4)==1,func_b(a)=cos(a);,
   if(($11%4)==2,func_b(a)=tan(a);,
   if(($11%4)==3,func_b(a)=atan(a);
   );
   );
   );
   );,
  func_b(a)=cos(a);
  );
  if(narg($12),
   if(($12%4)==0,func_c(a)=sin(a);,
   if(($12%4)==1,func_c(a)=cos(a);,
   if(($12%4)==2,func_c(a)=tan(a);,
   if(($12%4)==3,func_c(a)=atan(a);
   );
   );
   );
   );,
  func_c(a)=tan(a);
  );
  if(!narg($13),
   if(narg($10),
    if(($10%4)==0,func_d(a)=sin(a);,
    if(($10%4)==1,func_d(a)=cos(a);,
    if(($10%4)==2,func_d(a)=tan(a);,
    if(($10%4)==3,func_d(a)=atan(a);
    );
    );
    );
    );,
   func_d(a)=sin(a);
   );,
  if(($13%4)==0,func_d(a)=sin(a);,
  if(($13%4)==1,func_d(a)=cos(a);,
  if(($13%4)==2,func_d(a)=tan(a);,
  if(($13%4)==3,func_d(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($14),
   if(narg($11),
    if(($11%4)==0,func_e(a)=sin(a);,
    if(($11%4)==1,func_e(a)=cos(a);,
    if(($11%4)==2,func_e(a)=tan(a);,
    if(($11%4)==3,func_e(a)=atan(a);
    );
    );
    );
    );,
   func_e(a)=cos(a);
   );,
  if(($14%4)==0,func_e(a)=sin(a);,
  if(($14%4)==1,func_e(a)=cos(a);,
  if(($14%4)==2,func_e(a)=tan(a);,
  if(($14%4)==3,func_e(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($15),
   if(narg($12),
    if(($12%4)==0,func_f(a)=sin(a);,
    if(($12%4)==1,func_f(a)=cos(a);,
    if(($12%4)==2,func_f(a)=tan(a);,
    if(($12%4)==3,func_f(a)=atan(a);
    );
    );
    );
    );,
   func_f(a)=tan(a);
   );,
  if(($15%4)==0,func_f(a)=sin(a);,
  if(($15%4)==1,func_f(a)=cos(a);,
  if(($15%4)==2,func_f(a)=tan(a);,
  if(($15%4)==3,func_f(a)=atan(a);
  );
  );
  );
  );
  );
  popcorn_x(a,b)=a-H*func_a(b+func_b(K*b+func_c(K*b)));
  popcorn_y(a,b)=b-H*func_d(a+func_e(K*a+func_f(K*a)));
 ,
  if(narg($10),
   if(($10%4)==0,func_a(a)=sin(a);,
   if(($10%4)==1,func_a(a)=cos(a);,
   if(($10%4)==2,func_a(a)=tan(a);,
   if(($10%4)==3,func_a(a)=atan(a);
   );
   );
   );
   );,
  func_a(a)=sin(a);
  );
  if(narg($11),
   if(($11%4)==0,func_b(a)=sin(a);,
   if(($11%4)==1,func_b(a)=cos(a);,
   if(($11%4)==2,func_b(a)=tan(a);,
   if(($11%4)==3,func_b(a)=atan(a);
   );
   );
   );
   );,
  func_b(a)=tan(a);
  );
  if(!narg($12),
   if(narg($10),
    if(($10%4)==0,func_c(a)=sin(a);,
    if(($10%4)==1,func_c(a)=cos(a);,
    if(($10%4)==2,func_c(a)=tan(a);,
    if(($10%4)==3,func_c(a)=atan(a);
    );
    );
    );
    );,
   func_c(a)=sin(a);
   );,
  if(($12%4)==0,func_c(a)=sin(a);,
  if(($12%4)==1,func_c(a)=cos(a);,
  if(($12%4)==2,func_c(a)=tan(a);,
  if(($12%4)==3,func_c(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($13),
   if(narg($11),
    if(($11%4)==0,func_d(a)=sin(a);,
    if(($11%4)==1,func_d(a)=cos(a);,
    if(($11%4)==2,func_d(a)=tan(a);,
    if(($11%4)==3,func_d(a)=atan(a);
    );
    );
    );
    );,
   func_d(a)=tan(a);
   );,
  if(($13%4)==0,func_d(a)=sin(a);,
  if(($13%4)==1,func_d(a)=cos(a);,
  if(($13%4)==2,func_d(a)=tan(a);,
  if(($13%4)==3,func_d(a)=atan(a);
  );
  );
  );
  );
  );
  popcorn_x(a,b)=a-H*func_a(b+func_b(K*b));
  popcorn_y(a,b)=b-H*func_c(a+func_d(K*a));
 );
 for(ix=0,ix<w,ix+=density,
  for(iy=0,iy<h,iy+=density,
   xx=zoom*(ix-cx)/cx;
   yy=zoom*(iy-cy)/cy;
   xx*=sx;
   yy*=sy;
   xx+=origin_x;
   yy+=origin_y;
   for(ptn=0,ptn<pts,ptn++,
    xnew=popcorn_x(xx,yy);
    ynew=popcorn_y(xx,yy);
    xval=fvx();
    yval=fvy();
    if((round(xval)>=0&&round(yval)>=0)&&(round(xval)<w&&round(yval)<h),
     if(!i(#0,round(xval),round(yval),0,0),count++;);
     i(#0,round(xval),round(yval),0,0)+=1;
    );
    xx=xnew;
    yy=ynew;
    np++;
    if(!(np%100000),run('progress ',100*(np/total_pts)));
   );
  );
 );
 count;
 "
endl

 

 

Where I'm stuck is translating popcorn_x, and popcorn_y. As you can see, they use macros. Like func_a can be sin(a) or cos(a) or atan(a), and the popcorn_x(a,b) contains macros within macro. Not to mention that the macro within popcorn_x, and popcorn_y are different depending on whether it use Trig-4 or Trig-6. How would you do this?

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

21 minutes ago, toe_head2001 said:

I think if you describe what the popcorn macro is supposed to do, we could probably help you write some C# code for it.

 

I'll try, I hope it's better.

 

Here's a bit of pseudo code. It's hard to explain in words though:

 

If fx_1==0 func_a(a)=sin(a)
elseif fx_1==1 func_a(a)=cos(a)
elseif fx_1==2 func_a(a)=tan(a)
else           func_a(a)=atan(a)
endif

And this continues to fx_3...func_c(a). After that, fy_1==0 means func_d(a)=sin(a)...fy_3==0 means func_f(a)=sin(a).

If TrigMode==1
 popcorn(a,b)=a-H*func_a(b+func_b(K*b+func_c(K*b)));
else
 popcorn(a,b)=a-H*func_a(b+func_b(K*b))
fi

popcorn(a,b) is basically a function if one converts this to c# where you take a,b as arguments, and the following is what is used to evaluate popcorn(a,b).

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

It appears to me that what you want may be delegates.

 

Delegates are more or less what in C and C++ would be called function pointers. Certainly for func_a and func_b you need to use delegates. What's being done there, assigning the various trig functions to func_a, is exactly what delegates do. You'd define a delegate with one double argument, returning a double, then assign the trig functions to it.

 

I'll try to explain in more  detail when I have time later.

 

EDIT: Removed stuff about lambda expressions, which I think may not be necessary. I think delegates will do it.

 

EDIT 2: Unless I'm not thinking straight, a bit of care needs to be exercised. If popcorn_x, is a function or delegate external to Render() (i.e., at class level), then it couldn't access the delegates func_a and func_b, due to multiprocessing, func_a and func_b must be local to Render(). I believe the ways around this are either to pass func_a and func_b as arguments; or create a class containing popcorn_a, popcorn_b, func_a, and func_b, with a local instance for each instance of Render; or have popcorn_a and popcorn_b be local functions to Render(). I've never used local functions in C#, but it seems like that would work.

 

EDIT 3: The above comment only applies if the functions assigned to func_a and func_b may differ between different instances of Render(). If, for example, it depends only on a UI variable, so that it never changes during one rendering pass, then popcorn_a and popcorn_y, along with func_a and func_b, could be a class scope.

 

Link to comment
Share on other sites

@MJW I think I just successfully converted it. Problem: It's nowhere near as fast as the Popcorn Fractal I did for g'mic. Here's the code.

 

Spoiler


// Name:
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
IntSliderControl pts = 50; // [1,200] Points
DoubleSliderControl var_density = 1; // [0.01,2] Density
DoubleSliderControl H = 0.05; // [-5,5] H
DoubleSliderControl K = 3; // [-75,75] K
DoubleSliderControl var_zoom = 1; // [0.1,1] Zoom
AngleControl var_ang = 0; // [-180,180] Angle
PanSliderControl Origin = Pair.Create(0.0,0.0); // Origin
ListBoxControl TrigMode = 0; // Trig Mode|Trig-4|Trig-6
ListBoxControl func_x1 = 0; // X-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x2 = 0; // X-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x3 = 0; // X-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y1 = 0; // Y-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y2 = 0; // Y-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y3 = 0; // Y-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
DoubleSliderControl var_midpoint_shift = 0; // [-1,1] Midpoint Shift
DoubleSliderControl multiplier = 1; // [0.01,10] Multiplier
#endregion

double rot_x(double a, double b, double cos_ang,double sin_ang){
    return a * cos_ang - b * sin_ang;
}

double rot_y(double a, double b, double cos_ang,double sin_ang){
    return a * sin_ang + b * cos_ang;
}

double func_v(double v,int c){
    switch(c){
       case 0: return Math.Sin(v);
       case 1: return Math.Cos(v);
       case 2: return Math.Tan(v);
       case 3: return Math.Atan(v);
    }
    return Math.Atan(v);
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    int w = src.Width;
    int h = src.Height;    
    bool sd_cond = w>h;
    double dw = (double)(w);
    double dh = (double)(h);
    int [,] Popcorn_Array = new int [w,h];
    double midpoint = 1 - Math.Abs(var_midpoint_shift);
    bool cond_midpoint = var_midpoint_shift>0;
    double density = 1 / var_density;
    double zoom = 1 / var_zoom;
    double ang = (var_ang/180) * Math.PI;
    double origin_x = Origin.First * -1 * zoom;
    double origin_y = Origin.Second * zoom;
    double sd = Math.Max(dw,dh) / Math.Min(dw,dh);
    double sx = sd_cond ? sd : 1  ;
    double sy = sd_cond ? 1  : sd ;
    double cx = dw / 2;
    double cy = dh / 2;
    double osx = origin_x * sx ;
    double osy = origin_y * sy ;
    double cx_zoom = cx / zoom;
    double cy_zoom = cy / zoom;
    double cxsx = cx * sx;
    double cysy = cy * sy;
    double cos_ang = Math.Cos(ang);
    double sin_ang = Math.Sin(ang);
    bool angcondition = (var_ang - 360*Math.Floor(var_ang/360))>0;
    double xx,yy,xnew,ynew,xval,yval,pval;
    int xpos,ypos,val;
    int maxnum = 0;
    for (double ix=0 ; ix < dw ; ix+=density)
    {
        if (IsCancelRequested) return;

        for (double iy=0 ; iy < dh ; iy+=density)
        {
            xx = zoom * (ix-cx) / cx;
            yy = zoom * (iy-cy) / cy;
            xx*=sx;
            yy*=sy;
            xx+=origin_x;
            yy+=origin_y;

            for (int ptn=0 ; ptn < pts ; ptn++)
            {
                if (TrigMode == 1) {
                    xnew  = xx - H * func_v( yy + func_v( K * yy + func_v( K* yy,func_x3), func_x2), func_x1);
                    ynew  = yy - H * func_v( xx + func_v( K * xx + func_v( K* xx,func_x3), func_x2), func_x1);
                }
                else {
                    xnew = xx - H * func_v(yy+func_v(K*yy,func_x2),func_x1);
                    ynew = yy - H * func_v(xx+func_v(K*xx,func_y2),func_y1);
                }

                if (angcondition){
                    xval = ((rot_x(xnew,ynew,cos_ang,sin_ang) - osx)*cx_zoom + cxsx)/sx;
                    yval = ((rot_y(xnew,ynew,cos_ang,sin_ang) - osy)*cy_zoom + cysy)/sy;
                }
                else{
                    xval = ((xnew - osx)*cx_zoom+cxsx)/sx;
                    yval = ((ynew - osy)*cy_zoom+cysy)/sy;
                }
                if ( ( Math.Round(xval)>=0 && Math.Round(yval)>=0 ) && ( Math.Round(xval)<dw && Math.Round(yval)<dh ) ){
                    xpos = (int)(Math.Round(xval));
                    ypos = (int)(Math.Round(yval));
                    Popcorn_Array[xpos,ypos]++;
                    if (Popcorn_Array[xpos,ypos] > maxnum) { maxnum = Popcorn_Array[xpos,ypos]; }
                }
                xx = xnew;
                yy = ynew;
            }
        }
    }

    for (int y = rect.Top; y < rect.Bottom; y++)
    {        
        for (int x = rect.Left; x < rect.Right; x++)
        {
            pval = (double)(Popcorn_Array[x,y])/(double)(maxnum);
            if (cond_midpoint){pval=Math.Pow(pval,midpoint);}
            else{pval=1-(Math.Pow(1-pval,midpoint));}
            pval*=(double)(maxnum)*multiplier;
            val=(int)(pval);
            dst[x,y]=ColorBgra.FromBgraClamped(val,val,val,255);
        }
    }
}

 

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

Looks like you could take advantage of PreRender()

 

This makes your code much faster, since you no longer compute the same exact stuff over and over.


// Name:
// Submenu:
// Author:
// Title:
// Version:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
IntSliderControl pts = 50; // [1,200] Points
DoubleSliderControl var_density = 1; // [0.01,2] Density
DoubleSliderControl H = 0.05; // [-5,5] H
DoubleSliderControl K = 3; // [-75,75] K
DoubleSliderControl var_zoom = 1; // [0.1,1] Zoom
AngleControl var_ang = 0; // [-180,180] Angle
PanSliderControl Origin = Pair.Create(0.0,0.0); // Origin
ListBoxControl TrigMode = 0; // Trig Mode|Trig-4|Trig-6
ListBoxControl func_x1 = 0; // X-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x2 = 0; // X-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_x3 = 0; // X-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y1 = 0; // Y-S|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y2 = 0; // Y-T|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
ListBoxControl func_y3 = 0; // Y-U|Sinusoidal|Cosinusoidal|Tangent|Arc-Tangent
DoubleSliderControl var_midpoint_shift = 0; // [-1,1] Midpoint Shift
DoubleSliderControl multiplier = 1; // [0.01,10] Multiplier
#endregion



double rot_x(double a, double b, double cos_ang,double sin_ang){
    return a * cos_ang - b * sin_ang;
}

double rot_y(double a, double b, double cos_ang,double sin_ang){
    return a * sin_ang + b * cos_ang;
}

double func_v(double v,int c){
    switch(c){
       case 0: return Math.Sin(v);
       case 1: return Math.Cos(v);
       case 2: return Math.Tan(v);
       case 3: return Math.Atan(v);
    }
    return Math.Atan(v);
}

int maxnum;
int [,] Popcorn_Array;

void PreRender(Surface dst, Surface src)
{
    int w = src.Width;
    int h = src.Height;    
    bool sd_cond = w>h;
    double dw = (double)(w);
    double dh = (double)(h);
    Popcorn_Array = new int [w,h];
    double density = 1 / var_density;
    double zoom = 1 / var_zoom;
    double ang = (var_ang/180) * Math.PI;
    double origin_x = Origin.First * -1 * zoom;
    double origin_y = Origin.Second * zoom;
    double sd = Math.Max(dw,dh) / Math.Min(dw,dh);
    double sx = sd_cond ? sd : 1  ;
    double sy = sd_cond ? 1  : sd ;
    double cx = dw / 2;
    double cy = dh / 2;
    double osx = origin_x * sx ;
    double osy = origin_y * sy ;
    double cx_zoom = cx / zoom;
    double cy_zoom = cy / zoom;
    double cxsx = cx * sx;
    double cysy = cy * sy;
    double cos_ang = Math.Cos(ang);
    double sin_ang = Math.Sin(ang);
    bool angcondition = (var_ang - 360*Math.Floor(var_ang/360))>0;
    double xx,yy,xnew,ynew,xval,yval;
    int xpos,ypos;

    for (double ix=0 ; ix < dw ; ix+=density)
    {
        if (IsCancelRequested) return;

        for (double iy=0 ; iy < dh ; iy+=density)
        {
            xx = zoom * (ix-cx) / cx;
            yy = zoom * (iy-cy) / cy;
            xx*=sx;
            yy*=sy;
            xx+=origin_x;
            yy+=origin_y;

            for (int ptn=0 ; ptn < pts ; ptn++)
            {
                if (TrigMode == 1) {
                    xnew  = xx - H * func_v( yy + func_v( K * yy + func_v( K* yy,func_x3), func_x2), func_x1);
                    ynew  = yy - H * func_v( xx + func_v( K * xx + func_v( K* xx,func_x3), func_x2), func_x1);
                }
                else {
                    xnew = xx - H * func_v(yy+func_v(K*yy,func_x2),func_x1);
                    ynew = yy - H * func_v(xx+func_v(K*xx,func_y2),func_y1);
                }

                if (angcondition){
                    xval = ((rot_x(xnew,ynew,cos_ang,sin_ang) - osx)*cx_zoom + cxsx)/sx;
                    yval = ((rot_y(xnew,ynew,cos_ang,sin_ang) - osy)*cy_zoom + cysy)/sy;
                }
                else{
                    xval = ((xnew - osx)*cx_zoom+cxsx)/sx;
                    yval = ((ynew - osy)*cy_zoom+cysy)/sy;
                }
                if ( ( Math.Round(xval)>=0 && Math.Round(yval)>=0 ) && ( Math.Round(xval)<dw && Math.Round(yval)<dh ) ){
                    xpos = (int)(Math.Round(xval));
                    ypos = (int)(Math.Round(yval));
                    Popcorn_Array[xpos,ypos]++;
                    if (Popcorn_Array[xpos,ypos] > maxnum) { maxnum = Popcorn_Array[xpos,ypos]; }
                }
                xx = xnew;
                yy = ynew;
            }
        }
    }
}


void Render(Surface dst, Surface src, Rectangle rect)
{
    double midpoint = 1 - Math.Abs(var_midpoint_shift);
    bool cond_midpoint = var_midpoint_shift>0;
    double pval;
    int val;

    for (int y = rect.Top; y < rect.Bottom; y++)
    {        
        for (int x = rect.Left; x < rect.Right; x++)
        {
            pval = (double)(Popcorn_Array[x,y])/(double)(maxnum);
            if (cond_midpoint){pval=Math.Pow(pval,midpoint);}
            else{pval=1-(Math.Pow(1-pval,midpoint));}
            pval*=(double)(maxnum)*multiplier;
            val=(int)(pval);
            dst[x,y]=ColorBgra.FromBgraClamped(val,val,val,255);
        }
    }
}

 

  • Like 1

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

Using a switch statement is certainly a respectable way to do it; though it would probably be somewhat more efficiently done with delegates. I see from the code that func_x1, etc., are selected by the UI, so having them be delegates, external to Render() should pose no problem.

 

As a very minor detail, I'd write the switch statement as:

double func_v(double v,int c){
    switch(c){
       case 0: return Math.Sin(v);
       case 1: return Math.Cos(v);
       case 2: return Math.Tan(v);
       default: return Math.Atan(v);
    }
}

That seems a little clearer to me.

 

As far as efficiency, I'll mention two things;

First, though it may seen like delegates, with their generality, such as muticasting, might be quite inefficient, they don't seem to be at all. I understand that early in the C# development they were somewhat inefficient, but that seems no longer be true. I've stepped through the assembly code execution in which I've used them, and they were implemented exactly as function pointers, with no additional overhead. I assume the compiler is now smart enough to recognize when no fancy stuff is required.

Second, though the conditional jumps in the switch statement may strike me as inefficient, it's probably not too bad. Almost all modern processors rely on branch prediction, so when the branches are always the same, the overhead should me minimal.

 

I'm somewhat surprised there's a noticeable performance difference compared to the old g'mic version.

Link to comment
Share on other sites

10 minutes ago, Reptillian said:

I do have to point that I can't add anymore UI element or a error message shows up.

 

I'm not sure why that would be, unless it's a limitation in the CodeLab UI designer. My Texture Merger plugin has quite a few more controls than than the 16 or so in your plugin

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...