From 13abc803410020c9f7ac3e2a96391c7bcfc94942 Mon Sep 17 00:00:00 2001 From: Cristobal Date: Sat, 1 Jun 2024 17:32:03 -0500 Subject: [PATCH] Using a formula to calculate the trajectory --- src/frontend/simulations/matter.nim | 11 +- src/frontend/simulations/parabola.nim | 172 +++++++++++++++----------- 2 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/frontend/simulations/matter.nim b/src/frontend/simulations/matter.nim index eaf1669..30617d5 100644 --- a/src/frontend/simulations/matter.nim +++ b/src/frontend/simulations/matter.nim @@ -30,6 +30,15 @@ proc createRender*(options: JsObject): JsObject {.importjs: "Matter.Render.creat proc jsVector*(x, y: SomeNumber): JsVector = JsVector JsObject{x: x, y: y} proc jsVector*(x, y: JsObject): JsVector = JsVector JsObject{x: x, y: y} +proc `x`*(v: JsVector): float64 = + JsObject(v).x.to(float64) + +proc `y`*(v: JsVector): float64 = + JsObject(v).y.to(float64) + +proc toTuple*(v: JsVector): tuple[x, y: float64] = + (x: v.x, y: v.y) + proc `*`*(v1, v2: JsVector): JsVector = JsVector JsObject{x: JsObject(v1).x * JsObject(v2).x, y: JsObject(v1).y * JsObject(v2).y} @@ -53,5 +62,3 @@ proc `-`*(v1: JsVector, v2: float64): JsVector = proc `/`*(v1: JsVector, v2: float64): JsVector = JsVector JsObject{x: JsObject(v1).x / v2.toJs, y: JsObject(v1).y / v2.toJs} - - diff --git a/src/frontend/simulations/parabola.nim b/src/frontend/simulations/parabola.nim index d70c01b..27192e4 100644 --- a/src/frontend/simulations/parabola.nim +++ b/src/frontend/simulations/parabola.nim @@ -115,66 +115,27 @@ proc rotate(canon: var Canon, rad = degToRad(canonRotationDeg)) = proc rotateBack(canon: var Canon, rad = degToRad(canonRotationDeg)) = canon.rotate(-rad) -## Loads the simulation -proc load*(state: var ParabolaState) = - # Render all MathJax expressions asynchronously - MathJax.typesetPromise() - - # Load wrap's plugin and load matter aliases to point to the correct values - Matter.use("matter-wrap") - loadMatterAliases() - - state.canvas = getElementById("canvas") - state.engine = createEngine(JsObject{gravity: JsObject{x: 0, y: 1, scale: 0.001}, timing: JsObject{timeScale: timeScale}}) - state.render = createRender(JsObject{ - canvas: state.canvas, - engine: state.engine, - options: JsObject{ - width: state.canvas.clientWidth, - height: state.canvas.clientHeight, - showAngleIndicator: false, - showSleeping: false, - wireframes: false, - background: "transparent",#"rgb(20, 21, 31)", - } - }) - Render.run(state.render) - - state.runner = Runner.create(JsObject{delta: deltaTime}) - Runner.run(state.runner, state.engine) - - # Create and add all bodies to the world - state.canon.bulletOptions.plugin = JsObject{wrap: state.wrapObject()} - - state.canon.body = Bodies.rectangle(canonX, canonY, canonWidth, canonHeight, JsObject{ - isStatic: true, collisionFilter: JsObject{category: 0x2, mask: 0}, label: cstring"Canon", - render: JsObject{sprite: JsObject{ - texture: cstring canonTexture, - xOffset: 0, yOffset: 0 - }} - }) - state.canon.rotateBack(degToRad(60d)) - #constraint = Constraint.create(JsObject{pointA: jsVector(0, 0), bodyB: canon})#, length: 30, stiffness: 0.1}) +proc nextBulletPosition(state: ParabolaState): JsVector = + let vertice1 = state.canon.body.vertices[1] + let vertice2 = state.canon.body.vertices[2] + jsVector((vertice1.x + vertice2.x) / toJs 2, (vertice1.y + vertice2.y) / toJs 2) - state.ground = Bodies.rectangle(state.canvas.clientWidth / 2, - state.canvas.clientHeight + (groundHeight div 2), state.canvas.clientWidth * 1000, - groundHeight, JsObject{isStatic: true, label: cstring"Groubd"} - ) # 350, 495, 1200 +proc calcTrajectory(state: var ParabolaState) = + let pos = state.nextBulletPosition().toTuple() + let vel = state.canon.state.velocity + let g = to(state.engine.gravity.y * state.engine.gravity.scale, float) * 1750 + echo (p: pos, v: vel, g: g) - state.thingy = Bodies.rectangle(500, 350, 20, 80, JsObject{isStatic: false, label: cstring"Thingy", plugin: JsObject{wrap: state.wrapObject}}) + state.canon.trajectory.setLen(0) + var i = 0d + while i < 100: + let x = pos.x + (vel.x * i.float) + let y = pos.y + (-vel.y * i.float) + (g * 0.5 * (i.float ^ 2)) - state.mouse = Mouse.create(state.canvas) - state.mouseConstraint = MouseConstraint.create(state.engine, JsObject{mouse: state.mouse, collisionFilter: JsObject{mask: 0}}) - - Composite.add(state.engine.world, toJs [state.canon.body, state.mouseConstraint, - state.thingy, - # Walls - Bodies.rectangle(350, -200, 1000, 20, JsObject{isStatic: true}), # up - # Bodies.rectangle(690, 250, 20, 500, JsObject{isStatic: true}), # right - state.ground, # down - # Bodies.rectangle(10, 250, 20, 500, JsObject{isStatic: true}), # left - ]) + state.canon.trajectory.add JsVector JsObject{x: x, y: y} + i += 0.5 +proc loadEvents(state: var ParabolaState) = Events.on(state.mouseConstraint, "mousedown", proc(event: JsObject) = if Bounds.contains(state.canon.body.bounds, event.mouse.position).to(bool): state.canon.isDragging = true @@ -191,6 +152,7 @@ proc load*(state: var ParabolaState) = if state.canon.isDragging: let targetAngle = Vector.angle(canonPivot, state.mouse.position) state.canon.rotate(to(targetAngle - state.canon.body.angle, float)) + state.calcTrajectory() ) Events.on(state.engine, "collisionStart", proc(event: JsObject) = @@ -211,7 +173,7 @@ proc load*(state: var ParabolaState) = if state.canon.bullets.len > 0 and state.canon.status == csFlight: let pos = state.canon.bullet.position - print state.canon.bullet.velocity + drawArrow(state.render.context, pos.x, pos.y, pos.x, pos.y + (state.canon.bullet.velocity.y * toJs velocityVectorScale), @@ -240,6 +202,68 @@ proc load*(state: var ParabolaState) = ) ) +## Loads the simulation +proc load*(state: var ParabolaState) = + # Render all MathJax expressions asynchronously + MathJax.typesetPromise() + + # Load wrap's plugin and load matter aliases to point to the correct values + Matter.use("matter-wrap") + loadMatterAliases() + + state.canvas = getElementById("canvas") + state.engine = createEngine(JsObject{gravity: JsObject{x: 0, y: 1, scale: 0.001}, timing: JsObject{timeScale: timeScale}}) + state.render = createRender(JsObject{ + canvas: state.canvas, + engine: state.engine, + options: JsObject{ + width: state.canvas.clientWidth, + height: state.canvas.clientHeight, + showAngleIndicator: false, + showSleeping: false, + wireframes: false, + background: "transparent",#"rgb(20, 21, 31)", + } + }) + Render.run(state.render) + + state.runner = Runner.create(JsObject{delta: deltaTime}) + Runner.run(state.runner, state.engine) + + # Create and add all bodies to the world + state.canon.bulletOptions.plugin = JsObject{wrap: state.wrapObject()} + + state.canon.body = Bodies.rectangle(canonX, canonY, canonWidth, canonHeight, JsObject{ + isStatic: true, collisionFilter: JsObject{category: 0x2, mask: 0}, label: cstring"Canon", + render: JsObject{sprite: JsObject{ + texture: cstring canonTexture, + xOffset: 0, yOffset: 0 + }} + }) + state.canon.rotateBack(degToRad(60d)) + #constraint = Constraint.create(JsObject{pointA: jsVector(0, 0), bodyB: canon})#, length: 30, stiffness: 0.1}) + + state.ground = Bodies.rectangle(state.canvas.clientWidth / 2, + state.canvas.clientHeight + (groundHeight div 2), state.canvas.clientWidth * 1000, + groundHeight, JsObject{isStatic: true, label: cstring"Groubd"} + ) # 350, 495, 1200 + + state.thingy = Bodies.rectangle(500, 350, 20, 80, JsObject{isStatic: false, label: cstring"Thingy", plugin: JsObject{wrap: state.wrapObject}}) + + state.mouse = Mouse.create(state.canvas) + state.mouseConstraint = MouseConstraint.create(state.engine, JsObject{mouse: state.mouse, collisionFilter: JsObject{mask: 0}}) + + Composite.add(state.engine.world, toJs [state.canon.body, state.mouseConstraint, + state.thingy, + # Walls + Bodies.rectangle(350, -200, 1000, 20, JsObject{isStatic: true}), # up + # Bodies.rectangle(690, 250, 20, 500, JsObject{isStatic: true}), # right + state.ground, # down + # Bodies.rectangle(10, 250, 20, 500, JsObject{isStatic: true}), # left + ]) + + state.loadEvents() + ## Reloads the simulation proc reload*(state: var ParabolaState) = Composite.clear(state.engine.world) @@ -250,16 +274,13 @@ proc reload*(state: var ParabolaState) = state.load() ## Since matter measures y from the top of the screen, here we "normalize" it so that the 0 starts at the ground -proc normalizeBulletY(state: ParabolaState, y: int, bulletRadius: int): int = - -y + (state.ground.position.y.to(int) - (groundHeight div 2) - bulletRadius) +proc normalizeY(state: ParabolaState, y: int, height: int): int = + -y + (state.ground.position.y.to(int) - (groundHeight div 2) - height) proc fireBullet(state: var ParabolaState) = - let vertice1 = state.canon.body.vertices[1] - let vertice2 = state.canon.body.vertices[2] - + let pos = JsObject state.nextBulletPosition() let bullet = Bodies.circle( - (vertice1.x + vertice2.x) / toJs 2, - (vertice1.y + vertice2.y) / toJs 2, + pos.x, pos.y, state.canon.bulletRadius, state.canon.bulletOptions ) @@ -278,7 +299,7 @@ proc fireBullet(state: var ParabolaState) = # Invert velocity y since matter's coordinates start from the top instead of the bottom Body.setVelocity(bullet, jsVector(velocity.x / 2.5, -velocity.y / 2.5)) -proc calcTrajectory(state: var ParabolaState) {.async.} = +proc calcTrajectory2(state: var ParabolaState) {.async.} = var stop = false # Stop updating the engine state.fireBullet() @@ -338,7 +359,7 @@ proc renderTextDiv*(state: ParabolaState): VNode = let bullet = state.canon.bullet x = int bullet.position.x.to(float) - y = state.normalizeBulletY(int bullet.position.y.to(float), bullet.circleRadius.to(int)) + y = state.normalizeY(int bullet.position.y.to(float), bullet.circleRadius.to(int)) angle = normalizeAngle(bullet.angle.to(float)) speed = state.canon.state.speed @@ -372,18 +393,18 @@ proc renderSimDiv*(state: var ParabolaState): VNode = span(class = "material-symbols-outlined", text "rotate_left") proc onclick() = state.canon.rotateBack() - discard state.calcTrajectory() + state.calcTrajectory() button(): span(class = "material-symbols-outlined", text "rotate_right") proc onclick() = state.canon.rotate() - discard state.calcTrajectory() + state.calcTrajectory() #button(): # verbatim parabolaIconSvg # #img(src = "/public/img/parabola.svg", alt = "Parabola Trajectory") - # proc onclick() = discard calcTrajectory() + # proc onclick() = calcTrajectory() # #text "Trajectory" button(): @@ -430,13 +451,13 @@ proc addEventListeners*(state: var ParabolaState) = echo $event.key case $event.key of "t": - discard state.calcTrajectory() + state.calcTrajectory() of "ArrowRight": state.canon.rotate() - discard state.calcTrajectory() + state.calcTrajectory() of "ArrowLeft": state.canon.rotateBack() - discard state.calcTrajectory() + state.calcTrajectory() of "ArrowUp", " ": state.fireBullet() of "Backspace": @@ -446,10 +467,11 @@ proc addEventListeners*(state: var ParabolaState) = #of "r": # let exercise = exercises[curExercise] # let bullet = bullets[currentBullet] - # Body.setPosition(bullet, jsVector(exercise.pos.x, state.normalizeBulletY(exercise.pos.y))) + # Body.setPosition(bullet, jsVector(exercise.pos.x, state.normalizeY(exercise.pos.y))) # Body.setAngle(bullet, degToRad(float(360 - exercise.angle))) - # discard calcTrajectory() + # calcTrajectory() # exerciseStatus = csReady - #of "d": + of "d": echo state + print state.canon.bullet )