Reorganize; DFT; Use Transformation Matrices
This commit is contained in:
parent
79e9090c7e
commit
da0ca76748
|
@ -0,0 +1,9 @@
|
|||
local function DetectCollision(
|
||||
xi, yi, vx, vy, xf, yf,
|
||||
GetRadius,
|
||||
GetRadialDerivative)
|
||||
|
||||
|
||||
end
|
||||
|
||||
return DetectCollision
|
|
@ -0,0 +1,14 @@
|
|||
function love.conf( t )
|
||||
t.modules.joystick = false
|
||||
t.modules.physics = false
|
||||
t.modules.touch = false
|
||||
t.modules.video = false
|
||||
|
||||
t.window.title = "By Your Own Beat"
|
||||
t.window.width = 600
|
||||
t.window.height = 600
|
||||
t.window.resizable = true
|
||||
t.window.minwidth = 200
|
||||
t.window.minheight = 200
|
||||
|
||||
end
|
40
main.lua
40
main.lua
|
@ -4,13 +4,14 @@ local step = step
|
|||
local sitelenpona
|
||||
local text
|
||||
local marble
|
||||
local wave
|
||||
|
||||
local sounds = {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
local state
|
||||
state = {
|
||||
|
||||
|
@ -52,14 +53,29 @@ state = {
|
|||
},
|
||||
}
|
||||
|
||||
local function UpdateWindowTransform( w, h )
|
||||
local d = math.min( w, h )
|
||||
local r = love.graphics.get
|
||||
local tf = love.math.newTransform()
|
||||
|
||||
local size = 3
|
||||
|
||||
tf:translate( w / 2, h / 2)
|
||||
tf:scale( d / size, -d / size )
|
||||
transform = tf
|
||||
end
|
||||
|
||||
function love.load()
|
||||
UpdateWindowTransform( love.graphics.getDimensions() )
|
||||
|
||||
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
|
||||
--love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.
|
||||
sounds.goodPing = love.audio.newSource("soundTest.ogg", "static")
|
||||
sounds.badPing = love.audio.newSource("chime8.ogg", "static")
|
||||
sounds.goodPing = love.audio.newSource("sounds/soundTest.ogg", "static")
|
||||
sounds.badPing = love.audio.newSource("sounds/chime8.ogg", "static")
|
||||
sitelenpona = assert( require "sitelenpona" )
|
||||
text = assert( require "text" )
|
||||
marble = assert( require "marble" )
|
||||
wave = assert( require "wave" )
|
||||
return state.Reset()
|
||||
|
||||
end
|
||||
|
@ -134,7 +150,6 @@ end
|
|||
|
||||
|
||||
function love.draw()
|
||||
local w, h = love.graphics.getDimensions()
|
||||
|
||||
|
||||
love.graphics.setColor(1.0, 1.0, 1.0)
|
||||
|
@ -143,12 +158,18 @@ function love.draw()
|
|||
love.graphics.print( state.beatScoreThreshold, 0, 20)
|
||||
love.graphics.print( state.lastBeatScore, 0, 30 )
|
||||
|
||||
love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255 )
|
||||
love.graphics.circle("fill", w / 2, h / 2, 200)
|
||||
|
||||
|
||||
love.graphics.push( "transform" )
|
||||
love.graphics.applyTransform( transform )
|
||||
wave.Draw()
|
||||
love.graphics.pop()
|
||||
|
||||
sitelenpona.Draw( text.tok[state.currentBeat] )
|
||||
|
||||
marble.Draw()
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
@ -157,8 +178,10 @@ function love.update( dt )
|
|||
dt = dt + state.timeToSimulate
|
||||
while dt > step do
|
||||
marble.Update()
|
||||
wave.Update()
|
||||
dt = dt - step
|
||||
end
|
||||
state.timeToSimulate = dt
|
||||
end
|
||||
|
||||
function love.keypressed( key, code, isRepeat )
|
||||
|
@ -170,3 +193,8 @@ end
|
|||
function love.keyreleased( key, code )
|
||||
return marble.OnKey()
|
||||
end
|
||||
|
||||
function love.resize( w, h )
|
||||
UpdateWindowTransform( w, h )
|
||||
if marble then marble.Resize() end
|
||||
end
|
32
marble.lua
32
marble.lua
|
@ -1,11 +1,12 @@
|
|||
--Character controller. Renders point particle.
|
||||
local love = love
|
||||
--local wave = assert( require "wave" )
|
||||
local oldBuffer = love.graphics.newCanvas()
|
||||
local newBuffer = love.graphics.newCanvas()
|
||||
local step = assert( step )
|
||||
local state
|
||||
local FRICTION = 0.008
|
||||
local MAXSPEED = 1.0
|
||||
local FRICTION = 0.01
|
||||
local MAXSPEED = 5.0
|
||||
|
||||
local t, x, y, dx, dy, ddx, ddy
|
||||
|
||||
|
@ -16,11 +17,14 @@ local function Update()
|
|||
dy = (1.0 - FRICTION) * dy + FRICTION * ddy
|
||||
local xf = x + dx * step * MAXSPEED
|
||||
local yf = y + dy * step * MAXSPEED
|
||||
|
||||
x = xf
|
||||
y = yf
|
||||
end
|
||||
|
||||
local function OnKey()
|
||||
ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
|
||||
ddy = (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0)
|
||||
ddy = (love.keyboard.isScancodeDown( "w" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "s" ) and 1.0 or 0.0)
|
||||
|
||||
local n = math.sqrt( ddx * ddx + ddy * ddy )
|
||||
if n < 0.001 then return end
|
||||
|
@ -32,18 +36,22 @@ local function Draw()
|
|||
local xf, yf = x + dt * dx, y + dt * dy
|
||||
|
||||
local w, h = love.graphics.getDimensions()
|
||||
local xi, yi = 1000.0 * x + 0.5 * w, 1000.0 * y + 0.5 * h
|
||||
xf, yf = 1000.0 * xf + 0.5 * w, 1000.0 * yf + 0.5 * h
|
||||
local xi, yi = transform:transformPoint( x, y )
|
||||
xf, yf = transform:transformPoint( xf, yf )
|
||||
|
||||
love.graphics.setColor( 0, 0, 0, 1 )
|
||||
love.graphics.print( x, 0, 60 )
|
||||
love.graphics.print( y, 0, 80 )
|
||||
|
||||
|
||||
love.graphics.setCanvas( newBuffer )
|
||||
love.graphics.setColor( 1.0, 1.0, 1.0 , 0.9 ) --Trans blue.
|
||||
love.graphics.setColor( 1.0, 1.0, 1.0 , 0.99 ) -- White.
|
||||
love.graphics.draw( oldBuffer ) --Time-dependent fade: overlay canvas over itself.
|
||||
|
||||
--Render latest segment in trail.
|
||||
love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 ) --White?
|
||||
love.graphics.setColor( 245 / 255, 169 / 255, 184 / 255, 1.0 ) --Trans pink.
|
||||
love.graphics.circle( "fill", xf, yf, 4.0 ) --TODO: replace with segments.
|
||||
love.graphics.line( xi, yi, xf, yf )
|
||||
--love.graphics.line( xi, yi, xf, yf )
|
||||
|
||||
love.graphics.setCanvas( oldBuffer )
|
||||
love.graphics.clear( 1.0, 1.0, 1.0, 0.0 )
|
||||
|
@ -51,8 +59,9 @@ local function Draw()
|
|||
--Render circle directly to screen.
|
||||
love.graphics.setCanvas()
|
||||
love.graphics.draw( newBuffer )
|
||||
love.graphics.setColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
|
||||
love.graphics.circle( "fill", xf, yf, 5.0 )
|
||||
love.graphics.setColor( 1, 1, 1, 1.0 ) --White.
|
||||
love.graphics.circle( "line", xf, yf, 5.0 )
|
||||
|
||||
oldBuffer, newBuffer = newBuffer, oldBuffer
|
||||
|
||||
|
@ -64,6 +73,10 @@ end
|
|||
|
||||
--Window resize.
|
||||
local function Resize()
|
||||
newBuffer = love.graphics.newCanvas()
|
||||
--TODO: render oldBuffer to new newBuffer, but scaled down.
|
||||
oldBuffer = love.graphics.newCanvas()
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
@ -87,4 +100,5 @@ return {
|
|||
Draw = Draw,
|
||||
Impact = Impact,
|
||||
Reset = Reset,
|
||||
Resize = Resize,
|
||||
}
|
|
@ -10,7 +10,7 @@ end
|
|||
sp.Draw = function( str )
|
||||
local w, h = love.graphics.getDimensions()
|
||||
local x, y = 0.5 * w - 128, 0.5 * h - 128
|
||||
love.graphics.setColor( 1.0, 1.0, 1.0, 0.5 )
|
||||
love.graphics.setColor( 1.0, 1.0, 1.0, 1.0 )
|
||||
love.graphics.draw( sp[str] or sp.q, x, y )
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
--Render and simulate 1D wave equation.
|
||||
local love = love
|
||||
local step = assert( step )
|
||||
|
||||
local N = 25
|
||||
|
||||
--Calculate discrete fourier transform of radius function.
|
||||
local DFT
|
||||
do
|
||||
local twiddlec = {}
|
||||
local twiddles = {}
|
||||
for i = 1, N do
|
||||
twiddlec[i], twiddles[i] = math.cos( - 2.0 * ( i - 1 ) * math.pi / N ), math.sin( - 2.0 * (i - 1) * math.pi / N )
|
||||
end
|
||||
|
||||
local function Twiddle( j )
|
||||
j = 1 + j % N
|
||||
return twiddlec[j], twiddles[j]
|
||||
end
|
||||
|
||||
--Slow Discrete Fourier Transform of a real sequence.
|
||||
DFT = function( wave )
|
||||
local seq = wave.radii
|
||||
local dre, dim = wave.dftre, wave.dftim
|
||||
for k = 0, N - 1 do
|
||||
|
||||
local x, y = 0, 0 --Fourier coefficients in bin k.
|
||||
|
||||
for n = 0, N - 1 do
|
||||
local cos, sin = Twiddle( n * k )
|
||||
x = x + seq[n + 1] * cos
|
||||
y = y + seq[n + 1] * sin
|
||||
end
|
||||
|
||||
dre[k + 1], dim[k + 1] = x / N , y / N
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--Minimal-oscillation interpolant of a real function from its discrete Fourier coefficients.
|
||||
local Interpolate = function( wave, x )
|
||||
local re, im = wave.dftre, wave.dftim
|
||||
local y = re[1]
|
||||
|
||||
for k = 1, N / 2 do
|
||||
local c, s = math.cos( x * k ), math.sin( x * k )
|
||||
y = y
|
||||
+ c * re[k + 1]
|
||||
- s * im[k + 1]
|
||||
+ c * re[N - k + 1]
|
||||
+ s * im[N - k + 1]
|
||||
|
||||
end
|
||||
|
||||
return y
|
||||
end
|
||||
|
||||
--Derivative of the interpolation.
|
||||
local Derivative = function( wave, x )
|
||||
local re, im = wave.dftre, wave.dftim
|
||||
|
||||
for k = 1, N / 2 do
|
||||
local c, s = k * math.cos( x * k ), k * math.sin( x * k )
|
||||
y = y
|
||||
- c * im[k + 1]
|
||||
- s * re[k + 1]
|
||||
+ c * im[N - k + 1]
|
||||
- s * re[N - k + 1]
|
||||
|
||||
end
|
||||
|
||||
return y
|
||||
end
|
||||
|
||||
--Second derivative of the interpolation.
|
||||
local SecondDerivative = function( wave, x )
|
||||
local re, im = wave.dftre, wave.dftim
|
||||
|
||||
for k = 1, N / 2 do
|
||||
local c, s = k * k * math.cos( x * k ), k * k * math.sin( x * k )
|
||||
y = y
|
||||
- c * re[k + 1]
|
||||
+ s * im[k + 1]
|
||||
- c * re[N - k + 1]
|
||||
- s * im[N - k + 1]
|
||||
|
||||
end
|
||||
|
||||
return y
|
||||
end
|
||||
|
||||
local mt = { __index = {
|
||||
DFT = DFT,
|
||||
Interpolate = Interpolate,
|
||||
Derivative = Derivative,
|
||||
SecondDerivative = SecondDerivative } }
|
||||
|
||||
local function Wave( )
|
||||
local t = {
|
||||
--radii[k] = radius of point on curve at angle (k - 1) / 13
|
||||
radii = {},
|
||||
--TIME derivative of radius
|
||||
vrad = {},
|
||||
|
||||
--SPACE DFT of radius function (which is periodic)
|
||||
dftre = {},
|
||||
dftim = {},
|
||||
}
|
||||
|
||||
for i = 1, N do
|
||||
t.radii[i] = 1.0
|
||||
t.vrad[i] = 0.0
|
||||
end
|
||||
DFT( t )
|
||||
|
||||
return setmetatable(t, mt)
|
||||
end
|
||||
|
||||
|
||||
local old = Wave() --State at beginning of tick.
|
||||
local cur = Wave() --State at end of tick.
|
||||
|
||||
|
||||
local function Draw()
|
||||
|
||||
-- Blue circle.
|
||||
love.graphics.setColor( 91 / 255, 206 / 255, 250 / 255 )
|
||||
love.graphics.circle("fill", 0, 0, 1)
|
||||
local t = love.timer.getTime()
|
||||
|
||||
-- Debug dots.
|
||||
for i = 1, N do
|
||||
|
||||
local th = ( i - 1 ) * 2.0 * math.pi / N
|
||||
local cx, cy = cur.radii[i] * math.cos( th ), cur.radii[i] * math.sin( th )
|
||||
love.graphics.setCanvas()
|
||||
love.graphics.setColor( 0, 0, 0, 0.5 )
|
||||
love.graphics.circle( "fill", cx, cy, 0.02 )
|
||||
|
||||
love.graphics.setColor( 1.0, 0, 0, 0.5 )
|
||||
for k = 0.1, 1.0, 0.1 do
|
||||
th = ( i - 1 + k ) * 2.0 * math.pi / N
|
||||
r = cur:Interpolate( th )
|
||||
x, y = r * math.cos( th ), r * math.sin( th )
|
||||
love.graphics.circle( "fill", x, y, 0.01 )
|
||||
end
|
||||
end
|
||||
|
||||
love.graphics.setColor( 1, 1, 1, 0.5 )
|
||||
local r = cur:Interpolate( t )
|
||||
local x, y = r * math.cos( t ), r * math.sin( t )
|
||||
love.graphics.circle( "fill", x, y, 0.02 )
|
||||
end
|
||||
|
||||
local function Update()
|
||||
--Deep copy of current state to old state.
|
||||
for name, t in pairs( cur ) do
|
||||
for i = 1, 13 do
|
||||
old[name][i] = t[i]
|
||||
end
|
||||
end
|
||||
|
||||
local t = love.timer.getTime()
|
||||
for i = 1, N do
|
||||
cur.radii[i] = cur.radii[i] + old.vrad[i] * step
|
||||
cur.vrad[i] = cur.vrad[i] - cur:SecondDerivative( ( i - 1 ) / N )
|
||||
end
|
||||
|
||||
cur:DFT( )
|
||||
|
||||
end
|
||||
|
||||
local function DetectCollision( px, py, vpx, vpy )
|
||||
local xi, xf = px + vpx * step, py + vpy * step
|
||||
end
|
||||
|
||||
local function Reset()
|
||||
old = Wave()
|
||||
cur = Wave()
|
||||
end
|
||||
|
||||
Reset()
|
||||
|
||||
return {
|
||||
Reset = Reset,
|
||||
Update = Update,
|
||||
DetectCollision = DetectCollision,
|
||||
Draw = Draw,
|
||||
}
|
Loading…
Reference in New Issue