use crate::model::{CutShape, CutSource, Note, Refused, SurfaceRead, WellRead};
fn finite2(point: &[f64; 2]) -> bool {
point.iter().all(|value| value.is_finite())
}
fn finite3(point: &[f64; 3]) -> bool {
point.iter().all(|value| value.is_finite())
}
pub const NOT_A_NUMBER: &str = "notANumber";
#[allow(clippy::result_large_err)]
pub fn finite_well(mut well: WellRead) -> Result<(WellRead, Option<Note>), Refused> {
let name = if well.object.title.is_empty() {
well.object.uuid.clone()
} else {
well.object.title.clone()
};
let refuse = |well: &WellRead, reason: String| Refused {
object: well.object.clone(),
reason,
};
if !finite3(&well.head) {
return Err(refuse(
&well,
format!(
"`{name}`: its head has a coordinate that is not a number (no elevation?), so it cannot be placed"
),
));
}
let total = well.path.len();
let keep: Vec<bool> = (0..total)
.map(|at| {
finite3(&well.path[at])
&& well
.md
.as_ref()
.is_none_or(|md| md.get(at).is_none_or(|value| value.is_finite()))
})
.collect();
let dropped_nodes = keep.iter().filter(|kept| !**kept).count();
if dropped_nodes > 0 {
let mut at = 0;
well.path.retain(|_| {
at += 1;
keep[at - 1]
});
if let Some(md) = well.md.as_mut() {
let mut at = 0;
md.retain(|_| {
at += 1;
keep.get(at - 1).copied().unwrap_or(true)
});
}
}
if well.path.len() < 2 {
return Err(refuse(
&well,
format!(
"`{name}`: {dropped_nodes} of its {total} nodes have a coordinate or an MD that is not a number, too few are left to draw it"
),
));
}
let intervals = well.intervals.len();
well.intervals
.retain(|interval| interval.from.is_finite() && interval.to.is_finite());
let dropped_intervals = intervals - well.intervals.len();
let mut said = Vec::new();
if dropped_nodes > 0 {
said.push(format!(
"{dropped_nodes} of its {total} nodes dropped: a coordinate or an MD is not a number"
));
}
if dropped_intervals > 0 {
said.push(format!(
"{dropped_intervals} of its {intervals} intervals not drawn: their top or base MD is not a number"
));
}
let note = (!said.is_empty()).then(|| Note {
code: NOT_A_NUMBER.to_string(),
sections: Vec::new(),
objects: vec![well.object.clone()],
said: format!("`{name}`: {}", said.join("; ")),
});
Ok((well, note))
}
fn split_spans(spans: &mut Vec<Vec<[f64; 2]>>) -> usize {
split_spans_sourced(spans, &mut Vec::new())
}
fn split_spans_sourced(spans: &mut Vec<Vec<[f64; 2]>>, sources: &mut Vec<Vec<CutSource>>) -> usize {
let parallel = !sources.is_empty()
&& sources.len() == spans.len()
&& spans
.iter()
.zip(sources.iter())
.all(|(span, list)| span.len() == list.len());
let mut dropped = 0;
let mut out = Vec::with_capacity(spans.len());
let mut out_sources = Vec::new();
let mut lists = std::mem::take(sources).into_iter();
for span in spans.drain(..) {
let list = if parallel {
lists.next().unwrap_or_default()
} else {
Vec::new()
};
if span.iter().all(finite2) {
out.push(span);
if parallel {
out_sources.push(list);
}
continue;
}
let mut piece = Vec::new();
let mut piece_sources = Vec::new();
let mut list = list.into_iter();
for point in span {
let source = if parallel { list.next() } else { None };
if finite2(&point) {
piece.push(point);
piece_sources.extend(source);
} else {
dropped += 1;
if piece.len() >= 2 {
out.push(std::mem::take(&mut piece));
out_sources.push(std::mem::take(&mut piece_sources));
} else {
piece.clear();
piece_sources.clear();
}
}
}
if piece.len() >= 2 {
out.push(piece);
out_sources.push(piece_sources);
}
}
*spans = out;
if parallel {
*sources = out_sources;
}
dropped
}
fn drop_rings(rings: &mut Vec<Vec<[f64; 2]>>) -> usize {
let mut dropped = 0;
rings.retain(|ring| {
let bad = ring.iter().filter(|point| !finite2(point)).count();
dropped += bad;
bad == 0
});
dropped
}
pub fn finite_surface(surface: &mut SurfaceRead) {
if surface.age.is_some_and(|age| !finite2(&age)) {
surface.age = None;
}
for cut in &mut surface.cuts {
let dropped = match &mut cut.shape {
CutShape::Trace { spans } => split_spans_sourced(spans, &mut cut.sources),
CutShape::Envelope {
top,
bottom,
fills,
faults,
} => split_spans(top) + split_spans(bottom) + split_spans(faults) + drop_rings(fills),
};
if dropped > 0 {
cut.notes.push(format!(
"{dropped} point(s) of the cut are not numbers: the cut is broken there"
));
}
}
}