use std::{cell::Cell, num::NonZeroU64};
use rayon::prelude::*;
use crate::{
composition::InnerLayer,
consts,
math::AffineTransform,
utils::{ExtendTuple10, ExtendTuple3},
Path,
};
const MIN_LEN: usize = 1_024;
fn integers_between(a: f32, b: f32) -> u32 {
let min = a.min(b);
let max = a.max(b);
0.max((max.ceil() - min.floor() - 1.0) as u32)
}
fn manhattan_segment_length(p0x: f32, p1x: f32, p0y: f32, p1y: f32) -> u32 {
integers_between(p0x, p1x) + integers_between(p0y, p1y) + 1
}
fn prefix_sum(vals: &mut [u32]) -> u32 {
let mut sum = 0;
for val in vals {
sum += *val;
*val = sum;
}
sum
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct GeomId(NonZeroU64);
impl GeomId {
#[cfg(test)]
pub fn get(self) -> u64 {
self.0.get() - 1
}
#[inline]
pub fn next(self) -> Self {
Self(
NonZeroU64::new(
self.0
.get()
.checked_add(1)
.expect("id should never reach u64::MAX"),
)
.unwrap(),
)
}
}
impl Default for GeomId {
#[inline]
fn default() -> Self {
Self(NonZeroU64::new(1).unwrap())
}
}
#[derive(Debug, Default)]
pub struct SegmentBuffer {
view: SegmentBufferView,
cached_len: Cell<usize>,
cached_until: Cell<usize>,
}
impl SegmentBuffer {
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
if self.view.ids.len() <= self.cached_until.get() {
self.cached_len.get()
} else {
let new_len = self.cached_len.get()
+ self.view.ids[self.cached_until.get()..]
.iter()
.filter(|id| id.is_some())
.count();
self.cached_len.set(new_len);
self.cached_until.set(self.view.ids.len());
new_len
}
}
#[inline]
pub fn push_path(&mut self, id: GeomId, path: &Path) {
path.push_segments_to(&mut self.view.x, &mut self.view.y, id, &mut self.view.ids);
self.view.ids.resize(
self.view.x.len().checked_sub(1).unwrap_or_default(),
Some(id),
);
if self
.view
.ids
.last()
.map(Option::is_some)
.unwrap_or_default()
{
self.view.ids.push(None);
}
}
#[cfg(test)]
pub fn push(&mut self, id: GeomId, segment: [crate::math::Point; 2]) {
let new_point_needed =
if let (Some(&x), Some(&y)) = (self.view.x.last(), self.view.y.last()) {
let last_point = crate::math::Point { x, y };
last_point != segment[0]
} else {
true
};
if new_point_needed {
self.view.x.push(segment[0].x);
self.view.y.push(segment[0].y);
}
self.view.x.push(segment[1].x);
self.view.y.push(segment[1].y);
if self.view.ids.len() >= 2 {
match self.view.ids[self.view.ids.len() - 2] {
Some(last_id) if last_id != id => {
self.view.ids.push(Some(id));
self.view.ids.push(None);
}
_ => {
self.view.ids.pop();
self.view.ids.push(Some(id));
self.view.ids.push(None);
}
}
} else {
self.view.ids.push(Some(id));
self.view.ids.push(None);
}
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(GeomId) -> bool,
{
let len = self.view.x.len();
let mut del = 0;
let mut prev_id = None;
for i in 0..len {
let id = self.view.ids[i];
let should_retain = id
.or(prev_id)
.map(&mut f)
.expect("consecutive None values should not exist in ids");
prev_id = id;
if !should_retain {
del += 1;
continue;
}
if del > 0 {
self.view.x.swap(i - del, i);
self.view.y.swap(i - del, i);
self.view.ids.swap(i - del, i);
}
}
if del > 0 {
self.view.x.truncate(len - del);
self.view.y.truncate(len - del);
self.view.ids.truncate(len - del);
}
}
pub fn fill_cpu_view<F>(mut self, layers: F) -> SegmentBufferView
where
F: Fn(GeomId) -> Option<InnerLayer> + Send + Sync,
{
let ps_layers = self.view.x.par_windows(2).with_min_len(MIN_LEN).zip_eq(
self.view.y.par_windows(2).with_min_len(MIN_LEN).zip_eq(
self.view.ids[..self.view.ids.len().checked_sub(1).unwrap_or_default()]
.par_iter()
.with_min_len(MIN_LEN),
),
);
let par_iter = ps_layers.map(|(xs, (ys, &id))| {
let empty_line = Default::default();
let p0x = xs[0];
let p0y = ys[0];
let p1x = xs[1];
let p1y = ys[1];
if id.is_none() {
return empty_line;
}
let layer = match id.and_then(&layers) {
Some(layer) => layer,
None => return empty_line,
};
if let InnerLayer {
is_enabled: false, ..
} = layer
{
return empty_line;
}
let order = match layer.order {
Some(order) => order.as_u32(),
None => return empty_line,
};
fn transform_point(point: (f32, f32), transform: &AffineTransform) -> (f32, f32) {
(
transform
.ux
.mul_add(point.0, transform.vx.mul_add(point.1, transform.tx)),
transform
.uy
.mul_add(point.0, transform.vy.mul_add(point.1, transform.ty)),
)
}
let transform = layer
.affine_transform
.as_ref()
.map(|transform| &transform.0);
let (p0x, p0y, p1x, p1y) = if let Some(transform) = transform {
let (p0x, p0y) = transform_point((p0x, p0y), transform);
let (p1x, p1y) = transform_point((p1x, p1y), transform);
(p0x, p0y, p1x, p1y)
} else {
(p0x, p0y, p1x, p1y)
};
if p0y == p1y {
return empty_line;
}
let dx = p1x - p0x;
let dy = p1y - p0y;
let dx_recip = dx.recip();
let dy_recip = dy.recip();
let t_offset_x = if dx != 0.0 {
((p0x.ceil() - p0x) * dx_recip).max((p0x.floor() - p0x) * dx_recip)
} else {
0.0
};
let t_offset_y = if dy != 0.0 {
((p0y.ceil() - p0y) * dy_recip).max((p0y.floor() - p0y) * dy_recip)
} else {
0.0
};
let a = dx_recip.abs();
let b = dy_recip.abs();
let c = t_offset_x;
let d = t_offset_y;
let length = manhattan_segment_length(p0x, p1x, p0y, p1y);
(
order,
p0x * consts::PIXEL_WIDTH as f32,
p0y * consts::PIXEL_WIDTH as f32,
dx * consts::PIXEL_WIDTH as f32,
dy * consts::PIXEL_WIDTH as f32,
a,
b,
c,
d,
length,
)
});
ExtendTuple10::new((
&mut self.view.orders,
&mut self.view.x0,
&mut self.view.y0,
&mut self.view.dx,
&mut self.view.dy,
&mut self.view.a,
&mut self.view.b,
&mut self.view.c,
&mut self.view.d,
&mut self.view.lengths,
))
.par_extend(par_iter);
prefix_sum(&mut self.view.lengths);
self.view
}
pub fn fill_gpu_view<F>(mut self, layers: F) -> SegmentBufferView
where
F: Fn(GeomId) -> Option<InnerLayer> + Send + Sync,
{
fn transform_point(point: (f32, f32), transform: &AffineTransform) -> (f32, f32) {
(
transform
.ux
.mul_add(point.0, transform.vx.mul_add(point.1, transform.tx)),
transform
.uy
.mul_add(point.0, transform.vy.mul_add(point.1, transform.ty)),
)
}
if !self.view.ids.is_empty() {
let point = match self.view.ids[0].and_then(&layers) {
None
| Some(
InnerLayer {
is_enabled: false, ..
}
| InnerLayer { order: None, .. },
) => Default::default(),
Some(InnerLayer {
affine_transform: None,
..
}) => [self.view.x[0], self.view.y[0]],
Some(InnerLayer {
affine_transform: Some(transform),
..
}) => {
let (x, y) = transform_point((self.view.x[0], self.view.y[0]), &transform.0);
[x, y]
}
};
self.view.points.push(point);
}
let ps_layers = self.view.x.par_windows(2).with_min_len(MIN_LEN).zip_eq(
self.view
.y
.par_windows(2)
.with_min_len(MIN_LEN)
.zip_eq(self.view.ids.par_windows(2).with_min_len(MIN_LEN)),
);
let par_iter = ps_layers.map(|(xs, (ys, ids))| {
const NONE: u32 = u32::MAX;
let p0x = xs[0];
let p0y = ys[0];
let (p1x, p1y) = match ids[0].or(ids[1]).and_then(&layers) {
None
| Some(
InnerLayer {
is_enabled: false, ..
}
| InnerLayer { order: None, .. },
) => (0.0, 0.0),
Some(InnerLayer {
affine_transform: None,
..
}) => (xs[1], ys[1]),
Some(InnerLayer {
affine_transform: Some(transform),
..
}) => transform_point((xs[1], ys[1]), &transform.0),
};
let layer = match ids[0].and_then(&layers) {
Some(layer) => layer,
None => return (0, [p1x, p1y], NONE),
};
if let InnerLayer {
is_enabled: false, ..
} = layer
{
return (0, [p1x, p1y], NONE);
}
let order = match layer.order {
Some(order) => order.as_u32(),
None => return (0, [p1x, p1y], NONE),
};
let transform = layer
.affine_transform
.as_ref()
.map(|transform| &transform.0);
let (p0x, p0y) = if let Some(transform) = transform {
transform_point((p0x, p0y), transform)
} else {
(p0x, p0y)
};
if p0y == p1y {
return (0, [p1x, p1y], NONE);
}
let length = integers_between(p0x, p1x) + integers_between(p0y, p1y) + 1;
(length, [p1x, p1y], order)
});
ExtendTuple3::new((
&mut self.view.lengths,
&mut self.view.points,
&mut self.view.orders,
))
.par_extend(par_iter);
prefix_sum(&mut self.view.lengths);
self.view
}
}
#[derive(Debug, Default)]
pub struct SegmentBufferView {
pub x: Vec<f32>,
pub y: Vec<f32>,
pub ids: Vec<Option<GeomId>>,
pub orders: Vec<u32>,
pub x0: Vec<f32>,
pub y0: Vec<f32>,
pub dx: Vec<f32>,
pub dy: Vec<f32>,
pub a: Vec<f32>,
pub b: Vec<f32>,
pub c: Vec<f32>,
pub d: Vec<f32>,
pub points: Vec<[f32; 2]>,
pub lengths: Vec<u32>,
}
impl SegmentBufferView {
#[inline]
pub fn recycle(mut self) -> SegmentBuffer {
self.orders.clear();
self.x0.clear();
self.y0.clear();
self.dx.clear();
self.dy.clear();
self.a.clear();
self.b.clear();
self.c.clear();
self.d.clear();
self.lengths.clear();
self.points.clear();
SegmentBuffer {
view: self,
..Default::default()
}
}
pub fn len(&self) -> usize {
self.lengths.last().copied().unwrap_or_default() as usize
}
pub fn inner_len(&self) -> usize {
self.x.len() - 1
}
}