PDA

View Full Version : How to mirror in video processor - kaleidoscope


sonicowl
10-30-2018, 12:20 PM
How to mirror in video processor?
gfx_blit gives only x,y,w,h. Tried negative numbers for w and h, but no luck. Anybody knows how to do it?

Eliseat
10-31-2018, 01:11 AM
How to mirror in video processor?
gfx_blit gives only x,y,w,h. Tried negative numbers for w and h, but no luck. Anybody knows how to do it?

You don't have to mirror in the video processor. Just double click the video item to open the properties. Then click in video properties (down on the right side) and transform the video like you want. Mirroring, flipping etc.

Hope that helps.
Eli :)

Justin
10-31-2018, 07:36 AM
Try this:
input_info(0,w,h);
gfx_blit(0,0, 0,0,project_w,project_h,w,0,-w,h);


For more power you can also use gfx_deltablit() to do transformations, e.g.
input_info(0,w,h);
gfx_deltablit(0, 0,0, project_w,project_h,
w,0, // starting source coordinates
-w/project_w,0, // source delta-X/delta-Y with respect to output X
0,h/project_h // source delta-X/delta-Y with respect to output Y
);

Eliseat
10-31-2018, 07:54 AM
Try this:
input_info(0,w,h);
gfx_blit(0,0, 0,0,project_w,project_h,w,0,-w,h);


For more power you can also use gfx_deltablit() to do transformations, e.g.
input_info(0,w,h);
gfx_deltablit(0, 0,0, project_w,project_h,
w,0, // starting source coordinates
-w/project_w,0, // source delta-X/delta-Y with respect to output X
0,h/project_h // source delta-X/delta-Y with respect to output Y
);


Saved for the love of video processor. ;)

sonicowl
10-31-2018, 01:01 PM
Thank you both.
I will learn this video processor coding... so much you can do with it. But it will take time.
I would like to make kaleidoscope effect, so my first step was to figure out mirroring. But before I dive into it further, I better ask... Justin, do you maybe already have kaleidoscope preset made and can share it?

EpicSounds
10-31-2018, 02:12 PM
I did basic 4-quadrant kaleidoscope effect with the flip options in source properties but it wasn't quick or fun. A preset would be great.

sonicowl
10-31-2018, 03:24 PM
Here is some info on how to do kaleidoscope, with code in C. Too much for my abilities atm. https://github.com/timbz/KaleidoscopeGenerator

wwwmaze
10-31-2018, 07:12 PM
I couldn't find out how to bit blit triangles so here's my quick take using rectangles:

----
see below

sonicowl
11-01-2018, 02:42 AM
@wwwmaze: Your preset works so nicely. Patterns are so much fun. Thank you so much.

How about radial kaleidoscope effect like this?
https://youtu.be/q2fIWB8o-bs?t=221
Would this be achieved by creating vertical stripes, and then using spherical projection to make it a circle?

EDIT: Yes, after testing a bit, it is possible to get somewhat half circle kaleidoscope by adding Justin's radial transform. Probably below script could be modified to get proper 360° circle in the center?

input_info(0,w,h);
y=tab=0;
tabsz=60;
loop(tabsz,
x=0;
loop(tabsz,
dx = x/tabsz-.5;
dy = 1-y/tabsz;
ang = atan2(dx,dy);
dist = (sqr(dx)+sqr(dy));
z= ((ang-$pi/2)*w*.5)/$pi;
z<0?z+=w;
tab[]=z;
tab[1] = h-(dist*h/(.5^2+1))*2;

tab+=2;
x+=1;
);
y+=1;
);
tab=0;
gfx_mode=0x100;
gfx_fillrect(0,0,project_w,project_h);
gfx_xformblit(0, 0,0,project_w,project_h,tabsz,tabsz,tab,0);

sonicowl
11-01-2018, 04:06 AM
Here is a quick test with kaleidoscope, no music.
BeqYUJSuYe0

wwwmaze
11-01-2018, 04:11 AM
EDIT: Yes, after testing a bit, it is possible to get somewhat half circle kaleidoscope by adding Justin's radial transform. Probably below script could be modified to get proper 360° circle in the center?



Ah yes using circular segments could work, I will have a look later.

Meanwhile I modified the preset to specify window width+height. Set num tiles x/y to 1 and angle to 0° if you are interested in what that means.


//@param1:x 'window x' 0.25 0 1 0.5 0.001
//@param2:y 'window y' 0.25 0 1 0.5 0.001
//@param3:w 'window w' 0.5 0 1 0.5 0.001
//@param4:h 'window h' 0.5 0 1 0.5 0.001

//@param6:numX 'num tiles x' 4 1 100 50 1
//@param7:numY 'num tiles y' 4 1 100 50 1
//@param9:ang 'angle' 45 0 360 180 1
//@param11:bg 'bg' 0 0 1 0.5 0.01
tileW=(project_w/numX)|0; tileH=(project_h/numY)|0;

gfx_set(bg,bg,bg);
gfx_fillrect(0,0,project_w,project_h);

input_info(0,inW,inH);
gfx_dest = fs = gfx_img_resize(fs, project_w, project_h);
gfx_fillrect(0,0,project_w,project_h);
gfx_rotoblit(0,$pi/180*ang, 0,0,tileW,tileH, (x*inW)|0, (y*inH)|0, (w*inW)|0, (h*inH)|0);

gfx_dest=-1;
i=0;
loop(numX,
curXd=tileW*i;
i%2 ? (
curWd=tileW;
curXs=tileW;
curWs=-tileW;
):(
curWd=tileW+1;
curXs=0;
curWs=tileW;
);
j=0;
loop(numY,
curYd=tileH*j;
j%2 ? (
curHd=tileH;
curYs=tileH;
curHs=-tileH;
):(
curHd=tileH+1;
curYs=0;
curHs=tileH;
);
gfx_blit(fs,0, curXd,curYd,curWd,curHd, curXs,curYs,curWs,curHs);
j+=1;
);
i+=1;
);

wwwmaze
11-01-2018, 04:41 AM
Here is a quick test with kaleidoscope, no music.
BeqYUJSuYe0

Nice! Made me think of lord of the rings :)

Here's my test:
https://i.imgur.com/OdWqJJK.gif

sonicowl
11-01-2018, 04:46 AM
Ah yes using circular segments could work, I will have a look later.

Meanwhile I modified the preset to specify window width+height. Set num tiles x/y to 1 and angle to 0° if you are interested in what that means.



Thank you.
You get amazing effect by setting width to zero, and automate window x on any photo - those horizontal lines that come from extreme stretching, like in Photoshop, when you take vertical selection of one pixel wide, and stretch it horizontally to fill the canvas - but they move here. Great for quickly getting animated backgrounds.

sonicowl
11-01-2018, 05:02 AM
Here is a test with extreme stretch, and automating x position. At minimal setting it seems to be more than 1pixel wide, but still gives an effect.
https://i.imgur.com/fgRbkxE.gif

sonicowl
11-01-2018, 05:24 AM
Kaleidoscope is fun... just one parameter animated. Same static image used as in above two clips. :)
https://i.imgur.com/Cn4IpO8.gif

Eliseat
11-01-2018, 05:50 AM
YEAAAAHHH! This is just amazing. :)

And it works so nicely. Seems the possibilities of video processor are really endless. Thank you so much!

Eliseat
11-01-2018, 05:54 AM
And now imagine how cool it would be to use Justins new meter levels or spectrograms to mangle them thru the kaleidoscope. :D

wwwmaze
11-29-2018, 12:18 PM
I finally managed to finish the triangular shaped kaleidoscope (its not that straightforward):
https://i.imgur.com/vJ55696.gif

Hope it works for you guys. Have fun :)


// Kaleidoscope (triangle base)
// v0.1

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//@param1:preview 'preview' 0 0 1 0 1

//@param3:cxn 'center x' 0.5 0 1 0.5 0.01
//@param4:cyn 'center y' 0.5 0 1 0.5 0.01
//@param5:lenn 'length' 0.25 0 1 0.5 0.01
//@param6:rot 'rotate' 0 0 360 180 1
//@param7:sc 'scale' 1 0.01 5 2.5 0.01

//@param9:bg 'graylevel background' 0.5 0 1 0.5 0.01

///@param11:debug 'debug' 0 0 3 2 1

function gfx_line(x,y,x2,y2,t) local(i,n,dx,dy,dxn,dyn) (
dx=(x2-x); dy=(y2-y);
n=abs(dx)+abs(dy);
dxn=dx/n; dyn=dy/n;
i=0;
loop(n,
gfx_fillrect(x+i*dxn,y+i*dyn,t,t);
i+=1;
);
gfx_fillrect(x2,y2,1,1);
);
function pt(x,y) instance(x,y) (
this.x=x;
this.y=y;
);
function pRot(cx,cy,a) local(tx,ty) instance(x,y) (
tx=x-cx;
ty=y-cy;
x=tx*cos(a)-ty*sin(a);
y=tx*sin(a)+ty*cos(a);
x+=cx;
y+=cy;
);

!initdone ? (
sqrt3div6=sqrt(3)/6;
lrot=lcxn=lcyn=llenn=-1;
initdone=1;
);


lcxn!=cxn || lcyn!=cyn || llenn!=lenn || lrot!=rot || lsc!=sc || lproject_w!=project_w || lproject_h!=project_h ? (
cx=(cxn*project_w)|0;
cy=(cyn*project_h)|0;
lenNoSc=lenn*max(project_w,project_h);

trApoNoSc=(sqrt3div6*lenNoSc)|1; // apothem/height of center
trHnoSc=trApoNoSc*3;
lenNoSc=(trApoNoSc/sqrt3div6)|1;
wslenNoSc=(sqrt(2*((4*trApoNoSc)^2))+2)|1;

p1.pt(cx-(lenNoSc-1)/2, cy+trApoNoSc);
p2.pt(cx+(lenNoSc-1)/2, cy+trApoNoSc);
p3.pt(cx, cy-trApoNoSc*2);

rotrad=$pi/180*rot;
p1.pRot(cx,cy,rotrad);
p2.pRot(cx,cy,rotrad);
p3.pRot(cx,cy,rotrad);

linew=(max(project_w,project_h)/200)|1;

trApo=(sqrt3div6*lenNoSc*sc)|1;
trH=trApo*3;
len=(trApo/sqrt3div6)|1;
wslen=(sqrt(2*((4*trApo)^2))+2)|1;

startX=(wslen-len)/2;
l1.a=-2*trH/len;
l1.b=wslen/2+trApo-l1.a*startX;
l2.a=-l1.a;
l2.b=wslen/2+trApo-l2.a*(wslen+len)/2;
l3.b=wslen/2+trApo;

minX=min(p1.x, min(p2.x,p3.x)) -cx+project_w/2;
maxX=max(p1.x, max(p2.x,p3.x)) -cx+project_w/2;
minY=min(p1.y, min(p2.y,p3.y)) -cy+project_h/2;
maxY=max(p1.y, max(p2.y,p3.y)) -cy+project_h/2;
rotoDx=max(0,max(-minX,maxX-project_w));
rotoDy=max(0,max(-minY,maxY-project_h));

rwsw=project_w+2*rotoDx;
rwsh=project_h+2*rotoDy;

lcxn=cxn; lcyn=cyn; llenn=lenn; lrot=rot; lsc=sc; lproject_w=project_w; lproject_h=project_h;
);



input=0;
colorspace='RGBA';

preview ? (
gfx_blit(input);
gfx_set(1,0,0);
gfx_fillrect(cx-(linew-1)/2,cy-(linew-1)/2,linew,linew);
gfx_line(p1.x,p1.y,p2.x,p2.y,linew);
gfx_line(p2.x,p2.y,p3.x,p3.y,linew);
gfx_line(p3.x,p3.y,p1.x,p1.y,linew);
):(
gfx_set(bg);
// add margins to work with rotoblit()
gfx_dest = ws1 = gfx_img_resize(ws1,rwsw,rwsh,1);
gfx_blit(input,0,rotoDx,rotoDy,project_w,project_h );

// rotate
gfx_dest = ws2 = gfx_img_resize(ws2,rwsw,rwsh,1);
gfx_rotoblit(ws1,-rotrad, 0,0,rwsw,rwsh, 0,0,rwsw,rwsh, 0, -rwsw/2+cx+rotoDx, -rwsh/2+cy+rotoDy);

// crop to square (scaled)
gfx_dest = ws3 = gfx_img_resize(ws3,wslen,wslen,1);
gfx_blit(ws2,0, 0,0,wslen,wslen, rwsw/2-(wslenNoSc-1)/2,rwsh/2-(wslenNoSc-1)/2,wslenNoSc,wslenNoSc);

// set alpha (create triangle)
code="
_1>wslen-1 ? (_2+=1;_1=0;);
_2>wslen-1 ? _2=0;
_2>l3.b || _2<l1.a*(_1+1)+l1.b || _2<l2.a*_1+l2.b ? a=0;
_1+=1;
";
gfx_evalrect(0,0,wslen,wslen,code);

// create template of 6 rotated/mirrored triangles
gfx_dest = templates = gfx_img_resize(templates,3*len,2*trH);
// upper left
gfx_blit(ws3,0, 0,0,len,trH, startX,(wslen-1)/2-2*trApo,len,trH);

// upper mid
gfx_dest = rotTr = gfx_img_resize(rotTr,wslen,wslen,1);
gfx_rotoblit(ws3,$pi/3,0, 0, wslen, wslen , 0, 0, wslen, wslen, 0, 0, 0);
gfx_dest = mirrTr = gfx_img_resize(mirrTr,wslen,wslen,1);
gfx_deltablit(rotTr, 0,0, wslen,wslen, wslen-1,0, -1,0, 0,1);

gfx_dest=templates;
gfx_blit(mirrTr,0, len,0,len,trH, startX, (wslen-1)/2-trApo+1, len,trH);
gfx_evalrect(len,0,len,1,"a=255"); // remove some rotating artefacts (sporadic for some triangle length)

// upper right
gfx_dest = rotTr = gfx_img_resize(rotTr,wslen,wslen,1);
gfx_rotoblit(ws3,$pi*2/3,0, 0, wslen, wslen , 0, 0, wslen, wslen, 0, 0, 0);

gfx_dest=templates;
gfx_blit(rotTr,0, 2*len,0,len,trH, startX,(wslen-1)/2-2*trApo+1,len,trH);
gfx_evalrect(2*len,trH-1,len,1,"a=255;"); // remove artefacts

// create lower 3 triangles (mirror along x)
gfx_deltablit(templates, 0,trH, 3*len,trH, 0,trH-1, 1,0, 0,-1);


// build image
gfx_dest=-1;
gfx_set(bg);gfx_fillrect(0,0,project_w,project_w);
gfx_mode=gfx_mode|0x10000;

// !debug ? (
curX=-(len-1)/2;
curY=0;
curTr=lTr=0;
while(curY<=project_h) (
curTemX=(curTr%3)*len;
curTemY=(curTr/3)<1?0:trH;
gfx_blit(templates,0, curX,curY,len,trH, curTemX,curTemY,len,trH);
curX+=(len-1)/2;
curTr=(curTr+1)%6;
curX>project_w ? (
curY+=trH;
curX=-(len-1)/2;
curTr=lTr=lTr?0:3;
);
);
// ):debug==1 ? gfx_blit(ws2,1,0,0,project_w,project_h) : debug==2 ? gfx_blit(ws3,1,0,0,wslen,wslen) : debug==3 ? gfx_blit(templates,1,0,0,3*len,2*trH);
);

Eliseat
11-29-2018, 03:46 PM
wwwmaze, you are just THE video processor god! :D

So genius to make a preview. And it works just amazing. I love it. And I just got the hint with the cat in your preview gif. Its hard to see but recognizable. Testing with cats just makes more fun. ;)

Many, many thanks for this great preset. You are the wind in the sail of the new video section. This makes a lot of people very happy. Me too. :)

Eliseat
11-29-2018, 03:50 PM
Kaleidoscope is fun... just one parameter animated. Same static image used as in above two clips. :)
https://i.imgur.com/Cn4IpO8.gif

Imagine this with the upcoming track level inputs in presets. You then can animated a level meter and mangle it in every weird way while the beat comes thru. This will make a lot of fun I think. :)

wwwmaze
11-30-2018, 07:31 AM
wwwmaze, you are just THE video processor god! :D

So genius to make a preview. And it works just amazing. I love it. And I just got the hint with the cat in your preview gif. Its hard to see but recognizable. Testing with cats just makes more fun. ;)

Many, many thanks for this great preset. You are the wind in the sail of the new video section. This makes a lot of people very happy. Me too. :)

Haha thanks glad you like it Eli.
I wondered if you'd recognize Mr. Fluff :) I feel bad that poor kitty is getting morphed into his pals though :D

daxliniere
12-21-2018, 03:00 PM
As a new video user, I'm finding there's quite a steep barrier to getting new video filters. Currently, I've found I need to trawl through threads and even then I'm not sure if the one I've found is the most up-to-date.

Do you think these could be added to a ReaPack repo? Video subsection, perhaps?
@cfillion @X-Raym (<--- I wish tagging worked on the forum)

Eliseat
12-22-2018, 05:07 PM
As a new video user, I'm finding there's quite a steep barrier to getting new video filters. Currently, I've found I need to trawl through threads and even then I'm not sure if the one I've found is the most up-to-date.

Do you think these could be added to a ReaPack repo? Video subsection, perhaps?
@cfillion @X-Raym (<--- I wish tagging worked on the forum)

Yes, of course. But if you take a closer look you will see that 80% of the presets available were created in the last few weeks. Video in Reaper was kind of a shadow project and got now a little bit pushed since wwwmaze made his fantastic contributions and since we have a separate sub forum for this. And of course since Justin jumped on the wave and makes us happy with lots of new stuff and optimizations.

Those user presets don't follow any plans and sometimes evolve inside a thread, so its impossible to declare final versions. Even the preset codes you can find now are already replacements as bugfixes and little optimizations took place. There is no catalog or something like that. And its kind of typical for Reaper that you have to find the stuff for yourself. Only thing I could offer was to make a thread with all presets I could find.

But - to tell you something positive - a lot of new presets will find their way in one of the next official releases. (blur, histogram, white balance, spectrogram for example). This will make lots of fun.


So I absolutely understand your enthusiasm and that you want everything right now, but there is actually more action in the video section than ever before since implementing.

:)

https://i.ibb.co/4gSS91V/spectrogramm.gif

daxliniere
12-22-2018, 06:21 PM
Oh, I don't want it right now, I just think it would be better in one place. :) But it sounds like this is already happening so I will continue to hang around. (Y)

ashcat_lt
03-27-2022, 11:38 AM
I finally managed to finish the triangular shaped kaleidoscope (its not that straightforward):

Hope it works for you guys. Have fun :)
I audiofied this.

Adds the value of the audio sample on your chosen audio channel to any of the meaningful parameters.

It requires the video sample peeker, but if you want more than 2 channels, use my 64 channel version (forum url in code) instead, and don't forget to match the gmem names in the code.

It does rectify and includes a simple global smoothing for out of the box satisfaction. There is no gain/range stuff. That would be controlled by the volume of whatever is on that audio channel. Honestly, if you want much more control, probably condition the audio signal using whatever plugs you want before it gets to the video peeker.


// Kaleidoscope (triangle base)
// v0.1

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//@gmem=jsfx_to_video
// for more than 2 channels, use 64 channel video peeker and match gmem names
// https://forums.cockos.com/showthread.php?t=247691


//@param1:preview 'preview' 0 0 1 0 1

//@param3:cxn_p 'center x' 0.5 0 1 0.5 0.01
//@param4:cyn_p 'center y' 0.5 0 1 0.5 0.01
//@param5:lenn_p 'length' 0.25 0 1 0.5 0.01
//@param6:rot_p 'rotate' 0 0 360 180 1
//@param7:sc_p 'scale' 1 0.01 5 2.5 0.01

//@param9:cxn_ch 'center x channel' 0 0 64 32 1
//@param10:cyn_ch 'center y channel' 0 0 64 32 1
//@param11:lenn_ch 'length channel' 0 0 64 32 1
//@param12:rot_ch 'rotate channel' 0 0 64 32 1
//@param13:sc_ch 'scale channel' 0 0 64 32 1
//@param14:smooth 'smoothing' 0 0 1 0.5 0.01

//@param16:bg 'graylevel background' 0 0 1 0.5 0.01

///@param17:debug 'debug' 0 0 3 2 1



bufplaypos = gmem[0];
bufwritecursor = gmem[1];
bufsrate = gmem[2];
bufstart = gmem[3];
bufend = gmem[4];
nch = gmem[5];

dt=max(bufplaypos - project_time,0);
dt*bufsrate < dotcount ? underrun_cnt+=1;
rdpos = bufwritecursor - ((dt*bufsrate - dotcount)|0)*nch;
rdpos < bufstart ? rdpos += bufend-bufstart;

i_smooth = 1 - smooth;


function getpt(channel, rect_)
local (return)
(channel > 0 && channel <= nch ?
(return = gmem[rdpos + channel - 1];
rect_ == -1 ? return = -abs(return);
rect_ == 1 ? return = abs(return);
rect_ == 2 ? return = 0.5 * (return + 1);
);
(rdpos += nch)>=bufend ? rdpos=bufstart;
return;
);

function smooth (input)
instance (last)
(this.last = i_smooth * input + smooth * this.last;
);
i=0;


function gfx_line(x,y,x2,y2,t) local(i,n,dx,dy,dxn,dyn) (
dx=(x2-x); dy=(y2-y);
n=abs(dx)+abs(dy);
dxn=dx/n; dyn=dy/n;
i=0;
loop(n,
gfx_fillrect(x+i*dxn,y+i*dyn,t,t);
i+=1;
);
gfx_fillrect(x2,y2,1,1);
);
function pt(x,y) instance(x,y) (
this.x=x;
this.y=y;
);
function pRot(cx,cy,a) local(tx,ty) instance(x,y) (
tx=x-cx;
ty=y-cy;
x=tx*cos(a)-ty*sin(a);
y=tx*sin(a)+ty*cos(a);
x+=cx;
y+=cy;
);

!initdone ? (
sqrt3div6=sqrt(3)/6;
lrot=lcxn=lcyn=llenn=-1;
initdone=1;
);

cxn = cxn_p + 0.5 * cx_a.smooth(getpt(cxn_ch, 1));
cyn = cyn_p + 0.5 * cy_a.smooth(getpt(cyn_ch, 1));
lenn = lenn_p + 0.5 * lenn_a.smooth(getpt(lenn_ch, 1));
rot = rot_p + 180 * rot_a.smooth(getpt(rot_ch, 2));
sc = sc_p + 0.5 * sc_a.smooth(getpt(sc_ch, 1));
////@param7:sc 'scale' 1 0.01 5 2.5 0.01

lcxn!=cxn || lcyn!=cyn || llenn!=lenn || lrot!=rot || lsc!=sc || lproject_w!=project_w || lproject_h!=project_h ? (
cx=(cxn*project_w)|0;
cy=(cyn*project_h)|0;
lenNoSc=lenn*max(project_w,project_h);

trApoNoSc=(sqrt3div6*lenNoSc)|1; // apothem/height of center
trHnoSc=trApoNoSc*3;
lenNoSc=(trApoNoSc/sqrt3div6)|1;
wslenNoSc=(sqrt(2*((4*trApoNoSc)^2))+2)|1;

p1.pt(cx-(lenNoSc-1)/2, cy+trApoNoSc);
p2.pt(cx+(lenNoSc-1)/2, cy+trApoNoSc);
p3.pt(cx, cy-trApoNoSc*2);

rotrad=$pi/180*rot;
p1.pRot(cx,cy,rotrad);
p2.pRot(cx,cy,rotrad);
p3.pRot(cx,cy,rotrad);

linew=(max(project_w,project_h)/200)|1;

trApo=(sqrt3div6*lenNoSc*sc)|1;
trH=trApo*3;
len=(trApo/sqrt3div6)|1;
wslen=(sqrt(2*((4*trApo)^2))+2)|1;

startX=(wslen-len)/2;
l1.a=-2*trH/len;
l1.b=wslen/2+trApo-l1.a*startX;
l2.a=-l1.a;
l2.b=wslen/2+trApo-l2.a*(wslen+len)/2;
l3.b=wslen/2+trApo;

minX=min(p1.x, min(p2.x,p3.x)) -cx+project_w/2;
maxX=max(p1.x, max(p2.x,p3.x)) -cx+project_w/2;
minY=min(p1.y, min(p2.y,p3.y)) -cy+project_h/2;
maxY=max(p1.y, max(p2.y,p3.y)) -cy+project_h/2;
rotoDx=max(0,max(-minX,maxX-project_w));
rotoDy=max(0,max(-minY,maxY-project_h));

rwsw=project_w+2*rotoDx;
rwsh=project_h+2*rotoDy;

lcxn=cxn; lcyn=cyn; llenn=lenn; lrot=rot; lsc=sc; lproject_w=project_w; lproject_h=project_h;
);



input=0;
colorspace='RGBA';

preview ? (
gfx_blit(input);
gfx_set(1,0,0);
gfx_fillrect(cx-(linew-1)/2,cy-(linew-1)/2,linew,linew);
gfx_line(p1.x,p1.y,p2.x,p2.y,linew);
gfx_line(p2.x,p2.y,p3.x,p3.y,linew);
gfx_line(p3.x,p3.y,p1.x,p1.y,linew);
):(
gfx_set(bg);
// add margins to work with rotoblit()
gfx_dest = ws1 = gfx_img_resize(ws1,rwsw,rwsh,1);
gfx_blit(input,0,rotoDx,rotoDy,project_w,project_h );

// rotate
gfx_dest = ws2 = gfx_img_resize(ws2,rwsw,rwsh,1);
gfx_rotoblit(ws1,-rotrad, 0,0,rwsw,rwsh, 0,0,rwsw,rwsh, 0, -rwsw/2+cx+rotoDx, -rwsh/2+cy+rotoDy);

// crop to square (scaled)
gfx_dest = ws3 = gfx_img_resize(ws3,wslen,wslen,1);
gfx_blit(ws2,0, 0,0,wslen,wslen, rwsw/2-(wslenNoSc-1)/2,rwsh/2-(wslenNoSc-1)/2,wslenNoSc,wslenNoSc);

// set alpha (create triangle)
code="
_1>wslen-1 ? (_2+=1;_1=0;);
_2>wslen-1 ? _2=0;
_2>l3.b || _2<l1.a*(_1+1)+l1.b || _2<l2.a*_1+l2.b ? a=0;
_1+=1;
";
gfx_evalrect(0,0,wslen,wslen,code);

// create template of 6 rotated/mirrored triangles
gfx_dest = templates = gfx_img_resize(templates,3*len,2*trH);
// upper left
gfx_blit(ws3,0, 0,0,len,trH, startX,(wslen-1)/2-2*trApo,len,trH);

// upper mid
gfx_dest = rotTr = gfx_img_resize(rotTr,wslen,wslen,1);
gfx_rotoblit(ws3,$pi/3,0, 0, wslen, wslen , 0, 0, wslen, wslen, 0, 0, 0);
gfx_dest = mirrTr = gfx_img_resize(mirrTr,wslen,wslen,1);
gfx_deltablit(rotTr, 0,0, wslen,wslen, wslen-1,0, -1,0, 0,1);

gfx_dest=templates;
gfx_blit(mirrTr,0, len,0,len,trH, startX, (wslen-1)/2-trApo+1, len,trH);
gfx_evalrect(len,0,len,1,"a=255"); // remove some rotating artefacts (sporadic for some triangle length)

// upper right
gfx_dest = rotTr = gfx_img_resize(rotTr,wslen,wslen,1);
gfx_rotoblit(ws3,$pi*2/3,0, 0, wslen, wslen , 0, 0, wslen, wslen, 0, 0, 0);

gfx_dest=templates;
gfx_blit(rotTr,0, 2*len,0,len,trH, startX,(wslen-1)/2-2*trApo+1,len,trH);
gfx_evalrect(2*len,trH-1,len,1,"a=255;"); // remove artefacts

// create lower 3 triangles (mirror along x)
gfx_deltablit(templates, 0,trH, 3*len,trH, 0,trH-1, 1,0, 0,-1);


// build image
gfx_dest=-1;
gfx_set(bg);gfx_fillrect(0,0,project_w,project_w);
gfx_mode=gfx_mode|0x10000;

// !debug ? (
curX=-(len-1)/2;
curY=0;
curTr=lTr=0;
while(curY<=project_h) (
curTemX=(curTr%3)*len;
curTemY=(curTr/3)<1?0:trH;
gfx_blit(templates,0, curX,curY,len,trH, curTemX,curTemY,len,trH);
curX+=(len-1)/2;
curTr=(curTr+1)%6;
curX>project_w ? (
curY+=trH;
curX=-(len-1)/2;
curTr=lTr=lTr?0:3;
);
);
// ):debug==1 ? gfx_blit(ws2,1,0,0,project_w,project_h) : debug==2 ? gfx_blit(ws3,1,0,0,wslen,wslen) : debug==3 ? gfx_blit(templates,1,0,0,3*len,2*trH);
);