Thanks for your interest. OK, here's the code:
#region UICode
Pair<double, double> Amount1 = Pair.Create( 0.0 , 0.0 ); // Source Center
double Amount2 = 0; // [-180,180] Source Angle
double Amount3 = 0; // [-180,180] Destination Angle
double Amount4 = 1; // [0.1,10] Zoom
int Amount5 = 4; // [1,50] Repetitions
byte Amount6 = 0; // Imaging Type|Reflect| Left| Right
bool Amount7 = false; // [0,1] Clamp Edge
bool Amount8 = true; // [0,1] Ellipse
int Amount9 = 0; // [0,512] Border Width (0 for none)
bool Amount10 = false; // [0,1] Colored Ellipse Background
ColorBgra Amount11 = ColorBgra.FromBgr(0,0,0); // Border/BackGround Color
bool Amount12 = false; // [0,1] Distort
#endregion
// Submenu: Distort
// Name: DPL Kaleidoscope
// Title: DPL Kaleidoscope
// Author: pleabo
void Render(Surface dst, Surface src, Rectangle rect) {
Rectangle selection = this.EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
int xx, yy;
double PI = Math.PI;
ColorBgra CurrentPixel;
double ctrX = Amount1.First; // Source Center x
double ctrY = Amount1.Second; // Source Center y
double angleS = Amount2/180*PI; // [-180,180] Source Angle
double angleD = Amount3/180*PI; // [-180,180] Destination Angle
double zoom = Amount4; // [1,10] Zoom X
int nn = Amount5; // [1,50] Number of Reflections
byte itype = Amount6; // Reflect| Left| Right
bool clmp = Amount7; // [0,1] Clamp Edge
bool iselip = Amount8; // Do Elipse
int bdr = Amount9; // Border Thickness
bool bgclr = Amount10; // [0,1] Do Background Color
ColorBgra clrbg = Amount11; // The Background Color
bool distort = Amount12; // Distort the Destination
int width = src.Width; // Source - Whole Image
int height = src.Height;
int right = rect.Right; // Selection (Partial Destination)
int left = rect.Left;
int top = rect.Top;
int bottom = rect.Bottom;
int dright = selection.Right; // Selection (Whole Destination)
int dleft = selection.Left;
int dtop = selection.Top;
int dbottom = selection.Bottom;
int dwidth = dright - dleft;
int dheight = dbottom - dtop;
double magx = (double)width /dwidth;
double magy = (double)height/dheight;
double magxy = Math.Min(magx, magy); // Pick smallest side
int scx = (int)(ctrX*width /2+ width/2 ); // Source Center XY
int scy = (int)(ctrY*height/2+ height/2);
int dcx = (int)((dwidth)/2+dleft); // Destination Center XY
int dcy = (int)((dheight)/2+dtop);
if (itype==0) nn *= 2; // Make visual number of nodes match nn in reflect mode
if (distort) nn *= 2; // Keep number of nodes when distorting
if (bgclr) bdr=0; // No border if colored background
for (int y = top; y < bottom; y++) {
for (int x = left; x < right; x++) {
CurrentPixel = src[x,y];
if (IsIn(x,y,dleft, dtop, dright-dleft, dbottom-dtop, iselip)) {
CurrentPixel = clrbg; // Border color
int rmin = Math.Min(dright-dleft, dbottom-dtop)/2;
if (bdr<rmin) { // Keep center at clrbg for big border
if (IsIn(x,y,dleft+bdr, dtop+bdr, dright-dleft-2*bdr, dbottom-dtop-2*bdr, iselip)) {
int dx = x-dcx;
int dy = y-dcy;
double dist = distort ? 0.5 : 1.0;
double angle = dist*nn*(Math.Atan2(dy,-dx)+PI-angleD);
int n = (int)(((angle+(2*nn-1)*PI)/(PI/nn*2)+nn/2)/(nn))%(nn); // Section number - 0->(2*Reflections-1)
double angleL = (angle-n*2*PI)/(1*nn); // As Is
double angleR = 2*PI/(1*nn)-(angle-n*2*PI)/(1*nn); // Mirror
switch (itype) {
case 0: angle = ((n%2)>0)?angleL:angleR;break; // Alternate
case 1: angle = angleL; break; // Left
case 2: angle = angleR; break; // Right
}
double sinxy = Math.Sin(angle-angleS);
double cosxy = Math.Cos(angle-angleS);
int sx = (int)(dx*magxy);
int sy = (int)(dy*magxy);
double sxy2 = sx*sx + sy*sy; // Sum of squares
double rS = Math.Sqrt(sxy2)/zoom*magxy; // Radius (distance to center)*Konstant
xx = (int)(scx + rS*cosxy);
yy = (int)(scy + rS*sinxy);
if (clmp) {
xx = Clamp(xx,width -1);
yy = Clamp(yy,height-1);
}else{
xx = Repeat(xx,width );
yy = Repeat(yy,height);
}
CurrentPixel = src[Math.Abs(xx), Math.Abs(yy)];
}
}
} else CurrentPixel = bgclr ? clrbg : src[x,y]; // BackGround/Original Pixel
dst[x,y] = CurrentPixel;
}
}
}
// ---------------- Checking for x,y in rectangle or ellipse ------- x2/a2 + y2/b2 = 1 --
public bool IsIn(int x,int y,int rx, int ry, int rl, int rh, bool elips) {
bool rc = false;
if (elips) {
int cx = rx+rl/2, cy = ry+rh/2;
int dx = (x-cx)*(x-cx), dy = (y-cy)*(y-cy);
float z2 = rl*rl, w2 = rh*rh;
rc = (4*(dx/z2+dy/w2)<1);
} else
rc = (x<rx)?false:(x>(rx+rl))?false:(y<ry)?false:(y>(ry+rh))?false:true;
return rc;
}
// -------------- Clamp iValue to zero and upper ---------------
public int Clamp(int iValue, int upper) {
return (int)Math.Max(Math.Min(iValue, upper), 0);
}
// --------------- Repeat iValue below zero and above upper (modulo upper)
public int Repeat(int iValue, int upper) {
return iValue%upper;
}
PatrickL