///|
/// Framing utilities for adapting animated cameras to an output viewport.
pub enum FramingPolicy2 {
FitWidthFraming
FitHeightFraming
FitBoundsFraming
FillBoundsFraming
} derive(Debug)
///|
pub fn framing_policies2() -> Array[FramingPolicy2] {
[FitWidthFraming, FitHeightFraming, FitBoundsFraming, FillBoundsFraming]
}
///|
pub struct CameraFraming2 {
center : Point3
radius : Double
policy : FramingPolicy2
margin : Double
} derive(Debug)
///|
pub fn camera_framing2(
center : Point3,
radius : Double,
policy : FramingPolicy2,
margin? : Double = 0.0,
) -> CameraFraming2 {
{ center, radius: radius.max(0.0), policy, margin: margin.max(0.0) }
}
///|
pub fn CameraFraming2::center(self : CameraFraming2) -> Point3 {
self.center
}
///|
pub fn CameraFraming2::radius(self : CameraFraming2) -> Double {
self.radius
}
///|
pub fn CameraFraming2::policy(self : CameraFraming2) -> FramingPolicy2 {
self.policy
}
///|
pub fn CameraFraming2::margin(self : CameraFraming2) -> Double {
self.margin
}
///|
pub fn CameraFraming2::effective_radius(self : CameraFraming2) -> Double {
self.radius * (1.0 + self.margin)
}
///|
pub fn CameraRig::frame_sphere(
self : CameraRig,
framing : CameraFraming2,
) -> CameraRig {
self.dolly_to_fit(framing.center, framing.effective_radius())
}
///|
pub fn project_bounds3d(camera : CameraRig, bounds : Bounds3D) -> Bounds2D {
let minimum = bounds.minimum()
let maximum = bounds.maximum()
let corners = [
point3(x=minimum.x(), y=minimum.y(), z=minimum.z()),
point3(x=maximum.x(), y=minimum.y(), z=minimum.z()),
point3(x=minimum.x(), y=maximum.y(), z=minimum.z()),
point3(x=maximum.x(), y=maximum.y(), z=minimum.z()),
point3(x=minimum.x(), y=minimum.y(), z=maximum.z()),
point3(x=maximum.x(), y=minimum.y(), z=maximum.z()),
point3(x=minimum.x(), y=maximum.y(), z=maximum.z()),
point3(x=maximum.x(), y=maximum.y(), z=maximum.z()),
]
let first = camera.project(corners[0])
let mut min_x = first.x()
let mut min_y = first.y()
let mut max_x = first.x()
let mut max_y = first.y()
for corner in corners {
let point = camera.project(corner)
min_x = min_x.min(point.x())
min_y = min_y.min(point.y())
max_x = max_x.max(point.x())
max_y = max_y.max(point.y())
}
{ minimum: point2(x=min_x, y=min_y), maximum: point2(x=max_x, y=max_y) }
}
///|
pub fn camera_framing_error(
camera : CameraRig,
bounds : Bounds3D,
margin : Double,
) -> Double {
let projected = project_bounds3d(camera, bounds)
let viewport = camera.viewport()
let left = projected.minimum().x()
let right = projected.maximum().x()
let top = projected.minimum().y()
let bottom = projected.maximum().y()
let margin_x = viewport.width() * margin.max(0.0)
let margin_y = viewport.height() * margin.max(0.0)
(margin_x - left)
.max(0.0)
.max(right - viewport.width() + margin_x)
.max((margin_y - top).max(0.0))
.max(bottom - viewport.height() + margin_y)
}
///|
pub fn solve_orthographic_height(
camera : CameraRig,
bounds : Bounds3D,
margin : Double,
) -> Double {
let center = bounds.center()
let size = bounds.size()
let aspect = camera.viewport().aspect()
let required_height = size.y().max(size.x() / aspect) *
(1.0 + margin.max(0.0) * 2.0)
let _ = center
required_height
}
///|
pub fn camera_center_of_bounds(bounds : Bounds3D) -> Point3 {
bounds.center()
}
///|
pub fn camera_radius_of_bounds(bounds : Bounds3D) -> Double {
point3_length(bounds.size()) * 0.5
}
///|
pub struct ProjectionSample2 {
world : Point3
screen : Point2
visible : Bool
depth : Double
} derive(Debug)
///|
pub fn ProjectionSample2::world(self : ProjectionSample2) -> Point3 {
self.world
}
///|
pub fn ProjectionSample2::screen(self : ProjectionSample2) -> Point2 {
self.screen
}
///|
pub fn ProjectionSample2::visible(self : ProjectionSample2) -> Bool {
self.visible
}
///|
pub fn ProjectionSample2::depth(self : ProjectionSample2) -> Double {
self.depth
}
///|
pub fn project_samples2(
camera : CameraRig,
points : Array[Point3],
) -> Array[ProjectionSample2] {
let result : Array[ProjectionSample2] = []
for world in points {
let screen = camera.project(world)
let ray = camera.ray(screen)
let delta = point3(
x=world.x() - ray.0.x(),
y=world.y() - ray.0.y(),
z=world.z() - ray.0.z(),
)
result.push({
world,
screen,
visible: camera.visible(world),
depth: point3_dot(delta, ray.1),
})
}
result
}
///|
pub fn reprojection_error(camera : CameraRig, world : Point3) -> Double {
let screen = camera.project(world)
let reconstructed = camera.unproject(
screen,
point3_length(
point3(
x=world.x() - camera.pose().position().x(),
y=world.y() - camera.pose().position().y(),
z=world.z() - camera.pose().position().z(),
),
),
)
point3_distance(world, reconstructed)
}