///|
/// Spatial interpolation helpers for camera rigs and scene objects.
pub struct Transform3Delta {
translation : Point3
rotation : Quaternion
scale : Point3
} derive(Debug)
///|
pub fn Transform3Delta::translation(self : Transform3Delta) -> Point3 {
self.translation
}
///|
pub fn Transform3Delta::rotation(self : Transform3Delta) -> Quaternion {
self.rotation
}
///|
pub fn Transform3Delta::scale(self : Transform3Delta) -> Point3 {
self.scale
}
///|
pub fn transform3_delta(
first : Transform3D,
second : Transform3D,
) -> Transform3Delta {
let translation = point3(
x=second.position().x() - first.position().x(),
y=second.position().y() - first.position().y(),
z=second.position().z() - first.position().z(),
)
let rotation = first
.rotation()
.conjugate()
.multiply(second.rotation())
.normalized()
let scale = point3(
x=if first.scale().x() == 0.0 {
0.0
} else {
second.scale().x() / first.scale().x()
},
y=if first.scale().y() == 0.0 {
0.0
} else {
second.scale().y() / first.scale().y()
},
z=if first.scale().z() == 0.0 {
0.0
} else {
second.scale().z() / first.scale().z()
},
)
{ translation, rotation, scale }
}
///|
pub fn apply_transform3_delta(
value : Transform3D,
delta : Transform3Delta,
weight : Double,
) -> Transform3D {
let ratio = clamp01(weight)
value.combine(
transform3d(
position=delta.translation.lerp(point3(x=0.0, y=0.0, z=0.0), 1.0 - ratio),
rotation=Quaternion::identity().slerp(delta.rotation, ratio),
scale=point3(
x=1.0 + (delta.scale.x() - 1.0) * ratio,
y=1.0 + (delta.scale.y() - 1.0) * ratio,
z=1.0 + (delta.scale.z() - 1.0) * ratio,
),
),
)
}
///|
pub fn transform3_distance(first : Transform3D, second : Transform3D) -> Double {
let position = point3_distance(first.position(), second.position())
let scale = point3_distance(first.scale(), second.scale())
let rotation = 1.0 - first.rotation().dot(second.rotation()).abs()
position + scale + rotation
}
///|
pub fn transform3_clamp_scale(
value : Transform3D,
minimum : Point3,
maximum : Point3,
) -> Transform3D {
transform3d(
position=value.position(),
rotation=value.rotation(),
scale=point3(
x=clamp(value.scale().x(), minimum.x(), maximum.x()),
y=clamp(value.scale().y(), minimum.y(), maximum.y()),
z=clamp(value.scale().z(), minimum.z(), maximum.z()),
),
)
}
///|
pub fn transform3_look_at(
position : Point3,
target : Point3,
up : Point3,
) -> Transform3D {
let forward = point3_normalized(
point3(
x=target.x() - position.x(),
y=target.y() - position.y(),
z=target.z() - position.z(),
),
)
let right = point3_normalized(point3_cross(up, forward))
let corrected_up = point3_cross(forward, right)
let trace = right.x() + corrected_up.y() + forward.z()
let rotation = if trace > 0.0 {
let scale = @math.pow(trace + 1.0, 0.5) * 2.0
quaternion(
x=(corrected_up.z() - forward.y()) / scale,
y=(forward.x() - right.z()) / scale,
z=(right.y() - corrected_up.x()) / scale,
w=0.25 * scale,
)
} else {
Quaternion::identity()
}
transform3d(
position~,
rotation=rotation.normalized(),
scale=point3(x=1.0, y=1.0, z=1.0),
)
}
///|
pub fn transform3_axes(value : Transform3D) -> (Point3, Point3, Point3) {
(value.rotation().right(), value.rotation().up(), value.rotation().forward())
}
///|
pub fn transform3_local_point(value : Transform3D, point : Point3) -> Point3 {
value.apply(point)
}
///|
pub fn transform3_world_point(value : Transform3D, point : Point3) -> Point3 {
value.inverse().apply(point)
}
///|
pub fn transform3_track_error(
track : Transform3Track,
samples : Int,
) -> Double raise MotionError {
let values = track.sample_many(samples.max(2))
let mut maximum = 0.0
for index in 1..