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 sitelenpona
|
||||||
local text
|
local text
|
||||||
local marble
|
local marble
|
||||||
|
local wave
|
||||||
|
|
||||||
local sounds = {
|
local sounds = {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
local state
|
local state
|
||||||
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()
|
function love.load()
|
||||||
|
UpdateWindowTransform( love.graphics.getDimensions() )
|
||||||
|
|
||||||
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
|
love.graphics.setBackgroundColor( 245 / 255, 169 / 255, 184 / 255 ) --Trans pink.
|
||||||
--love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.
|
--love.graphics.setBackgroundColor( 91 / 255, 206 / 255, 250 / 255 ) --Trans blue.
|
||||||
sounds.goodPing = love.audio.newSource("soundTest.ogg", "static")
|
sounds.goodPing = love.audio.newSource("sounds/soundTest.ogg", "static")
|
||||||
sounds.badPing = love.audio.newSource("chime8.ogg", "static")
|
sounds.badPing = love.audio.newSource("sounds/chime8.ogg", "static")
|
||||||
sitelenpona = assert( require "sitelenpona" )
|
sitelenpona = assert( require "sitelenpona" )
|
||||||
text = assert( require "text" )
|
text = assert( require "text" )
|
||||||
marble = assert( require "marble" )
|
marble = assert( require "marble" )
|
||||||
|
wave = assert( require "wave" )
|
||||||
return state.Reset()
|
return state.Reset()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -134,7 +150,6 @@ end
|
||||||
|
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
local w, h = love.graphics.getDimensions()
|
|
||||||
|
|
||||||
|
|
||||||
love.graphics.setColor(1.0, 1.0, 1.0)
|
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.beatScoreThreshold, 0, 20)
|
||||||
love.graphics.print( state.lastBeatScore, 0, 30 )
|
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] )
|
sitelenpona.Draw( text.tok[state.currentBeat] )
|
||||||
|
|
||||||
marble.Draw()
|
marble.Draw()
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,8 +178,10 @@ function love.update( dt )
|
||||||
dt = dt + state.timeToSimulate
|
dt = dt + state.timeToSimulate
|
||||||
while dt > step do
|
while dt > step do
|
||||||
marble.Update()
|
marble.Update()
|
||||||
|
wave.Update()
|
||||||
dt = dt - step
|
dt = dt - step
|
||||||
end
|
end
|
||||||
|
state.timeToSimulate = dt
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed( key, code, isRepeat )
|
function love.keypressed( key, code, isRepeat )
|
||||||
|
@ -170,3 +193,8 @@ end
|
||||||
function love.keyreleased( key, code )
|
function love.keyreleased( key, code )
|
||||||
return marble.OnKey()
|
return marble.OnKey()
|
||||||
end
|
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.
|
--Character controller. Renders point particle.
|
||||||
local love = love
|
local love = love
|
||||||
|
--local wave = assert( require "wave" )
|
||||||
local oldBuffer = love.graphics.newCanvas()
|
local oldBuffer = love.graphics.newCanvas()
|
||||||
local newBuffer = love.graphics.newCanvas()
|
local newBuffer = love.graphics.newCanvas()
|
||||||
local step = assert( step )
|
local step = assert( step )
|
||||||
local state
|
local state
|
||||||
local FRICTION = 0.008
|
local FRICTION = 0.01
|
||||||
local MAXSPEED = 1.0
|
local MAXSPEED = 5.0
|
||||||
|
|
||||||
local t, x, y, dx, dy, ddx, ddy
|
local t, x, y, dx, dy, ddx, ddy
|
||||||
|
|
||||||
|
@ -16,11 +17,14 @@ local function Update()
|
||||||
dy = (1.0 - FRICTION) * dy + FRICTION * ddy
|
dy = (1.0 - FRICTION) * dy + FRICTION * ddy
|
||||||
local xf = x + dx * step * MAXSPEED
|
local xf = x + dx * step * MAXSPEED
|
||||||
local yf = y + dy * step * MAXSPEED
|
local yf = y + dy * step * MAXSPEED
|
||||||
|
|
||||||
|
x = xf
|
||||||
|
y = yf
|
||||||
end
|
end
|
||||||
|
|
||||||
local function OnKey()
|
local function OnKey()
|
||||||
ddx = (love.keyboard.isScancodeDown( "d" ) and 1.0 or 0.0) - (love.keyboard.isScancodeDown( "a" ) and 1.0 or 0.0)
|
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 )
|
local n = math.sqrt( ddx * ddx + ddy * ddy )
|
||||||
if n < 0.001 then return end
|
if n < 0.001 then return end
|
||||||
|
@ -32,18 +36,22 @@ local function Draw()
|
||||||
local xf, yf = x + dt * dx, y + dt * dy
|
local xf, yf = x + dt * dx, y + dt * dy
|
||||||
|
|
||||||
local w, h = love.graphics.getDimensions()
|
local w, h = love.graphics.getDimensions()
|
||||||
local xi, yi = 1000.0 * x + 0.5 * w, 1000.0 * y + 0.5 * h
|
local xi, yi = transform:transformPoint( x, y )
|
||||||
xf, yf = 1000.0 * xf + 0.5 * w, 1000.0 * yf + 0.5 * h
|
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.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.
|
love.graphics.draw( oldBuffer ) --Time-dependent fade: overlay canvas over itself.
|
||||||
|
|
||||||
--Render latest segment in trail.
|
--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.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.setCanvas( oldBuffer )
|
||||||
love.graphics.clear( 1.0, 1.0, 1.0, 0.0 )
|
love.graphics.clear( 1.0, 1.0, 1.0, 0.0 )
|
||||||
|
@ -51,8 +59,9 @@ local function Draw()
|
||||||
--Render circle directly to screen.
|
--Render circle directly to screen.
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
love.graphics.draw( newBuffer )
|
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.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
|
oldBuffer, newBuffer = newBuffer, oldBuffer
|
||||||
|
|
||||||
|
@ -64,6 +73,10 @@ end
|
||||||
|
|
||||||
--Window resize.
|
--Window resize.
|
||||||
local function Resize()
|
local function Resize()
|
||||||
|
newBuffer = love.graphics.newCanvas()
|
||||||
|
--TODO: render oldBuffer to new newBuffer, but scaled down.
|
||||||
|
oldBuffer = love.graphics.newCanvas()
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,4 +100,5 @@ return {
|
||||||
Draw = Draw,
|
Draw = Draw,
|
||||||
Impact = Impact,
|
Impact = Impact,
|
||||||
Reset = Reset,
|
Reset = Reset,
|
||||||
|
Resize = Resize,
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ end
|
||||||
sp.Draw = function( str )
|
sp.Draw = function( str )
|
||||||
local w, h = love.graphics.getDimensions()
|
local w, h = love.graphics.getDimensions()
|
||||||
local x, y = 0.5 * w - 128, 0.5 * h - 128
|
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 )
|
love.graphics.draw( sp[str] or sp.q, x, y )
|
||||||
end
|
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