import sdl2 import sdl2/ttf import strutils import linalg import sequtils import strformat import os var filename = "output.txt" if paramCount() > 0: filename = paramStr(1) let lines = filename.readFile().split('\n').filterIt(it.len() > 0) const windowWidth = 1000 windowHeight = 1000 ballRadius = 8 textHeight = 35 # assumed to be in ratio with the win width/height xMin = -50.0 xMax = 50.0 yMin = -50.0 yMax = 50.0 type Particle = object pos: Vector vel: Vector name: string var lineN = 1 proc parseLine(line: string, t: var float): seq[Particle] = type TokenType = enum ttFloat, ttString Token = object lineN: int case kind: TokenType: of ttFloat: floatVal: float of ttString: stringVal: string var c = 1 tokens: seq[Token] proc advance: char = c.inc line[c-1] proc peek: char = if c > line.high(): '\0' else: line[c] proc scanNum: float = let start = c - 1 while peek() in {'0'..'9'}: discard advance() if peek() == '.': discard advance() while peek() in {'0'..'9'}: discard advance() if peek() == 'e': discard advance() if peek() == '-' or peek() == '+': discard advance() while peek() in {'0'..'9'}: discard advance() return parseFloat(line[start..c-1]) proc scanIdent: string = let start = c - 1 while peek() in {'a' .. 'z'}: discard advance() return line[start..c-1] t = scanNum() while c < line.len(): case advance(): of '-': tokens.add(Token(lineN: lineN, kind: ttFloat, floatVal: scanNum())) of '0'..'9': tokens.add(Token(lineN: lineN, kind: ttFloat, floatVal: scanNum())) of 'a' .. 'z': tokens.add(Token(lineN: lineN, kind: ttString, stringVal: scanIdent())) else: discard #whitespace or [ or ] var i = 0 while i < tokens.len(): try: let name = tokens[i].stringVal let x = tokens[i+1].floatVal let y = tokens[i+2].floatVal let vx = tokens[i+3].floatVal let vy = tokens[i+4].floatVal i += 5 result.add(Particle(name: name, pos: vector(x, y), vel: vector(vx, vy))) except: echo "parsing crashed on line" & $tokens[i].lineN raise newException(Defect, "Parser crash") inc lineN proc vec2scr(v: Vector, x, y: var cint) = let xper = (v.x - xMin) / (xMax - xMin) let yper = (v.y - yMin) / (yMax - yMin) x = cint(xper * windowWidth) y = cint(yper * windowHeight) # traces var trace: array[2000, Vector] var traceIndex = 0 proc drawRectAt(v: Vector, renderer: RendererPtr) = trace[traceIndex] = v traceIndex.inc() if traceIndex > trace.high(): traceIndex = 0 # how many % of the screen it is at var x, y: cint vec2scr(v, x, y) if x < ballRadius or y < ballRadius: return if x + ballRadius > windowWidth or y + ballRadius > windowHeight: return x -= ballRadius y -= ballRadius let size = cint(2 * ballRadius) var r = rect(x, y, size, size) renderer.fillRect(r) proc drawArrow(start: Vector, delta: Vector, renderer: RendererPtr) = var x, y: cint var ex, ey: cint vec2scr(start, x, y) vec2scr(start+delta, ex, ey) renderer.drawLine(x, y, ex, ey) proc drawTrace(renderer: RendererPtr) = var x, y: cint for v in trace: vec2scr(v, x, y) renderer.drawPoint(x, y) proc draw(renderer: RendererPtr, font: FontPtr, frame: int, speed: int) = let line = lines[frame] var t = 0.0 let parts = parseLine(line, t) renderer.setDrawColor(0, 0, 0, 255) renderer.clear() renderer.setDrawColor(255, 255, 255, 255) for i in 0..parts.high: let part = parts[i] part.pos.drawRectAt(renderer) #part.pos.drawArrow(part.vel, renderer) renderer.setDrawColor(0, 0, 255, 255) #renderer.drawTrace() let color = color(255, 255, 255, 0) let text = &"t = {t:>09.2f} frame = {frame} speed = {speed}" let surface = ttf.renderTextSolid(font, text, color) let texture = renderer.createTextureFromSurface(surface) surface.freeSurface() defer: texture.destroy() let textWidth: int = text.len() * int(float(textHeight) * 0.6) var r = rect( cint((windowWidth - textWidth) div 2), 0, cint(textWidth), textHeight ) renderer.copy(texture, nil, addr r) renderer.present() type SDLException = object of Defect template sdlFailIf(cond: bool, reason: string) = if cond: raise newException(SDLException, reason & ", SDL error " & $getError()) proc main = sdlFailIf(not sdl2.init(INIT_VIDEO or INIT_TIMER or INIT_EVENTS)): "SDL2 init required" defer: sdl2.quit() let win = createWindow( title = "vis", x = SDL_WINDOWPOS_CENTERED, y = SDL_WINDOWPOS_CENTERED, w = windowWidth, h = windowHeight, flags = SDL_WINDOW_SHOWN ) sdlFailIf (win.isNil): "window couldn't be created" defer: win.destroy() let renderer = createRenderer( window = win, index = -1, flags = Renderer_Accelerated or Renderer_PresentVsync or Renderer_TargetTexture ) sdlFailIf renderer.isNil: "renderer couldn't be created" defer: renderer.destroy() sdlFailIf(not ttfInit()): "TTF init failed" defer: ttfQuit() let font = ttf.openFont("FreeMono.ttf", textHeight) sdlFailIf(font.isNil): "font couldn't be created" var running = true frame = 0 speed = 1 while running: var event = defaultEvent while pollEvent(event): case event.kind: of QuitEvent: running = false break of KeyDown: if event.key.keysym.scancode == SDL_SCANCODE_UP: speed *= 2 elif event.key.keysym.scancode == SDL_SCANCODE_DOWN: if speed > 1 or speed < -1: speed = speed div 2 elif event.key.keysym.scancode == SDL_SCANCODE_LEFT: speed = -speed else: discard frame += speed if frame < 1: frame = 1 if frame > lines.high(): frame = lines.high() draw(renderer, font, frame, speed) main()