use super::{
atlas_plan::AtlasSampler,
page_raster::Raster,
source::Source,
transfer_budget::TransferBudget,
transfer_math::*,
transfer_surface::{Observation, Surface},
transfer_target::{self, TargetTriangle},
transfer_types::*,
AppearanceItem,
};
pub(super) struct TransferSampler<'a> {
pub surface: Surface,
pub budget: TransferBudget,
pub image: Raster<'a>,
pub frame: &'a TransferFrame,
pub repeat: [bool; 2],
pub items: Vec<TransferItemCoverage>,
targets: Vec<TargetTriangle>,
samples: Vec<TransferCoverage>,
current: Option<(u32, u32)>,
}
#[derive(Clone, Copy)]
enum SampleKind {
Centroid,
Interior,
Guard,
}
impl<'a> TransferSampler<'a> {
pub fn new(
surface: Surface,
budget: TransferBudget,
image: Raster<'a>,
frame: &'a TransferFrame,
repeat: [bool; 2],
) -> Self {
Self {
surface,
budget,
image,
frame,
repeat,
items: Vec::new(),
targets: Vec::new(),
samples: Vec::new(),
current: None,
}
}
fn observe(
&mut self,
triangle: usize,
weights: Point,
kind: SampleKind,
) -> Result<(Observation, [f64; 2]), String> {
let target = &self.targets[triangle];
let result = self.surface.observe(
interpolate(target.points, weights),
target.normal,
&mut self.budget,
)?;
if !matches!(kind, SampleKind::Guard) {
let coverage = &mut self.samples[triangle];
let observed = result.0 == Observation::Observed;
match kind {
SampleKind::Centroid => {
coverage.centroid_samples += 1;
coverage.observed_centroid_samples += u64::from(observed);
}
SampleKind::Interior => {
coverage.raster_interior_texels += 1;
coverage.observed_raster_interior_texels += u64::from(observed);
}
SampleKind::Guard => unreachable!(),
}
coverage.samples += 1;
match result.0 {
Observation::Observed => coverage.observed_samples += 1,
Observation::Distance => coverage.unknown_distance_samples += 1,
Observation::Normal => coverage.unknown_normal_samples += 1,
Observation::Ambiguous => coverage.unknown_ambiguous_samples += 1,
}
}
Ok(result)
}
}
impl AtlasSampler for TransferSampler<'_> {
fn raster_guards(&self) -> bool { true }
fn begin_item(
&mut self,
source: &mut Source<'_>,
product: u32,
item: &AppearanceItem,
points: &[Point],
triangles: &[[u32; 3]],
mesh: &crate::types::mesh::MeshData,
) -> Result<(), String> {
self.budget
.reserve(triangles.len() * 256 + points.len() * 24)?;
self.budget.charge(triangles.len() * 8 + points.len())?;
self.targets =
transfer_target::prepare(source, product, points, triangles, mesh, self.frame)?;
self.samples = vec![TransferCoverage::default(); triangles.len()];
self.current = Some((product, item.geometry_item_id));
for triangle in 0..triangles.len() {
self.observe(triangle, [1. / 3.; 3], SampleKind::Centroid)?;
}
Ok(())
}
fn end_item(&mut self) -> Result<(), String> {
let mut coverage = TransferCoverage::default();
for (samples, target) in self.samples.iter_mut().zip(&self.targets) {
let fraction = samples.observed_samples as f64 / samples.samples as f64;
samples.observed_area_estimate_m2 = target.area * fraction;
samples.unknown_area_estimate_m2 = target.area * (1. - fraction);
accumulate(&mut coverage, samples);
}
let (product_id, geometry_item_id) = self
.current
.take()
.ok_or("Missing transfer coverage item")?;
self.items.push(TransferItemCoverage {
product_id,
geometry_item_id,
coverage,
});
Ok(())
}
fn reserve_pixels(&mut self, pixels: usize) -> Result<(), String> {
self.budget.reserve(
pixels
.checked_mul(17)
.ok_or("Transfer atlas allocation overflow")?,
)?;
self.budget.charge(pixels.checked_mul(3).ok_or("Transfer guard work overflow")?)
}
fn sample(
&mut self,
_: &AppearanceItem,
triangle: usize,
weights: Point,
interior: bool,
background: [f64; 4],
) -> Result<[u8; 4], String> {
let (observation, uv) = self.observe(
triangle,
weights,
if interior {
SampleKind::Interior
} else {
SampleKind::Guard
},
)?;
let color = if observation == Observation::Observed {
self.image.sample([uv[0], 1. - uv[1]], self.repeat)
} else {
background
};
Ok(color.map(|v| (v.clamp(0., 1.) * 255.).round() as u8))
}
}
pub(super) fn accumulate(total: &mut TransferCoverage, item: &TransferCoverage) {
total.samples += item.samples;
total.observed_samples += item.observed_samples;
total.centroid_samples += item.centroid_samples;
total.observed_centroid_samples += item.observed_centroid_samples;
total.raster_interior_texels += item.raster_interior_texels;
total.observed_raster_interior_texels += item.observed_raster_interior_texels;
total.unknown_distance_samples += item.unknown_distance_samples;
total.unknown_normal_samples += item.unknown_normal_samples;
total.unknown_ambiguous_samples += item.unknown_ambiguous_samples;
total.observed_area_estimate_m2 += item.observed_area_estimate_m2;
total.unknown_area_estimate_m2 += item.unknown_area_estimate_m2;
}