fixed wasm compatibility

This commit is contained in:
Walter Oggioni
2019-04-27 10:59:39 +01:00
parent 0127bbee0b
commit b0a890a85b
6 changed files with 115 additions and 16 deletions

55
src/geo2d.nim Normal file
View File

@@ -0,0 +1,55 @@
import mmath/smatrix
import mmath/svector
from nwo/utils import `...`
from math import sin, cos
type
X2d* = SquareSMatrix[3, float32]
P2d* = SVector[3, float32]
Rect2d* = object
tl*, br* : P2d
proc newP2d*(x, y : float32) : P2d =
P2d(buildSVector[3, float32](x,y,1f32))
proc x*(p : P2d) : float32 = p[0]
proc y*(p : P2d) : float32 = p[1]
proc newRect2d*(x, y, width, height : float32) : Rect2d = Rect2d(tl: newP2d(x,y), br: newP2d(x + width, y + height))
proc top*(rect : Rect2d) : float32 = rect.tl.y
proc bottom*(rect : Rect2d) : float32 = rect.br.y
proc left*(rect : Rect2d) : float32 = rect.tl.x
proc right*(rect : Rect2d) : float32 = rect.br.x
proc width*(rect : Rect2d) : float32 = rect.br.x - rect.tl.x
proc height*(rect : Rect2d) : float32 = rect.br.y - rect.tl.y
proc `*`*(rect : Rect2d, xform : X2d) : Rect2d = Rect2d(tl: rect.tl * xform, br: rect.br * xform)
proc rot*(alpha: float32) : X2d =
result = identity[3,float32]()
let ca = cos(alpha)
let sa = sin(alpha)
result[0,0] = ca
result[1,1] = ca
result[0,1] = sa
result[1,0] = -sa
proc scale*(x, y: float32) : X2d =
result = identity[3,float32]()
result[0,0] = x
result[1,1] = y
proc xlate*(x, y: float32) : X2d =
result = identity[3,float32]()
result[2,0] = x
result[2,1] = y
proc xlate*(p: P2d) : X2d =
result = identity[3,float32]()
result[2,0] = p.x
result[2,1] = p.y
proc scale*(center : P2d, x, y : float32) : X2d = xlate(-center.x, -center.y) * scale(x,y) * xlate(center.x, center.y)
proc rot*(center : P2d, angle : float32) : X2d = xlate(-center.x, -center.y) * rot(angle) * xlate(center.x, center.y)

82
src/grid.nim Normal file
View File

@@ -0,0 +1,82 @@
import random
from times import epochTime
from nwo/utils import nmod
type Grid* = object
rows*, columns* : int
data : seq[uint32]
proc newGrid*(rows, columns : int) : Grid =
Grid(
rows: rows,
columns: columns,
data: newSeq[uint32]((rows * columns + 8 - 1) div 8)
)
proc get(grid : Grid, x, y : int) : bool =
assert x < grid.columns
assert y < grid.rows
let index = y * grid.columns + x
(grid.data[index div 8] and (1u32 shl (index mod 8))) != 0
proc set(grid : var Grid, x, y : int, value : bool) : void =
assert x < grid.columns
assert y < grid.rows
let index = y * grid.columns + x
grid.data[index div 8] =
if value:
grid.data[index div 8] or (1u32 shl (index mod 8))
else:
grid.data[index div 8] and (not (1u32 shl (index mod 8)))
template `[]`*(grid : Grid, x,y : int) : bool =
get(grid, x, y)
template `[]=`*(grid : Grid, x,y : int, value : bool) =
set(grid, x, y, value)
proc next_step*(old_grid, new_grid : var Grid) =
for x in 0..<old_grid.columns:
for y in 0..<old_grid.rows:
var alive_neighbours = 0
if old_grid[nmod(x+1, old_grid.columns), y]:
alive_neighbours += 1
if old_grid[nmod(x+1, old_grid.columns), nmod(y+1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[nmod(x+1, old_grid.columns), nmod(y-1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[nmod(x-1, old_grid.columns), y]:
alive_neighbours += 1
if old_grid[nmod(x-1, old_grid.columns), nmod(y + 1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[nmod(x-1, old_grid.columns), nmod(y - 1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[nmod(x, old_grid.columns), nmod(y + 1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[nmod(x, old_grid.columns), nmod(y - 1, old_grid.rows)]:
alive_neighbours += 1
if old_grid[x, y]:
if alive_neighbours < 2 or alive_neighbours > 3:
new_grid[x, y] = false
else:
new_grid[x, y] = true
elif alive_neighbours == 3:
new_grid[x, y] = true
else:
new_grid[x, y] = false
let tmp = old_grid
old_grid = new_grid
new_grid = tmp
proc rand_init*(grid : var Grid) =
var rand = initRand(epochTime().int64)
for x in 0..<grid.columns:
for y in 0..<grid.rows:
grid[x, y] = (rand.next() mod 2) != 0
proc clear*(grid : var Grid) =
for x in 0..<grid.columns:
for y in 0..<grid.rows:
grid[x, y] = false

50
src/hello_sdl.nim Normal file
View File

@@ -0,0 +1,50 @@
import sdl2, sdl2/gfx
import geo2d
when defined(wasm):
{.emit: "#include <emscripten.h>".}
proc emscripten_set_main_loop_arg*(loopFunction: proc(ctx : pointer) {.cdecl.}, ctx : pointer, fps : cint, simulate_infinite_loop : cint) {.importc.}
discard sdl2.init(INIT_EVERYTHING)
var
window: WindowPtr
renderer: RendererPtr
window = createWindow("Game of Life in SDL",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_SHOWN or SDL_WINDOW_RESIZABLE)
renderer = createRenderer(window, -1, Renderer_Accelerated or Renderer_PresentVsync or Renderer_TargetTexture)
type Context = object
renderer : RendererPtr
proc mainloop(arg : pointer) {.cdecl.} =
let windowSize = block:
var w, h : cint
window.getSize(w, h)
Rect2d(tl: newP2d(0f32, 0f32), br: newP2d(w.float32, h.float32))
let ctx = cast[ptr[Context]](arg)
let renderer = ctx.renderer
renderer.setDrawColor(255, 0, 0, 255)
renderer.clear()
let ticks = getTicks()
var r : Rect;
r.x = ((ticks.float64 / 3000 * windowSize.width).cuint mod windowSize.width.cuint).cint
r.y = 50;
r.w = 50;
r.h = 50;
renderer.setDrawColor(0, 255, 255)
renderer.fillRect(r)
renderer.present()
var ctx = Context(renderer : renderer)
emscripten_set_main_loop_arg(mainloop, addr(ctx), -1, 1)
destroy renderer
destroy window

184
src/main.nim Normal file
View File

@@ -0,0 +1,184 @@
import options
import sdl2, sdl2/gfx
import grid
import geo2d
import mmath/smatrix
import mmath/svector
from nwo/utils import box
when defined(wasm):
{.emit: "#include <emscripten.h>".}
proc emscripten_set_main_loop_arg*(loopFunction: proc(ctx : pointer) {.cdecl.}, ctx : pointer, fps : cint, simulate_infinite_loop : cint) {.importc.}
discard sdl2.init(INIT_EVERYTHING)
proc drawLine(renderer : RendererPtr, p0, p1 : P2d) = renderer.drawLine(p0.x.cint, p0.y.cint, p1.x.cint, p1.y.cint)
proc fillRect(renderer : RendererPtr, rect : Rect2d) =
var rect : Rect = (rect.left.cint, rect.top.cint, rect.width.cint, rect.height.cint)
renderer.fillRect(addr(rect))
var
window: WindowPtr
render: RendererPtr
window = createWindow("Game of Life in SDL",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_SHOWN or SDL_WINDOW_RESIZABLE)
render = createRenderer(window, -1, Renderer_Accelerated or Renderer_PresentVsync or Renderer_TargetTexture)
var
evt = sdl2.defaultEvent
quit = false
freeze = true
# fpsman: FpsManager
# fpsman.init
let side = 5
let
pixelx = 200
pixely = 200
var
current_grid, next_grid : ref[Grid]
block:
var grid = newGrid(pixelx, pixely)
grid.rand_init()
current_grid = box(grid)
next_grid = box(grid)
let bb = newRect2d(0f32, 0f32, float32(side * pixelx), float32(side * pixely))
var step : uint = 3
var elapsed_time : uint = 0
var
xform : X2d = identity[3, float32]()
uxform : X2d = identity[3, float32]()
pan_start : Option[P2d] = none[P2d]()
proc main_loop(arg : pointer) {.cdecl.} =
let windowSize = block:
var w, h : cint
window.getSize(w, h)
Rect2d(tl: newP2d(0f32, 0f32), br: newP2d(w.float32, h.float32))
while pollEvent(evt):
case evt.kind:
of QuitEvent:
quit = true
break
of KeyDown:
let keyboardEvent = cast[KeyboardEventPtr](addr(evt))
case keyboardEvent.keysym.sym:
of ' '.int:
freeze = not freeze
of 'h'.int:
uxform = identity[3, float32]()
of 'c'.int:
current_grid[].clear()
of 's'.int:
next_step(current_grid[], next_grid[])
of '['.int:
if step > 0u:
step -= 1
of ']'.int:
if step < 10:
step += 1
else:
discard
of MouseButtonDown:
let mouseButtonDownEvent = cast[MouseButtonEventPtr](addr(evt))
case mouseButtonDownEvent.button:
of BUTTON_RIGHT:
pan_start = some(newP2d(mouseButtonDownEvent.x.float32, mouseButtonDownEvent.y.float32))
of BUTTON_LEFT:
let inv = xform.invert()
let position = newP2d(mouseButtonDownEvent.x.float32, mouseButtonDownEvent.y.float32) * inv / side.float32
let row = position.x.int
let column = position.y.int
current_grid[][row, column] = not current_grid[][row, column]
else:
discard
of MouseButtonUp:
let mouseButtonUpEvent = cast[MouseButtonEventPtr](addr(evt))
case mouseButtonUpEvent.button:
of BUTTON_RIGHT:
let xlation = newP2d(mouseButtonUpEvent.x.float32, mouseButtonUpEvent.y.float32) - pan_start.get()
pan_start = none[P2d]()
uxform = uxform * xlate(xlation.x, xlation.y)
else:
discard
of MouseWheel:
let mouseWheelEvent = cast[MouseWheelEventPtr](addr(evt))
let zoomCenter = (block:
var x,y : cint
getMouseState(x,y)
newP2d(x.float32, y.float32)
)
if mouseWheelEvent.y > 0:
uxform = uxform * scale(zoomCenter, 1.1f32, 1.1f32)
else:
uxform = uxform * scale(zoomCenter, 0.9f32, 0.9f32)
# of MouseMotion:
# let mouseMotionEvent = cast[MouseMotionEventPtr](addr(evt))
# if pan_start.isSome:
# pan = pan * xlate(newP2d(mouseMotionEvent.x.float32, mouseMotionEvent.y.float32) - pan_start.get())
else:
discard
# let dt = fpsman.getFramerate() / 1000
# let dt = 10
render.setDrawColor 0,0,0,255
render.clear
var
x : float32 = bb.left
y : float32 = bb.top
render.setDrawColor 127,127,127,255
let f = min(windowSize.width / bb.width, windowSize.height / bb.height)
# let pan_xform = block:
# if pan_start.isSome():
# x
let pan = block:
if pan_start.isSome:
var x, y : cint
getMouseState(x, y)
xlate(newP2d(x.float32, y.float32) - pan_start.get())
else:
identity[3, float32]()
xform = xlate(-bb.width / 2.0f32, -bb.height / 2.0f32) * scale(f, f) *
xlate(windowSize.width / 2.0f32, windowSize.height / 2.0f32) * uxform * pan
while x <= bb.width:
render.drawLine(newP2d(x, bb.top) * xform, newP2d(x, bb.height) * xform)
x += side.float32
while y <= bb.height:
render.drawLine(newP2d(bb.left, y) * xform, newP2d(bb.width, y) * xform)
y += side.float32
render.setDrawColor 255,255,255,255
for i in 0..<current_grid.columns:
for j in 0..<current_grid.rows:
if current_grid[][i, j]:
render.fillRect(newRect2d((i * side).float32, (j * side).float32, side.float32, side.float32) * xform)
render.present
# fpsman.delay
# delay(0)
let current_time = getTicks().uint
if not freeze and current_time - elapsed_time > step * 50:
elapsed_time = current_time
next_step(current_grid[], next_grid[])
when defined(wasm):
emscripten_set_main_loop_arg(mainloop, nil, -1, 1)
else:
while not quit:
main_loop(nil)
delay(0)
destroy render
destroy window