Are you referring to henon phase diagram? If so, it been released, and you could lower the number of lines.
Note that it needs optimization for other option other than no alpha gray.
If referring to the chaos games fractal. Absolutely can be done.
Are you referring to henon phase diagram? If so, it been released, and you could lower the number of lines.
Note that it needs optimization for other option other than no alpha gray.
If referring to the chaos games fractal. Absolutely can be done.
This https://people.duke.edu/~ng46/borland/chaos-1000-bw-triangle.jpg
I like the shapes around vertices only triangle is very dominant.
With the option of setting up random ratio, then yes, it can be done.
3/28/2021
I have updated and fixed Glass Vignette. It is also further optimized.
Popcorn Fractal gets a easier to understand code though not really that much easier.
Thorn Fractal (GUI) gets overload option separated. Overload Count is limited from 1-4, and a checkmark for negative overload function. This is for convenience of switching between the two, and you don’t need to slide bar to switch to negative overload mode.
Edit: I think I have my solution for earlier question.
I haven’t done any much new filters though that does make me feel like a failure. I am more focused in my other hobby these day and that makes it difficult to finish gmic scripts. I did add a Skew filter which was inspired by a PDN plugin creator. There is a possibility of a new filter which I had been collaborating with @grosgood.
Also, a question to @David_Tschumperle: Can you move Thorn Fractal to Rendering? I consider it completed and there is zero changes to be made.
Sigh, am failing to find a new filter to create though the earlier filter can be done.
This is what I’m trying to achieve now - Gradient Bars - Plugins - Publishing ONLY! - paint.net Forum
So far, here’s my code.
rep_random_gradient_bars:
#$1=Width#
#$2=Spacing#
#$3=Divisions#
#$4=Angle#
#$5=Skew#
#$6=Shift#
#$7=Mode#
#$8=Grayscale#
f "begin(
const ww=w-1;
const hh=h-1;
const sd=max(ww,hh)/min(ww,hh);
const sx=ww>hh?sd:1;
const sy=ww>hh?1:sd;
const wwsx=ww*sx;
const hhsy=hh*sy;
const ww_div_sx=ww/sx;
const cx=ww/2;
const cy=hh/2;
const ang=($4/180)*pi;
const cos_ang=cos(ang);
const sin_ang=sin(ang);
const cut_ang=atan2(cy,cy);
const cut_ang2=pi-cut_ang;
const total_width=$1+$2;
const spacing=($2/(total_width-1))/2;
rot_x(a,b)=a*cos_ang-b*sin_ang;
rot_y(a,b)=a*sin_ang+b*cos_ang;
fmod(a,b)=a-b*floor(a/b);
const trimmed_ang=fmod(ang,pi);
const vbars=((trimmed_ang>=cut_ang)&&(trimmed_ang<cut_ang2))?ceil((abs(1/sin(ang))*ww)/total_width):ceil((abs(1/cos(ang))*hh)/total_width);
v_shift=vectorvbars();
shift_bars=int(vbars/2)-2;
run('echo ',vbars);
);
initial_x=(x-cx)/wwsx;
initial_y=(y-cy)/hhsy;
gradient=rot_y(initial_x,initial_y);
point_x=rot_x(initial_x,initial_y)*ww_div_sx;
bars=floor(point_x/total_width)+shift_bars;
spaces=abs(fmod(point_x,total_width)/total_width-.5)>=spacing;
bars;
"
The plan is to create series of vectors with random values and to use “bars” image to define the output, and the “spaces” to control gaps. So far, test shows it’s really promising.
Seems like you are on your way: modulo arithmetic, and/or feeding ascending/descending ramps to trig functions.
I would carry it beyond pyrochild’s running down a straight line. That’s fourteen year old thinking; you’ve got a lot more chops than that. Trace along constant contours, laying down spots of the current modulo function as you go. Could be some interesting Moiré patterns to come of it.
Have fun, whatever you do.
I have no plan to try to do the contour version of it, and it’s already difficult as it with the line version. Adding trig functions is a interesting idea because I have done trigonometric mapping (it could use more features, but with all the options I inserted into it, it becomes so much harder to maintain, so it’d be part of the long list of TODO for later). That asides, I’m glad I completed a lot of my own filters. My goal for now is to try to convert as much as PDN plugins into gmic filters.
It’s coming, some more work done:
rep_random_gradient_bars:
skip ${11=}
#$1=Width#
#$2=Spacing#
#$3=Divisions#
#$4=Angle#
#$5=Skew_Angle#
#$6,$7=Position#
#$8=Gradient Shift#
#$9,$10=Gradient Min/Max Multiplier#
#$8=Grayscale#
specified_mode_out={narg($11)}
f "begin(
const ww=w-1;
const hh=h-1;
const sd=max(ww,hh)/min(ww,hh);
const sx=ww>hh?sd:1;
const sy=ww>hh?1:sd;
const wwsx=ww*sx;
const hhsy=hh*sy;
const pyth_img=sqrt(ww^2+hh^2);
const ww_div_sx=ww/sx;
const hh_div_sy=hh/sy;
const cx=ww/2;
const cy=hh/2;
const ox=cx-(cx*$6);
const oy=cy+(cy*$7);
const skew_ang=($5/180)*pi;
const mdist=tan(skew_ang)*pyth_img;
const ang=($4/180)*pi;
const cos_ang=cos(ang);
const sin_ang=sin(ang);
const cut_ang=atan2(cy,cy);
const cut_ang2=pi-cut_ang;
const total_width=$1+$2;
const spacing=($2/(total_width-1))/2;
rot_x(a,b)=a*cos_ang-b*sin_ang;
rot_y(a,b)=a*sin_ang+b*cos_ang;
fmod(a,b)=a-b*floor(a/b);
const trimmed_ang=fmod(ang,pi);
const nbars=((trimmed_ang>=cut_ang)&&(trimmed_ang<cut_ang2))?ceil((abs(1/sin(ang))*ww)/total_width):ceil((abs(1/cos(ang))*hh)/total_width);
const shift_bars=int(nbars/2);
v_base=vectornbars(0);
v_shift=v_base;
v_mult=v_base;
v_modulo_mode=v_base;
v_use_invert=v_base;
$8!=0?fill(v_shift,u(-$8,$8));
fill(v_mult,u($9,$10));
fill(v_modulo_mode,round(u(0,2)));
fill(v_use_invert,round(u(0,1))==1?1:-1 );
if(narg($11),
const outmode=$11%3;
outmode==2?v_modulo_mode=vectornbars(2):
v_modulo_mode=vectornbars(1);
);
);
initial_x=(x-ox)/ww*sx;
initial_y=(y-oy)/hh*sy;
gradient=rot_y(initial_x,initial_y)*hh_div_sy/pyth_img;
point_x=rot_x(initial_x,initial_y)*ww_div_sx+gradient*mdist;
bars=(floor(point_x/total_width)+shift_bars)%nbars;
spaces=abs(fmod(point_x,total_width)/total_width-.5)>=spacing;
spaces?(
new_gradient=gradient*v_use_invert[bars]*v_mult[bars]+v_shift[bars];
fmod(new_gradient,10);
);
"
"
The current gradient bars remind me of twist ties.
Modified, they could pass as abstracted hair; or you could make a stylize filter.
Just throwing in some ideas.
It can also be made as a “carbon fiber” filter.
More work has been done:
TODO:
rep_random_gradient_bars:
skip "${11=-1}","${12=}"
#$1=Width#
#$2=Spacing#
#$3=Angle#
#$4=Skew_Angle#
#$5,$6=Position#
#$7=Gradient Shift#
#$8,$9=Gradient Min/Max Multiplier#
#$10=Use Invert?#
#$11=Output Modulo#
#$12=Random Modulo Mode#
f "begin(
const ww=w-1;
const hh=h-1;
const sd=max(ww,hh)/min(ww,hh);
const sx=ww>hh?sd:1;
const sy=ww>hh?1:sd;
const wwsx=ww*sx;
const hhsy=hh*sy;
const pyth_img=sqrt(ww^2+hh^2);
const ww_div_sx=ww/sx;
const hh_div_sy=hh/sy;
const cx=ww/2;
const cy=hh/2;
const ox=cx-(cx*$5);
const oy=cy+(cy*$6);
const skew_ang=($4/180)*pi;
const mdist=tan(skew_ang)*pyth_img;
const ang=($3/180)*pi;
const cos_ang=cos(ang);
const sin_ang=sin(ang);
const cut_ang=atan2(cy,cy);
const cut_ang2=pi-cut_ang;
const total_width=$1+$2;
if($1==1&&$2<2
,const spacing=$2>0?.5:0;
if(abs($2),calc_spacing()=int(point_x)%2?1;,calc_spacing()=1);
,const spacing=1-($2/(total_width-1));
calc_spacing()=abs(1-fmod(point_x,total_width)/total_width-.5)*2<=spacing;
);
rot_x(a,b)=a*cos_ang-b*sin_ang;
rot_y(a,b)=a*sin_ang+b*cos_ang;
fmod(a,b)=a-b*floor(a/b);
fmod_cont(a,b)=floor(a/b)%2?b-fmod(a,b):fmod(a,b);
fcut(a,b)=(cut(a,-b,b)+b)/2;
const trimmed_ang=fmod(ang,pi);
const nbars=((trimmed_ang>=cut_ang)&&(trimmed_ang<cut_ang2))?ceil((abs(1/sin(ang))*ww)/total_width):ceil((abs(1/cos(ang))*hh)/total_width);
const shift_bars=int(nbars/2);
v_base=vectornbars(0);
v_shift=v_base;
v_mult=v_base;
v_use_invert=v_base;
$7!=0?fill(v_shift,u(-$7,$7));
fill(v_mult,u($8,$9));
$10?fill(v_use_invert,round(u(0,1))==1?1:0);
use_only_one_mode=$11>=0;
use_only_one_mode?(
modulo_out=$11%3;
modulo_out==2?v_modulo_mode=vectornbars(2):
modulo_out==1?v_modulo_mode=vectornbars(1):
v_modulo_mode=vectornbars(0);
):(
narg($12)?(
v_modulo_mode=v_base:
modulo_out=$12%3;
modulo_out==0?fill(v_modulo_mode,round(u(0,1))):
modulo_out==1?fill(v_modulo_mode,round(u(0,1))*2):
fill(v_modulo_mode,round(u(0,1))+1);
):(fill(v_modulo_mode,round(u(0,2))););
);
);
initial_x=(x-ox)/ww*sx;
initial_y=(y-oy)/hh*sy;
gradient=rot_y(initial_x,initial_y)*hh_div_sy/pyth_img;
point_x=rot_x(initial_x,initial_y)*ww_div_sx+gradient*mdist;
bar=(floor(point_x/total_width)+shift_bars)%nbars;
spaces=calc_spacing();
spaces?(
new_gradient=gradient*v_mult[bar]+v_shift[bar];
use_modulo_out=v_modulo_mode[bar];
use_modulo_out==2?(final_gradient=v_use_invert[bar]?10-fmod_cont(new_gradient,10):fmod_cont(new_gradient,10);):
use_modulo_out==1?(final_gradient=v_use_invert[bar]?10-fmod(new_gradient,10):fmod(new_gradient,10);):
(final_gradient=v_use_invert[bar]?10-fcut(new_gradient,10):fcut(new_gradient,10););
final_gradient;
);
"
You could make a wrist watch strap out of that pattern.
BTW, here is a fun shape you could try to make, if you are up to the challenge.
I finally gotten Lyapunov Fractal to work.
Also:
Finally got duotone color mode to work on my gradient bars.
There’s still a bit more work to do, however, I think it’s nearly done.
More teases on Random Gradient Bars
New Filter! Random Gradient Bars
EDIT: Fixed most bugs. Dynamic options are there, and symmetry bug is fixed.
Perfect for sound wave animation.
Yes, the code can utilize animation indeed.
Another test picture:
Next part is to create catmull-clark interpolation along pixel.
EDIT: Never mind. Forget that I asked. I found this in k3dsurf which can be used to create steerable guassian.
4*x*exp(-x^2-y^2)
I ended up making a fork of Poisson Noise by @David_Tschumperle as I didn’t want to use extract for speed purposes. Also, it does utilize mask as a option. For now, I’ll just share the code of the fork. I wanted to do this in a while, but done with it.
I might make another fork of it where density is localized.
#@cli rep_noise_poissondisk_to_coordinates : _radius[%]>0,_max_sample_attempts>0,_use_mask={ 0=ignore_mask | 1=utilize_mask }
#@cli : Generates coordinates utilizing a fast poisson disk generation. Forked from David Tschumperlé original code just for this purpose. Requires a image to have a mask.
#@cli : Implements the algorithm from the article "Fast Poisson Disk Sampling in Arbitrary Dimensions",
#@cli : by Robert Bridson (SIGGRAPH'2007).
#@cli : Default values: 'radius=8' and 'max_sample_attempts=30'.
#@cli : $ 300,300 rep_noise_poissondisk_to_coordinates 8
rep_noise_poissondisk_to_coordinates :
skip "${3=0}"
check "${1=8}>0 && ${2=30}>0"
e[^-1] "Extract poisson disk sampling points coordinates with radius $1 and max sample attempts $2."
repeat $! l[$>]
R={${"is_percent $1"}?max(w,h,d)*$1:$1}
dim={d>1?3:h>1?2:1}
cw={0.999*$R/sqrt($dim)}
({[w,h,d,1]}) y. c
{[ceil(I/$cw)]}
r[1] 1,1,1,$dim,-1
1,1,1,$dim 1,1,1,1
{vector$dim(2*ceil(sqrt($dim))+1)} r. 100%,100%,100%,2
f. "P=[x,y,z]-int([w/2,h/2,d/2]);[sum(sqr(P)),dot(P,[1,w#2,w#2*h#2])]"
r. {[whd,s,1,1,-1]} sort. +,x z. 0,1,100%,100% y. c
eval ${-math_lib}"
begin(
dotoff = resize([ 1,w#2,w#2*h#2 ],d#2>1?3:h#2>1?2:1,0););
const N = "$dim";
const radius = "$R";
const grid_cw = "$cw";
const max_sample_attempts = $2;
const value = iM#0 + (im#0==iM#0);
const use_mask = $3;
mag2(vec) = sum(sqr(vec));
prox = I#5;
lim = I#1;
dar_insert(#3,I#1,0); # dummy sample to simplify bounds checks
dar_insert(#3,u(I#1),1); # add initial sample to list
dar_insert(#4,1,0); # add its index to active list
I(#2,int(I[#3,1]/grid_cw)) = 1; # add its index to grid cell
I(#0,I[#3,1]) = value; # draw the point
while (dar_size(#4)>0,
R = int(u(dar_size(#4)-1e-4)); # choose a random active list index
P = i[#4,R]; # get the index of that sample
T = I[#3,P]; # position vector of that sample
repeat (max_sample_attempts,attempts,
do (S = 4*(u(vectorN(1)) - 0.5); M = mag2(S), M <= 1 || M > 4);
X = T + radius * S;
if (min(X)<0 || min(lim-X)<0, continue());
G = int(X/grid_cw);
GI = dot(G,dotoff);
for (K = 0; rejected = 0, K<size(prox), ++K,
V = i[#2,GI+prox[K]]; # sample index from grid to check
if (V>0 && mag2(I[#3,V]-X)<sqr(radius), rejected = 1; break())
);
if (!rejected,
if(use_mask,!sum(I(#0,X))?break(););
Q = dar_size(#3); # sample found, get new index
dar_insert(#3,X); # insert into samples list
dar_insert(#4,Q); # insert its index into active
I(#2,G) = Q; # insert its index into grid
break();
);
);
if (attempts==max_sample_attempts, dar_remove(#4,R));
);
end(resize(#3,1,dar_size(#3),1,s(#3),0););
"
td={(w#0>1)+(h#0>1)+(d#0>1)}
td-=1
channels[3] 0,$td
k[3]
endl done
Let me give credit where credit is due, the poissondisk noise has been implemented by @garagecoder, not me.