pub mod cache;
pub mod finite;
pub mod frame;
pub mod grids;
pub mod package;
pub mod surfaces;
pub mod wells;
pub use cache::{PackageCache, Work};
use std::collections::HashSet;
use crate::read::cache::HeadSlot;
use crate::read::wells::HeadRead;
use crate::spatial::Rect;
use energyml_domain::ArraySource as ValueSource;
use energyml_domain::reproject::Reprojector;
use energyml_core::registry::DynObject;
use energyml_domain::color::GraphicalSet;
use energyml_domain::package_index::PackageIndex;
use crate::model::{Asked, Contribution, SectionTrace, System};
pub type FramesOf<'a> = dyn Fn(&str) -> Vec<Box<dyn DynObject>> + 'a;
pub type ColourOf<'a> = dyn Fn(&str) -> Option<String> + 'a;
#[derive(Default)]
pub struct PackageContext<'a> {
pub graphics: &'a [(String, GraphicalSet)],
pub frames_of: Option<&'a FramesOf<'a>>,
pub index: Option<&'a PackageIndex>,
pub colour_of: Option<&'a ColourOf<'a>>,
}
pub struct Scene<'a> {
pub system: Option<&'a System>,
pub time_factor: Option<f64>,
pub reprojector: &'a dyn Reprojector,
}
pub const CORRIDOR_SLACK: f64 = 1e-9;
pub fn corridor_windows(sections: &[SectionTrace]) -> Option<Vec<Rect>> {
let mut windows = Vec::new();
for st in sections {
let reach = st.left.max(st.right);
let margin = reach * (1.0 + CORRIDOR_SLACK) + 1e-6;
if !margin.is_finite() {
return None;
}
for pair in st.trace.windows(2) {
windows.push(Rect::new(pair[0], pair[1])?.grown(margin));
}
}
(!windows.is_empty()).then_some(windows)
}
pub fn contribution(
source: &dyn ValueSource,
asked: &Asked,
sections: &[SectionTrace],
scene: &Scene<'_>,
well_context: &PackageContext<'_>,
) -> Contribution {
contribution_cached(
source,
asked,
sections,
scene,
well_context,
&mut PackageCache::new(),
)
}
#[allow(clippy::result_large_err)]
pub fn contribution_cached(
source: &dyn ValueSource,
asked: &Asked,
sections: &[SectionTrace],
scene: &Scene<'_>,
well_context: &PackageContext<'_>,
cache: &mut PackageCache,
) -> Contribution {
contribution_bounded(
source,
asked,
sections,
scene,
well_context,
cache,
Bound::Corridor,
)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Bound {
Corridor,
Everything,
}
#[allow(clippy::result_large_err, clippy::too_many_arguments)]
pub fn contribution_bounded(
source: &dyn ValueSource,
asked: &Asked,
sections: &[SectionTrace],
scene: &Scene<'_>,
well_context: &PackageContext<'_>,
cache: &mut PackageCache,
bound: Bound,
) -> Contribution {
cache.hold_for(&asked.store, asked.revision, scene);
let mut out = Contribution {
store: asked.store.clone(),
..Contribution::default()
};
let read_in_full = |cache: &mut PackageCache, uri: &str, out: &mut Contribution| {
match cache
.well(uri, || {
wells::read_well_with(source, &asked.store, uri, scene, well_context)
})
.and_then(finite::finite_well)
{
Ok((well, note)) => {
out.wells.push(well);
out.notes.extend(note);
}
Err(refused) => out.refused.push(refused),
}
};
match corridor_windows(sections).filter(|_| bound == Bound::Corridor && !asked.wells.is_empty()) {
None => {
for uri in &asked.wells {
read_in_full(cache, uri, &mut out);
}
}
Some(windows) => {
let slots: Vec<Option<(usize, HeadRead)>> = asked
.wells
.iter()
.map(|uri| {
match cache.head(uri, || wells::read_head(source, &asked.store, uri, scene).ok()) {
HeadSlot::Head { at, head } => Some((*at, (**head).clone())),
HeadSlot::Unusable => None,
}
})
.collect();
let near = cache.heads_in(&windows);
let near_wellbores: HashSet<&str> = slots
.iter()
.flatten()
.filter(|(at, _)| near.contains(at))
.filter_map(|(_, head)| head.wellbore.as_deref())
.collect();
for (uri, slot) in asked.wells.iter().zip(&slots) {
match slot {
Some((at, head))
if !near.contains(at)
&& !head
.wellbore
.as_deref()
.is_some_and(|wellbore| near_wellbores.contains(wellbore)) =>
{
out.wells.push(head.stub());
}
_ => read_in_full(cache, uri, &mut out),
}
}
}
}
let mut colours: Option<Option<GraphicalSet>> = None;
for uri in &asked.surfaces {
let read = cache.surface(uri, sections, || {
let set = colours
.get_or_insert_with(|| {
(well_context.colour_of.is_none() && !well_context.graphics.is_empty()).then(|| {
GraphicalSet::merged(well_context.graphics.iter().map(|(_, set)| set.clone()))
})
})
.as_ref();
let colouring = surfaces::Colouring {
set,
index: well_context.index,
resolver: well_context.colour_of,
};
surfaces::prepare_surface(source, &asked.store, uri, scene, colouring)
});
match read {
Ok(mut surface) => {
finite::finite_surface(&mut surface);
out.surfaces.push(surface);
}
Err(refused) => out.refused.push(refused),
}
}
out
}