generating Poincare Discs

Another step done: I’ve recoded the poincaré disk algorithm from scratch, so I can verify I’ve indeed understood the different geometric steps behind the poincaré disk.

Now, contrary to the original code of B. Horne, I’m also able to generate poincaré disks where the smallest primitive is not the (hyperbolic) triangle, but the whole (hyperbolic) polygon.

The latter will be better to have space to map image data into it.
Also, now I’ve understood the concept, the G’MIC code is also shorter :slight_smile:

Code:

foo :
  1400,1400,1,1,":
    begin(
      const p = 5;
      const q = 4;
      const nb_iter = 20;
      const is_polygon = 1;

      const pi2 = 2*pi;
      const pip = pi/p;
      const pi2p = 2*pip;

      const cosq2 = cos(pi/q)^2;
      const sinp2 = sin(pip)^2;
      const delta = cosq2 - sinp2;
      const r0 = sqrt(sinp2/delta);
      const x0 = sqrt(cosq2/delta);
      const y0 = 0;
    );

    # Cartesian coordinates (x,y) and polar coordinates (r,t) (assumed to be always synchronized).
    x = lerp(-1.1,1.1,x/(w-1));
    y = lerp(-1.1,1.1,y/(h-1));
    r = norm(x,y);
    t = (atan2(y,x) + pi2)%pi2;

    r>1?0:(
      parity = 0;
      repeat ((is_polygon?1:3)*nb_iter,k,

        # Test if point (x,y) is in fundamental region.
        is_polygon?(
          trm = (round(t - pip,pi2p) + pip)%pi2;
          P0 = rot(trm)*[ x0,y0 ];
          is_inside_region = norm([x,y] - P0)>=r0;
        ):(
          P0 = [ x0,y0 ];
          is_inside_region = t<pip && norm(x - x0, y -  y0)>=r0;
        );
        is_inside_region?break();

        # Otherwise, transform (x,y) coordinates.
        ++parity;
        is_polygon || (k3 = k%3)==2?(
          u = x - P0[0];
          v = y - P0[1];
          nr = norm(u,v);
          x = P0[0] + u*r0^2/nr^2;
          y = P0[1] + v*r0^2/nr^2;
          r = norm(x,y);
          t = atan2(y,x)%pi2):
        k3==1?(
          y*=-1;
          t = -t + pi2;
        ):
        (
          t = (((t + pip)%pi2p) - pip)%pi2;
          x = r*cos(t); y = r*sin(t)
        );
      );

      parity%3;
    )"
  r2dx 50%

Next step: map image pixels !

1 Like

Another step done : mapping image data was finally quite easy once the previous step was done.

Next step: Do an image filter for the plug-in! Wish me luck :slight_smile:

2 Likes

Fantastic ! Good luck, David !

Fantastic ! Good luck David !

Wow; fantastic stuff. You are awesome, David. Having a real GUI will make creating Poincare’ Disc renders to the next level. Maybe have an interactive warp step so you can fit the bitmap withing the target (yes, again, I asketh for too mucheth) would be cool, too. :slight_smile:

Intermediate step before the plug-in filter: New command poincare_disk has been added to the stdlib.

foo :
  repeat 4 { poincare_disk 1024,{3+$>},10 channels. 2 mod. 3 neq. 2 } resize2dx 50% n 0,255 frame 10,10,255 append_tiles ,

1 Like

OK, so here is a first version.
More controls to come, to allow a better positioning of the input image inside the Poincaré disk.

But, hey. Time to cook now :slight_smile:

1 Like

OK, I’ve added the possibility to shift and zoom the image :

I stop here for today. it took me most of the day. It was fun, but it’s still the weekend, there are other things to do!

2 Likes

You know me, I couldn’t resist adding a couple of options.

3 Likes

Can be used for rendering a cool kaleidoscope effect !

3 Likes

My first try.
Thanks a lot, David !

There’s a bug I found within that image, what with the left cutoff?

1 Like

Yes the default polygon settings have this clipping for me too
( EDIT : there is actually a second one near top right, and bottom right for @dinasset’s) :

EDIT : Easier to see here:

1 Like

Smiling Me PD. lolol

Thanks again, David. Looking forward to all the possibilities with this one. :slight_smile:

2 Likes

You call it a bug, it’s actually maths.
Joking apart, the way the poincaré disk is constructed makes this happen for odd values of q.
This also happens on the website @lylejk told about:

The only thing I can do is change the default value of q (to 6 rather than 7).

Creative sunday:

Code (ugly):

fx_poincare_disk_modified :
  p,q,size,nb_iter,angle,tiling,antialiasing,filling,shiftx,shifty,zoom,iangle,\
    outline,Ro,Go,Bo,R0,G0,B0,R1,G1,B1=${1-22}
  foreach {
    to_a
    if $antialiasing r 200%,200%,1,100%,3 fi
    poincare_disk {max(w,h)},$p,$q,$angle,$tiling,$nb_iter,$_x0,$_y0,$_x1,$_y1 s. c,-2

    if $filling # Image filling
      eval[1] ">begin(m = inf; M = -inf); i0!=-2?(m = min(m,i0,i1); M = max(M,i0,i1)); end(set('m',m); set('M',M))"
      f[1] "
        begin(
          const m = $m;
          const dM = $M - m;
          const mwh = min(w#0,h#0);
          const shiftx = ($shiftx - 50)*mwh%;
          const shifty = ($shifty - 50)*mwh%;
          const zoom = 1.25^-$zoom;
          R = rot(-$iangle);
        );
        i0==-2?[-1,-1]:
        U = R*[ i0,i1 ];
        [ w#0 - mwh - shiftx + (U[0]*zoom - m)*mwh/dM,
          h#0 - mwh - shifty + (U[1]*zoom - m)*mwh/dM ]"
      warp[0] [1],0,1,3
      if $outline
        +f. "const boundary = 1; v = i; v==j(1) && v==j(0,1)" erode. {$outline*($antialiasing?2:1)}
        sh[0] 0,{0,s-2} f. "i(#-2)?I:[ $Ro,$Go,$Bo ]" rm[-2,-1]
      fi

    else # Binary filling
      rm[0]
      if $tiling +%. 2 else +%. 3 ==. 2 fi
      ($R0,$R1^$G0,$G1^$B0,$B1) map.. . rm. to_a. mv. 0
    fi

    # Manage alpha-channel
    !=. -2 sh[0] 100% *. ..

    k[0]
    if $antialiasing r 50%,50%,1,100%,2 fi
  }

foo :
  sp colorful
  $HOME/cosmos.jpg r. {0,[w,h]},1,100%,0,0,0.5,0.5 store. bg

  N=800
  repeat $N {
    e:=2^lerp(-3,30,($>/$N))
    _x0,_y0,_x1,_y1:=-1/$e,-1/$e,2/$e,2/$e
    _x0,_x1+=0
    _y0,_y1+=-1
    tilt:=90*cos($>/20)/lerp(1,10,$>/$N)

    +fx_poincare_disk_modified[0] 5,8,100,20,-30,1,1,1,49.1379,49.8563,-1.58,0,1,255,255,255,0,0,0,255,255,255
    s. c,-3 *.. . /.. 255 a[-2,-1] c
    +shift. 0,-10 b. y,30 b. x,5 +. 50 rv[-2,-1] blend[-2,-1] alpha
    rotate. $tilt,1,0,50%,50%

    $bg rv[-2,-1] blend[-2,-1] alpha
    r. 70%,70%,1,100%,0,0,0.5,0.5

    on. frame.png,$>
    to. $>
    w.

    rm.
  }
6 Likes

Indeed. Leave the thread be for a couple of days and it explodes.

After breakfast, I trust.

David, it looks as if you have found a toy hehehe… Great

Now that’s one heck of an animation, David. lol

:slight_smile:

That video reminds me of the 90s demo scene stuff, just add the appropriate music :slight_smile:

1 Like