diff options
author | ubq323 <ubq323@ubq323.website> | 2025-02-17 14:48:34 +0000 |
---|---|---|
committer | ubq323 <ubq323@ubq323.website> | 2025-02-17 14:48:34 +0000 |
commit | f1492b52414b6f2ad6cfff45375c08677feab18c (patch) | |
tree | 2eac341c813f513b4acef7ff591f6f68936eb334 |
initial
-rw-r--r-- | camera.lua | 46 | ||||
-rw-r--r-- | class.lua | 31 | ||||
-rw-r--r-- | math.lua | 24 | ||||
-rw-r--r-- | noise.lua | 74 | ||||
-rw-r--r-- | pos.lua | 45 | ||||
-rw-r--r-- | print_good.lua | 18 |
6 files changed, 238 insertions, 0 deletions
diff --git a/camera.lua b/camera.lua new file mode 100644 index 0000000..19c5bd4 --- /dev/null +++ b/camera.lua @@ -0,0 +1,46 @@ +local class = require'r.class' +local Pos = require'r.pos' + +-- in screen units + +-- zoom is screen pixels per world unit +local Camera = class() +function Camera.make(cls,pos,zoom) + pos = pos or Pos:make(0,0) + zoom = zoom or 1 + return setmetatable({pos=pos,zoom=zoom},cls) +end +local function screen_offset() + -- in screen units, not in world units ! + local W,H = love.graphics.getDimensions() + return Pos:make(W/2,H/2) +end +function Camera.apply_trans(self) + local so = screen_offset() + + -- centre (0,0) in the middle of the screen + love.graphics.translate(so.x,so.y) + + -- apply camera transformations + love.graphics.scale(self.zoom) + love.graphics.translate(-self.pos.x,-self.pos.y) +end + +function Camera.screen_to_world(self,pos) + local so = screen_offset() + return (pos-so)/self.zoom + self.pos +end +function Camera.world_to_screen(self,pos) + local so = screen_offset() + return (pos-self.pos) * self.zoom + so +end + +function Camera.extents(self) + local W,H = love.graphics.getDimensions() + -- returns top left and bottom right pos's in world coords + return self:screen_to_world(Pos:make(0,0)), + self:screen_to_world(Pos:make(W,H)) +end + + +return Camera diff --git a/class.lua b/class.lua new file mode 100644 index 0000000..8fb84b0 --- /dev/null +++ b/class.lua @@ -0,0 +1,31 @@ +-- currently a class is a table T with T.__index = T +-- then to make an instance of this class, we do +-- setmetatable(instance,T) +-- this should be fine for anything we wish to do. +-- it is possible we will eventually split this into two separate +-- tables perhaps? i don't see why we would ever do that though + +local function class() + local T = {} + T.__index = T + return T +end + +local function extend(Base) + local T = {} + T.__index = T + for k,v in pairs(Base) do + if k:sub(1,2) == "__" and k~="__index" then + T[k]=v + end + end + setmetatable(T,{__index=Base}) + return T +end + +return setmetatable({ + class=class, + extend=extend +},{__call=class}) + + diff --git a/math.lua b/math.lua new file mode 100644 index 0000000..cd6745e --- /dev/null +++ b/math.lua @@ -0,0 +1,24 @@ +local M = {} + +function M.lerp(a,b,t) return (1-t)*a + t*b end +function M.smoothstep(x) + if x<0 then return 0 end + if x>1 then return 1 end + return x*x*(3 - 2*x) +end +function M.slerp(a,b,t) return M.lerp(a,b,M.smoothstep(t)) end + + +function M.sign(x) + if x == 0 then return 0 + elseif x < 0 then return -1 + elseif x > 0 then return 1 + end +end + +function M.clamp(x,minv,maxv) + return math.min(math.max(x,minv),maxv) +end + + +return M diff --git a/noise.lua b/noise.lua new file mode 100644 index 0000000..f802ea2 --- /dev/null +++ b/noise.lua @@ -0,0 +1,74 @@ +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} @@ -0,0 +1,45 @@ +local class = require'r.class' + +local Pos = class() +function Pos.new(cls) + return setmetatable({},cls) +end +function Pos.init(self,x,y) + self.x = x + self.y = y + return self +end +function Pos.make(cls,...) + return cls:new():init(...) +end +function Pos.__add(self,other) + return Pos:make(self.x+other.x,self.y+other.y) +end +function Pos.__sub(self,other) + return Pos:make(self.x-other.x,self.y-other.y) +end +function Pos.__mul(a,b) + if type(a) == "number" then + return Pos:make(a*b.x,a*b.y) + elseif type(b) == "number" then + return Pos:make(a.x*b,a.y*b) + else + error("can only multiply Pos by scalar") + end +end +function Pos.__div(a,b) + assert(type(b) == "number","can only divide Pos by scalar") + return a*(1/b) +end +function Pos.__eq(a,b) return a.x==b.x and a.y==b.y end +function Pos.lensq(self) return self.x^2 + self.y^2 end +function Pos.len(self) return math.sqrt(self:lensq()) end +function Pos.norm(self) return self/self:len() end +function Pos.dot(self,other) return self.x*other.x+self.y*other.y end +function Pos.__tostring(self) + return string.format("(%.2f,%.2f)",self.x,self.y) +end + +return Pos + + diff --git a/print_good.lua b/print_good.lua new file mode 100644 index 0000000..b5d18b9 --- /dev/null +++ b/print_good.lua @@ -0,0 +1,18 @@ +local font = love.graphics.getFont() +local text = love.graphics.newText(font) + +local function print_good(str,x,y) + text:set(str) + local w,h = text:getDimensions() + local W,H = love.graphics.getDimensions() + if x == "center" then x = (W/2)-(w/2) end + if y == "center" then y = (H/2)-(h/2) end + if x == "end" then x = W-w end + if y == "end" then y = H-h end + love.graphics.setColor(0,0,0,0.8) + love.graphics.rectangle("fill",x,y,w,h) + love.graphics.setColor(1,1,1) + love.graphics.draw(text,x,y) +end + +return print_good |