Announcing CoherentNoise.jl

I am proud to announce the first version of CoherentNoise.jl, a library consisting of several coherent noise algorithms and tools to compose them into interesting pipelines.

This is actually my first real package, only having used Julia for about 2 months now. I am not a master yet, but if you find this useful and/or find some things that could be written more idiomatically, feel free to open an issue or pull request. I really look forward to learning more through discussion and collaboration.

That said, I am in love with Julia after having used it to create this library. I hope you don’t mind that I will be sticking around for a while! :wink:

For information about what coherent noise is, please see the Overview section of the documentation, and for some simple examples, the Tutorial section: https://mfiano.github.io/CoherentNoise.jl

Thank you.

28 Likes

Without trying it, props for the nice documentation!

2 Likes

That looks really impressive for a first go at developing a package, kudos! Also interesting to read the Overview section to learn a bit about a topic I’d never heard of before (but which I guess has interesting real-world applications given your overview says one of the algorithms is patented for a certain number of dimensions!?)

4 Likes

Yes, IIRC Ken Perlin holds a patent for Simplex noise >= 3 dimensions when used in the context of texture generation. The details are vague, so I would stay away from it for that use case.

However, CoherentNoise has all sorts of applications. For example you could use it to drive a force-feedback game controller to give it a smoother feel, or really anything that requires a signal.

The tutorial has a typo seeed. Ideally doctests in CI would catch this.

Fixed, thanks. Also note that the documentation has a lot of errors, due to me not understanding Documenter.jl well enough. If any kind soul is bored enough and wants to PR this, see the bottom of: Initial commit · mfiano/CoherentNoise.jl@a92b2c5 · GitHub

Some @ref links point to nothing. I think this is due to Documenter.jl being a single-pass “compiler”, and forward references aren’t allowed/not expanded in “See also: …” doc sections. But it could also just be my improper way of writing docs.

Documentation writing is one of my worst qualities.

Edit: @digital_carver began working on this in a PR. Thanks!

It’s awesome!

output

7 Likes

Nice work! You might consider creating a PR to add this to the gallery :slight_smile:

1 Like

Well, the code is a bit clumsy and I am not yet satisfied with it and the results. You see the rotation and I want to get rid of this… still experimenting :slight_smile:

1 Like

I’m glad you’re enjoying it!

I’m actually interested in the code, messy or not, on the approach you took to create the animated GIF.

2 Likes

Sorry, but I recorded a MiniFB window with OBS and converted the result to gif with ffmpeg :wink:

Here is the code:

#] add https://github.com/mfiano/CoherentNoise.jl

using Luxor
using Colors
using MiniFB
using ColorSchemes
using CoherentNoise
using ThreadPools

struct Window
    c::ReentrantLock
    w::Int
    h::Int
    d::Drawing
    buffer::Matrix{ARGB32}
    function Window(w,h)
        c=ReentrantLock()
        b=zeros(ARGB32, w, h)
        d=Drawing(b)
        origin()
        new(c,w,h,d,b)
    end
end

function window_update_task(win::Window, showFPS=true)
    w=win.w
    h=win.h
    updateCount=0
    startTime=floor(Int, time())
    fps="0"
    sb=zeros(ARGB32, 105, 55)
    window = mfb_open_ex("MiniFB", w, h, MiniFB.WF_RESIZABLE)
    state=MiniFB.STATE_OK
    set_drawing = true
    while state == MiniFB.STATE_OK
        lock(win.c)
        if set_drawing
            currentdrawing(win.d)
            set_drawing = false
        end
        if showFPS
            elapsedTime=floor(Int,time())-startTime
            if elapsedTime > 1
                fps=string(round(Int,updateCount/elapsedTime))
                startTime=floor(Int,time())
                updateCount=0
            end
            sb.=win.buffer[1:105, 1:55]
            @layer begin
                (dx,dy) = Point(0.0, 0.0) - getworldposition(Point(0.0, 0.0);centered=false)
                setcolor(1.0, 0, 0, 0.5)
                fontsize(50)
                text(fps, Point(5+dx, 5+dy), halign=:left, valign = :top)
            end
        end
        state=mfb_update(window,win.buffer)
        if showFPS
            win.buffer[1:105, 1:55].=sb
        end
        unlock(win.c)
        sleep(1.0/120.0)
        updateCount+=1
    end
    println("\nWindow closed\n")
end

const WIDTH = 400
const HEIGHT = 400

win = Window(WIDTH, HEIGHT)

let window=win
   global t_window_update_task
   function t_window_update_task()
       window_update_task(window)
   end
end

spawnbg(t_window_update_task)

rng(sampler::AbstractSampler) = sampler.random_state.rng
function CoherentNoise.gen_image(buffer,
    sampler::S;
    w::Integer=1024,
    h::Integer=1024,
    xbounds::NTuple{2,Float64}=(-1.0, 1.0),
    ybounds::NTuple{2,Float64}=(-1.0, 1.0),
    colorscheme=nothing,
) where {N,S<:AbstractSampler{N}}
    x1, x2 = xbounds
    y1, y2 = ybounds
    xd = (x2 - x1) / w
    yd = (y2 - y1) / h
    zw = rand(rng(sampler), Float64, N - 2) * 1000
    Threads.@threads for x in 1:w
        cx = x * xd + x1
        for y in 1:h
            cy = y * yd + y1
            value = sample(sampler, cx, cy, zw...) * 0.5 + 0.5
            buffer[x, y] = colorscheme !== nothing ? colorscheme[value] : value
        end
    end
end

sampler1 = cache(OpenSimplex{2}(seed=1))
#sampler1 = cache(Constant(seed=1))

ub=10.0
lb=-10.0
rad=1.0

#while true
    for sc in 0.0:0.01:2pi
        #sampler2 = CoherentNoise.Modifiers.rotate(CoherentNoise.Modifiers.scale(OpenSimplex{2}(seed=2),0.8);z=rad)
        sampler2 = CoherentNoise.Modifiers.rotate(OpenSimplex{2}(seed=2);z=3.0+1.5*sin(sc))
        sampler3 = CoherentNoise.Modifiers.rotate(OpenSimplex{2}(seed=3);z=3.0-1.5*cos(sc))
        #println(rad)
        sampler = warp(sampler1;x=sampler2,y=sampler3)
        lock(win.c)
        gen_image(win.buffer,sampler,w=WIDTH,h=HEIGHT,colorscheme=ColorSchemes.terrain,xbounds=(ub,lb),ybounds=(ub,lb))
        unlock(win.c)
        #sleep(0.001)
    end
#end

3 Likes

The patent on simplex noise expired in January of this year.

1 Like

Thanks for the heads up. I updated the documentation to remove the warning.

For my taste it is very nice. Very cool package, and I’m already learning a lot from your questions and contributions on the Zulip chat. Welcome, and thanks!

3 Likes