local Pos = require'r.pos' local class = require'r.class' local rmath = require'r.math' local bit = require'bit' local tau = 2*math.pi math.randomseed(os.time()) local function hash_list(t) local h = #t for _,x in ipairs(t) do x = bit.bxor(bit.rshift(x,16),x) * 0x7feb352d x = bit.bxor(bit.rshift(x,15),x) * 0x846ca68b x = bit.bxor(bit.rshift(x,16),x) h = bit.bxor(h,x + 0x9e3779b9 + bit.lshift(h,6) + bit.rshift(h,2)) end return h end local function hash_to_unit_vec(t) local h = hash_list(t) math.randomseed(h) local theta = math.random()*tau return Pos:make(math.cos(theta),math.sin(theta)) end local PerlinNoise = class() function PerlinNoise.make(cls,seed) return setmetatable({seed=seed},cls) end function PerlinNoise.vertex(self,ix,iy) return hash_to_unit_vec{ix,iy,self.seed} end function PerlinNoise.at(self,x,y) local x0 = math.floor(x) local y0 = math.floor(y) local x1 = x0 + 1 local y1 = y0 + 1 local v00 = self:vertex(x0,y0) local v01 = self:vertex(x0,y1) local v10 = self:vertex(x1,y0) local v11 = self:vertex(x1,y1) local p = function(...) return Pos:make(...) end local d00 = v00:dot(p(x-x0,y-y0)) local d01 = v01:dot(p(x-x0,y-y1)) local d10 = v10:dot(p(x-x1,y-y0)) local d11 = v11:dot(p(x-x1,y-y1)) local q0 = rmath.slerp(d00,d01,y-y0) local q1 = rmath.slerp(d10,d11,y-y0) local z = rmath.slerp(q0,q1,x-x0) return z end local NoiseAgg = class() function NoiseAgg.make(cls,things) return setmetatable({things=things or {}},cls) end function NoiseAgg.at(self,x,y) local n = 0 local t = 0 assert(#self.things>0,"can't generate noise with no noise things") for _,thing in ipairs(self.things) do local gen,scale,amp = thing.gen,thing.scale,thing.amp n = n + amp t = t + gen:at(x/scale,y/scale)*amp end return t/n end return {PerlinNoise=PerlinNoise,NoiseAgg=NoiseAgg}