///|
fn json_escape(text : String) -> String {
text
.replace_all(old="\\", new="\\\\")
.replace_all(old="\"", new="\\\"")
.replace_all(old="\n", new="\\n")
}
///|
fn json_string(text : String) -> String {
"\"" + json_escape(text) + "\""
}
///|
pub fn channels_to_json(channels : Array[BvhChannel]) -> String {
let parts : Array[String] = []
for channel in channels {
parts.push(json_string(channel.to_string()))
}
"[" + parts.join(",") + "]"
}
///|
pub fn vec3_to_json(vec : BvhVec3) -> String {
let result =
$|{"x":\{vec.x},"y":\{vec.y},"z":\{vec.z}}
result
}
///|
pub fn joint_to_json(joint : BvhJoint) -> String {
let children : Array[String] = []
for child in joint.children {
children.push(joint_to_json(child))
}
let ends : Array[String] = []
for end_site in joint.end_sites {
let row =
$|{"path":\{json_string(end_site.path)},"offset":\{vec3_to_json(end_site.offset)},"depth":\{end_site.depth}}
ends.push(row)
}
let result =
$|{"name":\{json_string(joint.name)},"path":\{json_string(joint.path)},"kind":\{json_string(joint.kind.to_string())},"offset":\{vec3_to_json(joint.offset)},"channels":\{channels_to_json(joint.channels)},"channelStart":\{joint.channel_start},"channelCount":\{joint.channel_count},"depth":\{joint.depth},"children":[\{children.join(",")}],"endSites":[\{ends.join(",")}]}
result
}
///|
pub fn stats_to_json(stats : BvhStats) -> String {
let result =
$|{"jointCount":\{stats.joint_count},"endSiteCount":\{stats.end_site_count},"maxDepth":\{stats.max_depth},"channelCount":\{stats.channel_count},"frameCount":\{stats.frame_count},"frameTime":\{stats.frame_time},"durationSeconds":\{stats.duration_seconds},"rootMotionDistance":\{stats.root_motion_distance}}
result
}
///|
pub fn validation_to_json(report : BvhValidationReport) -> String {
let issues : Array[String] = []
for issue in report.issues {
let row =
$|{"severity":\{json_string(issue.severity.to_string())},"path":\{json_string(issue.path)},"code":\{json_string(issue.code)},"message":\{json_string(issue.message)}}
issues.push(row)
}
let result =
$|{"ok":\{report.ok()},"errors":\{report.error_count},"warnings":\{report.warning_count},"info":\{report.info_count},"issues":[\{issues.join(",")}]}
result
}
///|
pub fn document_to_json(document : BvhDocument) -> String {
let stats = analyze_bvh(document)
let result =
$|{"format":"BVH","sourceLength":\{document.source_length},"tokenCount":\{document.token_count},"stats":\{stats_to_json(stats)},"root":\{joint_to_json(document.root)}}
result
}
///|
pub fn joint_table_csv(document : BvhDocument) -> String {
let rows : Array[String] = [
"path,name,depth,channel_start,channel_count,channels,offset_x,offset_y,offset_z",
]
fn walk(joint : BvhJoint, rows : Array[String]) -> Unit {
rows.push(
"\{joint.path},\{joint.name},\{joint.depth},\{joint.channel_start},\{joint.channel_count},\{channel_names(joint.channels).join("|")},\{joint.offset.to_csv()}",
)
for child in joint.children {
walk(child, rows)
}
}
if document.root.name.length() > 0 {
walk(document.root, rows)
}
rows.join("\n")
}
///|
pub fn channel_range_csv(document : BvhDocument) -> String {
let rows : Array[String] = ["path,channel,index,min,max,span"]
for range in channel_ranges(document) {
rows.push(
"\{range.path},\{range.channel},\{range.index},\{range.min},\{range.max},\{range.span}",
)
}
rows.join("\n")
}
///|
pub fn root_motion_csv(document : BvhDocument) -> String {
let rows : Array[String] = ["frame,x,y,z,delta_x,delta_y,delta_z"]
for
sample in sample_root_motion(
document,
max_samples=document.motion.frames.length(),
) {
rows.push(
"\{sample.frame_index},\{sample.root_position.to_csv()},\{sample.root_delta_from_first.to_csv()}",
)
}
rows.join("\n")
}