G'MIC Tutorial Fragments

Or use the fillholes=1 parameter, to stuff the holes with odd-shaped tiles. In that case, the image won’t have an alpha channel. With an alpha channel (and holes) you can use Gimp, G’MIC, or whatever floats your boat to composite the tile image over something that looks like cement, or bricks or whatever.
Making spread larger generates larger holes. Making spread very large gives rise to gaps between tile rows. Play with disrupt, th and spread for a variety of tilings.

Just noticed that lightangle is broken and fixed at 45°. Didn’t map the command line variable to the implementation code - silly mistake. Fix it next commit, today or tomorrow.

gtutor_tileit:
      0<=_disrupt<=10,0<_th,0.5<=_spread,0<=_soft,_fillholes,0<=_lightangle<=180,_basec,_offsc

    Generate a mosaic from a binary black & white image.
    disrupt=5      Twists and offsets tiles. Suggest 0-10.
    th=16          Tile height, absolute pixels. Fixme: Effect changes with image size.
    spread=1.5     Size of gaps between tiles at corners, junctions and runs. Suggest 0-3.
    soft=0.5       Soften and dull shadows and highlights. Suggest 0-10.
    fillholes=1    True:  Fill gaps. No alpha channel.
                   False: Leave gaps transparent; image has alpha channel.
    lightangle=45  Degrees: 0-180. Azimuth from where light streams.
                   45° renders upper left lighting.
                   Provisional coloring. FIXME: sample from input image instead.
    basec=-60      Provisional: base color, degrees. 0° = green
    offsc=90       Provisional: offset color, degrees ± angle from basec 0°: no figure-
                   ground distinction.

OK, thanks for the clarification, waiting to see it available on Gimp-Gmic repository, and extended to act on any posterized image (n colored areas, not only black and white). Great job!

gtutor_tileit has been updated in preparation for an upcoming cookbook article on tiling surfaces, primarily a math expression oriented recipe. Commit 05a01d18575c has the current script. Sun May 16 14:57:19 2021 UTC. It will work its way to the server shortly for people with current gmic cli. No gimp/Krita/8bf plug in yet. Cookbook article first. Commit notes:

modified:   include/garry_osgood.gmic

gtutor_tileit:
1. Removed provisional coloring. Mosaics read color and luminosity from selected images
   See ccount. Parameters basec and offsc have been removed.
2. Generate tilings with or without light/shadow rendering. Use fcolor switch.
   Defaults to False: render light/shadow on tiles.
3. Set tile size relative to image size. relative size 1.0 → 2.5% of the image diagonal.
4. Bug: lightangle not being used. Fixed. Set light angle from -180° to 180° inclusive.

gtutor_tileit help:
Generate a mosaic from an image. Works best with line art cartoons with flat color.
disrupt=5      Rotates, scales and displaces tiles. Suggest 0-10.
tsize=1.0      Tile size relative to image. 1.0 → tile diagonal is 2.5% of the image diagonal.
spread=1.5     Increasing promotes dropped tiles, gaps at junctions and runs. Suggest 0-3.
soft=0.5       Soften and dull shadows and highlights. Suggest 0-5.
fillholes=1    True:  Fill gaps and holes. No alpha channel.
               False: Leave gaps and holes. Image has alpha channel.
lightangle=45° Degrees: -180° – 180°. Lighting angle.
               45°: Light appears to stream from viewer's upper left.
ccount=4       Set number of dominant colors, taken from those most frequently occurring in source.
fcolor=0       True:  Flat color tiling without lighting and shading.
               False: Do tile lighting and shading.

Play files:

  1. ampersand_bluegold.svg. Continues my ongoing obsession(s) with the Goudy 1911 ampersand.
    ampersand_bluegold
  2. pompeiimask.svg. Loosely based on theatre mask mosaics seen in some villas in the Roman city of Pompeii.
    pompeiimask

Some results:

  1. Defaults
    pompeiimask_tiled
    Disruption: 5. Tile size: 1.0. Tile spread: 1.5. Highlight softening: 0.5. Holes: filling. Light angle: 45° Color count: 4 and shaded color.

  2. Smaller tiling and more colors
    pompeiimask_tinytiles
    Disruption: 0.25. Tile size: 0.25. Tile spread: 0.75. Highlight softening: 8. Holes: leaving. Light angle: 90° Color count: 16 and shaded color.

  3. Increasing disruption and shrinking tile size, also blurring to introduce intermediary color tiles. Flat, unshaded color (Light angle, soft: accepted but ignored).
    amp_flatnoisytiles
    Disruption: 5. Tile size: 0.25. Tile spread: 0.5. Highlight softening: 8. Holes: leaving. Light angle: 135° Color count: 32 and unshaded color.

  4. Setting spread to a high value creates beaded effect. Consider compositing such beaded results with other tiled images of the same source, but made with different settings.
    amp_widespread
    Disruption: 0.125. Tile size: 0.25. Tile spread: 2. Highlight softening: 0. Holes: leaving. Light angle: -75° Color count: 32 and shaded color.

gtutor_tileit: 05a01d18575c Sun May 16 14:57:19 2021 UTC

“”"
#@cli gtutor_tileit : 0<=_disrupt<=10,0.05<_tsize,0.25<=_spread,0<=_soft,_fillholes:True,-180<=_lightangle<=180,2<=_ccount<=32,_fcolor:False
#@cli : Generate a mosaic from an image. Works best with line art cartoons with flat color.
#@cli : disrupt=5 Rotates, scales and displaces tiles. Suggest 0-10.
#@cli : tsize=1.0 Tile size relative to image. 1.0 → tile diagonal is 2.5% of the image diagonal.
#@cli : spread=1.5 Increasing promotes dropped tiles, gaps at junctions and runs. Suggest 0-3.
#@cli : soft=0.5 Soften and dull shadows and highlights. Suggest 0-5.
#@cli : fillholes=1 True: Fill gaps and holes. No alpha channel.
#@cli : False: Leave gaps and holes. Image has alpha channel.
#@cli : lightangle=45° Degrees: -180° – 180°. Lighting angle.
#@cli : 45°: Light appears to stream from viewer’s upper left.
#@cli : ccount=4 Set number of dominant colors, taken from those most frequently occuring in source.
#@cli : fcolor=0 True: Flat color tiling without lighting and shading.
#@cli : False: Do tile lighting and shading.
gtutor_tileit : -check “${1=5}>=0 && 1<=10 && {2=1.0}>=0.05 && {3=1.5}>=0.25 && {4=0.5}>=0 && isbool({5=1}) && {6=45}>=-180 && {6}<=180 && {7=4}>=2 && {7}<=32 && isbool({8=0})”
-echo[^-1] "Applying faux tiling to selected images. Disruption: “{1}". Tile size: "{2}”. Tile spread: “{3}". Highlight softening: "{4}”. Holes: "${arg\ 1+!5,filling,leaving}". Light angle: "{6}“° Color count: “{7}" and "{arg\ 1+!$8,unshaded,shaded}” color.”

Parameters

disrupt=$1
tsize=$20.025sqrt(w^2+h^2)
spread=$3
soft=$4
fillholes=$5
lightangle=$6
ccount=$7
fcolor=$8

-repeat ! -local[>]
# Color quantization + black
-remove_opacity.
+colormap. ${ccount},1,2
-index… .,0,1
-round 1
1
-append[-2,-1] x
-name. pallette
-move[pallette] 0
-name. qcolor
+luminance[qcolor]
-name. qlumin
-normalize[qlumin] 0.6,1

  # Outline qcolor
  -input 100%,100%,1,1,'I(#-2,x,y)==I(#-3,0,0)?1:0'
  +deriche. 1,1,x
  -deriche.. 1,1,y
  -sqr[-2,-1]
  -add[-2,-1]
  -fill. 'i>={iM/3.0}?1:0'
  -name[-1] outline

  # Cost: perturbs distance to outline measure, a basis of tile irregularity
  -input 100%,100%,1,1
  -name[-1] cost
  -plasma[cost] 4,4,{$disrupt}
  -normalize[cost] 0,{$disrupt/4.0}

  # Distance to outline + distance perturbation
  +distance[outline] 1,[cost],1
  -name. distvar
  +distance[outline] 1
  +add[-1,-2]
  -remove[-3,-2]

  # True distance: basis for marking off distance by tile height
  [-1]
  -name[-1] truedistance

  # Orient: source of tile rotation
  -name[-2] orienter

  # Make Warper: distorts tiles
  -gradient[cost] xy
  -append[-4,-3] c
  -blur[-3] 1,1,1
  -normalize... {-1.5*$disrupt},{1.5*$disrupt}
  -name... warper

  #  Measure out and scribe tile center-to-center lines
  -round[truedistance] {$spread*$tsize}
  +deriche[truedistance] 1,1,x
  -deriche[truedistance] 1,1,y
  -sqr[-2,-1]
  -add[-2,-1]
  -fill. 'i>{iM/4.5}?1:0'

  # Compute orientation field
  -gradient[orienter] xy
  -append[-3,-2] c
  -orientation[orienter]

  # Plotting field:
  [-1],[-1],1,4,'[-1,-1,-1,0]'
  -name. plottingfield

  # Draw tiles on a plotting field:
  # (1) xfrm: encodes tile translations and rotations
  # for tile corner points. (2) tl, tr, bl, br:
  # tile corners, expressed in homogeneous
  # coordinates.
  -fill[plottingfield] ">if(
                             0<i#-2 && I#-1==[-1,-1,-1,0],
                             xfrm=eye(3);
                             xfrm[0]=I#-3[1];
                             xfrm[1]=I#-3[0];
                             xfrm[2]=x;
                             xfrm[3]=-I#-3[0];
                             xfrm[4]=I#-3[1];
                             xfrm[5]=y;
                             tl=mul(xfrm,[-"$tsize",-"$tsize"/2.0,1],1);
                             tr=mul(xfrm,[ "$tsize",-"$tsize"/2.0,1],1);
                             bl=mul(xfrm,[-"$tsize", "$tsize"/2.0,1],1);
                             br=mul(xfrm,[ "$tsize", "$tsize"/2.0,1],1);
                             polygon(#-1,5,
                                     [tl[0],tl[1]],
                                     [tr[0],tr[1]],
                                     [br[0],br[1]],
                                     [bl[0],bl[1]],
                                     [tl[0],tl[1]],
                                     1,[[I#-7]*i#-6,255]);
                             polygon(#-1,-5,
                                     [tl[0],tl[1]],
                                     [tr[0],tr[1]],
                                     [br[0],br[1]],
                                     [bl[0],bl[1]],
                                     [tl[0],tl[1]],
                                     1,0xffffffff,[0,0,0,255])
                           );I"

  -cut[plottingfield] 0,255

  # Make isovalue targets for lighting height map
  100%,100%,1,1,'I#-1==[0,0,0,255]?1:0'
  -name. heightmap

  # Distort artwork
  -warp[plottingfield,heightmap] [warper],1,2,2

  # Clean up
  -remove[-3--6]
  -split_opacity[plottingfield]
  -index[plottingfield] [pallette],0,1
  -remove[pallette]
  -fill.. 'i>=127?255:0'
  -fill. 'i>0.05?1:0'
  -fill. 'i#-2==0?0:i'
     
  -if $fcolor # Do light and shade?
     -if $fillholes
        # Fill holes in the plotting field
        -fill[plottingfield] 'i#-2==0?I#-5*i#-4:I'
    -remove[-2]
 -else
    -append[-3,-2] c
 -fi
     -keep[plottingfield]
  -else
     # Distance from edges: proxy for
     # tile height. Shape height with
     # power function
     -distance[heightmap] 1
     -normalize[heightmap] 0,1
     -pow. 0.25
     -blur. 0.875,1,1
     -normalize[heightmap] 0,1

     -if {$fillholes==0}
        # Poke holes in the height map
        -fill[heightmap] 'i#-2==0?0:i'
     -else
        # Fill holes in the plotting field
        -fill[plottingfield] 'i#-2==0?I#-5*i#-4:I'
     -fi
     -remove[qcolor,qlumin]

     # Cheap Fingerpaint Lighting
     # Derive shadow, hightlight, color

     -normalize[plottingfield] 0,1

     # Light
     +gradient[heightmap] xy
     -append[-2,-1] c
     ({cos($lightangle*pi/180)}^{sin($lightangle*pi/180)})
     -resize. [-2],[-2],[-1],[-1],1
     +mul[-2,-1]
     -compose_channels. add
     -name. light
     -cut. {ia},{iM}
     -normalize. 0,1
     -pow. 0.5
     if $soft>0
        -blur. {$soft},1,1
     fi

     # Shadow
     -mul.. -1
     -mul[-3,-2]
     -compose_channels.. add
     -name.. shadow
     -cut.. {ia#-2},{iM#-2}
     -normalize.. 0,1
     -oneminus..
     -pow.. 2

     # Composite
     -mul[plottingfield,shadow]
     -mul[light] '{iM#0}'
     -add[plottingfield,light]
     -normalize[plottingfield] 0,255
     -if {$fillholes==0}
        -append[-3,-2] c
     -else
        -remove..
     -fi
     -keep[0]
  -fi # else doing light and shade

-endlocal
-done

“”"

Off to work on the cookbook recipe. Everybody have fun.

3 Likes

Pixl.us G’MIC rumbles - These need tutorial support somewhere

Only for .png and .tiff files.

1 Like

I really like the last image, it could be used for low-reliefs (3d printing, cnc…)

Garden maze design for the wealthy.

1 Like

New and renewed in tutorials, from April 19 to now-ish, May 28, and where I might be spending my time as we slide into summer here on the Eastern Seaboard of North America — and winter, for colleagues in the Antipodes. Tip of the hat to Augusta, Western Australia!

Not as long a list as the last time – alas! – Real Life has kept me preoccupied:

  1. -input was upgraded to 2.9x on May 13 to accommodate its relation to -store
  2. Tiled Art found its way into the Beginner’s Cookbook on May 27. Output of its demonstration command, gtutor_tileit, lurks upstream from this post. Tiled Art is specifically a structured drawing introduction, via the math expression function polygon(), its behavior guided by findings from an everyday two-channel vector map. It does not take a great deal of imagination to carry these ideas forward for other forays into structured drawing. To draw a small serif on this project, I plan to wrap the demonstration command gtutor_tileit so that it can live inside of gmic-qt. Perhaps this weekend.

Looking into June, I am alarmed that the 1.6x tutorials on oft-used control flow commands are more than slightly radioactive; these draw my attention first. Visiting -distance and -gradient while putting together Tiled Art reminds me that they too could use some love. In the waiting room, patiently tapping their toes, collaborations with @Reptorian and @myselfhimself. In a post to Can G’MIC do those kind of textures? @David_Tschumperle goes and throws out some Radial Basis Function catnip. Ooooooo… fun there. Be nice to have at that so that I am not entirely refurbishing old tutorials whilst these rare June days pass me by…

2 Likes

I really like how you write @grosgood !
On my side I am applying to a 3d animation / video game school, first selection deadline is June 4th. Then will come oral and written exams.
My chocolate exams are 90% done (taken but not clearly passed yet… July will speak).

I have no idea what ‘chocolate exams’ are. I think I would enjoy taking them. That is, until my aorta clogs. That would probably, likely, put a crease in my tutorial writing, I think, among other things.

In general, I congratulate you in seizing life by the horns and riding the beast wherever it takes you. Don’t ever stop doing that, from time to time.

I still owe you some writing. Real Soon Now (I think).

1 Like

I’m done with gtutor_tileit for the time being, and it is probably done with me. For those interested in the full script — instead of the syncopated version in Tiled Art — find it below, from commit 9153a5c9ec3d, Saturday, May 29 16:45:28 2021 UTC. There are some text buglets in the tutorial proper, along with some passages that could be phrased more aptly. Those, and perhaps some less-performant aspects of the script itself, will be dealt with when they annoy me enough, I think in a week’s time. For now, I’m working my way through my end-of-month remit, noted above.

gtutor_tileit — 9153a5c9ec3d: Sat May 29 12:45:28 2021 -0400
#@gui __
#@gui <b>Testing</b>
#@gui <i>Gmic Tutorials</i>
#@gui Rectangular Tiling : _rec_tileit, gtutor_rectangular_tiling_preview
#@gui : documentation  = note("Generate a mosaic from an image. Works best with line art cartoons with flat color.")
#@gui : sep                 = separator()
#@gui : Tile Size           = float(1.0,0.05,5.0)
#@gui : Disrupt Orientation = float(5.0,0.0,10.0)
#@gui : Spread Tiles Apart  = float(1.5,0.25,5.0)
#@gui : Fill Holes          = bool(1)
#@gui : Color Count         = int(4,2,32)
#@gui : Flat Color          = bool(0)
#@gui : Light Angle         = float(45.0,-180,180)
#@gui : Soften Lighting     = float(0.5,0.0,5.0)
#@gui : note           = note(<small>Author: <i>Garry R. Osgood</i>. Latest Update: <i>2021/29/05</i></small>)
#@gui : url            = link("Technical Notes",https://gmic.eu/tutorial/tiled_art.html)

gtutor_rectangular_tiling_preview :
    gui_split_preview "gtutor_tileit ${2},${1},${3},${8},${4},${7},${5},${6}",0
    is_fc={2*(1-$6)}
    u "{$1}{$2}{$3}{$4}{$5}{$6}"\
      "{$7}_"$is_fc\
      "{$8}_"$is_fc
_rec_tileit:
    gtutor_tileit ${2},${1},${3},${8},${4},${7},${5},${6}

#@cli gtutor_tileit : 0<=_disrupt<=10,0.05<_tsize,0.25<=_spread,0<=_soft,_fillholes:True,-180<=_lightangle<=180,2<=_ccount<=32,_fcolor:False
#@cli : Generate a mosaic from an image. Works best with line art cartoons with flat color. 
#@cli : disrupt=5      Rotates, scales and displaces tiles. Suggest 0-10.
#@cli : tsize=1.0      Tile size relative to image. 1.0 → tile diagonal is 2.5% of the image diagonal.
#@cli : spread=1.5     Increasing promotes dropped tiles, gaps at junctions and runs. Suggest 0-3.
#@cli : soft=0.5       Soften and dull shadows and highlights. Suggest 0-5.
#@cli : fillholes=1    True:  Fill gaps and holes. No alpha channel.
#@cli :                False: Leave gaps and holes. Image has alpha channel.
#@cli : lightangle=45° Degrees: -180° – 180°. Lighting angle.
#@cli :                45°: Light appears to stream from viewer's upper left.
#@cli : ccount=4       Set number of dominant colors, taken from those most frequently occuring in source.
#@cli : fcolor=0       True:  Flat color tiling without lighting and shading.
#@cli :                False: Do tile lighting and shading.
gtutor_tileit : -check "${1=5}>=0 && $1<=10 && ${2=1.0}>=0.05 && ${3=1.5}>=0.25 && ${4=0.5}>=0 && isbool(${5=1}) && ${6=45}>=-180 && ${6}<=180 && ${7=4}>=2 && ${7}<=32 && isbool(${8=0})"
-echo[^-1] "Applying faux tiling to selected images. Disruption: "${1}". Tile size: "${2}". Tile spread: "${3}". Highlight softening: "${4}". Holes: "${arg\ 1+!$5,filling,leaving}". Light angle: "${6}"°. Color count: "${7}", and "${arg\ 1+!$8,unshaded,shaded}" color."
# Parameters

disrupt=$1
tsize=$2*0.025*sqrt(w^2+h^2)
spread=$3
soft=$4
fillholes=$5
lightangle=$6	
ccount=$7
fcolor=$8

-repeat $!
   -local[$>]
      # Color quantization + black
      -remove_opacity.
      +colormap. ${ccount},1,2
      -index.. .,0,1
      -round 1
      1
      -append[-2,-1] x
      -name. pallette
      -move[pallette] 0
      -name. qcolor
      +luminance[qcolor]
      -name. qlumin
      -normalize[qlumin] 0.6,1

      # Outline qcolor
      -input 100%,100%,1,1,'I(#-2,x,y)==I(#-3,0,0)?1:0'
      -fill. ">abs(
                   i-j(1,1,0,0,0,1)
                  )>iv?
                 1:
                 abs(
                       i-j(1,0,0,0,0,1)
                    )>iv?
                    1:0"
      -name[-1] outline

      # Cost: perturbs distance to outline measure, a basis of tile irregularity
      -input 100%,100%,1,1
      -name[-1] cost
      -plasma[cost] 4,4,{$disrupt}
      -normalize[cost] 0,{$disrupt/4.0}

      # Distance to outline + distance perturbation
      +distance[outline] 1,[cost],1
      -name. distvar
      +distance[outline] 1
      +add[-1,-2]
      -remove[-3,-2]

      # True distance: basis for marking off distance by tile height
      [-1]
      -name[-1] truedistance

      # Orient: source of tile rotation
      -name[-2] orienter

      # Make Warper: distorts tiles
      -gradient[cost] xy
      -append[-4,-3] c
      -blur[-3] 1,1,1
      -normalize... {-1.5*$disrupt},{1.5*$disrupt}
      -name... warper

      #  Measure out and scribe tile center-to-center lines
      -round[truedistance] {$spread*$tsize}
      -fill[truedistance] ">
                          abs(i-j(1,1,0,0,0,1))>"{0.5*$spread*$tsize}"?
                          1:
                            abs(i-j(1,0,0,0,0,1))>"{0.5*$spread*$tsize}"?
                            1:0
                          "

      # Compute orientation field
      -gradient[orienter] xy
      -append[-3,-2] c
      -orientation[orienter]

      # Plotting field:
      [-1],[-1],1,4,'[-1,-1,-1,0]'
      -name. plottingfield

      # Draw tiles on a plotting field:
      # (1) xfrm: encodes tile translations and rotations
      # for tile corner points. (2) tl, tr, bl, br:
      # tile corners, expressed in homogeneous
      # coordinates.
      -fill[plottingfield] ">if(
                                 0<i#-2 && I#-1==[-1,-1,-1,0],
                                 xfrm=eye(3);
                                 xfrm[0]=I#-3[1];
                                 xfrm[1]=I#-3[0];
                                 xfrm[2]=x;
                                 xfrm[3]=-I#-3[0];
                                 xfrm[4]=I#-3[1];
                                 xfrm[5]=y;
                                 tl=xfrm*[-"$tsize", "$tsize"/2.0,1];
                                 tr=xfrm*[ "$tsize", "$tsize"/2.0,1];
                                 bl=xfrm*[-"$tsize",-"$tsize"/2.0,1];
                                 br=xfrm*[ "$tsize",-"$tsize"/2.0,1];
                                 polygon(#-1,5,
                                         [tl[0],tl[1]],
                                         [tr[0],tr[1]],
                                         [br[0],br[1]],
                                         [bl[0],bl[1]],
                                         [tl[0],tl[1]],
                                         1,[[I#-7]*i#-6,255]);
                                 polygon(#-1,-5,
                                         [tl[0],tl[1]],
                                         [tr[0],tr[1]],
                                         [br[0],br[1]],
                                         [bl[0],bl[1]],
                                         [tl[0],tl[1]],
                                         1,0xffffffff,[0,0,0,255]);
                               I,I)"

      -cut[plottingfield] 0,255

      # Make isovalue targets for lighting height map
      100%,100%,1,1,'I#-1==[0,0,0,255]?1:0'
      -name. heightmap

      # Distort artwork
      -warp[plottingfield,heightmap] [warper],1,2,2

      # Clean up
      -remove[-3--6]
      -split_opacity[plottingfield]
      -index[plottingfield] [pallette],0,1
      -remove[pallette]
      -fill.. 'i>=127?255:0'
      -fill. 'i>0.05?1:0'
      -fill. 'i#-2==0?0:i'
         
      -if $fcolor # Do light and shade?
         -if $fillholes
            # Fill holes in the plotting field
            -fill[plottingfield] 'i#-2==0?I#-5*i#-4:I'
	    -remove[-2]
	 -else
 	    -append[-3,-2] c
  	 -fi
         -keep[plottingfield]
      -else
         # Distance from edges: proxy for
         # tile height. Shape height with
         # power function
         -distance[heightmap] 1
         -normalize[heightmap] 0,1
         -pow. 0.25
         -blur. 0.875,1,1
         -normalize[heightmap] 0,1
   
         -if {$fillholes==0}
            # Poke holes in the height map
            -fill[heightmap] 'i#-2==0?0:i'
         -else
            # Fill holes in the plotting field
            -fill[plottingfield] 'i#-2==0?I#-5*i#-4:I'
         -fi
         -remove[qcolor,qlumin]
   
         # Cheap Fingerpaint Lighting
         # Derive shadow, hightlight, color
   
         -normalize[plottingfield] 0,1

         # Light
         +gradient[heightmap] xy
         -append[-2,-1] c
         ({cos($lightangle*pi/180)}^{sin($lightangle*pi/180)})
         -resize. [-2],[-2],[-1],[-1],1
         +mul[-2,-1]
         -compose_channels. add
         -name. light
         -cut. {ia},{iM}
         -normalize. 0,1
         -pow. 0.5
         if $soft>0
            -blur. {$soft},1,1
         fi

         # Shadow
         -mul.. -1
         -mul[-3,-2]
         -compose_channels.. add
         -name.. shadow
         -cut.. {ia#-2},{iM#-2}
         -normalize.. 0,1
         -oneminus..
         -pow.. 2

         # Composite
         -mul[plottingfield,shadow]
         -mul[light] '{iM#0}'
         -add[plottingfield,light]
         -normalize[plottingfield] 0,255
         -if {$fillholes==0}
            -append[-3,-2] c
         -else
            -remove..
         -fi
         -keep[0]
      -fi # else doing light and shade
   -endlocal
-done

Radial Basis Function tutorial Real Soon Now. Going through the push check list…

Ellipseflow
orientedellipses:
    -srand 5823619

    # Make a field of orientation angles through RBF interpolation
    6,1,1,4,{[u(2047),u(2047),u(2047),u(2047),u(2047),u(2047),\
              u(2047),u(2047),u(2047),u(2047),u(2047),u(2047),\
              u(-1,1),u(-1,1),u(-1,1),u(-1,1),u(-1,1),u(-1,1),\
              u(-1,1),u(-1,1),u(-1,1),u(-1,1),u(-1,1),u(-1,1)]}
    -rbf. 2048,2048 
    -orientation
    -name. orient

    # Markers to place ellipses
    -input 100%,100%,1,1
    -noise_poissondisk. 20
    -name. markers

    # Plotting field. Give it a shadow
    # of the orientation field
    -input 100%,100%,1,3,'[90,30,40]'
    -name. plottingfield
    -fill[plottingfield]
       ">begin(wv=vector3(1/sqrt(3))); 
        rot(wv,atan2(i(#-3,x,y,0,1),i(#-3,x,y,0,0)))*I"
    
    # Plot the ellipses themselves, aligned
    # with the orientation field.
    -fill[plottingfield]
    ">if(
       i#-2>0,
       ellipse(#-1,x,y,12,18,atan2(i(#-3,x,y,0,1),i(#-3,x,y,0,0)),0.5,[63,127,255]);
       ellipse(#-1,x,y,-12,-18,atan2(i(#-3,x,y,0,1),i(#-3,x,y,0,0)),1,0xffffffff,[255,255,0]);
       I,I
        )
    "
    -keep.
    -r2dx. 50%,5
    -normalize. 0,255
1 Like

I haven’t done flowfield yet, but that is admittedly not so explored as in what one can see in P5js, openprocessing community. Definitely will look forward to tutorials on flowfield in gmic. I had some ideas on how to do flowfield in mind like emulating an area of liquid, and then letting the image dictate the direction of the flow.

On that note, imaginary-number low-level commands are not that explored either. I’m actually translating this into a new filter named Complex Popcorn Fractal - https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.258.6666&rep=rep1&type=pdf .

Could be “Ellipseflow” is click-bait: Such infâme was not among my aims; just a name I pulled up — without much thought on how it would be read. But there are no dynamics in play here, flow or otherwise. Just ellipses turned to orient themselves in the direction of the local gradient, that from from an orientation field conjured from the aether by -rbf, working from a mere handful of random two-channel points: that conjure, of course, is in keeping with the aims of the tutorial.

That said, dynamics are not far to be found: at least playful dynamics, the kind not keyed to any physical phenomena like fluid simulations. Two timing loops suggest themselves, each operating at different scales but otherwise grounded on numbers drawn from randomness.

  1. Small-scale: Inner loop. On the tick, markers take a pixel-and-a-fragment step at their current orientation and then resample the orientation field at the new location. Redraw the ellipse at the new orientation. Next tick. And so on. And so forth… This gives rise to a scurry of ellipses barreling over the orientation field.
  2. Large-scale: After some tens-of-ticks, the outer loop wakes up for a step and subjects the handful of keypoints that -rbf consumes to a Brownian motion kick. -rbf updates the orientation field which shifts slightly — or maybe, in some places, not so slightly. The thin plate spline could snap from time-to-time. In any case, the orientation field upon which the markers are grounded exhibits a local earthquake, usually minor — sometimes major. Drop back into the inner loop and the markers do their reorientation dance on somewhat altered ground.

All the field generation and rendering bits are in place. Need vectors to track the markers and the random samples underlying the orientation field. Add those structures, the two timing loops and we have, methinks, a Beginner’s Cookbook topic of some worth, duly registered here and to be completed when I get a round tuit. The only ones I’ve been finding of late are square, and have fur all over them…


“I juni måned” (In the Month of June) Laurits Andersen Ring

James Russell Lowell’s paean “And what is so rare as a day in June?” reveals this poet, literary critic and Atlantic editor, as a native of the upper latitudes of North America, for June on this part of the seaboard, my home, takes one’s breath away. Lowell spent most his life here. In the latter decades of the nineteenth century, he circulated almost exclusively in the environs between New York City and Boston.

Here above the horse latitudes, on the American eastern seaboard, these rare June days of crystalline air and low dew points straddle the summer solstice, and soon give way to a conveyor belt of Gulf Stream sogginess that persists, with intermittent relief, for the balance of the summer, propelled by an anti-cyclonic high pressure system known formally the North Atlantic (Subtropical) Anticyclone, and informally as the Bermuda High. I hope that it leads to pleasantness in Bermuda; its effects are imperfectly welcome here.

June sped by quickly, but a few tutorial pieces came to the fore. The effort to bring antique program control tutorials to a 2.9x setting has come to past:

  1. The “iffis” lead the way on May 31:-if…-fi…-elif…-else…-endif. I think the tutorial was one of the earliest written of the 1.6x series (2013) and all of the old examples utilized file or directory existence conditions, a scheme no longer supported after 2.6.; it was this that set off radioactivity alarms in the control flow realm.
  2. -repeat…-done was pulled into th 2.9x realm on Sunday June 6 and was largely re-written. The 1.6x examples were no longer workable, so the example was re-tooled.
  3. -local…-endlocal was similarly dragged into the 2.9x on June 12, much of it also re-written from scratch. The old tutorial had no working examples and was not complete; methinks it was published before it was finished. The most missing discussion was, to my mind, what happens when G’MIC merges local and global numeration.
  4. -rbf a new tutorial on Radial Basis Functions, rounds out June. Longish, but somehow just scratching the surface. How could I have left out generating warping vector fields on-the-fly, à la Interactive deformation and morphing. Well, -warp_rbf awaits in the wings for a future tutorial play, as does morph_rbf, the animated version… a great deal in store for extrapolators along a curve.

July, a steamy month in this locale, threatens anyone’s desire to Get Things Done: It’s Too Darn Hot. That said, there are still many basic, fundamental control flow commands that have never had benefit of any tutorial. Expect work along that course.

Along the lines of addressing the extreme basics, I have been given much thought to how rank beginners get through the very basic learning curve of the G’MIC command tool. Part of this is motivated by the collaboration with @myselfhimself in conveying to pythonistas what to feed the run() command. Part of this is also knowing that those who get initial success in obtaining what they set out to do have a rewarding sense of accomplishment — and so they set out to do more. Conversely, those who hit brick walls are just motivated to use something else. The game is to improve the success rate on those very early, rank-beginner explorations, but I don’t wish to duck the issue by presenting a portfolio of easy commands, as done here as they bypass the heart of the matter. Rather, I want to get to the heart of the matter, which is — I think — coping with the broad (and difficult) image manipulation freedom that the command line tool offers. It overwhelms. So I have been going though many old discuss.pixl.us and gimpchat.com posts in a forensic frame of mind, wondering how best to ease people into this. Not sure where something like this will wind up in the tutorial tree. Stay tuned.

2 Likes

Hi Gary,
that was pure pleasure to read your intro of new tutorial fragments.

I think I have to learn better English and to look into Lowell. Still I had recently some similar experience reading “Levines Mühle” from Johannes Bobrowski in German. Possibly, you can manage that. J. B. was a baltic Lithuanian German in the last century, my parents generation.

@grosgood : Now I have another idea for you. I have made a sample code that actually performs the same operation on multiple threads at once, and this beats apply_tiles, apply_parallel as you’re not restricted to a image, and you can do things like dynamic array, and insert into a image.

Reptorian's Sample Code which allows a very much non-standard multithreaded operation
rep_new_dla:

n_threads={$_cpus}
mt={$n_threads-1}

l. 
 $n_threads,1,1,1
 +s. x
 r[^0] 100%,100%,100%,2
 store[^0] dla_ref
endl

length_const_line=""
xp_line=""

length_const_line.=begin(

repeat $n_threads
 
 insert_dar_pos={$>+2}
 insert_ref_pos={2+$n_threads+$>}

 length_const_line.="const v"
 length_const_line.=$>=
 length_const_line.=h#$insert_ref_pos
 length_const_line.=;
 
 xp_line.=x==
 xp_line.=$>
 xp_line.=?(
 xp_line.=repeat(v$>,refpos,
 xp_line.=dar_insert(#$insert_dar_pos,I(#$insert_ref_pos,0,refpos));
 xp_line.=);
 xp_line.=)
 
 if ($>!=$mt) 
  xp_line.=:
 else
  xp_line.=;
 fi
 
done

length_const_line.=);

echo $length_const_line
echo $xp_line

set 1,50%,50%,0

repeat $! l[$>]
 +rep_noise_poissondisk_to_coordinates 8
 l[0] $dla_ref endl
 s. y,$n_threads
 eval[1] :${-math_lib}$length_const_line$xp_line
endl done

The reason this works is that first, you generate the string which is dependent the number of threads that you have. And then, it will compile based on the string given.

This is the generated 12-threads code

Code
begin(
 const v0=h#14;
 const v1=h#15;
 const v2=h#16;
 const v3=h#17;
 const v4=h#18;
 const v5=h#19;
 const v6=h#20;
 const v7=h#21;
 const v8=h#22;
 const v9=h#23;
 const v10=h#24;
 const v11=h#25;
);
x==0?(
 repeat(v0,refpos,
  dar_insert(#2,I(#14,0,refpos));
 );
):x==1?(
 repeat(v1,refpos,
  dar_insert(#3,I(#15,0,refpos));
 );
):x==2?(
 repeat(v2,refpos,
  dar_insert(#4,I(#16,0,refpos));
 );
):x==3?(
 repeat(v3,refpos,
  dar_insert(#5,I(#17,0,refpos));
 );
):x==4?(
 repeat(v4,refpos,
  dar_insert(#6,I(#18,0,refpos));
 );
):x==5?(
 repeat(v5,refpos,
  dar_insert(#7,I(#19,0,refpos));
 );
):x==6?(
 repeat(v6,refpos,
  dar_insert(#8,I(#20,0,refpos));
 );)
:x==7?(
 repeat(v7,refpos,
  dar_insert(#9,I(#21,0,refpos));
 );
):x==8?(
 repeat(v8,refpos,
  dar_insert(#10,I(#22,0,refpos));
 );
):x==9?(
 repeat(v9,refpos,
  dar_insert(#11,I(#23,0,refpos));
 );
):x==10?(
 repeat(v10,refpos,
  dar_insert(#12,I(#24,0,refpos));
 );)
:x==11?(
 repeat(v11,refpos,
  dar_insert(#13,I(#25,0,refpos));
 );
);
1 Like

Some more awesome stuffs by @grosgood ! :beers: :+1:

https://gmic.eu/tutorial/dowhile.html

do_elecamp

Busy boxes presage a tutorial:

busybox

gmic -m busybox.gmic -busyboxes 512,0.008,98,-0.75,20 

This on the -for-done conditional — I believe such a construct is in the busybox script somewhere… I am sure of it!

busybox.gmic
busyboxes: check "isint(${1=128}) && ${1}>=128 && \ # Square image sz, pixels
                  isnum(${2=0.02})             && \ # Seeding
                  0.001<${2}&&${2}<=0.5        && \ # • Larger → more boxes
                  isnum(${3=96.5})             && \ # Limit spread
                  0<${3}&&${3}<100             && \ # • Smaller → more limited
                  isnum(${4=0})                && \ # Color
                  -1<=${4}&&${4}<=1            && \ # • Pink(-1) to Green(1)
                  isnum(${5=20})  && ${5} >0"       # Sharpness: Larger → +edginess
    $=busybx_
    lbound={exp(($busybx_3/100)-1)}
    ccolor={0.5*pi*$busybx_4}
    # Render image
    -input $busybx_1,$busybx_1,1,3
    -name. boxplots
    -noise $busybx_2,2
    -abs.
    # Convolution kernels
    -input 3,3,1,1,-0.125
    -set. 1,1,1
    -name. neg
    -input 3,3,1,1,0.125
    -set. 1,1,1
    -name. pos
    cnt=0
    # Convolve and spread
    -for abs(im#-3)<=$lbound&&$cnt<1000
        -if $cnt%2
           -convolve[boxplots] [pos],2,1
        -else
           -convolve[boxplots] [neg],2,1
        -fi
        cnt+=1
    -done
    -keep...
    # Colorize
    +luminance.
    -normalize 0,1
    -fill[boxplots] ">begin(rv=[1.0,0.23,0.1157]);
                      rot(rv,($ccolor+pi*(2*i(#-1)-1)))*I"
    -keep[boxplots]
    -sharpen $busybx_5
    -normalize[boxplots] 0,255
1 Like

@grosgood

Two three things.

  1. You don’t need to use ‘-’. If you note my commands, I never use - next to their commands.

  2. input isn’t necessary. Just 3,3,1,1,-.125 would do for example.

Also I find this interesting:

$=busybx_
    lbound={exp(($busybx_3/100)-1)}

What was the point of $=…? I only use it to access a specified argument. For example:

#@cli rep_mn: eq. to 'rep_multinormalize' : (+)
rep_mn: rep_multinormalize $*
#@cli rep_multinormalize: values
#@cli : Normalize based on channels using values.
#@cli : (eq. to 'rep_mn').\n
#@cli : Author: Reptorian.
rep_multinormalize:
repeat $! l[$>]
 repeat s
  $=a
  $=b
  val_a=${a{$>*2+1}}
  val_b=${b{$>*2+2}}
  sh $>
  normalize. $val_a,$val_b
  rm.
 done
endl done
1 Like