///|
/// Ray-field diagnostics for camera coverage and scan planning.
pub fn ray_field_direction_z_minimum(rays : ArrayView[@core.Ray3]) -> Double {
if rays.length() == 0 {
0.0
} else {
let mut result = rays[0].direction.z
for ray in rays {
result = result.min(ray.direction.z)
}
result
}
}
///|
pub fn ray_field_direction_z_maximum(rays : ArrayView[@core.Ray3]) -> Double {
let mut result = 0.0
for ray in rays {
result = result.max(ray.direction.z)
}
result
}
///|
pub fn ray_field_direction_z_mean(rays : ArrayView[@core.Ray3]) -> Double {
if rays.length() == 0 {
0.0
} else {
let mut result = 0.0
for ray in rays {
result += ray.direction.z
}
result / rays.length().to_double()
}
}
///|
pub fn ray_field_direction_z_ratio(
rays : ArrayView[@core.Ray3],
threshold : Double,
) -> Double {
if rays.length() == 0 {
0.0
} else {
let mut count = 0
for ray in rays {
if ray.direction.z >= threshold {
count += 1
}
}
count.to_double() / rays.length().to_double()
}
}
///|
pub fn ray_field_origin_bounds(
rays : ArrayView[@core.Ray3],
) -> (@core.Point3, @core.Point3) {
let points = camera_ray_field_origins(rays)
@core.point_cloud_bounds(points)
}
///|
pub fn ray_field_origin_centroid(rays : ArrayView[@core.Ray3]) -> @core.Point3 {
@core.point_cloud_centroid(camera_ray_field_origins(rays))
}
///|
pub fn ray_field_origin_spread(rays : ArrayView[@core.Ray3]) -> Double {
@core.point_cloud_safe_radius(camera_ray_field_origins(rays))
}
///|
pub fn ray_field_origin_quality(
rays : ArrayView[@core.Ray3],
maximum_spread : Double,
) -> Double {
if maximum_spread <= 0.0 {
0.0
} else {
(1.0 - ray_field_origin_spread(rays) / maximum_spread).max(0.0).min(1.0)
}
}
///|
pub fn ray_field_direction_quality(
rays : ArrayView[@core.Ray3],
minimum_forward_ratio : Double,
) -> Double {
(camera_ray_field_forward_ratio(rays) /
minimum_forward_ratio.max(0.000000000001)).min(1.0)
}
///|
pub fn ray_field_quality(
rays : ArrayView[@core.Ray3],
minimum_forward_ratio : Double,
maximum_spread : Double,
) -> Double {
ray_field_direction_quality(rays, minimum_forward_ratio) *
ray_field_origin_quality(rays, maximum_spread)
}
///|
pub fn ray_field_filter_forward(
rays : ArrayView[@core.Ray3],
minimum_z : Double,
) -> Array[@core.Ray3] {
let result : Array[@core.Ray3] = []
for ray in rays {
if ray.direction.z >= minimum_z {
result.push(ray)
}
}
result
}
///|
pub fn ray_field_filter_origin_z(
rays : ArrayView[@core.Ray3],
minimum_z : Double,
maximum_z : Double,
) -> Array[@core.Ray3] {
let result : Array[@core.Ray3] = []
for ray in rays {
if ray.origin.z >= minimum_z && ray.origin.z <= maximum_z {
result.push(ray)
}
}
result
}
///|
pub fn ray_field_direction_dot_sum(
left : ArrayView[@core.Ray3],
right : ArrayView[@core.Ray3],
) -> Double {
let count = left.length().min(right.length())
let mut result = 0.0
for i in 0.. Double {
let count = left.length().min(right.length())
if count == 0 {
0.0
} else {
ray_field_direction_dot_sum(left, right) / count.to_double()
}
}
///|
pub fn ray_field_reproject_depth(
rays : ArrayView[@core.Ray3],
depth : Double,
) -> Array[@core.Point3] {
let result : Array[@core.Point3] = []
for ray in rays {
result.push(ray.point_at(depth))
}
result
}
///|
pub fn ray_field_depth_range(
rays : ArrayView[@core.Ray3],
depth : Double,
) -> (@core.Point3, @core.Point3) {
@core.point_cloud_bounds(ray_field_reproject_depth(rays, depth))
}
///|
pub fn ray_field_depth_centroid(
rays : ArrayView[@core.Ray3],
depth : Double,
) -> @core.Point3 {
@core.point_cloud_centroid(ray_field_reproject_depth(rays, depth))
}
///|
pub fn ray_field_depth_radius(
rays : ArrayView[@core.Ray3],
depth : Double,
) -> Double {
let points = ray_field_reproject_depth(rays, depth)
@core.point_cloud_safe_radius(points)
}
///|
pub fn ray_field_depth_quality(
rays : ArrayView[@core.Ray3],
depth : Double,
maximum_radius : Double,
) -> Double {
if maximum_radius <= 0.0 {
0.0
} else {
(1.0 - ray_field_depth_radius(rays, depth) / maximum_radius)
.max(0.0)
.min(1.0)
}
}
///|
pub fn ray_field_scan_score(
rays : ArrayView[@core.Ray3],
depth : Double,
minimum_forward_ratio : Double,
maximum_radius : Double,
) -> Double {
ray_field_quality(rays, minimum_forward_ratio, maximum_radius) *
ray_field_depth_quality(rays, depth, maximum_radius)
}
///|
pub fn ray_field_depth_schedule(
minimum : Double,
maximum : Double,
steps : Int,
) -> Array[Double] {
let result : Array[Double] = []
let count = steps.max(1)
if count == 1 {
result.push(minimum)
return result
}
for i in 0.. Array[Double] {
let result : Array[Double] = []
for depth in schedule {
result.push(
ray_field_scan_score(rays, depth, minimum_forward_ratio, maximum_radius),
)
}
result
}