use std::fmt::Debug;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct WorldPos {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl WorldPos {
#[must_use]
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
#[must_use]
pub const fn flat(x: f64, y: f64) -> Self {
Self { x, y, z: 0.0 }
}
#[must_use]
pub fn is_finite(&self) -> bool {
self.x.is_finite() && self.y.is_finite() && self.z.is_finite()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WorldAabb {
pub min: WorldPos,
pub max: WorldPos,
}
impl WorldAabb {
#[must_use]
pub fn point(p: WorldPos) -> Self {
Self { min: p, max: p }
}
#[must_use]
pub fn empty() -> Self {
Self {
min: WorldPos::new(f64::INFINITY, f64::INFINITY, f64::INFINITY),
max: WorldPos::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.min.x > self.max.x || self.min.y > self.max.y
}
#[must_use]
pub fn union(self, o: Self) -> Self {
Self {
min: WorldPos::new(self.min.x.min(o.min.x), self.min.y.min(o.min.y), self.min.z.min(o.min.z)),
max: WorldPos::new(self.max.x.max(o.max.x), self.max.y.max(o.max.y), self.max.z.max(o.max.z)),
}
}
#[must_use]
pub fn overlaps_2d(&self, o: &Self) -> bool {
self.min.x <= o.max.x && self.max.x >= o.min.x && self.min.y <= o.max.y && self.max.y >= o.min.y
}
#[must_use]
pub fn centre(&self) -> WorldPos {
WorldPos::new(
(self.min.x + self.max.x) * 0.5,
(self.min.y + self.max.y) * 0.5,
(self.min.z + self.max.z) * 0.5,
)
}
}
pub trait PositionSource: Debug {
fn feature_count(&self) -> usize;
fn position(&self, id: u32) -> WorldPos;
fn bounds(&self, id: u32) -> WorldAabb {
WorldAabb::point(self.position(id))
}
fn positions(&self, ids: &[u32], out: &mut Vec<WorldPos>) {
out.clear();
out.reserve(ids.len());
out.extend(ids.iter().map(|&i| self.position(i)));
}
fn generation(&self) -> u64 {
0
}
fn kind(&self) -> &'static str;
}
#[derive(Clone, Debug, Default)]
pub struct Identity {
pts: Vec<[f64; 2]>,
}
impl Identity {
#[must_use]
pub fn new(pts: Vec<[f64; 2]>) -> Self {
Self { pts }
}
#[must_use]
pub fn from_f32(pts: &[(f32, f32)]) -> Self {
Self { pts: pts.iter().map(|&(x, y)| [x as f64, y as f64]).collect() }
}
}
impl PositionSource for Identity {
fn feature_count(&self) -> usize {
self.pts.len()
}
fn position(&self, id: u32) -> WorldPos {
match self.pts.get(id as usize) {
Some(&[x, y]) => WorldPos::flat(x, y),
None => WorldPos::new(f64::NAN, f64::NAN, f64::NAN),
}
}
fn kind(&self) -> &'static str {
"identity"
}
}
#[derive(Clone, Debug, Default)]
pub struct Layout {
pts: Vec<[f64; 2]>,
epoch: u64,
}
impl Layout {
#[must_use]
pub fn new(pts: Vec<[f64; 2]>) -> Self {
Self { pts, epoch: 1 }
}
pub fn set(&mut self, pts: Vec<[f64; 2]>) {
self.pts = pts;
self.epoch = self.epoch.wrapping_add(1);
}
pub fn set_f32(&mut self, pts: &[(f32, f32)]) {
self.set(pts.iter().map(|&(x, y)| [x as f64, y as f64]).collect());
}
#[must_use]
pub fn points(&self) -> &[[f64; 2]] {
&self.pts
}
}
impl PositionSource for Layout {
fn feature_count(&self) -> usize {
self.pts.len()
}
fn position(&self, id: u32) -> WorldPos {
match self.pts.get(id as usize) {
Some(&[x, y]) => WorldPos::flat(x, y),
None => WorldPos::new(f64::NAN, f64::NAN, f64::NAN),
}
}
fn generation(&self) -> u64 {
self.epoch
}
fn kind(&self) -> &'static str {
"layout"
}
}
pub struct Projected {
raw: Vec<[f64; 2]>,
project: Box<dyn Fn(f64, f64) -> (f64, f64) + Send + Sync>,
kind: &'static str,
}
impl Debug for Projected {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Projected").field("kind", &self.kind).field("len", &self.raw.len()).finish()
}
}
impl Projected {
#[must_use]
pub fn new(
raw: Vec<[f64; 2]>,
kind: &'static str,
project: impl Fn(f64, f64) -> (f64, f64) + Send + Sync + 'static,
) -> Self {
Self { raw, project: Box::new(project), kind }
}
}
impl PositionSource for Projected {
fn feature_count(&self) -> usize {
self.raw.len()
}
fn position(&self, id: u32) -> WorldPos {
match self.raw.get(id as usize) {
Some(&[a, b]) => {
let (x, y) = (self.project)(a, b);
WorldPos::flat(x, y)
}
None => WorldPos::new(f64::NAN, f64::NAN, f64::NAN),
}
}
fn kind(&self) -> &'static str {
self.kind
}
}
pub trait ElevationSource: Debug {
fn elevation(&self, id: u32, p: WorldPos) -> f64;
fn z_range(&self) -> (f64, f64) {
(0.0, 0.0)
}
fn elevations(&self, ids: &[u32], pos: &[WorldPos], out: &mut Vec<f64>) {
out.clear();
out.reserve(ids.len());
for (n, &id) in ids.iter().enumerate() {
out.push(self.elevation(id, pos.get(n).copied().unwrap_or_default()));
}
}
fn kind(&self) -> &'static str;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Flat;
impl ElevationSource for Flat {
fn elevation(&self, _id: u32, _p: WorldPos) -> f64 {
0.0
}
fn elevations(&self, ids: &[u32], _pos: &[WorldPos], out: &mut Vec<f64>) {
out.clear();
out.resize(ids.len(), 0.0);
}
fn kind(&self) -> &'static str {
"flat"
}
}
#[derive(Clone, Debug, Default)]
pub struct Metric {
values: Vec<f32>,
scale: f64,
lo: f64,
hi: f64,
}
impl Metric {
#[must_use]
pub fn new(values: Vec<f32>, scale: f64) -> Self {
let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
for &v in &values {
let z = v as f64 * scale;
if z.is_finite() {
lo = lo.min(z);
hi = hi.max(z);
}
}
if !lo.is_finite() {
lo = 0.0;
hi = 0.0;
}
Self { values, scale, lo, hi }
}
}
impl ElevationSource for Metric {
fn elevation(&self, id: u32, _p: WorldPos) -> f64 {
self.values.get(id as usize).map_or(0.0, |&v| v as f64 * self.scale)
}
fn z_range(&self) -> (f64, f64) {
(self.lo, self.hi)
}
fn kind(&self) -> &'static str {
"metric"
}
}
pub struct Sampled {
sample: Box<dyn Fn(f64, f64) -> f64 + Send + Sync>,
range: (f64, f64),
kind: &'static str,
}
impl Debug for Sampled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Sampled").field("kind", &self.kind).field("range", &self.range).finish()
}
}
impl Sampled {
#[must_use]
pub fn new(
kind: &'static str,
range: (f64, f64),
sample: impl Fn(f64, f64) -> f64 + Send + Sync + 'static,
) -> Self {
Self { sample: Box::new(sample), range, kind }
}
}
impl ElevationSource for Sampled {
fn elevation(&self, _id: u32, p: WorldPos) -> f64 {
(self.sample)(p.x, p.y)
}
fn z_range(&self) -> (f64, f64) {
self.range
}
fn kind(&self) -> &'static str {
self.kind
}
}
pub trait Hierarchy: Debug {
fn depth(&self) -> u32;
fn level_for(&self, scale: f32) -> u32;
fn members(&self, level: u32) -> &[u32];
fn expand(&self, level: u32, parent: u32) -> &[u32];
fn parent_of(&self, level: u32, child: u32) -> Option<u32>;
fn kind(&self) -> &'static str;
}
#[derive(Clone, Debug, Default)]
pub struct Flatten {
all: Vec<u32>,
}
impl Flatten {
#[must_use]
pub fn new(n: usize) -> Self {
Self { all: (0..n as u32).collect() }
}
}
impl Hierarchy for Flatten {
fn depth(&self) -> u32 {
1
}
fn level_for(&self, _scale: f32) -> u32 {
0
}
fn members(&self, _level: u32) -> &[u32] {
&self.all
}
fn expand(&self, _level: u32, _parent: u32) -> &[u32] {
&[]
}
fn parent_of(&self, _level: u32, _child: u32) -> Option<u32> {
None
}
fn kind(&self) -> &'static str {
"flatten"
}
}
#[derive(Clone, Debug, Default)]
pub struct Band {
pub members: Vec<u32>,
pub min_scale: f32,
pub children: std::collections::BTreeMap<u32, Vec<u32>>,
}
#[derive(Clone, Debug, Default)]
pub struct Bands {
levels: Vec<Band>,
parents: Vec<std::collections::BTreeMap<u32, u32>>,
kind: &'static str,
}
impl Bands {
#[must_use]
pub fn new(levels: Vec<Band>, kind: &'static str) -> Self {
let parents = levels
.iter()
.map(|b| {
let mut m = std::collections::BTreeMap::new();
for (&p, cs) in &b.children {
for &c in cs {
m.insert(c, p);
}
}
m
})
.collect();
Self { levels, parents, kind }
}
}
impl Hierarchy for Bands {
fn depth(&self) -> u32 {
self.levels.len().max(1) as u32
}
fn level_for(&self, scale: f32) -> u32 {
let mut chosen = 0u32;
for (i, b) in self.levels.iter().enumerate() {
if scale >= b.min_scale {
chosen = i as u32;
}
}
chosen
}
fn members(&self, level: u32) -> &[u32] {
self.levels.get(level as usize).map_or(&[], |b| &b.members)
}
fn expand(&self, level: u32, parent: u32) -> &[u32] {
self.levels
.get(level as usize)
.and_then(|b| b.children.get(&parent))
.map_or(&[], |v| v.as_slice())
}
fn parent_of(&self, level: u32, child: u32) -> Option<u32> {
let l = level.checked_sub(1)?;
self.parents.get(l as usize).and_then(|m| m.get(&child)).copied()
}
fn kind(&self) -> &'static str {
self.kind
}
}