From 52599c02f2c333f820b22a6cb2d9d2b4905136bd Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 31 Mar 2017 14:52:10 -0700 Subject: [PATCH] Added histogram for sanity checks, checked sanity and found it wanting. Fixed bug where variance was calculated incorrectly. --- histogram.lua | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.lua | 44 ++++++++++++++++++++++++++------ noise.lua | 4 +++ 3 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 histogram.lua diff --git a/histogram.lua b/histogram.lua new file mode 100644 index 0000000..86b4184 --- /dev/null +++ b/histogram.lua @@ -0,0 +1,70 @@ +local Histogram = setmetatable({}, {__call=function(self,options) + local h = setmetatable({},{__index=self}) + h:init(options) + return h +end}) + +function Histogram:init(options) + self.pos = options.pos + self.size = options.size + local total = 0 + for _,entry in ipairs(options.data) do + local x, count = unpack(entry) + total = total + count + end + local t = 0 + local iqrMin, iqrMax + for _,entry in ipairs(options.data) do + local x, count = unpack(entry) + t = t + count + if not iqrMin and t >= .25*total then iqrMin = x end + if not iqrMax and t >= .75*total then iqrMax = x end + end + local h = 2*(iqrMax-iqrMin)*total^(1/3) + local xmin, xmax = options.xmin or options.data[1][1], options.xmax or options.data[#options.data][1] + local numBuckets = options.numBuckets or math.max(3, math.min(options.size.x/10, math.ceil((xmax - xmin) * total / h))) + local buckets = {} + for i=1,numBuckets do + buckets[i] = 0 + end + local ymax = 0 + for _,entry in ipairs(options.data) do + local x, count = unpack(entry) + local b = 1 + math.floor((x - xmin)/(xmax - xmin) * (numBuckets-1)) + buckets[b] = (buckets[b] or 0) + count + ymax = math.max(buckets[b], ymax) + end + self.ymax = ymax + self.xmax = xmax + self.buckets = buckets + function self:getX(x) + return self.pos.x + self.size.x * (x-xmin)/(xmax-xmin) + end +end + +function Histogram:fromList(list, options) + table.sort(list) + local data = {} + for _,x in ipairs(list) do + if not data[#data] or data[#data][1] ~= x then + table.insert(data, {x,1}) + else + data[#data][2] = data[#data][2] + 1 + end + end + options.data = data + return Histogram(options) +end + +function Histogram:draw() + local height = self.size.y / (#self.buckets-1) + for i,count in ipairs(self.buckets) do + if count > 0 then + local width = count/self.ymax * self.size.x + love.graphics.rectangle('fill', self.pos.x, + self.pos.y+(#self.buckets-i-1)*height, width, height) + end + end +end + +return Histogram diff --git a/main.lua b/main.lua index bc93e65..023b151 100644 --- a/main.lua +++ b/main.lua @@ -1,5 +1,6 @@ lg = love.graphics W,H = lg.getDimensions() +Histogram = require 'histogram' local Noise = require "noise" local DRAW_RES = love.window.toPixels(4) local SCALE = 100 @@ -10,10 +11,10 @@ local function rng() local r = love.math.newRandomGenerator(1) return function() return r:random() end end -local n1 = Noise.make1d(5,rng(),decay) -local n2 = Noise.make2d(7,rng(),decay) +local n1 = Noise.make1d(8,rng(),decay) +local n2 = Noise.make2d(9,rng(),decay) local n3 = Noise.make3d(11,rng(),decay) -local s1 = Noise.make1dShader(7,rng(),decay) +local s1 = Noise.make1dShader(9,rng(),decay) local s2 = Noise.make2dShader(13,rng(),decay) local s3 = Noise.make3dShader(17,rng(),decay) @@ -25,6 +26,20 @@ function love.load() yOffset = 0 t0 = love.timer.getTime() love.mouse.setCursor(love.mouse.getSystemCursor("hand")) + + local data1,data2,data3 = {},{},{} + for i=1,100000 do + local x = math.random()*100000 + local y = math.random()*100000 + local z = math.random()*100000 + table.insert(data1, n1(x)) + table.insert(data2, n2(x,y)) + table.insert(data3, n3(x,y,z)) + end + histogram1 = Histogram:fromList(data1,{pos={x=0,y=0},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) + histogram2 = Histogram:fromList(data2,{pos={x=0,y=H/3},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) + histogram3 = Histogram:fromList(data3,{pos={x=0,y=2*H/3},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) + end function love.mousepressed() @@ -36,13 +51,22 @@ function love.mousereleased() end function love.draw() + if love.keyboard.isDown('h') then + lg.setColor(0,100,0) + histogram1:draw() + lg.setColor(0,0,150) + histogram2:draw() + lg.setColor(125,0,0) + histogram3:draw() + end + lg.setColor(255,255,255) lg.setLineWidth(love.window.toPixels(2)) lg.setLineJoin("none") local p1 = {} for x=0,W-H/3,DRAW_RES do table.insert(p1,x) - table.insert(p1,n1(x/SCALE+xOffset)*H/3) + table.insert(p1,(1-n1(x/SCALE+xOffset))*H/3) end lg.line(p1) @@ -50,7 +74,7 @@ function love.draw() local t = love.timer.getTime()-t0 for x=0,W-H/3,DRAW_RES do table.insert(p2,x) - table.insert(p2,(1+n2(x/SCALE+xOffset,t))*H/3) + table.insert(p2,(1+1-n2(x/SCALE+xOffset,t))*H/3) end lg.line(p2) @@ -58,7 +82,7 @@ function love.draw() local p3 = {} for x=0,W-H/3,DRAW_RES do table.insert(p3,x) - table.insert(p3,2/3*H+H/6*n3(x/SCALE+xOffset,y/SCALE+yOffset,t)+y) + table.insert(p3,2/3*H+H/6*(1-n3(x/SCALE+xOffset,y/SCALE+yOffset,t))+y) end lg.line(p3) end @@ -69,7 +93,7 @@ function love.draw() local x2 = x + slant*y if x2 < -2*DRAW_RES then goto continue end table.insert(p3,x2) - table.insert(p3,2/3*H+H/6*n3(x2/SCALE+xOffset,y/SCALE+yOffset,t)+y) + table.insert(p3,2/3*H+H/6*(1-n3(x2/SCALE+xOffset,y/SCALE+yOffset,t))+y) if x2 > W/2 then break end ::continue:: end @@ -93,7 +117,11 @@ function love.draw() lg.draw(canv,W-cw,2*ch) lg.setShader(nil) - lg.setColor(255,200,0) + lg.setColor(0,0,0,100) + lg.rectangle('fill',0,0,W,font:getHeight()+love.window.toPixels(5)) + lg.rectangle('fill',0,H/3,W,font:getHeight()+love.window.toPixels(5)) + lg.rectangle('fill',0,2*H/3,W,font:getHeight()+love.window.toPixels(5)) + lg.setColor(255,230,50) lg.setFont(font) lg.printf("1D_Noise(x)", 5,5, W) lg.printf("2D_Noise(x,time)", 5,5+H/3, W) diff --git a/noise.lua b/noise.lua index de528c6..8a8ec23 100644 --- a/noise.lua +++ b/noise.lua @@ -44,6 +44,7 @@ end noise.make2d = function(resolution,random,decayFn) resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random) + sigma = sigma/sqrt(2) return function(x,y) local noise = 0 for i,a in ipairs(amplitudes) do @@ -59,6 +60,7 @@ end noise.make3d = function(resolution,random,decayFn) resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random) + sigma = sigma/sqrt(3) return function(x,y,z) -- Find the biggest fibonacci number F_n such that F_n < resolution local n = floor(log((resolution-1)*SQRT5 + .5)/LOG_GR) @@ -117,6 +119,7 @@ noise.make2dShader = function(resolution,random,decayFn) local shader = lg.newShader("noise2d.glsl") resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random) + sigma = sigma/sqrt(2) local offsets2 = {} for i=1,#offsets-1,2 do table.insert(offsets2, {offsets[i],offsets[i+1]}) @@ -134,6 +137,7 @@ noise.make3dShader = function(resolution,random,decayFn) local shader = lg.newShader("noise3d.glsl") resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random) + sigma = sigma/sqrt(3) local offsets2 = {} for i=1,#offsets-1,3 do table.insert(offsets2, {offsets[i],offsets[i+1],offsets[i+2]})