Jump to content
How to Install Plugins ×

Rotate By Torus Map


Reptillian

Recommended Posts

Thanks to the help of @MJW, I was able to complete this plugin and it is a near complete conversion of my g'mic filter named "Rotate By Torus Map" found within Testing->Reptorian. That being said, it can be seen as a upgraded version of Donut Distortion by MadJik from the functional point of view, but that's where the similarity ends. The new functional features are wraparound mode, blending between distortion, and more distortion modes.

 

Sample Picture:

 

285f594503a92e0cdf4184554e2610d61563bec6

Note: Mind the seams on the edges of the picture. That doesn't happen with the plugin. I will replace this picture once I can sign back into imgur.

 

How to use:

 

1. Download Plugin - Rotate by Torus Map.zip

2. Install into Paint.NET

3. Load Paint.NET and go into Effect->Render.

4. See how parameters work and change them until your desired result comes.

 

- Source Codes -

License: CeCILL v2.0 - http://cecill.info/licences/Licence_CeCILL_V2-en.html

 

C# CodeLab

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 = 360; // [-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;

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.Min(mx,f_ww);}
}

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.Min(my,f_hh);}
}

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,int boundary){
    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,int boundary){
    if (enable_distortion_blend){
        return lerp(mode_choice(v,distortmode_a,boundary),mode_choice(v,distortmode_b,boundary));
    }
    else{
        return mode_choice(v,distortmode,boundary);
    }
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    int boundary;
    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,boundary)*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);
            tempcolor=(remove_background && (boundary == 0)) ? ColorBgra.Transparent : src.GetBilinearSample(out_posx(posx),out_posy(posy));
            dst[x,y]=tempcolor;
        }
           
    }
}

 

 

G'MIC-QT

Spoiler





#@cli rep_rbtt: eq. to 'rep_rotate_by_torus_top' : (+)
rep_rbtt: rep_rotate_by_torus_top $*
#@cli rep_rotate_by_torus_top: 0<=_torus_circu_min_dimension_percent_1(%)<=100,0<=_torus_circu_min_dimension_percent_2(%)<=100,_distort_ang,_mode_1={ 0=softmode | 1=midmode | 2=hardmode | 3=hardmode_alt | 4=distroymode | 5=invdistroymode | 6=quadextrudemode | 7=hexextrudemode },_mode_2={ 0=softmode | 1=midmode | 2=hardmode | 3=hardmode_alt | 4=distroymode | 5=invdistroymode | 6=quadextrudemode | 7=hexextrudemode },0<=_mode_percent_comb(%)<=100,_offx(%),_offy(%),_off_dup={ 0=no_duplicate_dist | 1=duplicate_dist },_bgremove={ 0=keep_background | 1=remove_background },_cent_isolat_torus_mode={ 0=do_not_center | 1=center },_interpolation={ 0=nearest | 1=linear },_boundary={ 0=none | 1=neumann | 2=periodic | 3=mirror }
#@cli : Inspired by the Donut Distortion plugin made by MadJik for Paint.NET, this version of donut distortion adds new features such as more modes, mode blending, isolated torus mode.
#@cli : (eq. to 'rep_rbtt')\n
#@cli : _torus_circu_min_dimension_percent_1 refers to the primary circumference of circle relative to the minimum image dimension.
#@cli : _torus_circu_min_dimension_percent_2 refers to the secondary circumference of circle relative to the minimum image dimension.
#@cli : _distort_ang refers to the maximum angle of distortion.
#@cli : _mode_1 refers to the first mode of donut distortion.
#@cli : _mode_1 refers to the second mode of donut distortion.
#@cli : _mode_percent_comb refers to the weighed average of combination of modes. This only works if and only if _mode_1 and _mode_2 are specified!
#@cli : _offx refers to the offset of donut distortion relative to the center in -1,1 x-coordinates. 100% means the center will be located in 1.
#@cli : _offy refers to the offset of donut distortion relative to the center in -1,1 y-coordinates. 100% means the center will be located in 1.
#@cli : _off_dup option defines whether distortion will wrap around when it reach out of bound.
#@cli : _bgremove defines whether the background is removed.
#@cli : _cent_isolat_torus_mode is a special mode used to aid into creating torus from image. This only works if _bgremove is active.
#@cli : _interpolation defines the smoothness of the image.
#@cli : _boundary defines how out-of-range values are wrapped as.
#@cli : Default values: '_torus_circu_min_dimension_percent_1=1','_torus_circu_min_dimension_percent_2=.5','_distort_ang=45','_mode_1=0','_mode_2=','_mode_percent_comb=50%','_offx=0%','_offy=0%','_off_dup=1','_bgremove=0','_cent_isolat_torus_mode=0','_interpolation=1','_boundary=2'
rep_rotate_by_torus_top:
skip ${1=1},${2=0},${3=45},${4=0},${5=},${6=.5},${7=0},${8=0},${9=1},${10=1},${11=0}${12=1},${13=2}

if $1==$2 error ""$"1!="$"2=F" fi
if ($1>1||$1<0)||($2>1||$2<0) error "(0>="$"1<=1)||(0>="$"2<=1)=F" fi
if $4<0||$4>7 error "0<="$"4<=7=F" fi
if narg($5) if $5<0||$5>7 error "0<="$"5<=7=F" fi fi
if $6<0||$6>1 error "0<="$"6<=1=f" fi

if $12>0 r2dx 200%,1 fi
f "
begin(
 boundary=0;

 const s_id=s-1;

 const sd=max(w,h)/min(w,h);
 const sx=w>h?sd:1;
 const sy=w>h?1:sd;

 const ww=w-1;
 const hh=h-1;
 const cx=ww/2;
 const cy=hh/2;
 const offx=cx*$7*-1;
 const offy=cy*$8;
 const eps=10^-10;

 vallim_x(v)=v-ww*floor(v/(ww+eps));
 vallim_y(v)=v-hh*floor(v/(hh+eps));

 if($9,
  start_x(v)=vallim_x(v+offx);
  start_y(v)=vallim_y(v+offy);
 ,
  start_x(v)=v+offx;
  start_y(v)=v+offy;
 );

 const torus_val_1=$1;
 const torus_val_2=$2;
 new_min=min(torus_val_1,torus_val_2);
 new_max=max(torus_val_1,torus_val_2);
 nm(v)=(v-new_min)*(1/(new_max-new_min));
 limcut(v)=v>1?1:(v<0?0:v);
 bndcut(v)=v>1||v<0?0:1;

 const maxang=$3*-1;
 ang2rad(v)=pi*(v/180);
 rot_x(a,b,c)=a*cos(ang2rad(c))-b*sin(ang2rad(c));
 rot_y(a,b,c)=a*sin(ang2rad(c))+b*cos(ang2rad(c));

 sur_x(v)=(v/ww-.5)*2*sx;
 sur_y(v)=(v/hh-.5)*2*sy;
 unsur_x(v)=(v/(2*sx)+.5)*ww;
 unsur_y(v)=(v/(2*sy)+.5)*hh;

 softmode(v)=(cos(v*(2*pi)-pi)+1)/2;
 midmode(v)=abs(cos(v*pi+pi/2));
 hardmode(v)=sqrt(1-(abs(v-.5)*2)^2);
 hardmode_alt(v)=(1-(abs(v-.5)*2)^2)^(1/(2+(1-v)));
 distroymode(v)=cos(v*pi)*boundary;
 invdistroymode(v)=(cos(v*pi)*-1)*boundary;
 quadextrudemode(v)=1-abs(v-.5)*2;
 hexextrudemode(v)=(r=(1-abs(v-.5)*2)*2;r>1?1:r);

  if($4==0,mode_1(v)=softmode(v);
 ,if($4==1,mode_1(v)=midmode(v);
 ,if($4==2,mode_1(v)=hardmode(v);
 ,if($4==3,mode_1(v)=hardmode_alt(v);
 ,if($4==4,mode_1(v)=distroymode(v);
 ,if($4==5,mode_1(v)=invdistroymode(v);
 ,if($4==6,mode_1(v)=quadextrudemode(v);
 ,if($4==7,mode_1(v)=hexextrudemode(v);
 );
 );
 );
 );
 );
 );
 );
 );

 if(narg($5),

   if($5==0,mode_2(v)=softmode(v);
  ,if($5==1,mode_2(v)=midmode(v);
  ,if($5==2,mode_2(v)=hardmode(v);
  ,if($5==3,mode_2(v)=hardmode_alt(v);
  ,if($5==4,mode_2(v)=distroymode(v);
  ,if($5==5,mode_2(v)=invdistroymode(v);
  ,if($5==6,mode_2(v)=quadextrudemode(v);
  ,if($5==7,mode_2(v)=hexextrudemode(v);
  );
  );
  );
  );
  );
  );
  );
  );

  if($6==0||$6==1
  ,if($6,mode(v)=mode_2(v);,mode(v)=mode_1(v););
  ,mode(v)=$6*mode_2(v)+(1-$6)*mode_1(v);
  );
 ,
  mode(v)=mode_1(v);
 );

 if($10&&(s==2||s>3)
  ,out(a,b)=[vectors_id(J(a,b,z,abs($12)?2:0,$13)),j(a,b,z,s_id,abs($12)?2:0,$13)*boundary]
  ,out(a,b)=J(a,b,z,abs($12)?2:0,$13);
 );
);
xx=start_x(x);
yy=start_y(y);
xx=sur_x(xx);
yy=sur_y(yy);
radial_gradient=nm(sqrt(xx^2+yy^2));
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);
xx=unsur_x(xx);
yy=unsur_y(yy);
diff_x=XX-xx;
diff_y=YY-yy;
out(diff_x,diff_y);
"

if $11&&$10
 offx={($7/2)*100*-1}
 offy={($8/2)*100}
 repeat $! l[$>]
  if s==2||s>3
   shift $offx%,$offy%,0,0,2,0
  fi
 endl done
fi

if $12>0 r2dx 50%,3 fi
#@gui Rotate by Torus Map:fx_rep_rbtt,fx_rep_rbtt_preview(0)
#@gui :_=note("<b>Main</b>")
#@gui :Circumference A (%)=float(100,0,100)
#@gui :Circumference B (%)=float(50,0,100)
#@gui :_=separator()_=note("<b>Distortion</b>")
#@gui :Distortion Angle=float(-180,-720,720)
#@gui :Distortion Mode=choice(3,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
#@gui :Distortion Mode A=choice(3,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
#@gui :Distortion Mode B=choice(7,"Soft","Medium","Hard","Alternative Hard","Distroy","Inverse-Distroy","Quad Extrude","Hexagonal Extrude")
#@gui :Distortion Mode Blending (%)=float(50,0,100)
#@gui :Enable Distortion Mode Blending=bool(1)
#@gui :Distortion Position=point(50,50,0,1,255,255,255,255)
#@gui :Wraparound Distortion?=bool(1)
#@gui :_=separator(),_=note("<b>Additional</b>")
#@gui :Remove Background?=bool(0)
#@gui :Activate Isolated Torus Mode?=bool(0)
#@gui :_=separator(),_=note("<b>Rendering</b>")
#@gui :Interpolation=choice(1,"None","Linear")
#@gui :Boundary Condition=choice(2,"None","Neumann","Periodic","Mirror")
#@gui :_=separator(),_=note("<b>Preview</b>"), Preview Type=choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"), Preview Split = point(50,50,0,0,200,200,200,0,10)_0
#@gui :_=separator(),_=note("<b>Information</b>\n"),_=note("This filter is inspired by <a href="https://forums.getpaint.net/topic/23400-donut-distortion-effect-plugin/">Donut Distortion Paint.NET plugin</a> by <a href="https://forums.getpaint.net/profile/45895-madjik/">MadJik</a>.\n\n This G'MIC version includes additional modes,wraparound mode, and special mode which isolates the distortion.")
#@gui :_=separator(),_=note("<small>Author: Reptorian. Latest Update: <i>2020/2/29</i>.</small>")
fx_rep_rbtt:
to_a
if $8 rep_rbtt {$1/100},{$2/100},$3,$5,$6,{$7/100},{($9/100-.5)*2},{($10/100-.5)*-2},$11,$12,$13,$14,$15
else  rep_rbtt {$1/100},{$2/100},$3,$4,,,{($9/100-.5)*2},{($10/100-.5)*-2},$11,$12,$13,$14,$15
fi
fx_rep_rbtt_preview: gui_split_preview "fx_rep_rbtt ${1-13},{$14*-1},$15",${-3--1}
u "{$1}"\
"{$2}"\
"{$3}"\
"{$4}_"{!$8?2:1}\
"{$5}_"{$8?2:1}\
"{$6}_"{$8?2:1}\
"{$7}_"{$8?2:1}\
"{$8}"\
"{$9,$10}"\
"{$11}"\
"{$12}"\
"{$13}_"{$12?2:1}\
"{$14}"\
"{$15}"\
"{$16}"\
"{$17,$18}"

 

 

Edited by Reptillian
  • Like 2
  • Upvote 2

G'MIC Filter Developer

Link to comment
Share on other sites

Interesting plugin, thank you.  I think you will find that this plugin is located in Effects > Distort and not Effects > Render

a96sESb.jpg

swIFX9v.png

 

 

 

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