///|
/// Serialise the current visual state of the world as JSON.
///
/// The payload is small and allocation friendly (a single string), so it can be
/// used to debug a simulation, to record a replay, or to hand the frame to a
/// front end that draws it:
///
/// ```json
/// {
/// "time": 2.5,
/// "bodies": [
/// {"i": 0, "shape": "polygon", "v": [[-20, 0], [20, 0], [20, 1], [-20, 1]],
/// "pos": [0, 0.5], "angle": 0, "asleep": true, "tag": 0},
/// {"i": 1, "shape": "circle", "r": 0.4, "pos": [0, 3], "angle": 0,
/// "asleep": false, "tag": 7}
/// ]
/// }
/// ```
///
/// Circles carry `r`, polygons carry their vertices `v`, both are in local
/// space: apply `pos` and `angle` to place them.
pub fn World::snapshot_json(self : World) -> String {
let out = StringBuilder(size_hint=2048)
out.write_string("{\"time\":")
write_json_number(out, self.time())
out.write_string(",\"bodies\":[")
let bodies = self.snapshot()
let mut first = true
for index, body in bodies {
if !first {
out.write_char(',')
}
first = false
write_json_body(out, index, body)
}
out.write_string("]}")
out.to_string()
}
///|
fn write_json_number(out : StringBuilder, value : Double) -> Unit {
out.write_string(value.to_string())
}
///|
fn write_json_vec(out : StringBuilder, value : Vec2) -> Unit {
out.write_char('[')
write_json_number(out, value.x())
out.write_char(',')
write_json_number(out, value.y())
out.write_char(']')
}
///|
fn write_json_body(
out : StringBuilder,
index : Int32,
body : BodySnapshot,
) -> Unit {
out.write_string("{\"i\":")
out.write_string(index.to_string())
out.write_string(",\"shape\":")
match body.shape() {
Circle(radius) => {
out.write_string("\"circle\",\"r\":")
write_json_number(out, radius)
}
Polygon(vertices) => {
out.write_string("\"polygon\",\"v\":[")
let mut first = true
for vertex in vertices {
if !first {
out.write_char(',')
}
first = false
write_json_vec(out, vertex)
}
out.write_char(']')
}
}
out.write_string(",\"pos\":")
write_json_vec(out, body.position())
out.write_string(",\"angle\":")
write_json_number(out, body.rotation())
out.write_string(",\"asleep\":")
out.write_string(if body.asleep() { "true" } else { "false" })
out.write_string(",\"tag\":")
out.write_string(body.user_data().to_string())
out.write_char('}')
}