#!/bin/bash

# blurryCircles (or rays) wallpaper maker

# needs: imagemagick, scrot or maim/slop, 
# schemer2 (https://github.com/thefryscorer/schemer2),
# pngcrush (optional)
# bash 4.3

# brex 2018

# license: WTFPL – Do What the **** You Want to Public License

# benchmark start
START=$(date +%s.%N)

# rays else circles
if [ "$1" == "rays" ] || [ "$1" == "r" ]; then
    rays="true" # rays or circles
fi

# config general
where="$HOME/Pictures/blurryCircles"
w="1920"
h="1200"
maxBright="240"             # 220
minBright="60"              # 50
howmany="12"                # how many variations to render out
parallel="4"                # How many in parallel
maim="true"                 # use maim instead of scrot (and you should)
scanlines="true"            # scanline fx, use scanlinesSrc as alpha image if true
scanlinesSrc="http://cdn.scrot.moe/images/2018/04/03/stripesNegative3.png"
# ^ this one needs internet, if that makes you nervous, copy the image to your hd and adjust this path.
noise="true"                # add some random noise
noiseAmount="0.07"          # noise amount 
vignette="true"             # vignette (slow)
vignetteBlur="150"          # softness
pngcrush="NOTtrue"          # optimize/reduce size of output png images
# config randomness
beingRandom () {
    
    # circles
    circlesSize=$((500 + RANDOM % 1420))
    circlesOpacity=$((1 + RANDOM % 5))
    circlesSizeVariance=$((100 + RANDOM % 200))
    threshold=$((1 + RANDOM % 50))
    
    # rays
    angle=$((5 + RANDOM % 40)) # between 5 and 45 degrees

}
# config rays
rayMul="3"                  # (3) ray oversampler multiplier when rayCrop or rayRotatePost are false
# ^ reducing this will drastically increase render speed and decrease quality
raysSize="22"               # Size of rays in output image (default 16)
raysSizeVariance="12"       # Maximum variance in rays size (default 8)
# either random rotate in post
rayRotatePost="true"        # random rotation of ray image in post (very slow due to large oversampling)
rayRotateMul="6"            # ray oversampler when rayRotatePost is true, note that numbers > 5 are extremely slow
# or crop mode
rayCrop="NOTtrue"           # move center of rays to corner
rayCropMul="4"              # ray oversampler when rayCrop is true
rayCropGravity="SouthWest"  # What part to crop


# tests
mkdir -p "$where"
cd "$where" || exit

# checks
if [ "$maim" = true ] ; then
    command -v maim >/dev/null 2>&1 || { echo "I need maim, exiting." >&2; exit 1; }
    command -v slop >/dev/null 2>&1 || { echo "I need slop, exiting." >&2; exit 1; }
else
    command -v scrot >/dev/null 2>&1 || { echo "I need scrot, exiting." >&2; exit 1; }
fi
command -v schemer2 >/dev/null 2>&1 || { echo "I need schemer2 (https://github.com/thefryscorer/schemer2), exiting." >&2; exit 1; }
command -v convert >/dev/null 2>&1 || { echo "I need imagemagick convert, exiting." >&2; exit 1; }
command -v mogrify >/dev/null 2>&1 || { echo "I need imagemagick mogrify, exiting." >&2; exit 1; }
if [ "$pngcrush" = true ] ; then
    command -v pngcrush >/dev/null 2>&1 || { echo "I need pngcrush, exiting." >&2; exit 1; }
fi
# tmp dir http://mywiki.wooledge.org/BashFAQ/062
tmpdir="/tmp/$RANDOM-$$"
trap '[ -n "$tmpdir" ] && rm -fr "$tmpdir"' EXIT
mkdir -m 700 "$tmpdir" || { echo '!! unable to create a tmpdir' >&2; tmpdir=; exit 1; }

# random word dictionary function
randomword()  {
    dict="/usr/share/dict/words"
    shuf -n1 "$dict" | tr -dc '[:alnum:]\n\r' | tr '[:upper:]' '[:lower:]'
}
word=$(randomword)

# where the magic happens
action () {
    # CIRCLES
    if [ "$rays" != true ] ; then   
        schemer2 -format img::img -in "$tmpdir/$name" -imageOutType circles -circlesBlurred -circlesFilled -circlesOpacity "$circlesOpacity" -circlesSize "$circlesSize" -circlesSizeVariance "$circlesSizeVariance" -minBright "$minBright" -maxBright "$maxBright" -threshold "$threshold" -width "$w" -height "$h" -out "$tmpdir/$n$name"
        
    # RAYS
    else                            
        # crop or not to crop
        if [ "$rayCrop" = true ] ; then
            rayMul="$rayCropMul"
        elif [ "$rayRotatePost" = true ] ; then
            rayMul="$rayRotateMul"
        fi
        # upsizing a bit due to aliasing problem
        schemer2 -format img::img -in "$tmpdir/$name" -imageOutType rays -raysLargeToSmall -raysSize "$raysSize" -raysSizeVariance "$raysSizeVariance" -minBright "$minBright" -maxBright "$maxBright" -threshold "$threshold" -width $(( w * rayMul )) -height $(( h * rayMul )) -out "$tmpdir/$n$name" 
        # -raysLargeToSmall
        # -raysDistributeEvenly
        
        # offset center
        #mogrify -chop 19%x19% "$tmpdir/$n$name" # 19 is 38/2 (golden percent)
        if [ "$rayCrop" = true ] ; then
            mogrify -gravity "$rayCropGravity" -crop 50%x50%+0+0 +repage "$tmpdir/$n$name"
            #mogrify -crop 99.5%x99.5%+0+0 +repage "$tmpdir/$n$name"
        elif [ "$rayRotatePost" = true ] ; then
                # just some rotation (https://stackoverflow.com/questions/29244011/imagemagick-convert-rotate-crop)
                # angle is defined in beingRandom function
                ratio=$(convert "$tmpdir/$n$name" -format \
                     "%[fx:aa=$angle*pi/180; min(w,h)/(w*abs(cos(aa))+h*abs(sin(aa)))]" \
                     info:)
                crop="%[fx:floor(w*$ratio)]x%[fx:floor(h*$ratio)]"
                crop="$crop+%[fx:ceil((w-w*$ratio)/2)]+%[fx:ceil((h-h*$ratio)/2)]"
                convert "$tmpdir/$n$name" -set option:distort:viewport "$crop" \
                          +distort SRT "$angle" +repage "$tmpdir/$n$name"
            
        fi
        # scale back down
        mogrify -resize "$w"x"$h" "$tmpdir/$n$name"
    fi

    # https://www.imagemagick.org/discourse-server/viewtopic.php?t=21381
    if [ "$noise" = true ] ; then
        convert "$tmpdir/$n$name" \( -size "$w"x"$h" xc:"gray(50%)" -seed "$circlesSizeVariance" +noise Poisson -modulate 100,0 -channel green -separate +channel \) \
        +swap -compose Mathematics -define compose:args="0,1,$noiseAmount,-0.075" -composite "$tmpdir/$n$name" # overwriting itself here
    fi

    if [ "$vignette" = true ] ; then
        convert "$tmpdir/$n$name" \( +clone -fill white -colorize 100 -background "gray(70%)" -vignette 0x"$vignetteBlur" \) \
        -compose multiply -composite "$tmpdir/$n$name"
    fi

    if [ "$scanlines" = true ] ; then
        convert "$tmpdir/$n$name" \( "$scanlinesSrc" -resize "$w"x"$h"\! \) -alpha off -compose copy_opacity -composite PNG32:"$tmpdir/$n$name" # overwriting itself here
    fi

    if [ "$pngcrush" = true ] ; then
        pngcrush -ow "$tmpdir/$n$name" &>/dev/null
    fi
    
    mv "$tmpdir/$n$name" "$where/$n$name"
    }



set -x

# naming convention
name="$word"_$(date +%H%M%^b%d%y).png

if [ "$maim" = true ] ; then
    maim -s -d 0.3 -c '0.1,0.1,0.1,0.3' "$tmpdir/$name"
else
    scrot -s -d 1 "$tmpdir/$name"
fi

# autolevels of input in circles mode
if [ "$rays" != true ] ; then
    mogrify -auto-level -normalize "$tmpdir/$name"
fi

# main loop
n=0 # while counter
c=0 # parallel counter
while [[ $n -lt $howmany ]]; do

    if (( c++ >= parallel )); then wait -n; fi

        beingRandom
    
        action &
    
        n=$((n+1))
    
done

wait

# beep 
command -v beepspeakers >/dev/null 2>&1 && beepspeakers 1

# benchmark end
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
echo "done in $DIFF seconds"