Reptorian G'MIC Filters

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.

1 Like

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:

image

Current code
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);
 );
 "
 "
1 Like

The current gradient bars remind me of twist ties. :stuck_out_tongue:

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.

image

3 Likes

More work has been done:

image

TODO:

  1. Symmetry Mode
  2. Color Input
Code to current gradient bars
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;
 );
 "
1 Like

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

2 Likes

I finally gotten Lyapunov Fractal to work.

Also:

Finally got duotone color mode to work on my gradient bars.

image

There’s still a bit more work to do, however, I think it’s nearly done.


More teases on Random Gradient Bars

2 Likes

New Filter! Random Gradient Bars

EDIT: Fixed most bugs. Dynamic options are there, and symmetry bug is fixed.

A interesting test:

1 Like

Perfect for sound wave animation.

1 Like

Yes, the code can utilize animation indeed.

Another test picture:

image

Next part is to create catmull-clark interpolation along pixel.

3 Likes

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.

Fork of Poisson Noise
#@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.

For reference, starts here: G'MIC exercises - #177 by garagecoder.