diff --git a/public/img/parabola.svg b/public/img/parabola.svg
deleted file mode 100644
index 8e81a06..0000000
--- a/public/img/parabola.svg
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
diff --git a/src/frontend/simulations/parabola.nim b/src/frontend/simulations/parabola.nim
index c616575..d32ec38 100644
--- a/src/frontend/simulations/parabola.nim
+++ b/src/frontend/simulations/parabola.nim
@@ -5,34 +5,32 @@ import matter, utils
type
Exercise = object
- pos: tuple[x, y: int]
- angle: int # Degrees
- angleRad: float
- speed: int
- velocity: tuple[x, y: float]
+ pos*: tuple[x, y: int]
+ angle*: int # Degrees
+ angleRad*: float
+ speed*: int
+ velocity*: tuple[x, y: float]
+ text*: string
ExerciseStatus = enum
esStart # Not launched yet
esStarted # In the air
esEnd # Touched something and stopped
-proc initExercise(pos: tuple[x, y: int], angle: int, speed: int): Exercise =
+proc initExercise(pos: tuple[x, y: int], angle: int, speed: int, text = ""): Exercise =
let angleRad = degToRad(float angle)
- Exercise(pos: pos, angle: angle, angleRad: angleRad, speed: speed, velocity: speedToVelRad(float speed, angleRad))
+ Exercise(pos: pos, angle: angle, angleRad: angleRad, speed: speed, velocity: speedToVelRad(float speed, angleRad), text: text)
const
deltaTime = 1000 / 60 # 60fps, 60 times in one second (1000 milliseconds)
secPerFrame = 1 / 60
canvasWidth = 700
canvasHeight = 500
- floorHeight = 20
- timeScale = 0.65
-
-let
- wrapObject = JsObject{min: JsObject{x: 0, y: undefined}, max: JsObject{x: canvasWidth, y: undefined}} # To avoid boilerplate
+ groundHeight = 20
+ timeScale = 0.60
var
- engine*, mrender*, bullet, floor, ground*, runner*: JsObject
+ engine*, mrender*, bullet, ground*, runner*, thingy*: JsObject
constraint*, mconstraint*, mouse*: JsObject
canvas*: Element
@@ -40,12 +38,29 @@ var
mConstraintDragEnded*: bool # True when you release the mouse constraint
# The first exercise is the default exercise and it is modifed as you modify the bullet
- exercises = @[initExercise(pos = (canvasWidth div 2, 0), angle = 0, speed = 20), initExercise(pos = (50, 0), angle = 48, speed = 30)]
+ exercises = @[
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (0, 0), angle = 0, speed = 20),
+ initExercise(pos = (50, 0), angle = 48, speed = 30, text =
+ "Una pelota se lanza a 12 m/s y a un ángulo de 67° " &
+ "respecto a la horizontal. ¿Cuál es su altura a los 2 segundos?"
+ )
+ ]
curExercise = 0
exerciseStatus: ExerciseStatus
exerciseTotalTime: float
paused = false # Is engine.timing.timeScale == 0?
+
+proc wrapObject(): JsObject =
+ JsObject{min: JsObject{x: 0, y: undefined}, max: JsObject{x: canvas.clientWidth, y: undefined}} # To avoid boilerplate
+
## Loads the simulation
proc load*() =
# Render all MathJax expressions asynchronously
@@ -61,10 +76,10 @@ proc load*() =
canvas: canvas,
engine: engine,
options: JsObject{
- width: canvasWidth,
- height: canvasHeight,
+ width: canvas.clientWidth,
+ height: canvas.clientHeight,
showAngleIndicator: false,
- background: "rgb(20, 21, 31)",
+ background: "transparent",#"rgb(20, 21, 31)",
}
})
Render.run(mrender)
@@ -73,14 +88,19 @@ proc load*() =
Runner.run(runner, engine)
# Create and add all bodies to the world
- bullet = Bodies.circle(300, 300, 25, JsObject{isStatic: false, frictionAir: 0, friction: 1, plugin: JsObject{wrap: wrapObject}})
+ bullet = Bodies.circle(300, 300, 25, JsObject{isStatic: false, frictionAir: 0, friction: 1, plugin: JsObject{wrap: wrapObject()}})
Body.setInertia(bullet, Infinity)
# Body.setAngle(bullet, degToRad(180d))
# constraint = Constraint.create(JsObject{pointA: jsVector(400, 300), bodyB: bullet, length: 30, stiffness: 0.1})
- floor = Bodies.rectangle(350, 495, 1200, floorHeight, JsObject{isStatic: true})
+ ground = Bodies.rectangle(canvas.clientWidth / 2,
+ canvas.clientHeight + (groundHeight div 2), canvas.clientWidth * 1000,
+ groundHeight, JsObject{isStatic: true}
+ ) # 350, 495, 1200
+
+ thingy = Bodies.rectangle(500, 350, 20, 80, JsObject{isStatic: false, plugin: JsObject{wrap: wrapObject()}})
mouse = Mouse.create(canvas)
mconstraint = MouseConstraint.create(engine, JsObject{mouse: mouse})
@@ -89,10 +109,9 @@ proc load*() =
# Walls
Bodies.rectangle(350, -200, 1000, 20, JsObject{isStatic: true}), # up
# Bodies.rectangle(690, 250, 20, 500, JsObject{isStatic: true}), # right
- floor, # down
+ ground, # down
# Bodies.rectangle(10, 250, 20, 500, JsObject{isStatic: true}), # left
- # Thingy
- Bodies.rectangle(500, 350, 20, 80, JsObject{isStatic: false, plugin: JsObject{wrap: wrapObject}})
+ thingy,
])
@@ -118,7 +137,7 @@ proc load*() =
if exerciseStatus == esStarted:
let pos = bullet.position
- drawArrow(mrender.context, pos.x, pos.y, pos.x + (bullet.velocity.x * toJs 10), pos.y + (bullet.velocity.y * toJs 10), toJs 5, toJs cstring"white")
+ drawArrow(mrender.context, pos.x, pos.y, pos.x + (bullet.velocity.x * toJs 5), pos.y + (bullet.velocity.y * toJs 5), toJs 5, toJs cstring"white")
mrender.context.globalAlpha = 1
Render.endViewTransform(mrender)
@@ -144,9 +163,9 @@ proc normalizeAngle(rad: float): int =
result = 360 - result
# echo "final ", result
-## Since matter measures y from the top of the screen, here we "normalize" it so that the 0 starts at the floor
+## Since matter measures y from the top of the screen, here we "normalize" it so that the 0 starts at the ground
proc normalizeY(y: int): int =
- -y + (floor.position.y.to(int) - (floorHeight div 2) - bullet.circleRadius.to(int))
+ -y + (ground.position.y.to(int) - (groundHeight div 2) - bullet.circleRadius.to(int))
proc sendBulletFlying(changeStatus = true) =
if paused: return
@@ -237,8 +256,8 @@ proc calcTrajectory() {.async.} =
# print bullet.jsonStringify()#Composite.allBodies(engine.world).jsonStringify()
proc renderTextDiv*(): VNode =
- buildHtml tdiv(style = "float: left; width: 40%;".toCss):
- p(text r"\(t_f = \frac{2 \cdot v_i \cdot \sin(\theta)}{g}\)", style = "font-size: 80px;".toCss)
+ buildHtml tdiv(style = "".toCss):
+ p(text r"\(t_f = \frac{2 \cdot v_i \cdot \sin(\theta)}{g}\)", style = "font-size: 50px;".toCss)
if not bullet.isNil:
p(text &"x = {int bullet.position.x.to(float)} y = {normalizeY(int bullet.position.y.to(float))}")
@@ -260,7 +279,7 @@ proc renderSimDiv*(): VNode =
discard calcTrajectory()
exerciseStatus = esStart
- buildHtml tdiv(style = "float: right; width: 40%;".toCss):
+ buildHtml tdiv(style = "".toCss):
button():
#text "Pause/Resume"
if engine.isNil:
@@ -292,53 +311,7 @@ proc renderSimDiv*(): VNode =
discard calcTrajectory()
button():
- verbatim """
-
- """
+ verbatim parabolaIconSvg
#img(src = "/public/img/parabola.svg", alt = "Parabola Trajectory")
proc onclick() = discard calcTrajectory()
#text "Trajectory"
@@ -349,22 +322,36 @@ proc renderSimDiv*(): VNode =
proc onclick() =
sendBulletFlying()
- canvas(id = "canvas", style = fmt"width: {canvasWidth}px; height: {canvasHeight}px; background: rgb(20, 21, 31)".toCss):
+ br()
+
+ #canvas(id = "canvas", style = fmt"width: {canvasWidth}px; height: {canvasHeight}px; background: rgb(20, 21, 31)".toCss):
+ canvas(id = "canvas", style = fmt"width: 50vw; min-width: 300px; height: 50vh; min-height: 100px; background: rgb(20, 21, 31)".toCss):
text "Matter-js simulation"
- tdiv(id = "exercises", style = "height: 200px; overflow-y: auto;".toCss):
+ tdiv(id = "exercises", style = "min-height: 0px; overflow-y: auto;".toCss):
for e, exercise in exercises:
if e == 0: continue # First exercise is the default exercise
- button(onclick = exerciseOnClick(e)):
- text &"#{e} angle = {exercise.angle} vi = {exercise.speed} pos = ({exercise.pos.x}, {exercise.pos.x})"
+ tdiv(style = "display: block;".toCss):
+ button(onclick = exerciseOnClick(e)):
+ text &"#{e} angle = {exercise.angle} vi = {exercise.speed} pos = ({exercise.pos.x}, {exercise.pos.x})"
proc render*(): VNode =
- buildHtml tdiv(style = "width: 100%; justify-content: center; align-items: center;".toCss):
+ buildHtml tdiv(style = "display: flex; flex-flow: column wrap; justify-content: start; align-items: center;".toCss):
renderTextDiv()
renderSimDiv()
-document.addEventListener("keyup", proc (event: Event) =
+window.addEventListener("resize", proc(event: Event) =
+ mrender.canvas.width = canvas.clientWidth
+ mrender.canvas.height = canvas.clientHeight
+
+ bullet.plugin.wrap = wrapObject()
+ thingy.plugin.wrap = wrapObject()
+
+ Body.setPosition(ground, JsObject{x: canvas.clientWidth / 2, y: canvas.clientHeight + (groundHeight div 2)})
+)
+
+document.addEventListener("keyup", proc(event: Event) =
let event = KeyboardEvent(event)
case $event.key
of "t":
diff --git a/src/frontend/simulations/utils.nim b/src/frontend/simulations/utils.nim
index e31969e..648b6a7 100644
--- a/src/frontend/simulations/utils.nim
+++ b/src/frontend/simulations/utils.nim
@@ -2,6 +2,54 @@ import std/[jsffi, math]
import matter
+const
+ parabolaIconSvg* = """
+
+"""
+
var
Infinity* {.importjs, nodecl.}: JsObject
MathJax* {.importjs, nodecl.}: JsObject