G'MIC Tutorial Fragments

Following -eval, I gather that onegraypixel's dynamic allocation growth is (at least) to the nearest even float32 increment, and that the 0x0a value at position 1,15 in the image list representation is a ‘behind-the-curtains’ marker that (on the MEP side) indicates a vector10[…]. I also just gathered that dar_insert(…) has two argument signatures: dar_insert(ind,elt,pos) and dar_insert(ind,elt) and I was using the latter in -foo ( gmic_stdlib.gmic ):

25730:   # Inserts new element 'elt' at the end of dynamic array #ind.
25731:  dar_insert(ind,elt) = (
25732:    _dar_siz = dar_size(#ind);
25733:    _dar_siz>=h(#ind) - 1?resize(#ind,1,_dar_siz*2 + 2,1,s(#ind),0);
25734:    unref(_dar_elt); _dar_elt = elt;
25735:    copy(i[#ind,_dar_siz],_dar_elt,max(1,size(_dar_elt)),h(#ind),1);
25736:    (dar_size(ind#)) = ++_dar_siz;
25737:  );

I gather that the resize(…) call on 25733: dictates the ‘grow-by-row’ character of the change in image size of onegraypixel.

It was with this other flavor of resize(A, size, _interpolation, _boundary_conditions) that I became aware that parameters like size must be, at compile time, traceable back to a constant positive integer, else the JIT compiler is left at sea, and that started my ruminations on this Tutorial fragment (Fragmentary? indeed it is. Gentle Reader: “Tutorial Fragments” are just that, posted here so that people like @Reptorian, @David_Tschumperle and others can drop thought bombs on them. And so that I can figure out, eventually, what it is I’m supposed to write. Don’t come here expecting coherency…)

Another tutorial idea though brief. Assigning a random value as a const in the JIT compiler. Courtesy of David.

This one doesn’t work:

rep_rand_to_const:
f "begin(const rv=u(0,1););rv;"

However, this does work:

rep_rand_to_const:
rv={u(0,1)}
f "begin(const rv="$rv";);rv;"

Basically, this bypass issue.

Also, things like rot(u,v,w,angle) can also be express with rot_x, and rot_y, and these are what I use in place of rot(u,v,w,angle).

1 Like

Explain. Are these commands, macros, or…?

Macros. rot_x(a,b)=… or rot_x(a,b,c)=… The latter is used when you want non-constant rotation while the former is for constant rotation.

Thanks for the example!

In the second, working, case, command line parsing has the opportunity to substitute "$rv" prior to the invocation of the math expression parser (with JIT == ‘Just In Time’ mode…). JIT never ‘sees’ the "$rv" construct, but instead an explicit numeric string, the contents of command line variable "$rv". So, from the point of view of the math expression parser, that explicit numeric string, substituted from "$rv" before the JIT was invoked, qualifies as a constant scalar because it is a specific numeric quantity known at the outset of the compilation of the math expression.

In the first, non-working case, the math expression parser cannot possibly get a hint at compile time what the value of rv is going to be, since u(0,1) can’t possibly run until after compilation. This violates the terms of the const qualifier.

From a tutorial-writing perspective, the key question for the student is: ‘Will what I’m writing appear to the math expression parser as an explicit, numeric string at the time of compilation’?
If they are writing explicit strings like ‘3.14159’, the answer is ‘yes.’ If they are writing $foo, it is best to check what $foo represents: a simple numeric string (the answer would be ‘yes’) or an image storage variable (the answer would be ‘no’).

Cookbook on making mosaics in the offing.

gmic -input ampersand.svg r2dx. 50% -gtutor_tileit. 10,50,2,0,0,60,0,40 -output. ampersand_tiled.png should be available for play on your next update.

Here is the play file:
ampersand

Main topics:

  1. drawing with the math expression parser,
  2. oriented tiling
  3. further and continued misuse of structure tensors.

Motivating discussion at Deformation (warped fill)
Current implementation (gmic-community @537d948f6fc2 Sat May 8 11:04:21 2021 -0400 (EDT)

gtutor_tileit
#@cli gtutor_tileit : 0<=_disrupt<=10,0<_th,0.5<=_spread,0<=_soft,_fillholes,0<=_lightangle<=180,_basec,_offsc
#@cli : Generate a mosaic from a binary black & white image.
#@cli : disrupt=5      Twists and offsets tiles. Suggest 0-10.
#@cli : th=16          Tile height, absolute pixels. Fixme: Effect changes with image size. 
#@cli : spread=1.5     Size of gaps between tiles at corners, junctions and runs. Suggest 0-3.
#@cli : soft=0.5       Soften and dull shadows and highlights. Suggest 0-10.
#@cli : fillholes=1    True:  Fill gaps. No alpha channel.
#@cli :                False: Leave gaps transparent; image has alpha channel.
#@cli : lightangle=45  Degrees: 0-180. Azimuth from where light streams.
#@cli :                45° renders upper left lighting.
#@cli :	               Provisional coloring. FIXME: sample from input image instead.
#@cli : basec=-60      Provisional: base color, degrees. 0° = green
#@cli : offsc=90       Provisional: offset color, degrees ± angle from basec 0°: no figure-
#@cli :                ground distinction.
gtutor_tileit : -check "${1=5}>=0 && $1<=10 && ${2=16}>0 && ${3=1.5}>=0.5 && ${4=0.5}>=0 && isbool(${5=1}) && ${6=45}>=0 && $6<=180" -skip ${7=0},${8=90}

# Selected: black/white binary value shapes. To do: simple color field input images instead
# Parameters

-verbose -
disrupt=$1
th=$2
spread=$3
soft=$4
fillholes=$5   
lightangle=$6	               
basec=$7
offsc=$8

# Outline shapes
+deriche. 1,1,x
+deriche.. 1,1,y
-sqr[^0]
-add[^0]
-fill. 'i>3?1:0'
-channels. 0
-name. outline

# Cost: perturbs distance to outline measure, a basis of tile irregularity
[-1],[-1],1,1
-name. cost
-noise_perlin[cost] {10*$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[distvar]

# True distance: basis for marking off distance by tile height
-name. truedistance

# Orient: source of tile rotation
-name.. orienter
-channels[^1] 0

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

#  Measure out and scribe tile center-to-center lines
-round[truedistance] {$spread*$th}
+deriche. 1,1,x
-deriche.. 1,1,y
-sqr[-2,-1]
-add[-2,-1]
-fill. 'i>0.5?1:0'
-channels[truedistance] 0
 
# Compute structure tensors
# Make an inpaint patch to
# fill in uncertain ridge values
-structuretensors[orienter] 0
+norm[orienter]
-name. patcher
-fill[patcher] 'i<=2?1:0'

# Patch structure tensors
-inpaint[orienter] [patcher],0,1
-remove[patcher]

# Extract orientation angles
# from structure tensors
-eigen[orienter]

# Plotting field:
[-1],[-1],1,2,'[-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,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,[-"$th",-"$th"/2.0,1],1);
                           tr=mul(xfrm,[ "$th",-"$th"/2.0,1],1);
                           bl=mul(xfrm,[-"$th", "$th"/2.0,1],1);
                           br=mul(xfrm,[ "$th", "$th"/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,[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,0xffffffff,[1,1])
                         );I"
# Distort artwork
-warp[0,outline,plottingfield] [warper],1,2,2
-if {$fillholes==0}
   -shared. 0
   -round. 1
   -rm.
-else
   -remove_opacity.
   -round. 1
-fi
-dilate_oct. 2

# Clean up z-axis data shifts
-cut[0] 0,255
-threshold[outline] 50%
-shared[plottingfield] 0
-fill. 'i#1>0?1:i#-1'

# Make height map for lighting. 
# Distance from edges: proxy for
# tile height. Shape height with
# power function
-distance. 1
-normalize. 0,1
-pow. 0.25
-rm.

# Drop used data sets,
# Normalize for lighting
-remove[^0,-1]
-normalize[0,-1] 0,255
-if {s#-1>1}
   -split_opacity[-1]
   -fill. '[i<127?0:255]'
   -move. 0
-fi
 
# Cheap Fingerpaint Lighting
# Derive shadow, hightlight, color
# Fixme: provisional coloring. Ought to
# be sampled from input image.
-blur. {$soft},1,1
+gradient. xy
-append[-2,-1] c
({cos(45*pi/180)}^{sin(45*pi/180)})
-resize. [-2],[-2],[-1],[-1],1
+mul[-2,-1]
-compose_channels. add

# Shadow 
-cut. {ia},{iM}
-normalize. 0,1
-pow. 0.5
-blur. {$soft},1,1
-mul.. -1 
-mul[-3,-2]

# Highlight
-compose_channels.. add
-cut.. {ia#-2},{iM#-2}
-normalize.. 0,1
-oneminus..
-pow.. 2

# Colorize. To do: sample color
# from input. For now, just mapping
# and colorspace twisting.
-blur.. {$soft},1,1
-normalize[-3] 0,1
-oneminus[-3]
-normalize[-3] 127,255
-map[-3] algae

# Provisional coloring
# Translate black/white of input into base° ± offset°
# colorspace rotations
-fill[-3] "begin(wv=vector3(1/sqrt(3)));
          if(i(#-4,x,y,0,0),ang="$basec"+"$offsc",ang="$basec"-"$offsc");
          rot(wv,ang)*I#-3"

# Composite 
-mul[-3,-2]
-normalize. 0,255
-add[-2,-1]
-normalize. 0,255
-if {$fillholes==0}
   -move[0] {$!}
   -append[-2,-1] c
-fi
2 Likes

Great!
Just fill the white holes (if you like) with the area colour or a kind of supplementary tile.
It’s a common problem of other mosaic stroke paths methods

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 .