Fork bvh_anim

This commit is contained in:
Danny McGee
2023-12-19 17:12:14 -05:00
parent 29edf101ce
commit 0dc07e9608
14 changed files with 5202 additions and 9 deletions
Generated
+114 -8
View File
@@ -174,6 +174,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "approx"
version = "0.5.1"
@@ -208,6 +214,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
version = "0.7.4"
@@ -1413,6 +1425,18 @@ dependencies = [
"serde",
]
[[package]]
name = "bitvec"
version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "blake3"
version = "1.5.0"
@@ -1420,7 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
dependencies = [
"arrayref",
"arrayvec",
"arrayvec 0.7.4",
"cc",
"cfg-if",
"constant_time_eq",
@@ -1467,6 +1491,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata 0.1.10",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
@@ -1527,7 +1562,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
"nom 7.1.3",
]
[[package]]
@@ -2063,6 +2098,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "futures-core"
version = "0.3.28"
@@ -2553,6 +2594,29 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "lexical"
version = "5.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f404a90a744e32e8be729034fc33b90cf2a56418fbf594d69aa3c0214ad414e5"
dependencies = [
"cfg-if",
"lexical-core",
]
[[package]]
name = "lexical-core"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec 0.5.2",
"bitflags 1.3.2",
"cfg-if",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.148"
@@ -2842,6 +2906,19 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "nom"
version = "6.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
dependencies = [
"bitvec",
"funty",
"lexical-core",
"memchr",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.3"
@@ -3142,7 +3219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55dc0e6db79bddbc5fd583569f7356cdcc63e1e9b2b93a9ab70dd8e717160e0"
dependencies = [
"approx",
"arrayvec",
"arrayvec 0.7.4",
"bitflags 1.3.2",
"downcast-rs",
"either",
@@ -3269,6 +3346,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "radsort"
version = "0.1.0"
@@ -3288,7 +3371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62a8a0bd9d3135f7b4eb45d0796540e7bab47b6b7c974f90567ccc5a0454f42b"
dependencies = [
"approx",
"arrayvec",
"arrayvec 0.7.4",
"bit-vec",
"bitflags 1.3.2",
"crossbeam",
@@ -3609,6 +3692,7 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
name = "studio"
version = "0.1.0"
dependencies = [
"anyhow",
"bevy",
"bevy-inspector-egui",
"bevy_mod_outline",
@@ -3626,6 +3710,16 @@ dependencies = [
"web-sys",
]
[[package]]
name = "studio-bvh"
version = "0.1.0"
dependencies = [
"bstr",
"lexical",
"nom 6.1.2",
"smallvec",
]
[[package]]
name = "svg_fmt"
version = "0.4.1"
@@ -3674,12 +3768,18 @@ version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82870da09c57a8a5a50f830ce8993a6637b9a4932f69257f12aea3fa68f588c9"
dependencies = [
"arrayvec",
"arrayvec 0.7.4",
"grid",
"num-traits",
"slotmap",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "termcolor"
version = "1.3.0"
@@ -4098,7 +4198,7 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed547920565c56c7a29afb4538ac5ae5048865a5d2f05bff3ad4fbeb921a9a2c"
dependencies = [
"arrayvec",
"arrayvec 0.7.4",
"cfg-if",
"js-sys",
"log",
@@ -4122,7 +4222,7 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f8a44dd301a30ceeed3c27d8c0090433d3da04d7b2a4042738095a424d12ae7"
dependencies = [
"arrayvec",
"arrayvec 0.7.4",
"bit-vec",
"bitflags 2.4.0",
"codespan-reporting",
@@ -4146,7 +4246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a80bf0e3c77399bb52850cb0830af9bad073d5cfcb9dd8253bef8125c42db17"
dependencies = [
"android_system_properties",
"arrayvec",
"arrayvec 0.7.4",
"ash",
"bit-set",
"bitflags 2.4.0",
@@ -4488,6 +4588,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "x11-dl"
version = "2.21.0"
+7 -1
View File
@@ -1,6 +1,12 @@
[workspace]
resolver = "2"
members = ["./studio"]
members = [
"./studio",
"./studio-bvh",
]
[workspace.dependencies]
anyhow = "1.0.75"
[workspace.dependencies.bevy]
version = "0.12.0"
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "studio-bvh"
version = "0.1.0"
edition = "2021"
authors = [
"George Burton <burtonageo@gmail.com>",
"Storyteller.ai",
]
[dependencies] # TODO: Update dependencies
bstr = "0.2"
lexical = "5.2"
nom = "6"
smallvec = "1.5"
+18
View File
@@ -0,0 +1,18 @@
Copyright 2019-2020 George Burton
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+32
View File
@@ -0,0 +1,32 @@
{
"name": "studio-bvh",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"sourceRoot": "./studio-bvh/src",
"targets": {
"build": {
"executor": "@nxrs/cargo:build",
"options": {
"release": false
},
"configurations": {
"production": {
"release": true
}
}
},
"test": {
"executor": "@nxrs/cargo:test",
"options": {}
},
"lint": {
"executor": "@nxrs/cargo:clippy",
"options": {
"fix": false,
"failOnWarnings": true,
"noDeps": true
}
}
},
"tags": []
}
+507
View File
@@ -0,0 +1,507 @@
//! Errors which may occur when manipulating `Bvh` files.
use crate::Axis;
use lexical::Error as LexicalError;
use std::{error::Error as StdError, fmt, io};
/// Errors which may arise when loading a `Bvh` file from
/// a `Reader`.
#[derive(Debug)]
pub struct LoadError {
/// The error kind.
kind: LoadErrorKind,
}
impl LoadError {
/// Get the line where the error occurred, or `None` if there is
/// no associated line number.
#[inline]
pub fn line(&self) -> Option<usize> {
match self.kind {
LoadErrorKind::Joints(ref e) => e.line(),
LoadErrorKind::Motion(ref e) => e.line(),
}
}
/// Returns the `LoadError` kind.
#[inline]
pub fn kind(&self) -> &LoadErrorKind {
&self.kind
}
/// Unwraps the `LoadErrorKind` from the `LoadError`.
#[inline]
pub fn into_kind(self) -> LoadErrorKind {
self.kind
}
}
impl<K: Into<LoadErrorKind>> From<K> for LoadError {
#[inline]
fn from(kind: K) -> Self {
LoadError { kind: kind.into() }
}
}
impl fmt::Display for LoadError {
#[inline]
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = match self.kind {
LoadErrorKind::Joints(_) => "Could not load hierarchy",
LoadErrorKind::Motion(_) => "Could not load motion",
};
write!(fmtr, "{}: {}", desc, self.source().unwrap())
}
}
impl StdError for LoadError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self.kind {
LoadErrorKind::Joints(ref e) => Some(e),
LoadErrorKind::Motion(ref e) => Some(e),
}
}
}
/// The kind of the `LoadError`.
#[derive(Debug)]
pub enum LoadErrorKind {
/// An error occurred when loading the joints hierarchy.
Joints(LoadJointsError),
/// An error occurred when loading the motion values.
Motion(LoadMotionError),
}
impl From<LoadJointsError> for LoadErrorKind {
#[inline]
fn from(e: LoadJointsError) -> Self {
LoadErrorKind::Joints(e)
}
}
impl From<LoadMotionError> for LoadErrorKind {
#[inline]
fn from(e: LoadMotionError) -> Self {
LoadErrorKind::Motion(e)
}
}
/// Represents an error which occurred when loading the `Joints` of the
/// bvh file.
#[derive(Debug)]
pub enum LoadJointsError {
/// An I/O error occurred.
Io(io::Error),
/// The skeletal hierarchy is missing the `Root` joint.
MissingRoot,
/// A name could not be found for the `Joint`.
MissingJointName {
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// A `CHANNELS` section was encountered in the wrong location.
UnexpectedChannelsSection {
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// The number of channels could not be parsed in a `CHANNELS` section.
ParseNumChannelsError {
/// The parse error, if there was a malformed string to parse.
error: Option<LexicalError>,
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// A channel type could not be parsed in the `CHANNELS` section.
ParseChannelError {
/// The parse error.
error: ParseChannelError,
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// An `OFFSET` section was encountered in the wrong location.
UnexpectedOffsetSection {
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// An axis in the `OFFSET` section could not be parsed into a value.
ParseOffsetError {
/// The parse error.
parse_float_error: LexicalError,
/// The axis of the offset which could not be parsed.
axis: Axis,
/// Line number in the source bvh where the error occurred.
line: usize,
},
/// An `OFFSET` section was missing an axis in the offset vector.
MissingOffsetAxis {
/// The smallest axis which was missing.
axis: Axis,
/// Line number in the source bvh where the error occurred.
line: usize,
},
}
impl LoadJointsError {
/// Get the line where the error occurred, or `None` if there is
/// no associated line number.
#[inline]
pub fn line(&self) -> Option<usize> {
match *self {
LoadJointsError::MissingJointName { line }
| LoadJointsError::UnexpectedChannelsSection { line }
| LoadJointsError::ParseNumChannelsError { line, .. }
| LoadJointsError::ParseChannelError { line, .. }
| LoadJointsError::UnexpectedOffsetSection { line }
| LoadJointsError::ParseOffsetError { line, .. }
| LoadJointsError::MissingOffsetAxis { line, .. } => Some(line),
_ => None,
}
}
}
impl From<io::Error> for LoadJointsError {
#[inline]
fn from(e: io::Error) -> Self {
LoadJointsError::Io(e)
}
}
impl fmt::Display for LoadJointsError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LoadJointsError::Io(ref e) => fmt::Display::fmt(&e, f),
LoadJointsError::MissingRoot => f.write_str("The root heirarchy could not be found"),
LoadJointsError::MissingJointName { line } => {
write!(f, "{}: the name is missing from the joints section", line)
}
LoadJointsError::UnexpectedChannelsSection { line } => write!(
f,
"{}: unexpectedly encountered a \"CHANNELS\" section",
line
),
LoadJointsError::ParseNumChannelsError { line, .. } => {
write!(f, "{}: could not find the number of channels", line)
}
LoadJointsError::ParseChannelError { line, .. } => {
write!(f, "{}: could not parse channel", line)
}
LoadJointsError::UnexpectedOffsetSection { line } => write!(
f,
"{}: unexpectedly encountered an \"OFFSET\" section",
line
),
LoadJointsError::ParseOffsetError { axis, line, .. } => {
write!(f, "{}: could not parse the {}-axis offset", line, axis,)
}
LoadJointsError::MissingOffsetAxis { axis, line } => {
write!(f, "{}: the {}-axis offset value is missing", line, axis)
}
}
}
}
impl StdError for LoadJointsError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
LoadJointsError::Io(ref e) => Some(e),
/*
LoadJointsError::ParseNumChannelsError { ref error, .. } => {
error.as_ref().map(|e| e as &(dyn StdError + 'static))
}
*/
LoadJointsError::ParseChannelError { ref error, .. } => Some(error),
/*
LoadJointsError::ParseOffsetError {
ref parse_float_error,
..
} => Some(parse_float_error),
*/
_ => None,
}
}
}
/// Represents an error which occurred when loading the motion of the
/// bvh file.
#[derive(Debug)]
pub enum LoadMotionError {
/// An I/O error occurred.
Io(io::Error),
/// The `MOTION` section is missing in the bvh.
MissingMotionSection {
/// The line where the error occurred.
line: usize,
},
/// The "Number of Frames" section could not be parsed in the bvh.
MissingNumFrames {
/// The parse error, or `None` if there was no number to be parsed.
parse_error: Option<LexicalError>,
/// The line where the error occurred.
line: usize,
},
/// The "Frame Time" section could not be parsed in the bvh.
MissingFrameTime {
/// The parse error, or `None` if there was no number to be parsed.
parse_error: Option<LexicalError>,
/// The line where the error occurred.
line: usize,
},
/// The motion values section could not be parsed in the bvh.
ParseMotionSection {
/// The parse error, or `None` if there was no number to be parsed.
parse_error: LexicalError,
/// The index of the motion value where the error occurred.
channel_index: usize,
/// The line where the error occurred.
line: usize,
},
/// There was a discrepancy between the number of motion values promised
/// by the file and the actual amount.
MotionCountMismatch {
/// Actual number of motion values parsed.
actual_total_motion_values: usize,
/// Expected number of motion values.
expected_total_motion_values: usize,
/// Expected number of frames.
expected_num_frames: usize,
/// Expected number of clips.
expected_num_clips: usize,
},
}
impl LoadMotionError {
/// Get the line where the error occurred, or `None` if there is
/// no associated line number.
pub fn line(&self) -> Option<usize> {
match *self {
LoadMotionError::MissingMotionSection { line }
| LoadMotionError::MissingNumFrames { line, .. }
| LoadMotionError::MissingFrameTime { line, .. }
| LoadMotionError::ParseMotionSection { line, .. } => Some(line),
_ => None,
}
}
}
impl fmt::Display for LoadMotionError {
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LoadMotionError::Io(ref e) => fmt::Display::fmt(e, fmtr),
LoadMotionError::MissingMotionSection { line } => {
write!(
fmtr,
"{}: The 'MOTION' section of the bvh file is missing",
line
)
}
LoadMotionError::MissingNumFrames {
ref parse_error,
line,
} => {
if let Some(_) = parse_error {
write!(fmtr, "{}: could not parse the num frames value", line)
} else {
write!(
fmtr,
"{}: The number of frames section is missing from the bvh file",
line
)
}
}
LoadMotionError::MissingFrameTime {
ref parse_error,
line,
} => {
if let Some(_) = parse_error {
write!(fmtr, "{}: could not parse the frame time", line)
} else {
write!(fmtr, "{}: Could not parse the frame time", line)
}
}
LoadMotionError::ParseMotionSection { line, .. } => {
write!(fmtr, "{}: Could not parse the motion value", line)
}
LoadMotionError::MotionCountMismatch {
actual_total_motion_values,
expected_total_motion_values,
expected_num_frames,
expected_num_clips,
} => {
write!(
fmtr,
"expected to find {} motion values, found {} values (num frames = {}, num clips = {})",
expected_total_motion_values,
actual_total_motion_values,
expected_num_frames,
expected_num_clips)
}
}
}
}
impl StdError for LoadMotionError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
LoadMotionError::Io(ref e) => Some(e),
/*
LoadMotionError::MissingFrameTime {
parse_error: Some(ref e),
..
} => Some(e),
LoadMotionError::ParseMotionSection {
ref parse_error, ..
} => Some(parse_error),
LoadMotionError::MissingNumFrames {
parse_error: Some(ref e),
..
} => Some(e),
*/
_ => None,
}
}
}
impl From<io::Error> for LoadMotionError {
#[inline]
fn from(e: io::Error) -> Self {
LoadMotionError::Io(e)
}
}
/// An error which may occurr when setting a motion which is out
/// of bounds.
#[derive(Clone, Debug, PartialEq)]
pub enum SetMotionError {
/// The frame was out of bounds.
BadFrame(usize),
/// The channel was out of bounds.
BadChannel(usize),
}
impl fmt::Display for SetMotionError {
#[inline]
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SetMotionError::BadFrame(frame) => write!(fmtr, "Frame {} was out of bounds", frame),
SetMotionError::BadChannel(channel) => {
write!(fmtr, "Channel {} of the bvh was out of bounds", channel,)
}
}
}
}
impl StdError for SetMotionError {
#[inline]
fn description(&self) -> &'static str {
match *self {
SetMotionError::BadFrame(_) => "The frame was out of bounds",
SetMotionError::BadChannel(_) => "The channel was out of bounds",
}
}
}
/// Represents an error which may occur when attempting to parse a
/// `BString` into a `ChannelType`.
#[derive(Debug)]
pub struct ParseChannelError {
// @TODO(burtonageo): Borrow the erroneous string when hrts
// land.
bad_string: Vec<u8>,
}
impl ParseChannelError {
/// Get the `BString` which caused the parse error.
#[inline]
pub fn into_inner(self) -> Vec<u8> {
self.bad_string
}
}
impl<S: Into<Vec<u8>>> From<S> for ParseChannelError {
#[inline]
fn from(bad_string: S) -> Self {
ParseChannelError {
bad_string: bad_string.into(),
}
}
}
impl fmt::Display for ParseChannelError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"The channel could not be parsed from the given string: {:?}",
&self.bad_string
)
}
}
impl StdError for ParseChannelError {}
/// An error which may occur when attempting to insert a frame into a
/// `Bvh`.
#[derive(Debug)]
pub struct FrameInsertError {
kind: FrameInsertErrorKind,
}
impl FrameInsertError {
pub(crate) const fn incorrect_len(expected: usize, actual: usize) -> Self {
Self {
kind: FrameInsertErrorKind::IncorrectFrameLength { expected, actual },
}
}
}
impl fmt::Display for FrameInsertError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
FrameInsertErrorKind::IncorrectFrameLength { expected, actual } => {
write!(
f,
"Attempted to insert a frame with {} channels into a bvh containing {} channels",
actual,
expected,
)
}
}
}
}
impl StdError for FrameInsertError {}
#[derive(Debug)]
enum FrameInsertErrorKind {
IncorrectFrameLength { expected: usize, actual: usize },
}
/// An error which may occur when attempting to remove a frame from a
/// `Bvh`.
#[derive(Debug)]
pub struct FrameRemoveError {
index: usize,
}
impl FrameRemoveError {
pub(crate) const fn new(index: usize) -> Self {
Self { index }
}
}
impl fmt::Display for FrameRemoveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Attempted to remove a frame at index {} which does not exist",
self.index
)
}
}
impl StdError for FrameRemoveError {}
+600
View File
@@ -0,0 +1,600 @@
use crate::{
errors::{FrameInsertError, FrameRemoveError},
frames::{Frame, FrameMut, Frames, FramesMut},
Bvh,
};
use std::cmp::min;
/// A `FrameCursor` is used to insert frames into a [`Bvh`]. It operates
/// similarly to a [`linked_list::Cursor`]: methods are provided to move
/// through frames, as well as insert and delete frames.
///
/// You can create a `FrameCursor` using the [`Bvh::frame_cursor`] method.
///
/// [`Bvh`]: ../struct.Bvh.html
/// [`linked_list::Cursor`]: http://doc.rust-lang.org/stable/std/collections/linked_list/struct.Cursor.html
/// [`Bvh::frame_cursor`]: ../struct.Bvh.html#method.frame_cursor
#[derive(Debug)]
pub struct FrameCursor<'bvh> {
bvh: &'bvh mut Bvh,
index: usize,
}
impl<'bvh> FrameCursor<'bvh> {
/// Returns the current index of the `FrameCursor`.
#[inline]
pub const fn index(&self) -> usize {
self.index
}
/// Returns the number of frames in the [`Bvh`] the cursor is is currently
/// pointing to.
///
/// [`Bvh`]: ../struct.Bvh.html
#[inline]
pub fn len(&self) -> usize {
self.bvh.frames().len()
}
/// Returns the number of channels in the [`Bvh`] the cursor is is currently
/// pointing to.
///
/// [`Bvh`]: ../struct.Bvh.html
#[inline]
pub const fn num_channels(&self) -> usize {
self.bvh.num_channels
}
/// Move the `FrameCursor` to the next frame of the `Bvh`'s motion values.
///
/// If the `FrameCursor` is currently at the last frame, then this method will
/// do nothing.
#[inline]
pub fn move_next(&mut self) -> &mut Self {
self.index = min(self.index + 1, self.bvh.frames().len());
self
}
/// Move the `FrameCursor` to the previous frame of the `Bvh`'s motion values.
///
/// If the `FrameCursor` is currently at the first frame, then this method will
/// do nothing.
#[inline]
pub fn move_prev(&mut self) -> &mut Self {
self.index = self.index.saturating_sub(1);
self
}
/// Move the `FrameCursor` to the first frame of the `Bvh`'s motion values.
///
/// If the `FrameCursor` is currently at the first frame, then this method will
/// do nothing.
#[inline]
pub fn move_first(&mut self) -> &mut Self {
self.index = 0;
self
}
/// Move the `FrameCursor` to the last frame of the `Bvh`'s motion values.
///
/// If the `FrameCursor` is currently at the last frame, then this method will
/// do nothing.
#[inline]
pub fn move_last(&mut self) -> &mut Self {
self.index = self.len();
self
}
/// Returns the frame next to the current index of the `FrameCursor`.
///
/// Returns `None` if there is no frame available.
#[inline]
pub fn peek_next(&self) -> Option<Frame<'_>> {
self.bvh.frames().nth(self.index)
}
/// Returns the frame before the current index of the `FrameCursor`.
///
/// Returns `None` if there is no frame available.
#[inline]
pub fn peek_prev(&self) -> Option<Frame<'_>> {
if self.index() > 0 {
self.bvh.frames().nth(self.index - 1)
} else {
None
}
}
/// Returns a mutable reference to the frame next to the current
/// index of the `FrameCursor`.
///
/// Returns `None` if there is no frame available.
#[inline]
pub fn peek_next_mut(&mut self) -> Option<FrameMut<'_>> {
self.bvh.frames_mut().nth(self.index)
}
/// Returns a mutable reference to the frame before the current index
/// of the `FrameCursor`.
///
/// Returns `None` if there is no frame available.
#[inline]
pub fn peek_prev_mut(&mut self) -> Option<FrameMut<'_>> {
if self.index() > 0 {
self.bvh.frames_mut().nth(self.index - 1)
} else {
None
}
}
/// Returns the frames surrounding the current index of the `FrameCursor`.
///
/// If either of the frames is not available, then `None` will be returned
/// for that frame.
#[inline]
pub fn surrounding_frames(&self) -> (Option<Frame<'_>>, Option<Frame<'_>>) {
(self.peek_prev(), self.peek_next())
}
/// Attempt to insert a new frame of animation after the current index.
/// The index of the cursor will be advanced to the end of the frames inserted.
///
/// # Errors
///
/// If the frame could not be inserted (because it was an incorrect size),
/// then an error will be returned.
///
/// # Examples
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// let mut simple_skeleton = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// // Hierarchy omitted...
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// # JOINT Middle1
/// # {
/// # OFFSET 0.0 0.0 15.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip1
/// # {
/// # OFFSET 0.0 0.0 30.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 45.0
/// # }
/// # }
/// # }
/// # JOINT Middle2
/// # {
/// # OFFSET 0.0 15.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip2
/// # {
/// # OFFSET 0.0 30.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 45.0 0.0
/// # }
/// # }
/// # }
/// }
///
/// MOTION
/// Frames: 3
/// Frame Time: 0.033333333333
/// 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
/// 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
/// 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
/// };
///
/// let mut frame_cursor = simple_skeleton.frame_cursor();
/// frame_cursor.move_last();
///
/// assert_eq!(frame_cursor.index(), 3);
///
/// frame_cursor
/// .try_insert_frame(&[3.0; 18])
/// .expect("Could not insert frame");
///
/// assert_eq!(frame_cursor.index(), 4);
/// # drop(frame_cursor);
/// assert_eq!(simple_skeleton.frames().len(), 4);
///
/// # for (i, frame) in simple_skeleton.frames().enumerate() {
/// # for motion in &frame.as_slice()[..] {
/// # assert_eq!(*motion, i as f32);
/// # }
/// # }
/// # } // fn main()
/// ```
pub fn try_insert_frame<F>(&mut self, frame: &F) -> Result<&mut Self, FrameInsertError>
where
F: AsRef<[f32]> + ?Sized,
{
let frame = frame.as_ref();
if frame.len() != self.num_channels() {
return Err(FrameInsertError::incorrect_len(
self.num_channels(),
frame.len(),
));
}
let idx = self.index * self.num_channels();
vec_insert_slice(&mut self.bvh.motion_values, idx, frame);
Ok(self.move_next())
}
/// Insert multiple frames contiguously at the current index. This is generally more
/// efficient than calling [`try_insert_frame`] multiple times because this method will
/// preallocate space for the multiple frames.
///
/// The index of the cursor will be advanced to the end of the frames inserted.
///
/// # Errors
///
/// If any of the frames could not be inserted (because they were an incorrect size),
/// then an error will be returned.
///
/// # Examples
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// let mut simple_skeleton = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// // Hierarchy omitted...
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// # JOINT Middle1
/// # {
/// # OFFSET 0.0 0.0 15.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip1
/// # {
/// # OFFSET 0.0 0.0 30.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 45.0
/// # }
/// # }
/// # }
/// # JOINT Middle2
/// # {
/// # OFFSET 0.0 15.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip2
/// # {
/// # OFFSET 0.0 30.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 45.0 0.0
/// # }
/// # }
/// # }
/// }
///
/// MOTION
/// Frames: 3
/// Frame Time: 0.033333333333
/// 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
/// 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
/// 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
/// };
///
/// simple_skeleton
/// .frame_cursor()
/// .move_last()
/// .try_insert_frames(&[[3.0; 18], [4.0; 18]])
/// .expect("Could not insert frames");
///
/// assert_eq!(simple_skeleton.frames().len(), 5);
///
/// # for (i, frame) in simple_skeleton.frames().enumerate() {
/// # for motion in &frame.as_slice()[..] {
/// # assert_eq!(*motion, i as f32);
/// # }
/// # }
/// # } // fn main()
/// ```
///
///[`try_insert_frame`]: ./struct.FrameInserter.html#method.try_insert_frame
pub fn try_insert_frames<'f, I, F>(&mut self, frames: I) -> Result<&mut Self, FrameInsertError>
where
I: IntoIterator<Item = &'f F>,
F: 'f + AsRef<[f32]> + ?Sized,
{
let frames = frames.into_iter();
let num_channels = self.num_channels();
let num_frames = {
let (low, up) = frames.size_hint();
up.unwrap_or(low)
};
self.bvh.motion_values.reserve(num_frames * num_channels);
for frame in frames.into_iter() {
let frame = frame.as_ref();
self.try_insert_frame(&frame)?;
}
Ok(self)
}
/// Attempt to remove a frame of animation at the current index.
///
/// # Errors
///
/// If the frame could not be removed, then an error will be returned.
///
/// # Examples
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// let mut simple_skeleton = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// // Hierarchy omitted...
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// # JOINT Middle1
/// # {
/// # OFFSET 0.0 0.0 15.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip1
/// # {
/// # OFFSET 0.0 0.0 30.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 45.0
/// # }
/// # }
/// # }
/// # JOINT Middle2
/// # {
/// # OFFSET 0.0 15.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip2
/// # {
/// # OFFSET 0.0 30.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 45.0 0.0
/// # }
/// # }
/// # }
/// }
///
/// MOTION
/// Frames: 3
/// Frame Time: 0.033333333333
/// 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
/// 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
/// 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
/// };
///
/// simple_skeleton
/// .frame_cursor()
/// .remove_frame()
/// .expect("Could not remove frame");
///
/// assert_eq!(simple_skeleton.frames().len(), 2);
///
/// # for (i, frame) in simple_skeleton.frames().enumerate() {
/// # for motion in &frame.as_slice()[..] {
/// # assert_eq!(*motion, (i + 1) as f32);
/// # }
/// # }
/// # } // fn main()
/// ```
pub fn remove_frame(&mut self) -> Result<&mut Self, FrameRemoveError> {
if self.len() == 0 {
return Err(FrameRemoveError::new(self.index));
}
let bnd = |idx| idx * self.num_channels();
let frame_bound = bnd(self.index)..bnd(self.index + 1);
let mut i = 0;
self.bvh
.motion_values
.retain(|_| (!frame_bound.contains(&i), i += 1).0);
Ok(self)
}
/// Removes every frame in the [`Bvh`].
//
/// # Examples
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// let mut simple_skeleton = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// // Hierarchy omitted...
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// # JOINT Middle1
/// # {
/// # OFFSET 0.0 0.0 15.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip1
/// # {
/// # OFFSET 0.0 0.0 30.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 45.0
/// # }
/// # }
/// # }
/// # JOINT Middle2
/// # {
/// # OFFSET 0.0 15.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # JOINT Tip2
/// # {
/// # OFFSET 0.0 30.0 0.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 45.0 0.0
/// # }
/// # }
/// # }
/// }
///
/// MOTION
/// Frames: 3
/// Frame Time: 0.033333333333
/// 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
/// 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
/// 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
/// };
///
/// let mut frame_cursor = simple_skeleton.frame_cursor();
/// assert_eq!(frame_cursor.len(), 3);
/// frame_cursor.remove_all_frames();
/// assert_eq!(frame_cursor.len(), 0);
/// # } // fn main()
/// ```
///
/// [`Bvh`]: ../struct.Bvh.html
#[inline]
pub fn remove_all_frames(&mut self) -> &mut Self {
self.bvh.motion_values.clear();
self
}
/// Shrinks the capacity of the motion values in the [`Bvh`] as much as possible.
///
/// [`Bvh`]: ../struct.Bvh.html
#[inline]
pub fn shrink_to_fit(&mut self) {
self.bvh.motion_values.shrink_to_fit();
}
/// Create a new `Frames` iterator from the `FramesCursor`, starting at the current
/// index.
#[inline]
pub fn into_frames(self) -> Frames<'bvh> {
// @TODO: Replace with `Iterator::advance_by` when stable.
let mut frames = self.bvh.frames();
for _ in 0..self.index {
if frames.next().is_none() {
break;
}
}
frames
}
/// Create a new `FramesMut` iterator from the `FramesCursor`, starting at the current
/// index.
#[inline]
pub fn into_frames_mut(self) -> FramesMut<'bvh> {
// @TODO: Replace with `Iterator::advance_by` when stable.
let mut frames = self.bvh.frames_mut();
for _ in 0..self.index {
if frames.next().is_none() {
break;
}
}
frames
}
}
impl<'bvh> From<&'bvh mut Bvh> for FrameCursor<'bvh> {
#[inline]
fn from(bvh: &'bvh mut Bvh) -> Self {
Self { bvh, index: 0 }
}
}
impl<'bvh> From<FrameCursor<'bvh>> for Frames<'bvh> {
#[inline]
fn from(cursor: FrameCursor<'bvh>) -> Self {
cursor.into_frames()
}
}
impl<'bvh> From<FrameCursor<'bvh>> for FramesMut<'bvh> {
#[inline]
fn from(cursor: FrameCursor<'bvh>) -> Self {
cursor.into_frames_mut()
}
}
/// A utility function which inserts the `slice` into the `vec` at the given `index`.
///
/// # Panics
///
/// Panics if `index` is out of bounds.
fn vec_insert_slice<T: Clone + Default>(vec: &mut Vec<T>, index: usize, slice: &[T]) {
assert!(
index <= vec.len(),
"attempted to insert a slice at index {} into a vec of length {}",
index,
vec.len(),
);
let spare_capacity = vec.capacity().saturating_sub(vec.len());
if slice.len() > spare_capacity {
vec.reserve(slice.len() - spare_capacity);
}
let old_len = vec.len();
let new_len = vec.len() + slice.len();
vec.resize_with(new_len, T::default);
// Move the existing items to the end
for (to_move, end_index) in (index..old_len).into_iter().zip(old_len + index..new_len) {
vec.swap(to_move, end_index);
}
// Clone the slice into the new area
for (i, item) in slice.iter().enumerate() {
vec[i + index] = item.clone();
}
}
#[test]
fn test_vec_insert_slice() {
let mut v;
let sl = &[100, 200, 300];
v = vec![1, 2, 3];
vec_insert_slice(&mut v, 0, sl);
assert_eq!(v, &[100, 200, 300, 1, 2, 3]);
v = vec![1, 2, 3];
vec_insert_slice(&mut v, 1, sl);
assert_eq!(v, &[1, 100, 200, 300, 2, 3]);
v = vec![1, 2, 3];
vec_insert_slice(&mut v, 2, sl);
assert_eq!(v, &[1, 2, 100, 200, 300, 3]);
v = vec![1, 2, 3];
vec_insert_slice(&mut v, 3, sl);
assert_eq!(v, &[1, 2, 3, 100, 200, 300]);
}
+325
View File
@@ -0,0 +1,325 @@
use crate::{errors::SetMotionError, Channel};
use std::{
borrow::{Borrow, BorrowMut},
iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator},
mem,
ops::{Index, IndexMut},
slice::{ChunksExact, ChunksExactMut, SliceIndex},
};
/// An iterator over the frames of a `Bvh`.
///
/// This type is created using the [`Bvh::frames`] method.
///
/// [`Bvh::frames`]: ../struct.Bvh.html#method.frames
#[derive(Debug)]
pub struct Frames<'a> {
/// Note: `chunks` is wrapped in an option because having a `ChunksExact`
/// iterator over 0-length chunks panics, and empty `Bvh`s have empty
/// frames.
pub(crate) chunks: Option<ChunksExact<'a, f32>>,
}
impl<'a> Iterator for Frames<'a> {
type Item = Frame<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.chunks.as_mut().and_then(|c| c.next().map(Frame))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.chunks
.as_ref()
.map(|c| c.size_hint())
.unwrap_or((0, Some(0)))
}
}
impl<'a> DoubleEndedIterator for Frames<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.chunks.as_mut().and_then(|c| c.next_back().map(Frame))
}
}
impl<'a> ExactSizeIterator for Frames<'a> {
#[inline]
fn len(&self) -> usize {
self.chunks.as_ref().map(|c| c.len()).unwrap_or(0)
}
}
impl<'a> FusedIterator for Frames<'a> {}
/// A mutable iterator over the frames of a [`Bvh`].
///
/// This type is created using the [`Bvh::frames_mut`] method.
///
/// [`Bvh`]: ../struct.Bvh.html
/// [`Bvh::frames_mut`]: ../struct.Bvh.html#method.frames_mut
#[derive(Debug)]
pub struct FramesMut<'a> {
/// Note: `chunks` is wrapped in an option for the same reason
/// that `Frames<'_>`'s `chunks` is wrapped.
pub(crate) chunks: Option<ChunksExactMut<'a, f32>>,
}
impl<'a> Iterator for FramesMut<'a> {
type Item = FrameMut<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.chunks.as_mut().and_then(|c| c.next().map(FrameMut))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.chunks
.as_ref()
.map(|c| c.size_hint())
.unwrap_or((0, Some(0)))
}
}
impl<'a> DoubleEndedIterator for FramesMut<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.chunks
.as_mut()
.and_then(|c| c.next_back().map(FrameMut))
}
}
impl<'a> ExactSizeIterator for FramesMut<'a> {
#[inline]
fn len(&self) -> usize {
self.chunks.as_ref().map(|c| c.len()).unwrap_or(0)
}
}
impl<'a> FusedIterator for FramesMut<'a> {}
/// A wrapper for a slice of motion values, so that they can be indexed by [`Channel`].
///
/// [`Channel`]: ./struct.Channel.html
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Frame<'a>(&'a [f32]);
impl<'a> Frame<'a> {
/// Return the number of values in the `Frame`.
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
/// Returns `true` if there are `0` values in the `FrameMut`. Otherwise,
/// returns `false`.
#[inline]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Convert the `Frame` into a `&[f32]`.
#[inline]
pub const fn as_slice(&self) -> &[f32] {
self.0
}
/// Attempts to get the motion value at `index`. Otherwise, returns `None`.
#[inline]
pub fn get<I: FrameIndex>(&self, index: I) -> Option<&Output<I>> {
self.0.get(index.to_slice_index())
}
}
impl<'a> AsRef<[f32]> for Frame<'a> {
#[inline]
fn as_ref(&self) -> &[f32] {
self.0
}
}
impl<'a> Borrow<[f32]> for Frame<'a> {
#[inline]
fn borrow(&self) -> &[f32] {
&*self.0
}
}
impl<'a, I: FrameIndex> Index<I> for Frame<'a> {
type Output = Output<I>;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.index(index.to_slice_index())
}
}
/// A wrapper for a mutable slice of motion values, so that they can be indexed by [`Channel`].
///
/// [`Channel`]: ./struct.Channel.html
#[derive(Debug, PartialEq)]
pub struct FrameMut<'a>(&'a mut [f32]);
impl<'a> FrameMut<'a> {
/// Return the number of values in the `FrameMut`.
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
/// Returns `true` if there are `0` values in the `FrameMut`. Otherwise,
/// returns `false`.
#[inline]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Convert the `FrameMut` into a `&[f32]`.
#[inline]
pub const fn as_slice(&self) -> &[f32] {
&*self.0
}
/// Convert the `FrameMut` into a `&mut [f32]`.
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [f32] {
self.0
}
/// Attempts to get the motion value at `index`. Otherwise, returns `None`.
#[inline]
pub fn get<I: FrameIndex>(&self, index: I) -> Option<&Output<I>> {
self.0.get(index.to_slice_index())
}
/// Attempts to return a mutable reference to the motion value at `channel`.
/// Otherwise, returns `None`.
#[inline]
pub fn get_mut<I: FrameIndex>(&'a mut self, index: I) -> Option<&mut Output<I>> {
self.0.get_mut(index.to_slice_index())
}
/// Updates the `motion` value at `channel` to `new_motion`.
///
/// # Notes
///
/// Returns the previous motion value if the operation was successful, and `Err(_)` if
/// the operation was out of bounds.
pub fn try_set_motion<I>(&mut self, index: I, new_motion: f32) -> Result<f32, SetMotionError>
where
I: FrameIndex<SliceIndex = usize>,
{
let index = index.to_slice_index();
let motion = self
.0
.get_mut(index)
.ok_or(SetMotionError::BadChannel(index))?;
Ok(mem::replace(motion, new_motion))
}
}
impl<'a> Borrow<[f32]> for FrameMut<'a> {
#[inline]
fn borrow(&self) -> &[f32] {
&*self.0
}
}
impl<'a> BorrowMut<[f32]> for FrameMut<'a> {
#[inline]
fn borrow_mut(&mut self) -> &mut [f32] {
self.0
}
}
impl<'a> AsRef<[f32]> for FrameMut<'a> {
#[inline]
fn as_ref(&self) -> &[f32] {
&*self.0
}
}
impl<'a> AsMut<[f32]> for FrameMut<'a> {
#[inline]
fn as_mut(&mut self) -> &mut [f32] {
self.0
}
}
impl<'a, I: FrameIndex> Index<I> for FrameMut<'a> {
type Output = Output<I>;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.index(index.to_slice_index())
}
}
impl<'a, I: FrameIndex> IndexMut<I> for FrameMut<'a> {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.0.index_mut(index.to_slice_index())
}
}
impl<'a> From<&'a FrameMut<'a>> for Frame<'a> {
#[inline]
fn from(frame_mut: &'a FrameMut<'a>) -> Self {
Frame(&*frame_mut.0)
}
}
impl<'a> From<FrameMut<'a>> for Frame<'a> {
#[inline]
fn from(frame_mut: FrameMut<'a>) -> Self {
Frame(&*frame_mut.0)
}
}
mod private {
pub trait Sealed {}
}
/// A helper trait used for indexing into a frame.
pub trait FrameIndex: Sized + private::Sealed {
/// The type of the index generated.
type SliceIndex: SliceIndex<[f32]>;
/// Convert `Self` to a `SliceIndex`.
fn to_slice_index(self) -> Self::SliceIndex;
}
/// Type alias for the result of a `FrameIndex` operation.
pub type Output<I> = <<I as FrameIndex>::SliceIndex as SliceIndex<[f32]>>::Output;
impl<T: SliceIndex<[f32]>> FrameIndex for T {
type SliceIndex = Self;
#[inline]
fn to_slice_index(self) -> Self::SliceIndex {
self
}
}
// @TODO: Combine these into one impl for `impl Borrow<Channel>` when specialization lands.
macro_rules! impl_channel_frame_index {
($($chn:ty),* $(,)?) => {
$(
impl FrameIndex for $chn {
type SliceIndex = usize;
#[inline]
#[inline]
fn to_slice_index(self) -> Self::SliceIndex {
self.motion_index
}
}
)*
}
}
impl_channel_frame_index!(Channel, &'_ Channel);
impl private::Sealed for Channel {}
impl private::Sealed for &'_ Channel {}
impl<T: SliceIndex<[f32]>> private::Sealed for T {}
+852
View File
@@ -0,0 +1,852 @@
#![allow(dead_code, missing_docs)]
use crate::Channel;
use bstr::{BStr, BString, ByteSlice};
use smallvec::SmallVec;
use std::{
cmp::{Ordering, PartialEq, PartialOrd},
ffi::{CStr, CString},
fmt, mem,
ops::{Deref, DerefMut},
str,
};
/// A 3-element point representing the location of a joint offset or
/// end site.
pub type Offset = [f32; 3];
/// Internal representation of a joint.
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum JointData {
/// Root of the skeletal heirarchy.
Root {
/// Name of the root `Joint`.
name: JointName,
/// Positional offset of this `Joint` relative to the parent.
offset: Offset,
/// The channels applicable to this `Joint`.
channels: SmallVec<[Channel; 6]>,
},
/// A child joint in the skeleton.
Child {
/// Name of the `Joint`.
name: JointName,
/// Positional offset of this `Joint` relative to the parent.
offset: Offset,
/// The channels applicable to this `Joint`.
channels: SmallVec<[Channel; 3]>,
/// End site offset.
end_site_offset: Option<Offset>,
/// Private data.
private: JointPrivateData,
},
}
impl JointData {
#[inline]
pub(crate) const fn is_root(&self) -> bool {
match *self {
JointData::Root { .. } => true,
_ => false,
}
}
#[inline]
pub(crate) const fn is_child(&self) -> bool {
match *self {
JointData::Child { .. } => true,
_ => false,
}
}
#[inline]
pub(crate) fn name(&self) -> &[u8] {
let name = match *self {
JointData::Root { ref name, .. } | JointData::Child { ref name, .. } => name,
};
name.as_bytes()
}
#[inline]
pub(crate) const fn offset(&self) -> &Offset {
match *self {
JointData::Child { ref offset, .. } | JointData::Root { ref offset, .. } => offset,
}
}
#[inline]
pub(crate) const fn end_site(&self) -> Option<&Offset> {
match *self {
JointData::Child {
ref end_site_offset,
..
} => end_site_offset.as_ref(),
_ => None,
}
}
#[inline]
pub fn channels(&self) -> &[Channel] {
match *self {
JointData::Child { ref channels, .. } => &channels[..],
JointData::Root { ref channels, .. } => &channels[..],
}
}
/// Returns a mutable reference to ordered array of `Channel`s of this `JointData`.
#[inline]
pub fn channels_mut(&mut self) -> &mut [Channel] {
match *self {
JointData::Child {
ref mut channels, ..
} => &mut channels[..],
JointData::Root {
ref mut channels, ..
} => &mut channels[..],
}
}
/// Return the index of this `Joint` in the array.
#[inline]
pub fn index(&self) -> usize {
self.private_data().map(|d| d.self_index).unwrap_or(0)
}
/// Returns the index of this joint's parent in the `Bvh`'s array of joints,
/// or `None` if this `Joint` is the root joint.
#[inline]
pub fn parent_index(&self) -> Option<usize> {
self.private_data().map(|d| d.parent_index)
}
/// Returns a reference to the `JointPrivateData` of the `JointData` if it
/// exists, or `None`.
#[inline]
pub(crate) const fn private_data(&self) -> Option<&JointPrivateData> {
match *self {
JointData::Child { ref private, .. } => Some(private),
_ => None,
}
}
/// Get the depth of the `JointData` in the heirarchy.
#[inline]
pub(crate) const fn depth(&self) -> usize {
match *self {
JointData::Child { ref private, .. } => private.depth,
_ => 0,
}
}
#[inline]
pub(crate) fn empty_root() -> Self {
JointData::Root {
name: Default::default(),
offset: [0.0, 0.0, 0.0].into(),
channels: Default::default(),
}
}
#[inline]
pub(crate) fn empty_child() -> Self {
JointData::Child {
name: Default::default(),
offset: [0.0, 0.0, 0.0].into(),
channels: Default::default(),
end_site_offset: Default::default(),
private: JointPrivateData::empty(),
}
}
#[inline]
pub(crate) fn set_name<S: Into<JointName>>(&mut self, new_name: S) {
match *self {
JointData::Root { ref mut name, .. } => *name = new_name.into(),
JointData::Child { ref mut name, .. } => *name = new_name.into(),
}
}
pub(crate) fn set_offset(&mut self, new_offset: impl Into<Offset>, is_site: bool) {
let new_offset = new_offset.into();
match *self {
JointData::Root { ref mut offset, .. } => *offset = new_offset,
JointData::Child {
ref mut offset,
ref mut end_site_offset,
..
} => {
if is_site {
*end_site_offset = Some(new_offset);
} else {
*offset = new_offset;
}
}
}
}
#[inline]
pub(crate) fn set_channels(&mut self, new_channels: SmallVec<[Channel; 6]>) {
match *self {
JointData::Root {
ref mut channels, ..
} => *channels = new_channels,
JointData::Child {
ref mut channels, ..
} => *channels = new_channels.iter().map(|c| *c).collect(),
}
}
/// Returns a mutable reference to the `JointPrivateData` of the `JointData` if it
/// exists, or `None`.
#[inline]
pub(crate) fn private_data_mut(&mut self) -> Option<&mut JointPrivateData> {
match *self {
JointData::Child {
ref mut private, ..
} => Some(private),
_ => None,
}
}
}
/// A string type for the `Joint` name. A `SmallVec` is used for
/// better data locality.
pub(crate) type JointNameInner = SmallVec<[u8; mem::size_of::<String>()]>;
/// Wrapper struct for the `Joint` name type.
#[derive(Clone, Default, Eq, Hash, Ord)]
pub(crate) struct JointName(pub(crate) JointNameInner);
impl JointName {
pub(crate) fn as_bytes(&self) -> &[u8] {
self.as_ref()
}
}
impl Deref for JointName {
type Target = JointNameInner;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for JointName {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<B: ?Sized + AsRef<[u8]>> PartialEq<B> for JointName {
#[inline]
fn eq(&self, rhs: &B) -> bool {
AsRef::<[u8]>::as_ref(self) == rhs.as_ref()
}
}
impl<B: ?Sized + AsRef<[u8]>> PartialOrd<B> for JointName {
#[inline]
fn partial_cmp(&self, rhs: &B) -> Option<Ordering> {
AsRef::<[u8]>::as_ref(self).partial_cmp(rhs.as_ref())
}
}
macro_rules! impl_from {
() => {};
(;) => {};
($t:ty; $($rest:tt)*) => {
impl From<$t> for JointName {
#[inline]
fn from(b: $t) -> Self {
JointName(b.bytes().collect())
}
}
impl_from!($($rest)*);
}
}
impl From<CString> for JointName {
#[inline]
fn from(s: CString) -> Self {
From::from(s.into_bytes())
}
}
impl From<&'_ CStr> for JointName {
#[inline]
fn from(s: &'_ CStr) -> Self {
From::from(s.to_bytes())
}
}
impl From<Vec<u8>> for JointName {
#[inline]
fn from(s: Vec<u8>) -> Self {
JointName(JointNameInner::from(s))
}
}
impl From<&'_ [u8]> for JointName {
#[inline]
fn from(s: &'_ [u8]) -> Self {
JointName(s.into_iter().copied().collect::<JointNameInner>())
}
}
impl_from! {
String; &'_ str;
BString; &'_ BStr;
}
macro_rules! impl_as_ref {
($( $t:ty { ref => $method:path, mut => $mut_method:path } )*) => {
$(
impl AsRef<$t> for JointName {
#[inline]
fn as_ref(&self) -> &$t {
$method(&self.0[..])
}
}
impl AsMut<$t> for JointName {
#[inline]
fn as_mut(&mut self) -> &mut $t {
$mut_method(&mut self.0[..])
}
}
)*
}
}
impl_as_ref! {
BStr { ref => AsRef::<BStr>::as_ref, mut => AsMut::<BStr>::as_mut }
[u8] { ref => AsRef::<[u8]>::as_ref, mut => AsMut::<[u8]>::as_mut }
}
impl From<JointNameInner> for JointName {
#[inline]
fn from(s: JointNameInner) -> Self {
JointName(s)
}
}
impl From<JointName> for JointNameInner {
#[inline]
fn from(j: JointName) -> Self {
j.0
}
}
impl fmt::Debug for JointName {
#[inline]
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(AsRef::<BStr>::as_ref(self), fmtr)
}
}
impl fmt::Display for JointName {
#[inline]
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(AsRef::<BStr>::as_ref(self), fmtr)
}
}
/// Data private to joints.
#[doc(hidden)]
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct JointPrivateData {
/// Index of this `Joint` in the array.
pub(crate) self_index: usize,
/// The parent index in the array of `JointPrivateData`s in the `Bvh`.
pub(crate) parent_index: usize,
/// Depth of the `Joint`. A depth of `1` signifies a `Joint` attached to
/// the root.
pub(crate) depth: usize,
}
impl JointPrivateData {
#[inline]
pub(crate) const fn new(self_index: usize, parent_index: usize, depth: usize) -> Self {
JointPrivateData {
self_index,
parent_index,
depth,
}
}
#[inline]
pub(crate) const fn empty() -> Self {
Self::new(0, 0, 0)
}
}
impl fmt::Debug for JointPrivateData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("JointPrivateData { .. }")
}
}
/// An iterator over the `Joint`s of a `Bvh` skeleton.
pub struct Joints<'a> {
pub(crate) joints: &'a [JointData],
// pub(crate) motion_values: &'a [f32],
pub(crate) current_joint: usize,
pub(crate) from_child: Option<usize>,
}
impl fmt::Debug for Joints<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Joints { .. }")
}
}
impl<'a> Joints<'a> {
/// Create a `Joints` iterator over all the `joints` in a `Bvh` file.
pub(crate) const fn iter_root(joints: &'a [JointData]) -> Self {
Joints {
joints,
// clips,
current_joint: 0,
from_child: None,
}
}
/// Create a `Joints` iterator over all the child joints of `joint`.
///
/// # Notes
///
/// This function only iterates over the direct children of `joint`. If you
/// need to iterate through to the end sites of all children, you will
/// need to continually call `iter_children` on each `Joint` in the iterator.
pub(crate) fn iter_children(joint: &Joint<'a>) -> Self {
let first_child = joint
.joints
.iter()
.find(|jd| {
if let Some(p) = jd.private_data() {
p.parent_index == joint.index
} else {
false
}
})
.map(JointData::index)
.unwrap();
Joints {
joints: joint.joints,
// clips: joint.clips,
current_joint: joint.data().index(),
from_child: Some(first_child),
}
}
/// Finds the `Joint` named `joint_name`, or `None` if it doesn't exist.
#[inline]
pub fn find_by_name<B>(&mut self, joint_name: &B) -> Option<Joint<'a>>
where
B: ?Sized + AsRef<[u8]>,
{
self.find(|b| b.data().name() == joint_name.as_ref())
}
#[allow(unused)]
pub(crate) fn nth_child(joint: &Joint<'a>, child: usize) -> Option<usize> {
Joints::iter_children(joint)
.nth(child)
.map(|joint| joint.index)
}
}
impl<'a> Iterator for Joints<'a> {
type Item = Joint<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_joint >= self.joints.len() {
return None;
}
let joint = Some(Joint {
index: self.current_joint,
joints: self.joints,
});
if self.from_child.is_none() {
self.current_joint += 1;
} else {
unimplemented!()
}
joint
}
}
/// A mutable iterator over the `Joint`s of a `Bvh` skeleton.
#[allow(unused)]
pub struct JointsMut<'a> {
pub(crate) joints: &'a mut [JointData],
pub(crate) current_joint: usize,
pub(crate) from_child: Option<usize>,
}
impl<'a> JointsMut<'a> {
pub(crate) fn iter_root(joints: &'a mut [JointData]) -> Self {
JointsMut {
joints,
// clips,
current_joint: 0,
from_child: None,
}
}
}
impl<'a> Iterator for JointsMut<'a> {
type Item = JointMut<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl fmt::Debug for JointsMut<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("JointsMut { .. }")
}
}
/*
impl<'a> Iterator for JointsMut<'a> {
type Item = JointMut<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.joints.next().map(JointMut::from_joint)
}
}
*/
/// A view of a joint which provides access to various relevant data.
pub struct Joint<'a> {
/// Index of the `Joint` in the skeleton.
pub(crate) index: usize,
/// `Joints` array which the joint is part of.
pub(crate) joints: &'a [JointData],
}
impl PartialEq for Joint<'_> {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
let (d0, d1) = (self.data(), rhs.data());
PartialEq::eq(d0, d1)
}
}
impl Joint<'_> {
/// Returns `true` if the `Joint` is the root `Joint`, or `false` if it isn't.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// End Site {
/// OFFSET 0.0 0.0 30.0
/// }
/// }
/// MOTION
/// Frames: 0
/// Frame Time: 0.0333333
/// };
///
/// let root = bvh.root_joint().unwrap();
/// assert!(root.is_root());
/// ```
#[inline]
pub const fn is_root(&self) -> bool {
self.data().is_root()
}
/// Returns `true` if the `Joint` is a child `Joint`, or `false` if it isn't.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// let bvh = bvh! {
/// # HIERARCHY
/// # ROOT Base
/// # {
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// # JOINT End
/// # {
/// # OFFSET 0.0 0.0 15.0
/// # CHANNELS 3 Zrotation Xrotation Yrotation
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 30.0
/// # }
/// # }
/// # }
/// # MOTION
/// # Frames: 0
/// # Frame Time: 0.0333333
/// // bvh hierarchy unspecified ..
/// };
///
/// for joint in bvh.joints().skip(1) {
/// assert!(joint.is_child());
/// }
/// ```
#[inline]
pub const fn is_child(&self) -> bool {
self.data().is_child()
}
/// Returns the name of the `JointData`.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// // ...
/// # End Site {
/// # OFFSET 0.0 0.0 30.0
/// # }
/// }
/// MOTION
/// // ...
/// # Frames: 0
/// # Frame Time: 0.0333333
/// };
///
/// let root = bvh.root_joint().unwrap();
/// assert_eq!(root.name(), b"Hips");
/// ```
#[inline]
pub fn name(&self) -> &[u8] {
self.data().name()
}
/// Returns the offset of the `JointData` if it exists, or `None`.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 1.2 3.4 5.6
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// // ...
/// # End Site {
/// # OFFSET 0.0 0.0 30.0
/// # }
/// }
/// MOTION
/// // ...
/// # Frames: 0
/// # Frame Time: 0.0333333
/// };
///
/// let root = bvh.root_joint().unwrap();
/// assert_eq!(root.offset(), &[1.2, 3.4, 5.6]);
/// ```
#[inline]
pub const fn offset(&self) -> &Offset {
self.data().offset()
}
/// Returns the `end_site` position if this `Joint` has an end site, or `None` if
/// it doesn't.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// JOINT Tip
/// {
/// OFFSET 0.0 0.0 5.0
/// CHANNELS 3 Zrotation Xrotation Yrotation
/// End Site
/// {
/// OFFSET 5.4 3.2 1.0
/// }
/// }
/// }
/// MOTION
/// // ...
/// # Frames: 0
/// # Frame Time: 0.0333333
/// };
/// let mut joints = bvh.joints();
/// let base = joints.next().unwrap();
/// assert!(base.end_site().is_none());
///
/// let tip = joints.next().unwrap();
/// assert_eq!(tip.end_site(), Some([5.4, 3.2, 1.0].into()).as_ref());
/// ```
#[inline]
pub const fn end_site(&self) -> Option<&Offset> {
self.data().end_site()
}
/// Returns `true` if the `Joint` has an `end_site_offset`, or `false` if it doesn't.
#[inline]
pub const fn has_end_site(&self) -> bool {
self.end_site().is_some()
}
/// Returns the ordered array of `Channel`s of this `JointData`.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::{bvh, ChannelType};
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// // ...
/// # End Site {
/// # OFFSET 0.0 0.0 30.0
/// # }
/// }
/// MOTION
/// // ...
/// # Frames: 0
/// # Frame Time: 0.0333333
/// };
///
/// let root = bvh.root_joint().unwrap();
/// let expected_channels = &[
/// ChannelType::PositionX,
/// ChannelType::PositionY,
/// ChannelType::PositionZ,
/// ChannelType::RotationZ,
/// ChannelType::RotationX,
/// ChannelType::RotationY,
/// ];
///
/// for (channel, &expected) in root
/// .channels()
/// .iter()
/// .map(|c| c.channel_type())
/// .zip(expected_channels.iter())
/// {
/// assert_eq!(channel, expected);
/// }
/// ```
#[inline]
pub fn channels(&self) -> &[Channel] {
self.data().channels()
}
/// Returns the index of this joint in the `Bvh`'s array of joints.
#[inline]
pub const fn index(&self) -> usize {
self.index
}
/// Returns the index of this joint's parent in the `Bvh`'s array of joints,
/// or `None` if this `Joint` is the root joint.
#[inline]
pub fn parent_index(&self) -> Option<usize> {
self.data().parent_index()
}
/// Return the parent `Joint` if it exists, or `None` if it doesn't.
#[inline]
pub fn parent(&self) -> Option<Joint<'_>> {
self.data().parent_index().map(|idx| Joint {
index: idx,
joints: self.joints,
})
}
/// Returns an iterator over the children of `self`.
#[inline]
pub fn children(&self) -> Joints<'_> {
Joints::iter_children(&self)
}
/// Access a read-only view of the internal data of the `Joint`.
#[inline]
pub(crate) const fn data(&self) -> &JointData {
&self.joints[self.index]
}
}
impl fmt::Debug for Joint<'_> {
#[inline]
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr.debug_struct("Joint")
.field("index", &self.index)
.field("data", &self.data())
.finish()
}
}
/// A view of a joint which provides mutable access.
pub struct JointMut<'a> {
/// Index of the `Joint` in the skeleton.
pub(crate) index: usize,
/// `Joints` array which the joint is part of.
pub(crate) joints: &'a mut [JointData],
}
impl<'a> JointMut<'a> {
/*
/// Return the parent `Joint` if it exists, or `None` if it doesn't.
#[inline]
pub fn parent(&self) -> Option<Joint<'_>> {
self.data().parent_index().map(|idx| Joint {
self_index: idx,
skeleton: self.skeleton,
clips: self.clips,
})
}
pub fn
/// Returns an iterator over the children of `self`.
#[inline]
pub fn children(&self) -> Joints<'_> {
Joints::iter_children(&self)
}
/// Access a read-only view of the internal data of the `Joint`.
#[inline]
pub fn data(&self) -> &JointData {
&self.joints[self_index]
}
*/
/// Mutable access to the internal data of the `JointMut`.
#[inline]
pub(crate) fn data_mut(&mut self) -> &mut JointData {
&mut self.joints[self.index]
}
}
+42
View File
@@ -0,0 +1,42 @@
use crate::Bvh;
use mint::Vector3;
#[derive(Debug)]
pub struct JointCursor<'a> {
bvh: &'a mut Bvh,
index: usize,
}
impl<'a> JointCursor<'a> {
#[inline]
pub const fn index(&self) -> usize {
self.index
}
#[inline]
pub fn push_joints(&mut self) -> PushJointsChain<'a> {
PushJointsChain { cursor: self }
}
}
#[derive(Debug)]
pub struct PushJointsChain<'a> {
cursor: &'a mut JointCursor<'a>,
}
impl<'a> PushJointsChain<'a> {
pub fn push_joint<B: AsRef<[u8]>, V: Into<Vector3<f32>>>(
&mut self,
name: B,
offset: V,
) -> Self {
todo!()
}
}
impl<'a> From<&'a mut Bvh> for JointCursor<'a> {
#[inline]
fn from(bvh: &'a mut Bvh) -> Self {
Self { bvh, index: 0 }
}
}
+944
View File
@@ -0,0 +1,944 @@
#![warn(missing_docs)]
#![deny(bare_trait_objects, unsafe_code)]
//! # About this library
//!
//! A small library for loading and manipulating BioVision motion files.
//!
//! ## The bvh file format
//!
//! The `Bvh` file format is comprised of two main sections: the 'Heirarchy' section,
//! which defines the joints of the skeleton, and the 'Motion' section, which defines
//! the motion values for each channel.
//!
//! This project contains some samples in the [`data` directory][`data` directory].
//!
//! ### Heierarchy
//!
//! The 'Heirarchy' section defines the skeleton as a tree of joints, where there is
//! a single root joint, and a chain of child joints extending out from each joint,
//! terminated by an 'End Site' section.
//!
//! Each joint has:
//!
//! * A list of channels, which are the degrees of freedom in which the joint may move.
//! Channels are listed in the order in which the transformation should be applied
//! to the global transform for the root.
//! * An offset, which is the vector distance from the parent joint.
//! * An optional end site, which is used to cap off a chain of joints. This is only used
//! to calculate the length of the final bone in the chain.
//!
//! ```text
//! HEIRARCHY
//! ROOT <Root-name>
//! {
//! OFFSET <Root-offset-x> <Root-offset-y> <Root-offset-z>
//! CHANNELS <Num-root-joint-channels> Xposition Yposition <other-root-channels ...>
//! JOINT <Joint-1-name>
//! {
//! OFFSET <Joint-1-offset-x> <Joint-1-offset-y> <Joint-1-offset-z>
//! CHANNELS <Num-joint-1-channels> <Joint-1-channels ...>
//! JOINT <Joint-2-name>
//! {
//! OFFSET <Joint-2-offset-x> <Joint-2-offset-y> <Joint-2-offset-z>
//! CHANNELS <Num-joint-2-channels> <Joint-2-channels ...>
//! JOINT <Joint-with-end-site>
//! {
//! OFFSET ...
//! CHANNELS ...
//! End Site
//! {
//! OFFSET <end-site-offset-x> <end-site-offset-y> <end-site-offset-z>
//! }
//! }
//! ... More child joints
//! }
//! JOINT <Joint-3-name>
//! {
//! OFFSET <Joint-3-offset-x> <Joint-3-offset-y> <Joint-3-offset-z>
//! CHANNELS <Num-joint-3-channels> <Joint-3-channels ...>
//! ... More child joints
//! }
//! ... More child joints
//! }
//! ... More child joints
//! }
//! ```
//!
//! Note that the bvh data is defined in terms of a right-handed coordinate system, where
//! the positive y-axis is the up vector.
//!
//! ### Motion
//!
//! The `MOTION` section of the bvh file records the number of frames, the frame time, and
//! defines the full range of motions for each channel, frame by frame.
//!
//! ```text
//! MOTION
//! Frames: <num-frames>
//! Frame Time: <frame-time>
//! <frame-0-channel-0-value> <frame-0-channel-1-value> <frame-0-channel-2-value> ...
//! <frame-1-channel-0-value> <frame-1-channel-1-value> <frame-1-channel-2-value> ...
//! <frame-2-channel-0-value> <frame-2-channel-1-value> <frame-2-channel-2-value> ...
//! ⋮
//! ```
//!
//! The frame time is recorded in seconds, and tells the animation system how long each frame
//! of the animation should last for. This value is usually around 0.033333333, which is close
//! to 30 frames per second.
//!
//! The list of motion values is a matrix, where each row represents a frame. Each column of
//! the row represents a transformation around the channel axis - for example a motion value
//! of 130.0 for an `Xposition` channel would correspond to a rotation of 130.0 degrees around
//! the x-axis.
//!
//! Note that rotations are conventionally in degrees, although it will be up to your application
//! how to interpret each motion's value.
//!
//! ## Using this library.
//!
//! ### Creating a [`Bvh`][`Bvh`] struct:
//!
//! There are a few ways to create a [`Bvh`][`Bvh`] struct:
//!
//! * You can use the [`from_reader`][`from_reader`] function, which will parse a `BufRead`
//! as a bvh file. The [`from_bytes`][`from_bytes`] function is a convenient wrapper function
//! to parse an in-memory slice of bytes as a `bvh` file. Note that the file does not need to
//! be strictly UTF-8, although it should be an ascii-compatible encoding. These functions are
//! also available as associated methods on the `Bvh` type directly as [`Bvh::from_reader`]
//! [`Bvh::from_reader`] and [`Bvh::from_bytes`][`Bvh::from_bytes`]
//!
//! * You can use the [`bvh!`][`bvh!`] macro to construct a [`Bvh`][`Bvh`] instance in your source files
//! using the same syntax as you would use for a standard bvh file.
//!
//! * You can use the [`builder`][`builder`] module to dynamically construct a bvh. This is useful
//! for converting data from other formats into a [`Bvh`][`Bvh`] struct.
//!
//! * You can create an empty [`Bvh`][`Bvh`] using the [`Bvh::new`][`Bvh::new`] or [`Default::default`]
//! [`Default::default`] methods.
//!
//! ### Other operations:
//!
//! Once you have a valid [`Bvh`][`Bvh`] struct, there are a number of ways you can inspect and
//! manipulate it:
//!
//! * The [`Bvh::joints`][`Bvh::joints`] method can be used to iterate through each [`Joint`][`Joint`]
//! of the [`Bvh`][`Bvh`]. Each [`Joint`][`Joint`] can be inspected through its [`JointData`]
//! [`JointData`], which can be obtained with the [`Joint::data`][`Joint::data`] method.
//!
//! * The [`Bvh::frames`][`Bvh::frames`] method returns a [`Frames`][`Frames`] iterator over each
//! frame of the animation. A [`Frame`][`Frame`] can only be indexed by a [`Channel`][`Channel`]
//! belonging to an associated [`Joint`][`Joint`] of the [`Bvh`][`Bvh`], although you can convert
//! it into an [`&[`][`slice`][`f32`][`f32`][`]`][`slice`] using the [`Frame::as_slice`][`Frame::as_slice`] method.
//!
//! * You can serialise the [`Bvh`][`Bvh`] into a [`Write`][`Write`] type using the [`Bvh::write_to`]
//! [`Bvh::write_to`] method. There is also the [`Bvh::to_bstring`][`Bvh::to_bstring`] method, which
//! converts the [`Bvh`][`Bvh`] into a [`BString`][`BString`]. Various aspects of the formatting
//! can be customised using the [`WriteOptions`][`WriteOptions`] type, such as the line termination
//! style, indentation method, and floating point accuracy.
//!
//! ## Examples
//!
//! This library comes with some example applications, which can be viewed on [Github][Github].
//!
//! ## Other resources
//!
//! * More information on this file format can be found [here][bvh_html].
//! * A large library of bvh files is freely available from [CMU's motion capture database]
//! [CMU's motion capture database].
//!
//! [`data` directory]: https://github.com/burtonageo/bvh_anim/tree/master/data
//! [`bvh`]: struct.Bvh.html
//! [`from_reader`]: fn.from_reader.html
//! [`from_bytes`]: fn.from_bytes.html
//! [`Bvh::from_reader`]: struct.Bvh.html#method.from_reader
//! [`Bvh::from_bytes`]: struct.Bvh.html#method.from_bytes
//! [`bvh!`]: macro.bvh.html
//! [`builder`]: builder/index.html
//! [`Bvh::new`]: struct.Bvh.html#method.new
//! [`Default::default`]: https://doc.rust-lang.org/stable/std/default/trait.Default.html#tymethod.default
//! [`Bvh::joints`]: struct.Bvh.html#method.joints
//! [`Joint`]: struct.Joint.html
//! [`JointData`]: enum.JointData.html
//! [`Joint::data`]: struct.Joint.html#method.data
//! [`Bvh::frames`]: struct.Bvh.html#method.frames
//! [`Frames`]: struct.Frames.html
//! [`Frame`]: struct.Frame.html
//! [`slice`]: https://doc.rust-lang.org/std/primitive.slice.html
//! [`f32`]: https://doc.rust-lang.org/stable/std/primitive.f32.html
//! [`Channel`]: struct.Channel.html
//! [`Frame::as_slice`]: struct.Frame.html#method.as_slice
//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
//! [`Bvh::write_to`]: struct.Bvh.html#method.write_to
//! [`Bvh::to_bstring`]: struct.Bvh.html#method.to_bstring
//! [`BString`]: https://docs.rs/bstr/0.1.2/bstr/struct.BString.html
//! [`WriteOptions`]: write/struct.WriteOptions.html
//! [Github]: https://github.com/burtonageo/bvh_anim/tree/master/examples
//! [bvh_html]: https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html
//! [CMU's motion capture database]: https://sites.google.com/a/cgspeed.com/cgspeed/motion-capture/daz-friendly-release
#[macro_use]
mod macros;
pub mod errors;
pub mod write;
mod frame_cursor;
mod frame_iter;
pub mod joint;
mod parse;
use crate::{
errors::{LoadError, ParseChannelError},
frames::{FrameCursor, Frames, FramesMut},
joint::{JointData, Offset},
};
use bstr::{
io::{BufReadExt, ByteLines},
BStr, ByteSlice,
};
use std::{
convert::TryFrom,
fmt,
io::{self, Cursor, Write},
iter::Enumerate,
mem,
num::NonZeroUsize,
str::{self, FromStr},
time::Duration,
};
pub mod frames {
//! This module contains types and functions used for accessing and modifying
//! frame data.
pub use crate::frame_cursor::FrameCursor;
pub use crate::frame_iter::{Frame, FrameIndex, FrameMut, Frames, FramesMut};
}
pub use joint::{Joint, JointMut, Joints, JointsMut};
#[doc(hidden)]
pub use macros::BvhLiteralBuilder;
struct CachedEnumerate<I> {
iter: Enumerate<I>,
last_enumerator: Option<usize>,
}
impl<I> CachedEnumerate<I> {
#[inline]
fn new(iter: Enumerate<I>) -> Self {
CachedEnumerate {
iter,
last_enumerator: None,
}
}
#[inline]
fn last_enumerator(&self) -> Option<usize> {
self.last_enumerator
}
}
impl<I: Iterator> Iterator for CachedEnumerate<I> {
type Item = <Enumerate<I> as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (curr, item) = self.iter.next()?;
self.last_enumerator = Some(curr);
Some((curr, item))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
type EnumeratedLines<'a> = CachedEnumerate<ByteLines<&'a mut dyn BufReadExt>>;
impl EnumeratedLines<'_> {
pub(crate) fn next_non_empty_line(&mut self) -> Option<<Self as Iterator>::Item> {
let mut next = self.next();
loop {
match next {
None => return None,
Some((idx, result)) => {
let string = match result {
Ok(s) => s,
Err(e) => return Some((idx, Err(e))),
};
if string.trim().is_empty() {
next = self.next()
} else {
return Some((idx, Ok(string)));
}
}
}
}
}
}
/// Loads the `Bvh` from the `reader`.
#[inline]
pub fn from_reader<R: BufReadExt>(data: R) -> Result<Bvh, LoadError> {
Bvh::from_reader(data)
}
/// Parse a sequence of bytes as if it were an in-memory `Bvh` file.
///
/// # Examples
///
/// ```
/// # use bvh_anim::{self, from_bytes};
/// let bvh_string = br#"
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 3 Xposition Yposition Zposition
/// End Site
/// {
/// OFFSET 0.0 0.0 0.0
/// }
/// }
/// MOTION
/// Frames: 1
/// Frame Time: 0.033333333
/// 0.0 0.0 0.0
/// "#;
///
/// let bvh = from_bytes(&bvh_string[..])?;
/// # let _ = bvh;
/// # Result::<(), bvh_anim::errors::LoadError>::Ok(())
/// ```
#[inline]
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Bvh, LoadError> {
Bvh::from_bytes(bytes)
}
/// Parse a `str` as if it were an in-memory `Bvh` file.
///
/// # Examples
///
/// ```
/// # use bvh_anim::{self, from_str};
/// let bvh_string = "
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 3 Xposition Yposition Zposition
/// End Site
/// {
/// OFFSET 0.0 0.0 0.0
/// }
/// }
/// MOTION
/// Frames: 1
/// Frame Time: 0.033333333
/// 0.0 0.0 0.0
/// ";
///
/// let bvh = from_str(bvh_string)?;
/// # let _ = bvh;
/// # Result::<(), bvh_anim::errors::LoadError>::Ok(())
/// ```
#[inline]
pub fn from_str(string: &str) -> Result<Bvh, LoadError> {
Bvh::from_str(string)
}
/// A complete `bvh` file.
///
/// See the [module documentation](index.html#using-this-library)
/// for more information.
#[derive(Clone, Debug, PartialEq)]
pub struct Bvh {
/// The list of joints. If the root joint exists, it is always at
/// index `0`.
joints: Vec<JointData>,
/// The motion values of the `Frame`.
motion_values: Vec<f32>,
/// The number of `Channel`s in the bvh.
num_channels: usize,
/// The total time it takes to play one frame.
frame_time: Duration,
}
impl Bvh {
/// Create an empty `Bvh`.
#[inline]
pub const fn new() -> Self {
Self {
joints: Vec::new(),
motion_values: Vec::new(),
num_channels: 0,
frame_time: Duration::from_secs(0),
}
}
/// Parse a sequence of bytes as if it were an in-memory `Bvh` file.
///
/// # Examples
///
/// ```
/// # use bvh_anim::{self, Bvh};
/// let bvh_string = br#"
/// HIERARCHY
/// ROOT Hips
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 3 Xposition Yposition Zposition
/// End Site
/// {
/// OFFSET 0.0 0.0 0.0
/// }
/// }
/// MOTION
/// Frames: 1
/// Frame Time: 0.033333333
/// 0.0 0.0 0.0
/// "#;
///
/// let bvh = Bvh::from_bytes(&bvh_string[..])?;
/// # let _ = bvh;
/// # Result::<(), bvh_anim::errors::LoadError>::Ok(())
/// ```
#[inline]
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, LoadError> {
Bvh::from_reader(Cursor::new(bytes))
}
/// Loads the `Bvh` from the `reader`.
#[inline]
pub fn from_reader<R: BufReadExt>(mut reader: R) -> Result<Self, LoadError> {
#[inline(never)]
fn from_reader_(reader: &mut dyn BufReadExt) -> Result<Bvh, LoadError> {
let mut lines = CachedEnumerate::new(reader.byte_lines().enumerate());
let mut bvh = Bvh::default();
bvh.read_joints(&mut lines)?;
bvh.read_motion(&mut lines)?;
Ok(bvh)
}
from_reader_(reader.by_ref())
}
/// Writes the `Bvh` using the `bvh` file format to the `writer`, with
/// the default formatting options.
///
/// # Notes
///
/// To customise the formatting, see the [`WriteOptions`][`WriteOptions`] type.
///
/// # Examples
///
/// ```no_run
/// # use bvh_anim::bvh;
/// # use std::io;
/// # use std::fs::File;
/// let bvh = bvh! {
/// // fields unspecified
/// };
///
/// let mut out_file = File::create("./out_file.bvh")?;
/// bvh.write_to(&mut out_file)?;
/// # Result::<(), io::Error>::Ok(())
/// ```
///
/// [`WriteOptions`]: write/struct.WriteOptions.html
#[inline]
pub fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
write::WriteOptions::default().write(self, writer)
}
/// Writes the `Bvh` using the `bvh` file format into a `Vec<u8>` with
/// the default formatting options.
///
/// The data is guaranteed to be `ASCII`, except for the joint names, which
/// may contain arbitrary bytes.
///
/// # Notes
///
/// To customise the formatting, see the [`WriteOptions`][`WriteOptions`] type.
///
/// [`WriteOptions`]: write/struct.WriteOptions.html
#[inline]
pub fn to_string(&self) -> Vec<u8> {
write::WriteOptions::default().write_to_string(self)
}
/// Returns the root joint if it exists, or `None` if the skeleton is empty.
///
/// # Examples
///
/// ```
/// # use bvh_anim::{Bvh, bvh};
/// let bvh = Bvh::new();
/// assert!(bvh.root_joint().is_none());
///
/// let bvh = bvh! {
/// HIERARCHY
/// ROOT Hips
/// {
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 0
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 0.0
/// # }
/// // Joints...
/// }
/// MOTION
/// # Frames: 0
/// # Frame Time: 0.033333333
/// // Frames...
/// };
///
/// assert!(bvh.root_joint().is_some());
/// ```
#[inline]
pub fn root_joint(&self) -> Option<Joint<'_>> {
if self.joints.is_empty() {
None
} else {
Some(Joint {
index: 0,
joints: &self.joints[..],
})
}
}
/// Returns an iterator over all the `Joint`s in the `Bvh`.
#[inline]
pub fn joints(&self) -> Joints<'_> {
Joints::iter_root(&self.joints[..])
}
/// Returns a mutable iterator over all the joints in the `Bvh`.
#[inline]
pub fn joints_mut(&mut self) -> JointsMut<'_> {
JointsMut::iter_root(&mut self.joints[..])
}
/// Returns a `Frames` iterator over the frames of the bvh.
///
/// # Example
///
/// ```
/// # use bvh_anim::Bvh;
/// # fn use_bvh(bvh: Bvh) {
/// let bvh = // ...
/// # bvh;
/// for frame in bvh.frames() {
/// // use frame...
/// # let _ = frame;
/// }
/// # } // fn use_bvh()
/// ```
#[inline]
pub fn frames(&self) -> Frames<'_> {
Frames {
chunks: NonZeroUsize::new(self.num_channels).map(|_| {
self.motion_values
.as_slice()
.chunks_exact(self.num_channels)
}),
}
}
/// Returns a mutable iterator over the frames of the bvh.
///
/// # Example
///
/// ```
/// # use bvh_anim::Bvh;
/// # fn use_bvh(bvh: Bvh) {
/// let mut bvh = // ...
/// # bvh;
/// for frame in bvh.frames_mut() {
/// // use frame...
/// # let _ = frame;
/// }
/// # } // fn use_bvh()
/// ```
#[inline]
pub fn frames_mut(&mut self) -> FramesMut<'_> {
FramesMut {
chunks: NonZeroUsize::new(self.num_channels).map(move |_| {
self.motion_values
.as_mut_slice()
.chunks_exact_mut(self.num_channels)
}),
}
}
/// Removes all frame data from the `Bvh`, returning the previous frames.
///
/// # Example
///
/// ```
/// # use bvh_anim::bvh;
/// let mut bvh = bvh! {
/// HIERARCHY
/// // hierarchy omitted...
/// # ROOT Hips
/// # {
/// # OFFSET 0.0 0.0 0.0
/// # CHANNELS 3 Xposition Yposition Zposition
/// # End Site
/// # {
/// # OFFSET 0.0 0.0 0.0
/// # }
/// # }
/// MOTION
/// Frames: 1
/// Frame Time: 0.033333333
/// 0.0 0.0 0.0
/// };
///
/// let frames = bvh.extract_frames();
/// assert_eq!(bvh.num_frames(), 0);
/// assert_eq!(bvh.frames().len(), 0);
/// assert_eq!(frames, &[0.0, 0.0, 0.0]);
/// ```
#[inline]
pub fn extract_frames(&mut self) -> Vec<f32> {
mem::take(&mut self.motion_values)
}
/// Get the number of frames in the `Bvh`.
#[inline]
#[deprecated(note = "Please use `frames().len()` instead,")]
pub fn num_frames(&self) -> usize {
self.frames().len()
}
/// Get the number of channels in the `Bvh`.
#[inline]
pub const fn num_channels(&self) -> usize {
self.num_channels
}
/// Get the duration each frame should play for in the `Bvh`.
///
/// # Example
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// # use std::time::Duration;
/// let bvh = bvh! {
/// # HIERARCHY
/// # MOTION
/// # Frames: 0
/// // ...
/// Frame Time: 1
/// // ...
/// };
///
/// assert_eq!(*bvh.frame_time(), Duration::from_secs(1));
/// # } // fn main()
/// ```
#[inline]
pub const fn frame_time(&self) -> &Duration {
&self.frame_time
}
/// Set the duration each frame should play for in the `Bvh` to `new_frame_time`.
///
/// # Example
///
/// ```
/// # fn main() {
/// # use bvh_anim::bvh;
/// # use std::time::Duration;
/// let mut bvh = bvh! {
/// // ...
/// };
///
/// let new_frame_time = Duration::from_secs(21);
/// bvh.set_frame_time(new_frame_time);
///
/// assert_eq!(*bvh.frame_time(), new_frame_time);
/// # } // fn main()
/// ```
#[inline]
pub fn set_frame_time(&mut self, new_frame_time: Duration) {
self.frame_time = new_frame_time;
}
/// Create a new `FrameCursor` for inserting and removing frames.
#[inline]
pub fn frame_cursor(&mut self) -> FrameCursor<'_> {
From::from(self)
}
}
impl Default for Bvh {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl FromStr for Bvh {
type Err = LoadError;
#[inline]
fn from_str(string: &str) -> Result<Self, Self::Err> {
Bvh::from_bytes(string.as_bytes())
}
}
impl TryFrom<&'_ str> for Bvh {
type Error = LoadError;
#[inline]
fn try_from(string: &'_ str) -> Result<Self, Self::Error> {
FromStr::from_str(string)
}
}
impl TryFrom<&'_ BStr> for Bvh {
type Error = LoadError;
#[inline]
fn try_from(string: &'_ BStr) -> Result<Self, Self::Error> {
Bvh::from_bytes(string.as_bytes())
}
}
impl TryFrom<&'_ [u8]> for Bvh {
type Error = LoadError;
#[inline]
fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
Bvh::from_bytes(bytes)
}
}
/// A `Channel` composed of a `ChannelType` and an index into the
/// corresponding motion data.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Channel {
/// The type of the `Channel`.
channel_type: ChannelType,
/// The index into the `Frame` which corresponds to this `Channel`.
motion_index: usize,
}
impl Channel {
#[inline]
const fn new(channel_type: ChannelType, motion_index: usize) -> Self {
Channel {
channel_type,
motion_index,
}
}
/// Returns the `ChannelType` to which this `Channel` corresponds.
#[inline]
pub const fn channel_type(&self) -> ChannelType {
self.channel_type
}
/// Returns the index of the motion value to which this `Channel` corresponds.
#[inline]
pub const fn motion_index(&self) -> usize {
self.motion_index
}
}
/// The available degrees of freedom along which a `Joint` may be manipulated.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ChannelType {
/// Can be translated along the `x` axis.
PositionX,
/// Can be translated along the `y` axis.
PositionY,
/// Can be translated along the `z` axis.
PositionZ,
/// Can be rotated along the `x` axis.
RotationX,
/// Can be rotated along the `y` axis.
RotationY,
/// Can be rotated along the `z` axis.
RotationZ,
}
impl ChannelType {
/// Attempt to parse a bvh channel byte string into a `ChannelType`.
/// Returns `Err` if the string cannot be parsed.
///
/// # Examples
///
/// ```
/// # use bvh_anim::ChannelType;
/// assert_eq!(
/// ChannelType::from_bytes("Xrotation").unwrap(),
/// ChannelType::RotationX);
///
/// let err = ChannelType::from_bytes("Hello").unwrap_err();
/// assert_eq!(err.into_inner(), "Hello");
/// ```
#[inline]
pub fn from_bytes<B>(s: &B) -> Result<Self, ParseChannelError>
where
B: AsRef<[u8]> + ?Sized,
{
let s = s.as_ref();
match s {
b"Xrotation" => Ok(ChannelType::RotationX),
b"Yrotation" => Ok(ChannelType::RotationY),
b"Zrotation" => Ok(ChannelType::RotationZ),
b"Xposition" => Ok(ChannelType::PositionX),
b"Yposition" => Ok(ChannelType::PositionY),
b"Zposition" => Ok(ChannelType::PositionZ),
_ => Err(ParseChannelError::from(s)),
}
}
/// Returns `true` if this channel corresponds to a rotational
/// transform, otherwise `false`.
///
/// # Example
///
/// ```
/// # use bvh_anim::ChannelType;
/// let channel_type = ChannelType::RotationX;
/// assert!(channel_type.is_rotation());
/// ```
#[inline]
pub const fn is_rotation(&self) -> bool {
match *self {
ChannelType::RotationX | ChannelType::RotationY | ChannelType::RotationZ => true,
_ => false,
}
}
/// Returns `true` if this channel corresponds to a positional
/// transform, otherwise `false`.
///
/// # Example
///
/// ```
/// # use bvh_anim::ChannelType;
/// let channel_type = ChannelType::PositionZ;
/// assert!(channel_type.is_position());
/// ```
#[inline]
pub const fn is_position(&self) -> bool {
!self.is_rotation()
}
/// Get the `Axis` about which this `Channel` transforms.
///
/// # Example
///
/// ```
/// # use bvh_anim::{Axis, ChannelType};
/// let channel_type = ChannelType::PositionX;
/// assert_eq!(channel_type.axis(), Axis::X);
/// ```
#[inline]
pub const fn axis(&self) -> Axis {
match *self {
ChannelType::RotationX | ChannelType::PositionX => Axis::X,
ChannelType::RotationY | ChannelType::PositionY => Axis::Y,
ChannelType::RotationZ | ChannelType::PositionZ => Axis::Z,
}
}
/// Returns the string representation of the `ChannelType`.
#[inline]
pub const fn as_str(&self) -> &'static str {
match *self {
ChannelType::RotationX => "Xrotation",
ChannelType::RotationY => "Yrotation",
ChannelType::RotationZ => "Zrotation",
ChannelType::PositionX => "Xposition",
ChannelType::PositionY => "Yposition",
ChannelType::PositionZ => "Zposition",
}
}
}
impl TryFrom<&'_ [u8]> for ChannelType {
type Error = ParseChannelError;
#[inline]
fn try_from(string: &[u8]) -> Result<Self, Self::Error> {
ChannelType::from_bytes(string)
}
}
impl TryFrom<&'_ str> for ChannelType {
type Error = ParseChannelError;
#[inline]
fn try_from(string: &str) -> Result<Self, Self::Error> {
ChannelType::from_bytes(string.as_bytes())
}
}
impl FromStr for ChannelType {
type Err = ParseChannelError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
ChannelType::from_bytes(s)
}
}
impl fmt::Display for ChannelType {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// An enum which represents an axis along a direction in 3D space.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Axis {
/// `x` axis.
X,
/// `y` axis.
Y,
/// `z` axis.
Z,
}
impl Axis {
/// Returns the `Offset` which represents the axis.
///
/// # Examples
///
/// ```
/// # use bvh_anim::Axis;
/// assert_eq!(Axis::X.vector(), [1.0, 0.0, 0.0]);
/// assert_eq!(Axis::Y.vector(), [0.0, 1.0, 0.0]);
/// assert_eq!(Axis::Z.vector(), [0.0, 0.0, 1.0]);
/// ```
#[inline]
pub fn vector(&self) -> Offset {
match *self {
Axis::X => [1.0, 0.0, 0.0],
Axis::Y => [0.0, 1.0, 0.0],
Axis::Z => [0.0, 0.0, 1.0],
}
}
}
impl fmt::Display for Axis {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
Axis::X => "x",
Axis::Y => "y",
Axis::Z => "z",
};
f.write_str(s)
}
}
+611
View File
@@ -0,0 +1,611 @@
use crate::{
joint::{JointData, Offset},
Bvh, Channel, ChannelType,
};
#[doc(hidden)]
#[macro_export]
macro_rules! match_channels {
($builder:ident; ) => {
};
($builder:ident; Xposition $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::PositionX);
$crate::match_channels!($builder; $($rest)*);
};
($builder:ident; Yposition $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::PositionY);
$crate::match_channels!($builder; $($rest)*);
};
($builder:ident; Zposition $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::PositionZ);
$crate::match_channels!($builder; $($rest)*);
};
($builder:ident; Xrotation $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::RotationX);
$crate::match_channels!($builder; $($rest)*);
};
($builder:ident; Yrotation $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::RotationY);
$crate::match_channels!($builder; $($rest)*);
};
($builder:ident; Zrotation $($rest:ident)*) => {
$builder.push_channel($crate::ChannelType::RotationZ);
$crate::match_channels!($builder; $($rest)*);
};
($builder:expr; $($other:tt)*) => {
compile_error!("Unknown tokens");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! parse_offset {
($builder:ident ($ofst_x:literal $ofst_y:literal $ofst_z:literal)) => {
let offset = [f32::from($ofst_x), f32::from($ofst_y), f32::from($ofst_z)];
$builder.push_joint_offset(offset.into(), false);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! parse_joints_internal {
($builder:ident (
$(
JOINT $joint_nm:ident
{
$( $children:tt )*
}
)*
)) => {
$(
$builder.push_joint(stringify!($joint_nm));
$builder.current_depth += 1;
$crate::parse_joints_internal!($builder ( $( $children )* ));
$builder.current_depth -= 1;
)*
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 0
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 1 $ch0:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ; $ch0);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 2 $ch0:ident $ch1:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ; $ch0 $ch1);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 3 $ch0:ident $ch1:ident $ch2:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ; $ch0 $ch1 $ch2);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 4 $ch0:ident $ch1:ident $ch2:ident $ch3:ident
$($rest:tt)*
)) => {
bvh_anim$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
bvh_anim$crate::match_channels!($builder ; $ch0 $ch1 $ch2 $ch3);
bvh_anim$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 5
$ch0:ident
$ch1:ident
$ch2:ident
$ch3:ident
$ch4:ident
$($rest:tt)*
)) => {
bvh_anim::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
bvh_anim::match_channels!($builder ; $ch0 $ch1 $ch2 $ch3 $ch4);
bvh_anim::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 6
$ch0:ident
$ch1:ident
$ch2:ident
$ch3:ident
$ch4:ident
$ch5:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ;
$ch0 $ch1 $ch2 $ch3 $ch4 $ch5);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
CHANNELS 7
$ch0:ident
$ch1:ident
$ch2:ident
$ch3:ident
$ch4:ident
$ch5:ident
$ch6:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ;
$ch0 $ch1 $ch2 $ch3 $ch4 $ch5 $ch6);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 8
$ch0:ident
$ch1:ident
$ch2:ident
$ch3:ident
$ch4:ident
$ch5:ident
$ch6:ident
$ch7:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ;
$ch0 $ch1 $ch2 $ch3 $ch4 $ch5 $ch6 $ch7);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS 9
$ch0:ident
$ch1:ident
$ch2:ident
$ch3:ident
$ch4:ident
$ch5:ident
$ch6:ident
$ch7:ident
$ch8:ident
$($rest:tt)*
)) => {
$crate::parse_offset!($builder ($ofst_x $ofst_y $ofst_z));
$crate::match_channels!($builder ;
$ch0 $ch1 $ch2 $ch3 $ch4 $ch5 $ch6 $ch7 $ch8);
$crate::parse_joints_internal!($builder ( $($rest)* ));
};
($builder:ident (
OFFSET $ofst_x:literal $ofst_y:literal $ofst_z:literal
CHANNELS $unsupported:literal $($rest:tt)*
)) => {
compile_error!("No more than 9 channels supported in CHANNELS statement");
};
($builder:ident (
End Site
{
OFFSET $end_x:literal $end_y:literal $end_z:literal
}
)) => {
let offset = [
f32::from($end_x),
f32::from($end_y),
f32::from($end_z),
];
$builder.push_joint_offset(offset.into() , true);
};
}
/// Create a new [`Bvh`][`Bvh`] object using a macro literal. Useful for
/// testing.
///
/// # Notes
///
/// If you have a very complex `Bvh` file with a large number of joints and frames, then
/// this macro will scale badly to it, and compilation time will suffer.
///
/// # Example
///
/// ```
/// # use bvh_anim::bvh;
/// let simple_skeleton = bvh! {
/// HIERARCHY
/// ROOT Base
/// {
/// OFFSET 0.0 0.0 0.0
/// CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
/// JOINT Middle1
/// {
/// OFFSET 0.0 0.0 15.0
/// CHANNELS 3 Zrotation Xrotation Yrotation
/// JOINT Tip1
/// {
/// OFFSET 0.0 0.0 30.0
/// CHANNELS 3 Zrotation Xrotation Yrotation
/// End Site
/// {
/// OFFSET 0.0 0.0 45.0
/// }
/// }
/// }
/// JOINT Middle2
/// {
/// OFFSET 0.0 15.0 0.0
/// CHANNELS 3 Zrotation Xrotation Yrotation
/// JOINT Tip2
/// {
/// OFFSET 0.0 30.0 0.0
/// CHANNELS 3 Zrotation Xrotation Yrotation
/// End Site
/// {
/// OFFSET 0.0 45.0 0.0
/// }
/// }
/// }
/// }
///
/// MOTION
/// Frames: 3
/// // Time in seconds.
/// Frame Time: 0.033333333333
/// 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
/// 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
/// 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
/// };
/// ```
///
/// You can use the `bvh` macro to create empty `Bvh` instances:
///
/// ```
/// # use bvh_anim::bvh;
/// let empty = bvh!{};
///
/// let empty = bvh! {
/// HIERARCHY
/// MOTION
/// };
///
/// let empty = bvh! {
/// HIERARCHY
/// MOTION
/// Frames: 0
/// };
/// ```
///
/// [`bvh`]: struct.Bvh.html
#[macro_export]
macro_rules! bvh {
() => {
$crate::Bvh::default()
};
(
HIERARCHY
MOTION
) => {
$crate::Bvh::default()
};
(
HIERARCHY
MOTION
Frames: 0
) => {
$crate::Bvh::default()
};
(
HIERARCHY
MOTION
Frames: 0
Frame Time: $frame_time:literal
) => {
{
use std::time::Duration;
let mut bvh = $crate::Bvh::default();
bvh.set_frame_time(Duration::from_secs_f64(f64::from($frame_time)));
bvh
}
};
(
HIERARCHY
ROOT $root_name:ident
{
$( $joints:tt )*
}
MOTION
Frames: 0
Frame Time: $frame_time:literal
) => {
{
use std::time::Duration;
use $crate::parse_joints_internal;
let mut builder = $crate::BvhLiteralBuilder::default();
builder.push_root(stringify!($root_name));
builder.current_depth += 1;
parse_joints_internal!(builder ($($joints)*));
builder.current_depth -= 1;
builder.set_num_channels();
builder.bvh.set_frame_time(Duration::from_secs_f64(f64::from($frame_time)));
builder.bvh
}
};
(
HIERARCHY
ROOT $root_name:ident
{
$( $joints:tt )*
}
MOTION
Frames: $num_frames:literal
Frame Time: $frame_time:literal
$(
$motion:literal
)+
) => {
{
use $crate::bvh;
let mut new_bvh = bvh! {
HIERARCHY
ROOT $root_name
{
$( $joints )*
}
MOTION
Frames: 0
Frame Time: $frame_time
};
let num_channels = new_bvh.num_channels();
new_bvh
.frame_cursor()
.try_insert_frames([ $( f32::from($motion) ),+ ].chunks(num_channels))
.expect("Could not create bvh type from macro literal");
assert_eq!(new_bvh.frames().len(), $num_frames as usize);
new_bvh
}
};
}
// @TODO: refactor this into a general `Builder` so that we have an
// easy interface to get other animation/skeleton data into a `bvh`
// file.
/// Helper struct to build a `Bvh` from the macro without exposing
/// too many internals.
#[doc(hidden)]
#[derive(Default)]
pub struct BvhLiteralBuilder {
pub bvh: Bvh,
pub current_channel_index: usize,
pub current_depth: usize,
pub current_index: usize,
}
#[doc(hidden)]
impl BvhLiteralBuilder {
pub fn push_root(&mut self, name: &str) {
let mut root = JointData::empty_root();
root.set_name(name);
self.bvh.joints.push(root);
self.current_index += 1;
}
pub fn push_joint(&mut self, name: &str) {
// @TODO: make this shared
#[inline]
fn get_parent_index(joints: &[JointData], for_depth: usize) -> usize {
joints
.iter()
.rev()
.find(|jd| jd.depth() == for_depth.saturating_sub(1))
.and_then(|jd| jd.private_data().map(|p| p.self_index))
.unwrap_or(0)
}
let idx = self.current_index;
let dpth = self.current_depth;
let parent = get_parent_index(&self.bvh.joints[..], dpth);
let mut joint = JointData::empty_child();
joint.set_name(name);
if let Some(ref mut private) = joint.private_data_mut() {
private.self_index = idx;
private.depth = dpth;
private.parent_index = parent;
};
self.bvh.joints.push(joint);
self.current_index += 1;
}
pub fn push_channel(&mut self, channel: ChannelType) {
let channel = Channel::new(channel, self.current_channel_index);
self.last_joint().map(|joint| match *joint {
JointData::Root {
ref mut channels, ..
} => {
channels.push(channel);
}
JointData::Child {
ref mut channels, ..
} => {
channels.push(channel);
}
});
self.current_channel_index += 1;
}
pub fn push_joint_offset(&mut self, offset: Offset, is_end_site: bool) {
self.last_joint().map(|joint| {
joint.set_offset(offset, is_end_site);
});
}
#[inline]
pub fn set_num_channels(&mut self) {
self.bvh.num_channels = self.current_channel_index;
}
#[inline]
fn last_joint(&mut self) -> Option<&mut JointData> {
self.bvh.joints.last_mut()
}
}
#[cfg(test)]
mod tests {
// Needed for macros
use std::time::Duration;
#[test]
fn macro_create() {
let bvh = bvh! {
HIERARCHY
ROOT Base
{
OFFSET 0.0 0.0 0.0
CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
JOINT End
{
OFFSET 0.0 0.0 15.0
CHANNELS 3 Zrotation Xrotation Yrotation
End Site
{
OFFSET 0.0 0.0 30.0
}
}
}
MOTION
Frames: 1
Frame Time: 0.033333333333
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
};
{
use super::{ChannelType, JointData, Offset};
fn check_joint<O: Into<Option<Offset>>>(
joint: &JointData,
expected_name: &str,
expected_offset: Offset,
channels: &[ChannelType],
end_site: O,
) {
assert_eq!(joint.name(), expected_name);
assert_eq!(*joint.offset(), expected_offset);
for (chan, expected_chan) in joint
.channels()
.iter()
.map(|c| c.channel_type())
.zip(channels.iter())
{
assert_eq!(chan, *expected_chan);
}
let end_site = end_site.into().map(Into::into);
assert_eq!(joint.end_site(), end_site.as_ref());
}
let mut joints = bvh.joints();
check_joint(
joints.next().unwrap().data(),
"Base",
[0.0, 0.0, 0.0],
&[
ChannelType::PositionX,
ChannelType::PositionY,
ChannelType::PositionZ,
ChannelType::RotationZ,
ChannelType::RotationX,
ChannelType::RotationY,
],
None,
);
check_joint(
joints.next().unwrap().data(),
"End",
[0.0, 0.0, 15.0],
&[
ChannelType::RotationZ,
ChannelType::RotationX,
ChannelType::RotationY,
],
[0.0, 0.0, 30.0],
);
}
assert_eq!(*bvh.frame_time(), Duration::from_nanos(33333333));
let frames = bvh.frames();
assert_eq!(frames.len(), 1);
for frame in frames {
assert_eq!(frame.len(), 9);
for channel in frame.as_slice().iter() {
assert_eq!(*channel, 0.0);
}
}
}
#[test]
fn test_empty_create() {
macro_rules! assert_empty {
($bvh:expr) => {
assert!($bvh.joints().next().is_none());
assert_eq!(*$bvh.frame_time(), Duration::default());
assert!($bvh.frames().next().is_none());
};
}
let empty = bvh! {};
assert_empty!(empty);
let empty_2 = bvh! {
HIERARCHY
MOTION
};
assert_empty!(empty_2);
}
}
+626
View File
@@ -0,0 +1,626 @@
#![allow(unused)]
use crate::{
errors::{LoadJointsError, LoadMotionError},
joint::{JointData, JointName},
Axis, Bvh, Channel, ChannelType, EnumeratedLines,
};
use bstr::ByteSlice;
use lexical::parse;
use std::time::Duration;
/*
use nom::{
alt, char, delimited, digit, do_parse, map, map_res, named, opt, pair, recognize, space, tag,
take_while, parse, ws, Err as NomErr, IResult,
};
*/
use smallvec::{smallvec, SmallVec};
use std::{convert::TryFrom, mem, str};
/*
named! {
unsigned_float(&[u8]) -> f64,
map!(
recognize!(alt!(
delimited!(digit, tag!("."), opt!(digit))
| delimited!(opt!(digit), tag!("."), digit)
)),
lexical::parse
)
}
named! {
float(&[u8]) -> f64,
map!(
pair!(
opt!(alt!(tag!("+") | tag!("-"))),
unsigned_float
),
|(sign, value): (Option<&[u8]>, f64)| {
sign.and_then(|s|
if s[0] == (b'-') {
Some(-1f64)
} else {
None
})
.unwrap_or(1f64) * value
}
)
}
named! {
unsigned_int(&[u8]) -> u64,
map!(
recognize!(
delimited!(space, digit, space)
),
lexical::parse
)
}
named! {
token(&[u8]) -> &[u8],
delimited!(
space,
take_while!(|ch| char::from(ch).is_alphanumeric()),
space
)
}
named!(single_channel(&[u8]) -> ChannelType,
map_res!(
alt!(
tag!("Xposition") |
tag!("Yposition") |
tag!("Zposition") |
tag!("Xrotation") |
tag!("Yrotation") |
tag!("Zrotation")
),
ChannelType::from_bytes
)
);
named! {
offset(&[u8]) -> Vector3<f32>,
ws!(
do_parse!(
tag!(b"OFFSET") >>
x: float >>
y: float >>
z: float >>
(Vector3 { x: x as f32, y: y as f32, z: z as f32 })
)
)
}
named! {
end_site(&[u8]) -> Vector3<f32>,
ws!(
do_parse!(
tag!(b"End") >>
tag!(b"Site") >>
end_site: delimited!(char!('{'), offset, char!('}')) >>
(end_site)
)
)
}
*/
/*
fn heirarchy<'a>(data: &'a [u8]) -> IResult<&'a [u8], Vec<JointData>> {
let mut joints = Vec::new();
let mut num_channels = 0usize;
let mut joint_index = 0usize;
let mut channels = |data: &'a [u8]| -> IResult<&'a [u8], SmallVec<[Channel; 6]>> {
let (mut data, expected_num_channels) = do_parse!(
data,
tag!(b"CHANNELS") >> num_channels: unsigned_int >> (num_channels)
)?;
let mut out_channels = smallvec![];
for i in 0..expected_num_channels {
let (new_data, channel_ty) = single_channel(data)?;
let channel = Channel::new(channel_ty, num_channels);
num_channels += 1;
data = new_data;
}
Ok((&*data, out_channels))
};
struct JointMembers {
name: JointName,
offset: Vector3<f32>,
channels: SmallVec<[Channel; 6]>,
end_site: Option<Vector3<f32>>,
}
let joint_members = |mut data: &'a [u8]| -> IResult<&'a [u8], JointMembers> {
let (new_data, name) =
do_parse!(data, tag!(b"Joint") >> joint_name: token >> (joint_name))?;
let mut offset_and_channels =
|data: &'a [u8]| -> IResult<&'a [u8], (Vector3<f32>, SmallVec<[Channel; 6]>)> {
do_parse!(data, offst: offset >> chans: channels >> (offst, chans))
};
let opt_end_site = |data: &'a [u8]| -> IResult<&'a [u8], Option<Vector3<f32>>> {
match end_site(data) {
Ok((data, site)) => Ok((data, Some(site))),
Err(NomErr::Error(_)) => Ok((data, None)),
// @TODO: is this right?
Err(e) => Err(e),
}
};
let (data, members) = do_parse!(
data,
name: token
>> char!('{')
>> offst_and_chans: offset_and_channels
>> end_site: opt_end_site
>> (name, offst_and_chans.0, offst_and_chans.1, end_site)
)?;
let members = JointMembers {
name: JointName::from(members.0),
offset: members.1,
channels: members.2,
end_site: members.3,
};
Ok((data, members))
};
/*
parse!(data,
do_parse!(
tag!("HIERARCHY") >>
opt!(
tag!("Root") >>
root_joint_name: token >>
char!('{') >>
root_offset: offset >>
root_channels: channels >>
char!('}') >>
()
) >>
()
)
);
*/
Ok((data, joints))
}
named! {
frame_metadata(&[u8]) -> (std::time::Duration, usize),
ws!(do_parse!(
tag!("Frames") >>
char!(':') >>
num_frames: unsigned_int >>
tag!("Frame") >>
tag!("Time") >>
char!(':') >>
frame_time: unsigned_float >>
(Duration::from_secs_f64(frame_time), num_frames as usize)
))
}
fn motion_section(
data: &[u8],
num_channels: usize,
) -> IResult<&[u8], (std::time::Duration, usize, Vec<f32>)> {
let (frame_time, num_frames) = frame_metadata(data)?;
let motion_data = Vec::with_capacity(num_channels * num_frames);
for f in 0..num_frames {
let chomped = do_parse!(data, take_while!(|ch| char::from(ch).is_ascii_whitespace()));
let (output, motion_val) = float(chomped);
motion_data.push(motion_val);
data = output;
}
Ok((data, (frame_time, num_frames, motion_data)))
}
*/
impl Bvh {
// @TODO: Remove panics
/// Logic for parsing the data from a `BufRead`.
pub(crate) fn read_joints(
&mut self,
lines: &mut EnumeratedLines<'_>,
) -> Result<(), LoadJointsError> {
const HEIRARCHY_KEYWORD: &[u8] = b"HIERARCHY";
const ROOT_KEYWORD: &[u8] = b"ROOT";
const JOINT_KEYWORD: &[u8] = b"JOINT";
const ENDSITE_KEYWORDS: &[&[u8]] = &[b"End", b"Site"];
const OPEN_BRACE: &[u8] = b"{";
const CLOSE_BRACE: &[u8] = b"}";
const OFFSET_KEYWORD: &[u8] = b"OFFSET";
const CHANNELS_KEYWORD: &[u8] = b"CHANNELS";
#[derive(Debug, Eq, PartialEq)]
enum ParseMode {
NotStarted,
InHeirarchy,
Finished,
}
#[allow(unused)]
#[derive(Eq, PartialEq)]
enum NextExpectedLine {
Hierarchy,
Channels,
Offset,
OpeningBrace,
ClosingBrace,
JointName,
RootName,
}
let mut joints = vec![];
let mut curr_mode = ParseMode::NotStarted;
let mut curr_channel = 0usize;
let (mut curr_index, mut curr_depth) = (0usize, 0usize);
let mut next_expected_line = NextExpectedLine::Hierarchy;
let mut curr_joint = JointData::empty_root();
let mut in_end_site = false;
let mut pushed_end_site_joint = false;
#[inline]
fn get_parent_index(joints: &[JointData], for_depth: usize) -> usize {
joints
.iter()
.rev()
.find(|jd| jd.depth() == for_depth.saturating_sub(2))
.and_then(|jd| jd.private_data().map(|p| p.self_index))
.unwrap_or(0)
}
for (line_num, line) in lines {
let line = line?;
let line = line.trim();
let mut tokens = line.fields_with(|c: char| c.is_ascii_whitespace() || c == ':');
let first_token = match tokens.next() {
Some(tok) => tok,
None => continue,
};
match first_token.as_bytes() {
HEIRARCHY_KEYWORD => {
if curr_mode != ParseMode::NotStarted {
panic!("Unexpected hierarchy");
}
curr_mode = ParseMode::InHeirarchy;
next_expected_line = NextExpectedLine::RootName;
}
ROOT_KEYWORD => {
if curr_mode != ParseMode::InHeirarchy
|| next_expected_line != NextExpectedLine::RootName
{
panic!("Unexpected root: {:?}", curr_mode);
}
if let Some(name) = tokens.next() {
curr_joint.set_name(name);
} else {
panic!("Missing root name!");
}
}
OPEN_BRACE => {
curr_depth += 1;
}
CLOSE_BRACE => {
curr_depth -= 1;
if curr_depth == 0 {
// We have closed the brace of the root joint.
curr_mode = ParseMode::Finished;
}
if in_end_site {
if let JointData::Child {
ref mut private, ..
} = curr_joint
{
private.self_index = curr_index;
private.parent_index = get_parent_index(&joints, curr_depth);
private.depth = curr_depth - 1;
}
let new_joint = mem::replace(&mut curr_joint, JointData::empty_child());
joints.push(new_joint);
curr_index += 1;
in_end_site = false;
pushed_end_site_joint = true;
}
}
kw if kw == ENDSITE_KEYWORDS[0] => {
if tokens.next() == Some(ENDSITE_KEYWORDS[1]) {
in_end_site = true;
} else {
panic!("Unexpected end keyword");
}
}
JOINT_KEYWORD => {
if curr_mode != ParseMode::InHeirarchy {
panic!("Unexpected Joint");
}
if !pushed_end_site_joint {
if let JointData::Child {
ref mut private, ..
} = curr_joint
{
private.self_index = curr_index;
private.parent_index = get_parent_index(&joints, curr_depth);
private.depth = curr_depth - 1;
}
let new_joint = mem::replace(&mut curr_joint, JointData::empty_child());
joints.push(new_joint);
curr_index += 1;
} else {
pushed_end_site_joint = false;
}
if let Some(mut name) = tokens.next() {
curr_joint.set_name(name);
} else {
panic!("Missing joint name!");
}
}
OFFSET_KEYWORD => {
if curr_mode != ParseMode::InHeirarchy {
return Err(LoadJointsError::UnexpectedOffsetSection { line: line_num });
}
let mut offset = [0.0, 0.0, 0.0];
macro_rules! parse_axis {
($axis_field:literal, $axis_enum:ident) => {
if let Some(tok) = tokens.next() {
offset[$axis_field] =
parse(tok).map_err(|e| LoadJointsError::ParseOffsetError {
parse_float_error: e,
axis: Axis::$axis_enum,
line: line_num,
})?;
} else {
return Err(LoadJointsError::MissingOffsetAxis {
axis: Axis::$axis_enum,
line: line_num,
});
}
};
}
parse_axis!(0, X);
parse_axis!(1, Y);
parse_axis!(2, Z);
curr_joint.set_offset(offset, in_end_site);
}
CHANNELS_KEYWORD => {
if curr_mode != ParseMode::InHeirarchy {
return Err(LoadJointsError::UnexpectedChannelsSection { line: line_num });
}
let num_channels: usize = tokens
.next()
.ok_or(LoadJointsError::ParseNumChannelsError {
error: None,
line: line_num,
})
.and_then(|tok| match parse(tok) {
Ok(c) => Ok(c),
Err(e) => Err(LoadJointsError::ParseNumChannelsError {
error: Some(e),
line: line_num,
}),
})?;
let mut channels: SmallVec<[Channel; 6]> = Default::default();
channels.reserve(num_channels);
while let Some(tok) = tokens.next() {
let channel_ty = ChannelType::try_from(tok).map_err(|e| {
LoadJointsError::ParseChannelError {
error: e,
line: line_num,
}
})?;
let channel = Channel::new(channel_ty, curr_channel);
curr_channel += 1;
channels.push(channel);
}
curr_joint.set_channels(channels);
}
_ => {}
}
if curr_mode == ParseMode::Finished {
break;
}
}
if curr_mode != ParseMode::Finished {
return Err(LoadJointsError::MissingRoot);
}
self.joints = joints;
self.num_channels = curr_channel;
Ok(())
}
pub(crate) fn read_motion(
&mut self,
lines: &mut EnumeratedLines<'_>,
) -> Result<(), LoadMotionError> {
const MOTION_KEYWORD: &[u8] = b"MOTION";
const FRAMES_KEYWORD: &[u8] = b"Frames";
const FRAME_TIME_KEYWORDS: &[&[u8]] = &[b"Frame", b"Time:"];
macro_rules! last_line_num {
() => {
lines.last_enumerator().unwrap_or(0)
};
}
lines
.next_non_empty_line()
.ok_or(LoadMotionError::MissingMotionSection {
line: last_line_num!(),
})
.and_then(|(line_num, line)| {
let line = line?;
let line = line.trim();
if line == MOTION_KEYWORD {
Ok(())
} else {
Err(LoadMotionError::MissingMotionSection { line: line_num })
}
})?;
let num_frames = lines
.next_non_empty_line()
.ok_or(LoadMotionError::MissingNumFrames {
parse_error: None,
line: last_line_num!(),
})
.and_then(|(line_num, line)| {
let line = line?;
let line = line.trim();
let mut tokens = line.fields_with(|c: char| c.is_ascii_whitespace() || c == ':');
if tokens.next() != Some(FRAMES_KEYWORD) {
return Err(LoadMotionError::MissingNumFrames {
parse_error: None,
line: line_num,
});
}
let parse_num_frames = |token: Option<&[u8]>| {
if let Some(num_frames) = token.and_then(|b| str::from_utf8(b).ok()) {
parse::<usize, _>(num_frames)
.map_err(|e| LoadMotionError::MissingNumFrames {
parse_error: Some(e),
line: line_num,
})
.map_err(Into::into)
} else {
Err(LoadMotionError::MissingNumFrames {
parse_error: None,
line: line_num,
})
}
};
match tokens.next() {
Some(tok) if tok == b":" => parse_num_frames(tokens.next()),
Some(tok) => parse_num_frames(Some(tok)),
None => Err(LoadMotionError::MissingNumFrames {
parse_error: None,
line: line_num,
}),
}
})?;
self.frame_time = lines
.next_non_empty_line()
.ok_or(LoadMotionError::MissingFrameTime {
parse_error: None,
line: last_line_num!(),
})
.and_then(|(line_num, line)| {
let line = line?;
let mut tokens = line.fields();
let frame_time_kw = tokens.next();
if frame_time_kw == FRAME_TIME_KEYWORDS.get(0).map(|b| *b) {
// do nothing
} else {
return Err(LoadMotionError::MissingFrameTime {
parse_error: None,
line: line_num,
});
}
let frame_time_kw = tokens.next();
if frame_time_kw == FRAME_TIME_KEYWORDS.get(1).map(|b| *b) {
// do nothing
} else {
return Err(LoadMotionError::MissingFrameTime {
parse_error: None,
line: line_num,
});
}
let parse_frame_time = |token: Option<&[u8]>| {
if let Some(frame_time) = token {
let frame_time_secs = parse::<f64, _>(frame_time).map_err(|e| {
LoadMotionError::MissingFrameTime {
parse_error: Some(e),
line: line_num,
}
})?;
Ok(Duration::from_secs_f64(frame_time_secs))
} else {
Err(LoadMotionError::MissingFrameTime {
parse_error: None,
line: line_num,
})
}
};
match tokens.next() {
Some(tok) if tok == b":" => parse_frame_time(tokens.next()),
Some(tok) => parse_frame_time(Some(tok)),
None => Err(LoadMotionError::MissingNumFrames {
parse_error: None,
line: line_num,
}),
}
})?;
let expected_total_motion_values = self.num_channels * num_frames;
self.motion_values.reserve(expected_total_motion_values);
for (line_num, line) in lines {
let line = line?;
let tokens = line.fields();
for (channel_index, token) in tokens.enumerate() {
let motion =
parse::<f32, _>(token).map_err(|e| LoadMotionError::ParseMotionSection {
parse_error: e,
channel_index,
line: line_num,
})?;
self.motion_values.push(motion);
}
}
if self.motion_values.len() != self.num_channels * num_frames {
return Err(LoadMotionError::MotionCountMismatch {
actual_total_motion_values: self.motion_values.len(),
expected_total_motion_values,
expected_num_frames: num_frames,
expected_num_clips: self.num_channels,
});
}
Ok(())
}
}
+510
View File
@@ -0,0 +1,510 @@
//! Contains options for `bvh` file formatting.
use crate::{frames::Frames, Bvh, Joint, Joints};
use smallvec::SmallVec;
use std::{
fmt,
io::{self, Write},
iter, mem,
num::NonZeroUsize,
};
/// Specify formatting options for writing a `Bvh`.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct WriteOptions {
/// Which indentation style to use for nested bones.
pub indent: IndentStyle,
/// Which style new line terminator to use when writing the `bvh`.
pub line_terminator: LineTerminator,
/// Number of significant figures to use when writing `OFFSET` values.
///
/// If this is `None`, then the minimum precision required will be used
pub offset_significant_figures: Option<usize>,
/// Number of significant figures to use when writing the `Frame Time` value.
///
/// If this is `None`, then the minimum precision required will be used
pub frame_time_significant_figures: Option<usize>,
/// Number of significant figures to use when writing `MOTION` values.
///
/// If this is `None`, then the minimum precision required will be used
pub motion_values_significant_figures: Option<usize>,
#[doc(hidden)]
_nonexhaustive: (),
}
impl Default for WriteOptions {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl WriteOptions {
/// Create a new `WriteOptions` with default values.
#[inline]
pub const fn new() -> Self {
WriteOptions {
indent: IndentStyle::Tabs,
line_terminator: LineTerminator::native(),
offset_significant_figures: None,
frame_time_significant_figures: None,
motion_values_significant_figures: None,
_nonexhaustive: (),
}
}
/// Output the `Bvh` file to the `writer` with the given options.
pub fn write<W: Write>(&self, bvh: &Bvh, writer: &mut W) -> io::Result<()> {
let mut curr_chunk = vec![];
let mut curr_bytes_written = 0usize;
let mut curr_string_len = 0usize;
let mut iter_state = WriteOptionsIterState::new();
while self.next_chunk(bvh, &mut curr_chunk, &mut iter_state) != false {
let bytes: &[u8] = curr_chunk.as_ref();
curr_string_len += bytes.len();
curr_bytes_written += writer.write(bytes)?;
if curr_bytes_written != curr_string_len {
return Err(io::Error::new(
io::ErrorKind::Other,
"Data has been dropped while writing to file",
));
}
}
writer.flush()
}
/// Output the `Bvh` file to the `string` with the given options.
pub fn write_to_string(&self, bvh: &Bvh) -> Vec<u8> {
let mut curr_chunk = vec![];
let mut out_string = vec![];
let mut iter_state = WriteOptionsIterState::new();
while self.next_chunk(bvh, &mut curr_chunk, &mut iter_state) != false {
out_string.extend(curr_chunk.drain(..));
}
out_string
}
/// Sets `indent` on `self` to the new `IndentStyle`.
#[inline]
pub const fn with_indent(self, indent: IndentStyle) -> Self {
Self { indent, ..self }
}
/// Sets `line_terminator` on `self` to the new `LineTerminator`.
#[inline]
pub const fn with_line_terminator(self, line_terminator: LineTerminator) -> Self {
Self {
line_terminator,
..self
}
}
/// Sets `offset_significant_figures` on `self` to the new `offset_significant_figures`.
#[inline]
pub fn with_offset_significant_figures<N>(self, offset_significant_figures: N) -> Self
where
N: Into<Option<usize>>,
{
Self {
offset_significant_figures: offset_significant_figures.into(),
..self
}
}
/// Sets `motion_values_significant_figures` on `self` to the new `motion_values_significant_figures`.
#[inline]
pub fn with_frame_time_significant_figures<N>(self, frame_time_significant_figures: N) -> Self
where
N: Into<Option<usize>>,
{
Self {
frame_time_significant_figures: frame_time_significant_figures.into(),
..self
}
}
/// Sets `motion_values_significant_figures` on `self` to the new `motion_values_significant_figures`.
#[inline]
pub fn with_motion_values_significant_figures<N>(
self,
motion_values_significant_figures: N,
) -> Self
where
N: Into<Option<usize>>,
{
Self {
motion_values_significant_figures: motion_values_significant_figures.into(),
..self
}
}
// @TODO: Refactor all of this
/// Get the next text chunk of the written bvh file. This function is
/// structured so that the `chunk` string can be continually
/// re-used without allocating and de-allocating memory.
///
/// # Returns
///
/// Returns `true` when there are still more lines available,
/// `false` when all lines have been extracted.
fn next_chunk<'a, 'b: 'a>(
&self,
bvh: &'b Bvh,
chunk: &mut Vec<u8>,
iter_state: &'a mut WriteOptionsIterState<'b>,
) -> bool {
chunk.clear();
let terminator = self.line_terminator.as_bytes();
match *iter_state {
WriteOptionsIterState::WriteHierarchy { ref mut written } => {
if !*written {
*chunk = b"HIERARCHY".to_vec();
chunk.extend_from_slice(self.line_terminator.as_bytes());
*written = true;
} else {
let mut joints = bvh.joints();
*iter_state = WriteOptionsIterState::WriteJoints {
current_joint: joints.next(),
joints,
wrote_name: false,
wrote_offset: false,
wrote_channels: false,
};
}
}
WriteOptionsIterState::WriteJoints {
current_joint: None,
..
} => {
*iter_state = WriteOptionsIterState::WriteMotion { written: false };
}
WriteOptionsIterState::WriteJoints {
ref mut joints,
ref mut current_joint,
ref mut wrote_name,
ref mut wrote_offset,
ref mut wrote_channels,
} => {
if let Some(ref joint) = current_joint {
let joint_data = joint.data();
let mut depth = joint_data.depth();
if *wrote_name {
depth += 1
}
match (&mut *wrote_name, &mut *wrote_offset, &mut *wrote_channels) {
(&mut false, _, _) => {
chunk.extend(self.indent.prefix_chars(depth));
if joint_data.is_root() {
chunk.extend_from_slice(b"ROOT ");
} else {
chunk.extend_from_slice(b"JOINT ");
}
chunk.extend_from_slice(joint_data.name().as_ref());
chunk.extend_from_slice(terminator);
chunk.extend(self.indent.prefix_chars(depth));
chunk.push(b'{');
chunk.extend_from_slice(terminator);
*wrote_name = true;
}
(&mut true, &mut false, _) => {
chunk.extend(self.indent.prefix_chars(depth));
let [x, y, z] = joint_data.offset();
let offset_str = match self.offset_significant_figures {
Some(sf) => {
format!("OFFSET {:.*} {:.*} {:.*}", sf, x, sf, y, sf, z,)
}
None => format!("OFFSET {:.} {:.} {:.}", x, y, z),
};
chunk.extend_from_slice(offset_str.as_bytes());
chunk.extend_from_slice(terminator);
*wrote_offset = true;
}
(&mut true, &mut true, &mut false) => {
chunk.extend(self.indent.prefix_chars(depth));
let channels = joint_data.channels();
let channels_str = channels
.iter()
.map(|ch| ch.channel_type().as_str())
.collect::<SmallVec<[_; 6]>>()
.join(" ");
let channels_str =
format!("CHANNELS {} {}", channels.len(), channels_str);
chunk.extend_from_slice(channels_str.as_bytes());
chunk.extend_from_slice(terminator);
*wrote_channels = true;
}
(&mut true, &mut true, &mut true) => {
if let Some([x, y, z]) = joint_data.end_site() {
chunk.extend(self.indent.prefix_chars(depth));
chunk.extend_from_slice(b"End Site");
chunk.extend_from_slice(terminator);
chunk.extend(self.indent.prefix_chars(depth));
chunk.push(b'{');
chunk.extend_from_slice(terminator);
chunk.extend(self.indent.prefix_chars(depth + 1));
let offset_str = match self.offset_significant_figures {
Some(sf) => {
format!("OFFSET {:.*} {:.*} {:.*}", sf, x, sf, y, sf, z)
}
None => format!("OFFSET {:.} {:.} {:.}", x, y, z),
};
chunk.extend_from_slice(offset_str.as_bytes());
chunk.extend_from_slice(terminator);
chunk.extend(self.indent.prefix_chars(depth));
chunk.push(b'}');
chunk.extend_from_slice(terminator);
let next_joint = joints.next();
let prev_joint = mem::replace(current_joint, next_joint).unwrap();
let (curr_depth, mut depth_difference) =
if let Some(ref curr_j) = *current_joint {
let curr_depth = curr_j.data().depth();
(curr_depth, Some(prev_joint.data().depth() - curr_depth))
} else {
(0, Some(prev_joint.data().depth()))
};
while let Some(d) = depth_difference {
chunk.extend(self.indent.prefix_chars(curr_depth + d));
chunk.push(b'}');
chunk.extend_from_slice(terminator);
depth_difference =
depth_difference.and_then(|d| d.checked_sub(1));
}
} else {
*current_joint = joints.next();
}
*wrote_name = false;
*wrote_offset = false;
*wrote_channels = false;
}
}
}
}
WriteOptionsIterState::WriteMotion { ref mut written } => {
if !*written {
*chunk = b"MOTION".to_vec();
chunk.extend_from_slice(terminator);
*written = true;
} else {
*iter_state = WriteOptionsIterState::WriteNumFrames { written: false };
}
}
WriteOptionsIterState::WriteNumFrames { ref mut written } => {
if !*written {
*chunk = format!("Frames: {}", bvh.frames().len()).into_bytes();
chunk.extend_from_slice(terminator);
*written = true;
} else {
*iter_state = WriteOptionsIterState::WriteFrameTime { written: false };
}
}
WriteOptionsIterState::WriteFrameTime { ref mut written } => {
if !*written {
*chunk = match self.frame_time_significant_figures {
Some(sf) => {
format!("Frame Time: {:.*}", sf, bvh.frame_time().as_secs_f64())
.into_bytes()
}
None => {
format!("Frame Time: {:.}", bvh.frame_time().as_secs_f64()).into_bytes()
}
};
chunk.extend_from_slice(terminator);
*written = true;
} else {
let frames = bvh.frames();
*iter_state = WriteOptionsIterState::WriteFrames { frames };
}
}
WriteOptionsIterState::WriteFrames { ref mut frames } => {
return frames
.next()
.map(|frame| {
let motion_values = frame
.as_slice()
.iter()
.map(|motion| match self.motion_values_significant_figures {
Some(sf) => format!("{:.*}", sf, motion),
None => format!("{:.}", motion),
})
.collect::<Vec<_>>()
.join(" ");
*chunk = motion_values.into_bytes();
chunk.extend_from_slice(terminator);
true
})
.unwrap_or_default();
}
}
true
}
}
enum WriteOptionsIterState<'a> {
WriteHierarchy {
written: bool,
},
WriteJoints {
joints: Joints<'a>,
current_joint: Option<Joint<'a>>,
wrote_name: bool,
wrote_offset: bool,
wrote_channels: bool,
},
WriteMotion {
written: bool,
},
WriteNumFrames {
written: bool,
},
WriteFrameTime {
written: bool,
},
WriteFrames {
frames: Frames<'a>,
},
}
impl WriteOptionsIterState<'_> {
#[inline]
const fn new() -> Self {
WriteOptionsIterState::WriteHierarchy { written: false }
}
}
impl Default for WriteOptionsIterState<'_> {
#[inline]
fn default() -> Self {
Self::new()
}
}
/// Specify indentation style to use when writing the `Bvh` joints.
///
/// By default, this value is set to 1 tab.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum IndentStyle {
/// Do not indent nested joints.
NoIndentation,
/// Use a single tab (`'\t'`) for indentation.
Tabs,
/// Use `n` spaces for indentation.
Spaces(NonZeroUsize),
}
impl IndentStyle {
/// Create a new `IndentStyle` with `n` preceeding spaces.
///
/// If `n` is `0`, then `IndentStyle::NoIndentation` is returned.
#[inline]
pub fn with_spaces(n: usize) -> Self {
NonZeroUsize::new(n)
.map(IndentStyle::Spaces)
.unwrap_or(IndentStyle::NoIndentation)
}
/// Return an `Iterator` which yields bytes corresponding to the ascii
/// chars which form the `String` this indentation style would take.
#[inline]
fn prefix_chars(&self, depth: usize) -> impl Iterator<Item = u8> {
match *self {
IndentStyle::NoIndentation => iter::repeat(b'\0').take(0),
IndentStyle::Tabs => iter::repeat(b'\t').take(depth),
IndentStyle::Spaces(n) => iter::repeat(b' ').take(n.get() * depth),
}
}
}
/// Create a new `IndentStyle` using a single tab.
impl Default for IndentStyle {
#[inline]
fn default() -> Self {
IndentStyle::Tabs
}
}
/// Represents which line terminator style to use when writing a `Bvh` file.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LineTerminator {
/// Use Unix-style line endings (`'\n'`).
Unix,
/// Use Windows-style line endings (`'\r\n'`).
Windows,
}
impl LineTerminator {
/// Get the line terminator style native to the current OS:
///
/// * On Windows, this returns `LineTerminator::Windows`.
/// * Otherwise, this returns `LineTerminator::Unix`.
#[inline]
pub const fn native() -> Self {
if cfg!(windows) {
LineTerminator::Windows
} else {
LineTerminator::Unix
}
}
/// Return the characters of the `LineTerminator` as a `&str`.
#[inline]
pub const fn as_str(&self) -> &str {
match *self {
LineTerminator::Unix => "\n",
LineTerminator::Windows => "\r\n",
}
}
/// Returns the escaped characters of the `LineTerminator` as a `&str`.
#[inline]
pub const fn as_escaped_str(&self) -> &str {
match *self {
LineTerminator::Unix => r"\n",
LineTerminator::Windows => r"\r\n",
}
}
/// Return the characters of the `LineTerminator` as a `&[u8]`.
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
/// Returns the escaped characters of the `LineTerminator` as a `&[u8]`.
#[inline]
pub const fn as_escaped_bytes(&self) -> &[u8] {
self.as_escaped_str().as_bytes()
}
}
/// Returns the native line terminator for the current OS.
impl Default for LineTerminator {
#[inline]
fn default() -> Self {
LineTerminator::native()
}
}
impl fmt::Display for LineTerminator {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}