125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Summarize an ACDREAM_CAPTURE_PLAYER_QUANTA JSONL for issue #269."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def vector(value: dict[str, Any]) -> tuple[float, float, float]:
|
|
return float(value["x"]), float(value["y"]), float(value["z"])
|
|
|
|
|
|
def length(value: tuple[float, float, float]) -> float:
|
|
return math.sqrt(sum(component * component for component in value))
|
|
|
|
|
|
def horizontal(value: tuple[float, float, float]) -> float:
|
|
return math.hypot(value[0], value[1])
|
|
|
|
|
|
def subtract(
|
|
left: tuple[float, float, float],
|
|
right: tuple[float, float, float],
|
|
) -> tuple[float, float, float]:
|
|
return tuple(a - b for a, b in zip(left, right, strict=True))
|
|
|
|
|
|
def scale(
|
|
value: tuple[float, float, float],
|
|
scalar: float,
|
|
) -> tuple[float, float, float]:
|
|
return tuple(component * scalar for component in value)
|
|
|
|
|
|
def input_active(record: dict[str, Any]) -> bool:
|
|
state = record["input"]
|
|
return any(
|
|
bool(state[key])
|
|
for key in (
|
|
"forward",
|
|
"backward",
|
|
"strafeLeft",
|
|
"strafeRight",
|
|
"turnLeft",
|
|
"turnRight",
|
|
"jump",
|
|
)
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("capture", type=Path)
|
|
parser.add_argument(
|
|
"--tail",
|
|
type=int,
|
|
default=45,
|
|
help="quanta printed after the final directional-input release",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
with args.capture.open("r", encoding="utf-8") as stream:
|
|
records = [json.loads(line) for line in stream if line.strip()]
|
|
|
|
if not records:
|
|
print("capture contains no physics quanta")
|
|
return 2
|
|
|
|
release_indices = [
|
|
index
|
|
for index in range(1, len(records))
|
|
if input_active(records[index - 1]) and not input_active(records[index])
|
|
]
|
|
start = release_indices[-1] if release_indices else max(0, len(records) - args.tail)
|
|
stop = min(len(records), start + args.tail)
|
|
|
|
print(
|
|
"seq dt walk(pre/post) |v|pre |v|fric |v|commit "
|
|
"horizCommit rootDelta collision fsf"
|
|
)
|
|
for record in records[start:stop]:
|
|
dt = float(record["dt"])
|
|
pre = record["preIntegration"]
|
|
post_integration = record["postIntegration"]
|
|
post_commit = record["postCommit"]
|
|
pre_velocity = vector(pre["velocity"])
|
|
integrated_velocity = vector(post_integration["velocity"])
|
|
acceleration = vector(pre["acceleration"])
|
|
friction_velocity = subtract(
|
|
integrated_velocity,
|
|
scale(acceleration, dt),
|
|
)
|
|
commit_velocity = vector(post_commit["velocity"])
|
|
transient_pre = int(pre["transientState"])
|
|
transient_post = int(post_commit["transientState"])
|
|
pre_walk = (transient_pre & 0x2) != 0
|
|
post_walk = (transient_post & 0x2) != 0
|
|
root_delta = length(vector(record["rootAndManagerDelta"]))
|
|
resolve = record["resolve"]
|
|
collision = "yes" if resolve["collisionNormalValid"] else "no"
|
|
print(
|
|
f'{record["sequence"]:5d} {dt:0.5f} '
|
|
f"{int(pre_walk)}/{int(post_walk)} "
|
|
f"{length(pre_velocity):8.4f} "
|
|
f"{length(friction_velocity):8.4f} "
|
|
f"{length(commit_velocity):8.4f} "
|
|
f"{horizontal(commit_velocity):8.4f} "
|
|
f"{root_delta:8.4f} {collision:>3s} "
|
|
f'{post_commit["framesStationaryFall"]:d}'
|
|
)
|
|
|
|
if release_indices:
|
|
print(f"\nlast directional-input release: sequence {records[start]['sequence']}")
|
|
else:
|
|
print("\nno directional-input release edge found; showing capture tail")
|
|
print(f"records: {len(records)}, displayed: {stop - start}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|