254 lines
6.6 KiB
Nim
254 lines
6.6 KiB
Nim
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() |