G'MIC Tutorial Fragments

Tutorial request:

I found this on cimg.h

#if cimg_display==1
    // Define keycodes for X11-based graphical systems.
    const unsigned int keyESC        = XK_Escape;
    const unsigned int keyF1         = XK_F1;
    const unsigned int keyF2         = XK_F2;
    const unsigned int keyF3         = XK_F3;
    const unsigned int keyF4         = XK_F4;
    const unsigned int keyF5         = XK_F5;
    const unsigned int keyF6         = XK_F6;
    const unsigned int keyF7         = XK_F7;
    const unsigned int keyF8         = XK_F8;
    const unsigned int keyF9         = XK_F9;
    const unsigned int keyF10        = XK_F10;
    const unsigned int keyF11        = XK_F11;
    const unsigned int keyF12        = XK_F12;
    const unsigned int keyPAUSE      = XK_Pause;
    const unsigned int key1          = XK_1;
    const unsigned int key2          = XK_2;
    const unsigned int key3          = XK_3;
    const unsigned int key4          = XK_4;
    const unsigned int key5          = XK_5;
    const unsigned int key6          = XK_6;
    const unsigned int key7          = XK_7;
    const unsigned int key8          = XK_8;
    const unsigned int key9          = XK_9;
    const unsigned int key0          = XK_0;
    const unsigned int keyBACKSPACE  = XK_BackSpace;
    const unsigned int keyINSERT     = XK_Insert;
    const unsigned int keyHOME       = XK_Home;
    const unsigned int keyPAGEUP     = XK_Page_Up;
    const unsigned int keyTAB        = XK_Tab;
    const unsigned int keyQ          = XK_q;
    const unsigned int keyW          = XK_w;
    const unsigned int keyE          = XK_e;
    const unsigned int keyR          = XK_r;
    const unsigned int keyT          = XK_t;
    const unsigned int keyY          = XK_y;
    const unsigned int keyU          = XK_u;
    const unsigned int keyI          = XK_i;
    const unsigned int keyO          = XK_o;
    const unsigned int keyP          = XK_p;
    const unsigned int keyDELETE     = XK_Delete;
    const unsigned int keyEND        = XK_End;
    const unsigned int keyPAGEDOWN   = XK_Page_Down;
    const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
    const unsigned int keyA          = XK_a;
    const unsigned int keyS          = XK_s;
    const unsigned int keyD          = XK_d;
    const unsigned int keyF          = XK_f;
    const unsigned int keyG          = XK_g;
    const unsigned int keyH          = XK_h;
    const unsigned int keyJ          = XK_j;
    const unsigned int keyK          = XK_k;
    const unsigned int keyL          = XK_l;
    const unsigned int keyENTER      = XK_Return;
    const unsigned int keySHIFTLEFT  = XK_Shift_L;
    const unsigned int keyZ          = XK_z;
    const unsigned int keyX          = XK_x;
    const unsigned int keyC          = XK_c;
    const unsigned int keyV          = XK_v;
    const unsigned int keyB          = XK_b;
    const unsigned int keyN          = XK_n;
    const unsigned int keyM          = XK_m;
    const unsigned int keySHIFTRIGHT = XK_Shift_R;
    const unsigned int keyARROWUP    = XK_Up;
    const unsigned int keyCTRLLEFT   = XK_Control_L;
    const unsigned int keyAPPLEFT    = XK_Super_L;
    const unsigned int keyALT        = XK_Alt_L;
    const unsigned int keySPACE      = XK_space;
    const unsigned int keyALTGR      = XK_Alt_R;
    const unsigned int keyAPPRIGHT   = XK_Super_R;
    const unsigned int keyMENU       = XK_Menu;
    const unsigned int keyCTRLRIGHT  = XK_Control_R;
    const unsigned int keyARROWLEFT  = XK_Left;
    const unsigned int keyARROWDOWN  = XK_Down;
    const unsigned int keyARROWRIGHT = XK_Right;
    const unsigned int keyPAD0       = XK_KP_0;
    const unsigned int keyPAD1       = XK_KP_1;
    const unsigned int keyPAD2       = XK_KP_2;
    const unsigned int keyPAD3       = XK_KP_3;
    const unsigned int keyPAD4       = XK_KP_4;
    const unsigned int keyPAD5       = XK_KP_5;
    const unsigned int keyPAD6       = XK_KP_6;
    const unsigned int keyPAD7       = XK_KP_7;
    const unsigned int keyPAD8       = XK_KP_8;
    const unsigned int keyPAD9       = XK_KP_9;
    const unsigned int keyPADADD     = XK_KP_Add;
    const unsigned int keyPADSUB     = XK_KP_Subtract;
    const unsigned int keyPADMUL     = XK_KP_Multiply;
    const unsigned int keyPADDIV     = XK_KP_Divide;

#elif cimg_display==2
    // Define keycodes for Windows.
    const unsigned int keyESC        = VK_ESCAPE;
    const unsigned int keyF1         = VK_F1;
    const unsigned int keyF2         = VK_F2;
    const unsigned int keyF3         = VK_F3;
    const unsigned int keyF4         = VK_F4;
    const unsigned int keyF5         = VK_F5;
    const unsigned int keyF6         = VK_F6;
    const unsigned int keyF7         = VK_F7;
    const unsigned int keyF8         = VK_F8;
    const unsigned int keyF9         = VK_F9;
    const unsigned int keyF10        = VK_F10;
    const unsigned int keyF11        = VK_F11;
    const unsigned int keyF12        = VK_F12;
    const unsigned int keyPAUSE      = VK_PAUSE;
    const unsigned int key1          = '1';
    const unsigned int key2          = '2';
    const unsigned int key3          = '3';
    const unsigned int key4          = '4';
    const unsigned int key5          = '5';
    const unsigned int key6          = '6';
    const unsigned int key7          = '7';
    const unsigned int key8          = '8';
    const unsigned int key9          = '9';
    const unsigned int key0          = '0';
    const unsigned int keyBACKSPACE  = VK_BACK;
    const unsigned int keyINSERT     = VK_INSERT;
    const unsigned int keyHOME       = VK_HOME;
    const unsigned int keyPAGEUP     = VK_PRIOR;
    const unsigned int keyTAB        = VK_TAB;
    const unsigned int keyQ          = 'Q';
    const unsigned int keyW          = 'W';
    const unsigned int keyE          = 'E';
    const unsigned int keyR          = 'R';
    const unsigned int keyT          = 'T';
    const unsigned int keyY          = 'Y';
    const unsigned int keyU          = 'U';
    const unsigned int keyI          = 'I';
    const unsigned int keyO          = 'O';
    const unsigned int keyP          = 'P';
    const unsigned int keyDELETE     = VK_DELETE;
    const unsigned int keyEND        = VK_END;
    const unsigned int keyPAGEDOWN   = VK_NEXT;
    const unsigned int keyCAPSLOCK   = VK_CAPITAL;
    const unsigned int keyA          = 'A';
    const unsigned int keyS          = 'S';
    const unsigned int keyD          = 'D';
    const unsigned int keyF          = 'F';
    const unsigned int keyG          = 'G';
    const unsigned int keyH          = 'H';
    const unsigned int keyJ          = 'J';
    const unsigned int keyK          = 'K';
    const unsigned int keyL          = 'L';
    const unsigned int keyENTER      = VK_RETURN;
    const unsigned int keySHIFTLEFT  = VK_SHIFT;
    const unsigned int keyZ          = 'Z';
    const unsigned int keyX          = 'X';
    const unsigned int keyC          = 'C';
    const unsigned int keyV          = 'V';
    const unsigned int keyB          = 'B';
    const unsigned int keyN          = 'N';
    const unsigned int keyM          = 'M';
    const unsigned int keySHIFTRIGHT = VK_SHIFT;
    const unsigned int keyARROWUP    = VK_UP;
    const unsigned int keyCTRLLEFT   = VK_CONTROL;
    const unsigned int keyAPPLEFT    = VK_LWIN;
    const unsigned int keyALT        = VK_LMENU;
    const unsigned int keySPACE      = VK_SPACE;
    const unsigned int keyALTGR      = VK_CONTROL;
    const unsigned int keyAPPRIGHT   = VK_RWIN;
    const unsigned int keyMENU       = VK_APPS;
    const unsigned int keyCTRLRIGHT  = VK_CONTROL;
    const unsigned int keyARROWLEFT  = VK_LEFT;
    const unsigned int keyARROWDOWN  = VK_DOWN;
    const unsigned int keyARROWRIGHT = VK_RIGHT;
    const unsigned int keyPAD0       = 0x60;
    const unsigned int keyPAD1       = 0x61;
    const unsigned int keyPAD2       = 0x62;
    const unsigned int keyPAD3       = 0x63;
    const unsigned int keyPAD4       = 0x64;
    const unsigned int keyPAD5       = 0x65;
    const unsigned int keyPAD6       = 0x66;
    const unsigned int keyPAD7       = 0x67;
    const unsigned int keyPAD8       = 0x68;
    const unsigned int keyPAD9       = 0x69;
    const unsigned int keyPADADD     = VK_ADD;
    const unsigned int keyPADSUB     = VK_SUBTRACT;
    const unsigned int keyPADMUL     = VK_MULTIPLY;
    const unsigned int keyPADDIV     = VK_DIVIDE;

#else
    // Define random keycodes when no display is available.
    // (should rarely be used then!).
    const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent)
    const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent)
    const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent)
    const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent)
    const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent)
    const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent)
    const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent)
    const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent)
    const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent)
    const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent)
    const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent)
    const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent)
    const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent)
    const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent)
    const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent)
    const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent)
    const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent)
    const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent)
    const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent)
    const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent)
    const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent)
    const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent)
    const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent)
    const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent)
    const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent)
    const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent)
    const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent)
    const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent)
    const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent)
    const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent)
    const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent)
    const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent)
    const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent)
    const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent)
    const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent)
    const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent)
    const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent)
    const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent)
    const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent)
    const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent)
    const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent)
    const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent)
    const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent)
    const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent)
    const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent)
    const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent)
    const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent)
    const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent)
    const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent)
    const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent)
    const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent)
    const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent)
    const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent)
    const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent)
    const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent)
    const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent)
    const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent)
    const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent)
    const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent)
    const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent)
    const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent)
    const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent)
    const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent)
    const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent)
    const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent)
    const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent)
    const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent)
    const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent)
    const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent)
    const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent)
    const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent)
    const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent)
    const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent)
    const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent)
    const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent)
    const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent)
    const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent)
    const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent)
    const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent)
    const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent)
    const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent)
    const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent)
    const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent)
    const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent)
    const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent)
    const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent)
    const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent)
    const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent)
#endif

These points to all keyboard keys available for use in creation of interactive filter. I don’t recall a page where you can see all available ones.

Also, I note no period or comma there. No '[' or ']' either.

Lazy Arabesques
Related to Post 76. It’s great grandfather is the Roses one liner. And, there is an appearance of epicycles, but written in G’MIC this time, and not Python. Epicycles constitute the grande finale graphic of the upcoming Cookbook, and then there is this:


If I was a decent fellow, I’d post the listing for this, but the listing for this is a mess at the moment. It will be constituted, in some way or sideways, in the Arabesque Cookbook, coming next week in a tutorial set near you. For now, let it be known that the wheelies (epicycles) which draw arabesques live in the alternate reality of the frequency domain, where the analogues drift about in a lazy way. Back here in the time domain, a little z-blurring across frames renders the arabesques ever more languid. That’s it for now. Time to finish this thing. I said ‘next week’ last September, dammit.

3 Likes

Resurgence of the screen saver? At least in the mind of @grosgood… With some tweaks and a randomizer, it has the potential to be a fun G’MIC demo, among the existing ones like the Tower of Hanoi, if you are willing to put it there.

Take your time. There are a lot of TODOs on my side. Mostly optimization and better coding to existing script which is plenty of fun and sometimes boring to do.

For the first time in months and months! playtime with G’MIC. Topic: some illustrations for the upcoming arabesque plotting Cookbook recipe, an exercise in spectral play and using the math parser’s polygon() command. The demo code for all that lives in spectralarabesque.gmic.

First, basic use:

gmic -command spectralarabesque.gmic  \
     sw=512                           \
     -input '{$sw}','{$sw}',1,1       \
     -name. template                  \
     -repeat 360 k='{$>}'             \
         u='{$k/360}'                 \
         v='{2*$u*(1-$u)}'            \
         +gtutor_fwheelie[template] '{0.05+0.45*$v}',90,-1,'{0.125+0.5*$v}','{3*$k}',2,'{0.375*$v}','{-$k}',-5,'{0.25*$v}','{2*$k}',7 \
         -gtutor_specplot.            \
     -done                            \
     -remove[template]                \
     -output basic.mp4,24,H264        \

Next, an excuse to use thin plate splines in radial basis functions:

gmic -command spectralarabesque.gmic  \
     sw=1024                          \
     bi=15                            \
    -input '{$sw}','{$sw}',1,1        \
    -name. template                   \
    -repeat 360 k='{$>}'              \
        u='{$k/360}'                  \
        v='{3*$u*(1-$u)^2}'           \
        w='{3*($u^2)*(1-$u)}'         \
        +gtutor_fwheelie[template] '{0.5*$v}',90,-3,'{0.75*$w}','{3*$k}',2,'{0.5*$v}','{-$k}',-5,'{0.45+0.05*$w}','{2*$k}',-1 \
        -gtutor_specplot.             \
        -name. spiral_'{$k}'          \
    -done                             \
    -remove[template]                 \
    -append z                         \
    -deriche. '{$bi}',1,z,2           \
    -name. swirl                      \
    -normalize[swirl] 0,255           \
    -apply_curve[swirl] 1,0,0,63,40,127,160,200,240,255,255                          \
    -input '(0,63,127,189,255^255,250,220,0,0^127,230,220,50,255^20,80,180,100,255)' \
    -rbf. 255                         \
    -name. palette                    \
    -normalize[palette] 0,255         \
    -map[swirl] [palette],2           \
    -keep[swirl]                      \
    -split[swirl] z                   \
    -resize2dx '{0.5*$sw}',5,1        \
    -output rbfderiche.mp4,24,H264

Finally, let’s torment the cat.

gmic -command spectralarabesque.gmic  \
    bi=80                             \
    sw=512                            \
    -sample cat,'{$sw}'               \
    -name. cat                        \
    -input 100%,100%,1,1              \
    -name. template                   \
    -repeat 360 k='{$>}'              \
        u='{$k/360}'                  \
        v='{3*$u*(1-$u)^2}'           \
        w='{3*($u^2)*(1-$u)}'         \
        +gtutor_fwheelie[template]   '{0.5*$v}',90,1,'{0.5*$w}','{$k}',-2,'{0.5*(1-$v)}','{$k}',5 \
        -gtutor_specplot.             \
        -blur. '{$bi*$w}',1           \
        -resize. '{w#$template}','{h#$template}',100%,100%,5 \
        -normalize. 0,255             \
        +deriche. '{$bi*$w}',1,y,2    \
        -deriche.. '{$bi*$v}',1,x,2   \
        -append[-2,-1] c              \
        -normalize. -1,1              \
        -name. warper                 \
        -mul[warper] '{w#$cat*$w}'    \
        +warp[cat]  [warper],1,2,3    \
        -remove[warper]               \
        -name. warpedcat_'{$k}'       \
    -done                             \
    -remove[cat,template]             \
    -output warpedcat.mp4,24,H264

Here is where gtutor_fwheelie and gtutor_specplot live.

spectralarabesque.gmic
#@cli : gtutor_fwheelie : radius₀,angle₀,angular_velocity₀…
#@cli : Plot a two channel discrete frequency domain image corresponding to
#@cli : the supplied wheelie parameters: r, θ and ±ω triplet(s) on the command
#@cli : line, One triplet for each wheelie. Image suitable as a gtutor_specplot
#@cli : selection, which generates the arabesque.
#@cli : $ 1024,1024,1,1 gtutor_fwheelie. 0.5,67,1,0.25,0,-3 gtutor_specplot. name. circ_segment_triangle

gtutor_fwheelie :
   # Pseudo assignment expands to command line arguments
   $=a

   # Expect data triplets $a1,$a2,$a3…

   -check {!($#%3)}
 
    $=a
    dwheelcnt={$#/3}
    -input $dwheelcnt,1,1,3
    -name. args
    -repeat $# j=$>
       if   $j%3==0
           -set[args] ${a{$j+1}},{int($j/3)},0,0,0
       elif $j%3==1
           -set[args] ${a{$j+1}},{int($j/3)},0,0,1
       else
           -set[args] ${a{$j+1}},{int($j/3)},0,0,2
       fi
    -done
    -store[args] deltawheelies

   foreach {
      # For each selected image:
      # Fetch and check radius
      # velocity parameters. Aggregate Σf -> $accsf; Σθ → $acca
      # specw={k=int(min(w,h)/2);!(k%2)?k+1:k}
      specw={int(min(w,h)/2)}

      #coeffcient image
      -input $specw,1,1,2
      -name carray
      -store[carray] coefficientarray

      # Iterate over arguments; populate carray and aggregate.
   
      -eval "const wc=$dwheelcnt;
             const sw=$specw;
	     dw=get('deltawheelies',wc*3);
	     ca=get('coefficientarray',sw*2);
	     acca=0;
	     accsf=0;
	     repeat(
                      wc,
                      k,
                      r=dw[k];
                      acca+=deg2rad(dw[wc+k]);
                      accsf=dw[2*wc+k]+accsf;
		      aidx=accsf%sw;
                      ca[aidx]+=r*cos(acca);
                      ca[sw+aidx]+=r*sin(acca);
                   );
             store('coefficientarray',ca,sw,1,1,2)"
   
      # Scale freq. dom. by dom. length - for ifft.
      # carray: frequency domain image generated from
      # the given wheelie chain.
      -input $coefficientarray
      -name. carray
      -mul[carray] $specw
      -keep[carray]
   }

#@cli gtutor_specplot : 
#@cli : Generate a phase plot from the selected frequency
#@cli : domain images.

gtutor_specplot :
   -foreach {
        # Frequency domain → time domain
        -name. carray
        -split[carray] c
        -ifft[-2,-1]
        -append[-2,-1] c
        -name. temporal
        sw={w#$temporal}
     
        # Screenspace transform.
        -fill[temporal] ">
                         begin(
                                specw=w#$temporal;
                                id=eye(3);
                                id[0]=specw;
                                id[2]=specw;
                                id[4]=-specw;
                                id[5]=specw;
                              );
                         (id*[I(x,y),1])[0,2];
                       "
        -permute[temporal] cyzx
        -input {2*$sw},{2*$sw},1,1
        -name. canvas
        -eval[canvas] "begin(
                              PV=crop(#$temporal);
                              polygon(
                                       #$canvas,
                                       -int(size(PV)/2),
                                       PV,
                                       1,
                                       0xffffffff,
                                       255
                                     )
                            )"
        -keep[canvas]
     }

Tomorrow looks like a good writing day. Might even push this Cookbook thing…

5 Likes

Garry, that’s beautiful. I couldn’t resist sharing your post on Twitter :slight_smile:

Glad to see you back with new stuffs!

1 Like

@grosgood The warping of the cat is fine but the shaking gives me an eye-/headache. :face_with_spiral_eyes:

It’s an outgrowth of the warp field eventually spanning the entire image, pulling all pixels, and these subject to rapidly changing gradient directions. That seemed in keeping with the feline abuse theme, but — while aesthetically consistent — I myself don’t find it pleasing; I’ve put it in the kit for whenever I need a cheap earthquake effect.

I have another idea for a tutorial, generating Aegean Number as images. To see what it looks like:

Base Symbol:

1234 in Aegean:

You could explore this so many ways.

1 Like

As in the single number: one thousand, two hundred and thirty four, I trust. On my first read, the glyphs came off the screen for me as ‘1,2,3,4’ (𐄇,𐄈,𐄉,𐄊). The mind is a funny place.

What do you have in mind? an exercise in structured drawing using math expression plotters, polygon() and ellipse()?

Perhaps a rough cheat sheet article can state the generality of putting plot point coordinates that fit in an origin-centered unit square in a math vector, an archetype entry in some sort of ‘glyph library’, then, in the specific plotting event, affine-transform the generic coordinates of a glyph library entry to serve the particular positioning of the glyph, the results of that being fed to polygon(). That cheat sheet may draw most of its content from ‘fun with affine transforms,’ to wit: scale, rotate, flip, shear, mirror and the like. However, your: ‘You could explore this so many ways’ suggests an interest in the Minoan/Mycenaean numbering system itself. That could be a fun off-topic jaunt, which I do from time to time in tutorials, but in the main, I don’t see that mapping into an expository on G’MIC technique.

Alas, but I have to throw that on the pile of TODOs. It has been 𐄛𐄗𐄇days since I’ve pushed to gmic-community, and the Arabesque article perpetually seems but 𐄇𐄋days to completion: it is a bit like nuclear fusion — the tutorial of the future! Always has been; always will be.

“I write to discover what I think.”
— Daniel J. Boorstin

There is @David_Tschumperle’s How to write clean G’MIC code ? thread. I went on at length on the topic of code clarity in Post #61: Voynich Manuscript, and will spare you all an encore.

I’ve no quibble with this. G’MIC doodling is a kind of image-processing solitaire with the personal, private aim of seeing what might crop up, and sometimes something good does. I’ve misplaced the link, but I remember a post that @David_Tschumperle made in his personal blog about one of the motivations of designing G’MIC in a particular way: to have a playpen where one can concentrate on the game at hand, with a minimum distraction from the language’s execution requirements: no code-compile iterations, for example. G’MIC has been designed for play.

For me, these private playpen activities spawn tutorials, but whilst there, I do not worry too much about wielding the appurtenances of clarity — careful variable naming, commenting, clean coding; it is not time for that. But I do have clarity in mind, because, for me, G’MIC doodling is very much an exercise in the Boorstin sense: I am trying to clarify what I am thinking. Out of that clarification a tutorial may spring forth, and should that happen, then the game changes: I have come into possession of some discovery that I wish to share. From there on, along that line of an express intent to communicate, the rules of clarity come into play.

2 Likes

Personally, I disagree with this assessment if one goes to math parser approach often like I often do, or if one does $_persistent things which can get really complicated and often involves rewrite of the original code with _persistent taken into account.

Things like P5js are a bit more popular because it meets this requirement better. Where these languages IMO fails is that they’re not as extensible as G’MIC or Python.

I view G’MIC as the only solution to have multiple filters in multiple programs, and the most flexible in content of raster graphics processing as if nothing comes close in flexibility. However, there are things I can’t do in G’MIC like things that requires user-defined functions (I written a bug report wishing for a way to math parser to force evaluation of macro only once or a function), and it has no bigint or ways to have multiple types output. So, sometimes, clever things has to be done to get around these problems.

If you wanted to change that, solutions are possible, but that would mean delving into and extending CImg (and whatever) that powers G’MIC from the background. :thinking:

1 Like

“If you can’t explain it to a six year old, you don’t understand it yourself.” — Albert Einstein

@David_Tschumperle provided this assessment in Image processing made easier with a powerful math expression evaluator.

Alas! David’s personal blog, opensource.graphics, dropped off the Internet sometime between 01-December-2021 and 23-February-2022, when it was first spied in the claws of some shiny-pants domain-name flippers. Thanks be to Brewster Kahle and friends for being on the planet, because the Internet Archive’s Wayback Machine grabbed David’s pages before opensource.graphics slid forever beneath the (digital) ocean waves.

Since a Wayback recall can be dreadfully slow, I’ve excerpted the quotes that, I think, conveys David’s original G’MIC motivations (but don’t trust my interpretations — read the entire post if you can suffer through the download):

To be more precise, an important part of my work is even to design (and hopefully publish) my own image processing methods. Most of the time of course, my trials end up with clunky, ineffective and slow operators which give nothing interesting else than knowing the approach is not good enough to be followed. Someone who says everything he tries works right the first time is a liar. Step by step, I try to refine/optimize my prototypes or sometimes even take a completely different direction. Quickly, you realize that it is crucial in this job not to waste time when doing algorithm prototyping because the rate of success is in fact very low.

That’s actually one of the reason[s] why I’ve started the G’MIC project. It was primarily designed as a helper to create and run custom image processing pipelines quickly (from the shell, basically). It saves me time, everyday.

So whilst you’re in the discovery playpen, a tool that gets you quickly past the junk-thinking is very important indeed.

David further writes that this motivation not to waste time with tool mechanics extends down to the pixel processing scale, the math expression evaluator’s operational level, and that if he wished to have a playpen of ready-to-use pixel processors on par with the utility of macro-level G’MIC commands, then he needed to teach the math expression evaluator how to do looping constructs, thus transporting the math expression evaluator from the realm of elementary expression parsing (a calculator) to something resembling a programming language. But the necessary modifications to CImg.h, where the math expression evaluator lives, seemed technically in reach and the goal was a good one:

Until now, when I was trying to implement this kind of algorithms, I was resigned to go back coding them in C++: It is one language I feel comfortable with, and I’m sure it will run fast enough most of the time. Indeed, computation time is often a bottleneck in image processing. Some of my colleagues are using scripting languages as Matlab or Python for algorithm prototyping. But they often need some tricks to avoid writing explicit code loops, or need to write at least some fast C/C++ modules that will be compiled and run from those higher-level interfaces, to ensure they get something fast enough (even for prototyping, I’m definitely not talking about optimized production code here!)

But, I’m not really satisfied with my C++ solution: Generally, I end up with several small pieces of C++ sources I need to compile and maintain. I can hardly re-use them in a bigger pipeline, or redistribute them as clean packages without a lot of extra work. Because they are just intended to be prototypes: They often have only basic command-line interfaces and thus cannot be directly integrated into bigger and user-friendly image processing frameworks. Making a prototype algorithm really usable by others requires at least to wrap it as a plug-in/module for [ …copy the name of your favorite image processing tool or language here… ]. This generally represents a lot of boring coding work, that may even require more time and efforts than writing the algorithm itself!… I’d definitely prefer a simpler solution that let me spend more time on writing the algorithm itself than packaging it or making it usable. After all, the primary purpose of my work is to create cool algorithms, not really coding user interfaces for them.… [So], how to make those prototyped algorithms finally usable without spending too much time on making them usable?

So, methinks, “to have a playpen where one can concentrate on the game at hand, with a minimum distraction from the language’s execution requirements” is not a far-off assessment of @David_Tschumperle’s gmic motivations. And if you allow me to voice an impression or two on your own oeuvre, I think I can make the case that your choice of the word ‘disagree’ is not entirely apt, considering the words that follow: your game runs orthogonally to that of the ‘G’MIC-as-playpen’ model I advanced. Not at loggerheads, but at right angles, thereby increasing the span of the discussion space. In ways far beyond my game, you canvass where image-processing and -generator artists water and assess the closed-source or undocumented tools they’re (trying) to use, and reverse-engineer these as filters for the gmic_qt plug-in, making functional work-alikes available wherever gmic_qt lives. That is good work, but offers up its frustrations in the realm of how well interpretive languages like gmic map to the computational resources of hardware: so your words following divulge — not disagreement! — but a keen interest in the operational innards of gmic and the math expression evaluator, this with the aim of finding optimal coding technique. That is not entirely athwart with @David_Tschumperle’s ‘G’MIC-as-playpen’ aims, nor is it entirely aligned. David, methinks, is willing to forego some performance in gmic so long as the environmental housekeeping chores remain slight. On the other hand, your appetite for performance remains whetted.

Fortunately, before he fell into the research scientist rabbit-hole, he plied the software engineering trade, and like most worthies of that profession, takes a particular pride in his work, and is not disinclined to better that effort — so long as it does not distract too much from the siren song of research. Be gentle with him, for whatever else there might be in his lot, there is that militant, radical, bomb-throwing-whilst-Einstein-quoting Technical Writer, who has herded onto the Discourse server with all the rest of the maverick cats, and wants people to just be able to read the stuff. Or so he claims. But, should there be a frisson of apprehension coursing through the place, it may be that the Technical Writer could fall over — any minute now! — and start shilling All The Rules of The Software Development Life Cycle, this louder than a NYFD hook-and-ladder caught in the 5 PM rush. ‘David: comment your code!’ ‘DAVID! What the h-e-double-tooth-picks is $=a supposed to mean???’ ‘Daaaa-vid! Your math expressions look like the noise of a dropping comm line!!!’ ‘DAAAVID — *!*’

Fear not. The Technical Writer has mellowed in his age. Somewhat. A little. Perhaps. But don’t nudge him; he may yet fall over and then have Words with you too.

2 Likes

Hah, now this makes me smile (laugh even) :slight_smile:
Interesting stuff in there too, a relief to not lose the pages from that old domain - it had good descriptions of algorithms on it.

@grosgood Welcome back! I saw you pushed to gmic-community.


Those of you who follow the GitHub commit logs will note an upload of tutorial material, the first in many a day, month, year, alas. Some ages ago in Deformation (warped fill) #19 I posted a Python-generated animation of epicycles drawing my face. “This should be done in G’MIC” I thought, and from there a Cookbook series on epicycles started writing itself, more-or-less backwards. I thought it might take a month or two to write; alas — Life intervened; the work proceeded in fits-and-starts and was shelved for long periods. So yes — of course! — one may do epicyclic epics in G’MIC, and the course from first principles to the results at hand is an interesting one. At least, I think so.

On tap, I have a Math Expression article that is to be rooted from the tutorial main page, as mathematical expressions are one of those core areas on par with pipelines and image structure. I am making no predictions about when I might post, but it is rather further along than Arabesques and epicycles were when I first undertook that. Hopefully, the COVID related distractions may be mostly behind me. Fingers crossed.

3 Likes

This is really good news!
So glad to see you back with us @grosgood !

Moreover, your new tutorial pages made me discover a new bug in the G’MIC interpreter, always a good thing to discover some before making a new release :slight_smile:

@grosgood I took the time to read the new tutorial pages, Garry. Just wow! :heart:

Tutorial Upgrades for May 7, 2023. Significant changes:

  • A new index page, with an emphasis on doing finger exercises. The former content has been moved to a new file: Introduction.gmd, which branches off of the index. Still a pleasant read, but it doesn’t get off the dime for new people who just want to start.
  • A new primary-subject page, Math.gmd, to serve as the root of math tutorials. The specific topics branching from this root will come in future tutorial updates. This is a Whitman Sampler introduction to the topic.
  • A new cheat: exec_cheat.gmd, executing subprocesses from G’MIC; indirectly supports the Arabesques Cookbook article.
  • A new cheat: subs_cheat.gmd, G’MIC substitution basics.

On tap: Additions to the Math tutorial sections. Bringing up to current spec a number of 1.6x tutorials. Be nice if I could upload that in that rarest of months, June. Take care!

2 Likes