Jump to content

Rotate By Torus Map


Recommended Posts

To expand on that a bit, PDN, internally, divides the selection up into a bunch of sub-regions called ROIs. It hands them off the different cores for processing, so one core is rendering one ROI, while simultaneously other cores are rendering different ROIs.

  • You're a Smart Cookie! 1
Link to comment
Share on other sites

Spoiler

// Name: Rotate By Torus Map
// Submenu: Distort
// Author: Reptorian
// Title: Rotation by Torus Map
// Version: 1
// Desc: Rotates images using a Torus Map.
// Keywords: distortion
// URL: https://forums.getpaint.net/profile/85868-reptillian/
// Help:
#region UICode
DoubleSliderControl v_circ_a = 45; // [0,100] Circumference A (%)
DoubleSliderControl v_circ_b = 100; // [0,100] Circumference B (%)
DoubleSliderControl angle = 45; // [-720,720] Angle
ListBoxControl distortmode = 3; // {!enable_distortion_blend} Distortion Mode|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal
ListBoxControl distortmode_a = 0; // {enable_distortion_blend} Distortion Mode A|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal
ListBoxControl distortmode_b = 3; // {enable_distortion_blend} Distortion Mode B|Soft|Medium|Hard|Alternative Hard|Distroy|Inverse-Distroy|Quad Extrude|Hexagonal
DoubleSliderControl v_distort_blend = 50; // [0,100] {enable_distortion_blend} Distortion Mode Blending (%)
CheckboxControl enable_distortion_blend = true; // Enable Distortion Blending Mode?
PanSliderControl distpos = Pair.Create(0.000, 0.000); // Distortion Position
CheckboxControl wraparound_mode = true; // Wraparound Distortion?
CheckboxControl remove_background = false; // Remove Background?
ListBoxControl outofrange_boundary_choice = 0; // Boundary|None|Neumann|Periodic|Mirror
#endregion

double ww,hh,cx,cy,offx,offy,eps,sx,sy,new_min,new_max,pi,distort_blend;
float f_w,f_h,f_ww,f_hh;
int boundary;

double vallim_x(double v){
    return v - ww * Math.Floor(v/(ww+eps));
}

double vallim_y(double v){
    return v - hh * Math.Floor(v/(hh+eps));
}


double start_x(double v){
    if (wraparound_mode){ return vallim_x(v+offx);}
    else                { return v+offx;}

}

double start_y(double v){
    if (wraparound_mode){ return vallim_y(v+offy);}
    else                { return v+offy;}
}

double nm(double v){
    return (v-new_min)*(1/(new_max - new_min));
}

double limcut(double v){
    return Math.Max(0,Math.Min(1,v));
}

int bndcut(double v){
    return v>1||v<0?0:1;
}


double ang2rad(double v){
    return Math.PI * (v/180) ;
}

double rot_x(double a, double b, double c){
    return a * Math.Cos(ang2rad(c)) - b * Math.Sin(ang2rad(c));
}
double rot_y(double a, double b, double c){
    return a * Math.Sin(ang2rad(c)) + b * Math.Cos(ang2rad(c));
}

double sur_x(double v){
    return (v/ww - .5) * 2 * sx;
}


double sur_y(double v){
    return (v/hh - .5) * 2 * sy;
}

double unsur_x(double v){
    return (v/(2*sx) + .5) * ww;
}

double unsur_y(double v){
    return (v/(2*sy) + .5) * hh;
}

double lerp(double a,double b){
    return a * distort_blend + b * (1 - distort_blend);
}

float mod(float a,float b){
    return a - b * (float)(Math.Floor(a/b));
}

float neumann_x(float x){
    return Math.Min(f_ww,Math.Max(x,0));
}

float neumann_y(float y){
    return Math.Min(f_hh,Math.Max(y,0));
}

float periodic_x(float x){
    return x-f_ww*(float)(Math.Floor(x/f_ww));
}

float periodic_y(float y){
    return y-f_hh*(float)(Math.Floor(y/f_hh));
}

float mirror_x(float x){
    float mx = mod(x,f_w*2);
    if (mx>=f_w) {return 2*f_w - mx - 1;}
    else {return Math.Abs(mx);}
}

float mirror_y(float y){
    float my = mod(y,f_h*2);
    if (my>=f_h) {return 2*f_h - my - 1;}
    else {return Math.Abs(my);}
}

float out_posx(float posx){
    switch(outofrange_boundary_choice){
        case 0: return posx;
        case 1: return neumann_x(posx);
        case 2: return periodic_x(posx);
        case 3: return mirror_x(posx);
    }

    return 0;
}

float out_posy(float posy){
    switch(outofrange_boundary_choice){
        case 0: return posy;
        case 1: return neumann_y(posy);
        case 2: return periodic_y(posy);
        case 3: return mirror_y(posy);
    }

    return 0;
}

double mode_choice(double v,int mode){
    switch(mode){
        case 0: return (Math.Cos(v*(2*pi)-pi)+1)/2;
        case 1: return Math.Abs(Math.Cos(v*pi+pi/2));
        case 2: return Math.Sqrt(1-Math.Pow(Math.Abs(v-.5)*2,2));
        case 3: return Math.Pow(1-Math.Pow(Math.Abs(v-.5)*2,2),1/(2+(1-v)));
        case 4: return Math.Cos(v*pi)*boundary;
        case 5: return Math.Cos(v*pi)*boundary*-1;
        case 6: return 1 - Math.Abs(v-.5) * 2;
        case 7: 
            double r = (1-Math.Abs(v-.5)*2)*2;
            return r > 1 ? 1 : r ;
    }
    return 0;
}

double mode(double v){
    if (enable_distortion_blend){
        return lerp(mode_choice(v,distortmode_a),mode_choice(v,distortmode_b));
    }
    else{
        return mode_choice(v,distortmode);
    }
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    int w = src.Width;
    int h = src.Height;
    f_w  = (float)(w);
    f_h  = (float)(h);
    f_ww = (float)(w)-1;
    f_hh = (float)(h)-1;
    double d_w = (double)(w);
    double d_h = (double)(h);
    double sd  = Math.Max(d_w,d_h)/Math.Min(d_w,d_h);
    double circ_a = v_circ_a / 100;
    double circ_b = v_circ_b / 100;
    double maxang = angle * -1;
    sx = w > h ? sd : 1  ;
    sy = w > h ? 1  : sd ;
    ww = d_w - 1;
    hh = d_h - 1;
    cx = ww/2;
    cy = hh/2;
    offx = cx * distpos.First  * -1;
    offy = cy * distpos.Second * -1;
    eps = Math.Pow(10,-10);
    new_min=Math.Min(circ_a,circ_b);
    new_max=Math.Max(circ_a,circ_b);
    distort_blend = v_distort_blend / 100;
    pi = Math.PI;
    double xx,yy,radial_gradient,norm_gradient,z_depth,XX,YY,diff_x,diff_y,tyy,nxx,nyy;
    float posx,posy;
    ColorBgra tempcolor;

    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        tyy=start_y((double)(y));
        for (int x = rect.Left; x < rect.Right; x++)
        {
            nxx=start_x((double)(x));
            nyy=tyy;
            xx=sur_x(nxx);
            yy=sur_y(nyy);
            radial_gradient=nm(Math.Sqrt(xx*xx+yy*yy));
            norm_gradient=limcut(radial_gradient);
            boundary=bndcut(radial_gradient);
            z_depth=mode(norm_gradient)*maxang;
            XX=rot_x(xx,yy,z_depth);
            YY=rot_y(xx,yy,z_depth);
            XX=unsur_x(XX);
            YY=unsur_y(YY);
            diff_x=XX-nxx;
            diff_y=YY-nyy;
            posx=(float)(x)+(float)(diff_x);
            posy=(float)(y)+(float)(diff_y);
            if(outofrange_boundary_choice>0){
                posx=Math.Max(0,Math.Min(f_ww,out_posx(posx)));
                posy=Math.Max(0,Math.Min(f_hh,out_posy(posy)));
            }
            tempcolor=src.GetBilinearSample(posx,posy);
            if (remove_background){
                dst[x,y]=ColorBgra.FromBgra(tempcolor.B,tempcolor.G,tempcolor.R,boundary == 1 ? tempcolor.A : (byte)(0));
            }
            else{
               dst[x,y] = tempcolor;
            }
        }
    }
}

 

 

G'MIC Filter Developer

Link to comment
Share on other sites

This is unrelated to the background problem (which I've yet to figure out), but you can make the code more efficient by moving as much of the code as you can prior to the render loops into PreRender(). Not only is it more efficient, it's also clearer, because it shows what is always the same for all rendering. It will, of course require some variable be moved to outside the methods, to communicate them between PreRender() and Render().

 

 

Link to comment
Share on other sites

Just now, MJW said:

 

Why wouldn't it be possible?

I tested different ways to use boundary since when boundary is at 1, that means there is a distortion. No matter how I structure the conditional, and the result, the background problem will stay there. There were 10 different ways I coded it, and exhausted all options I could think of.

G'MIC Filter Developer

Link to comment
Share on other sites

What I think you should do first is follow my suggestion of putting the initialization in PreRender(). That may possibly fix the problem, or at least make it easier to find. If you have variables outside the methods (like all the variables defined at the top of the file), they can never be set to different values for different Render() routines, even temporarily. If they are, one Render() routine can end up using the values set in a different Render() routine.  By doing the initialization in PreRender(), this is pretty much avoided. Any variable that can't be initialized in PreRender()  should be local to Render(), not defined outside it. I rather suspect the problem you're seeing may be related to this.

 

Also, why not just do:

tempcolor = (remove_background && (boundary == 0)) ? ColorBgra.Transparent : src.GetBilinearSample(posx, posy);

 

Isn't that the goal, to make it transparent outside the boundary? Do you really need to preserve the old RGB if the alpha is 0?

Link to comment
Share on other sites

Put the initialization in PreRender() while leaving the rendering where it is, and I think everything will be clearer.

 

Adding antialiasing is quite easy. I can explain it once you get the plugin working as is. Basically, you change the render loop so it calls a method (subroutine) to compute the color, instead of doing it inline, then paste in a couple of routines.

 

I wrote some step-by-step instructions on it, but they may make it seem more difficult then it is.

Link to comment
Share on other sites

If you can make the program work in PreRender(), you can make it work in Render(). What you might have to do, though, is add a few extra arguments to routines that get called, instead of relying of "global" (i.e., class-level) variables to communicate.  As I mentioned, global variables can never be used for values that might be different between Render() instances.

Link to comment
Share on other sites

1 minute ago, MJW said:

If you can make the program work in PreRender(), you can make it work in Render(). What you might have to do, though, is add a few extra arguments to routines that get called, instead of relying of "global" (i.e, class-level) variables to communicate.  As I mentioned, global variable can never be used for values that might be different between Render() instances.

So, that is why it does not work how I like yet. In that case, I will address those.

G'MIC Filter Developer

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...