use std::{ops::Deref, sync::Arc};
use crate::{geometry, map};
pub struct RenderingState
{
pub zoom_level: f64,
}
#[derive(Clone, Copy)]
pub struct Rgba(color::AlphaColor<color::Srgb>);
impl Deref for Rgba
{
type Target = color::AlphaColor<color::Srgb>;
fn deref(&self) -> &Self::Target
{
&self.0
}
}
ccutils::assert_impl_all!(Rgba: Sync, Send);
impl Rgba
{
pub fn random() -> Self
{
rgba(rand::random(), rand::random(), rand::random(), 1.0)
}
pub fn red(&self) -> f32
{
self.0.components[0]
}
pub fn green(&self) -> f32
{
self.0.components[1]
}
pub fn blue(&self) -> f32
{
self.0.components[2]
}
pub fn alpha(&self) -> f32
{
self.0.components[3]
}
pub const TRANSPARENT: Self = rgba(0.0, 0.0, 0.0, 0.0);
pub const BLACK: Self = rgb(0.0, 0.0, 0.0);
pub const WHITE: Self = rgb(1.0, 1.0, 1.0);
pub const OSM_BLUE: Self = rgb(170.0 / 255.0, 211.0 / 255.0, 223.0 / 255.0);
pub const OSM_WHITE: Self = rgb(213.0 / 255.0, 210.0 / 255.0, 205.0 / 255.0);
}
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Rgba
{
Rgba(color::AlphaColor::<color::Srgb>::new([r, g, b, a]))
}
pub const fn rgb(r: f32, g: f32, b: f32) -> Rgba
{
rgba(r, g, b, 1.0)
}
pub(crate) type Checker<TFeature> =
Box<dyn Fn(&RenderingState, &TFeature) -> bool + Sync + Send + 'static>;
pub struct Rule<TFeature: map::Feature>
{
pub check: Checker<TFeature>,
pub symbol: Symbol<TFeature>,
}
ccutils::assert_impl_all!(for(TFeature: map::Feature) Rule<TFeature>: Sync, Send);
pub struct RuleBuilder<TFeature: map::Feature>
{
geom_type: Option<geometry::GeometryType>,
check: Option<Checker<TFeature>>,
zoom_range: (Option<f64>, Option<f64>),
pub symbol: Symbol<TFeature>,
pub style_builder: StyleBuilder<TFeature>,
}
impl<TFeature: map::Feature + 'static> RuleBuilder<TFeature>
{
pub fn geometry_type(mut self, geom_type: geometry::GeometryType) -> Self
{
self.geom_type = Some(geom_type);
self
}
pub fn check<T>(
mut self,
check: impl Fn(&RenderingState, &T) -> bool + Sync + Send + 'static,
) -> Self
where
for<'a> &'a TFeature: TryInto<&'a T>,
{
self.check = Some(Box::new(move |rendering_state, feat| {
let t: Option<&T> = feat.try_into().ok();
match t
{
Some(t) => check(rendering_state, t),
None => false,
}
}));
self
}
pub fn min_zoom_level(mut self, level: f64) -> Self
{
let zoom_range = self.zoom_range;
self.zoom_range = (Some(level), zoom_range.1);
self
}
pub fn max_zoom_level(mut self, level: f64) -> Self
{
let zoom_range = self.zoom_range;
self.zoom_range = (zoom_range.0, Some(level));
self
}
pub fn finish_rule(self) -> StyleBuilder<TFeature>
{
let mut style_builder = self.style_builder;
let geom_type = self.geom_type;
let check = self.check;
let zoom_range = self.zoom_range;
style_builder.style.rules.push(Rule {
check: Box::new(move |rendering_state, feat| {
if let Some(geom_type) = geom_type
&& feat.geometry_type() != geom_type
&& (feat.geometry_type() != crate::GeometryType::Collection
|| feat.element_geometry_type() != geom_type)
{
return false;
}
if let Some(min_level) = zoom_range.0
&& rendering_state.zoom_level < min_level
{
return false;
}
if let Some(max_level) = zoom_range.1
&& rendering_state.zoom_level > max_level
{
return false;
}
if let Some(check) = &check
{
check(rendering_state, feat)
}
else
{
true
}
}),
symbol: self.symbol,
});
style_builder
}
}
pub struct Style<TFeature: map::Feature>
{
pub(super) rules: Vec<Rule<TFeature>>,
pub(super) background_color: Rgba,
}
ccutils::assert_impl_all!(for(TFeature: map::Feature) Style<TFeature>: Sync);
ccutils::assert_impl_all!(for(TFeature: map::Feature) Style<TFeature>: Send);
impl<TFeature: map::Feature> Style<TFeature>
{
pub(super) fn new() -> Self
{
Self {
rules: vec![],
background_color: Rgba::TRANSPARENT,
}
}
pub(crate) fn rules(&self) -> impl Iterator<Item = &Rule<TFeature>>
{
self.rules.iter()
}
pub(crate) fn background_color(&self) -> Rgba
{
self.background_color
}
}
type Colorer<TFeature> = Box<dyn Fn(&RenderingState, &TFeature) -> Rgba + Sync + Send>;
#[derive(Clone)]
pub struct SymbolColor<TFeature: map::Feature>(Arc<Colorer<TFeature>>);
ccutils::assert_impl_all!(for(TFeature: map::Feature) SymbolColor<TFeature>: Sync);
ccutils::assert_impl_all!(for(TFeature: map::Feature) SymbolColor<TFeature>: Send);
impl<TFeature: map::Feature> Deref for SymbolColor<TFeature>
{
type Target = Arc<Box<dyn Fn(&RenderingState, &TFeature) -> Rgba + Sync + Send>>;
fn deref(&self) -> &Self::Target
{
&self.0
}
}
impl<TFeature: map::Feature> SymbolColor<TFeature>
{
pub fn new_from<T>(
colorization: impl Fn(&RenderingState, &T) -> Rgba + Sync + Send + 'static,
) -> Self
where
for<'a> &'a TFeature: TryInto<&'a T>,
{
Self::new(move |rs, f| {
let t = f.try_into().ok();
match t
{
Some(t) => colorization(rs, t),
None => Rgba::TRANSPARENT,
}
})
}
pub fn new(
colorization: impl Fn(&RenderingState, &TFeature) -> Rgba + Sync + Send + 'static,
) -> Self
{
Self(Arc::new(Box::new(colorization)))
}
pub fn random() -> SymbolColor<TFeature>
{
Self::new(move |_, _| Rgba::random())
}
}
impl<TFeature: map::Feature> From<Rgba> for SymbolColor<TFeature>
{
fn from(val: Rgba) -> Self
{
SymbolColor::<TFeature>::new(move |_, _| val)
}
}
#[derive(Clone)]
pub struct Symbol<TFeature: map::Feature>
{
pub stroke_width: f64,
pub stroke_color: SymbolColor<TFeature>,
pub fill_color: SymbolColor<TFeature>,
pub radius: f64,
pub label: Option<LabelConfig<TFeature>>,
}
ccutils::assert_impl_all!(for(TFeature: map::Feature) Symbol<TFeature>: Sync);
ccutils::assert_impl_all!(for(TFeature: map::Feature) Symbol<TFeature>: Send);
impl<TFeature: map::Feature> Default for Symbol<TFeature>
{
fn default() -> Self
{
Self {
stroke_width: 0.0,
stroke_color: Rgba::TRANSPARENT.into(),
fill_color: Rgba::TRANSPARENT.into(),
radius: 0.0,
label: None,
}
}
}
pub type TextGetter<TFeature> = Arc<Box<dyn Fn(&TFeature) -> Option<String> + Send + Sync>>;
#[derive(Clone)]
pub struct LabelConfig<TFeature: map::Feature>
{
pub text: TextGetter<TFeature>,
pub font_size: f32,
pub color: SymbolColor<TFeature>,
pub halo_color: SymbolColor<TFeature>,
pub min_zoom: f64,
}
ccutils::assert_impl_all!(for(TFeature: map::Feature) LabelConfig<TFeature>: Sync, Send);
pub struct StyleBuilder<TFeature: map::Feature>
{
style: Style<TFeature>,
}
impl<TFeature: map::Feature + 'static> Default for StyleBuilder<TFeature>
{
fn default() -> Self
{
Self::new()
}
}
impl<TFeature: map::Feature + 'static> StyleBuilder<TFeature>
{
pub fn new() -> Self
{
Self {
style: Style::new(),
}
}
pub fn add_rule(self, symbol: Symbol<TFeature>) -> RuleBuilder<TFeature>
{
RuleBuilder {
check: None,
geom_type: None,
style_builder: self,
zoom_range: (None, None),
symbol,
}
}
pub fn add_rule_for_type(self, typ: geometry::GeometryType, symbol: Symbol<TFeature>) -> Self
{
self.add_rule(symbol).geometry_type(typ).finish_rule()
}
pub fn add_rule_for_type_and<T>(
self,
typ: geometry::GeometryType,
check: impl Fn(&RenderingState, &T) -> bool + Sync + Send + 'static,
symbol: Symbol<TFeature>,
) -> Self
where
for<'a> &'a TFeature: TryInto<&'a T>,
{
self
.add_rule(symbol)
.geometry_type(typ)
.check(check)
.finish_rule()
}
pub fn set_background_color(mut self, bg: Rgba) -> Self
{
self.style.background_color = bg;
self
}
}
impl<TFeature: map::Feature> From<StyleBuilder<TFeature>> for Style<TFeature>
{
fn from(val: StyleBuilder<TFeature>) -> Self
{
val.style
}
}