use alloc::{vec::Vec, vec};
use crate::Token;
use crate::nav::NavPathNavigator;
use crate::node::function::Function;
pub type Dimension = u64;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct CalculatedPoint {
pub x: u64,
pub y: u64,
}
impl CalculatedPoint {
pub fn dx(&self, delta: i64) -> CalculatedPoint {
CalculatedPoint { x: (self.x as i64 + delta) as u64, y: self.y }
}
pub fn dy(&self, delta: i64) -> CalculatedPoint {
CalculatedPoint { x: self.x, y: (self.y as i64 + delta) as u64 }
}
pub fn to_viewport_point(&self, viewport: Option<&Viewport>) -> ViewportPoint {
if let Some(viewport) = viewport {
ViewportPoint {
x: self.x as i64 - viewport.offset.x as i64,
y: self.y as i64 - viewport.offset.y as i64,
}
} else {
ViewportPoint { x: self.x as i64, y: self.y as i64 }
}
}
}
pub struct Viewport {
pub size: Area,
pub offset: CalculatedPoint,
}
impl Viewport {
pub fn new(size: Area) -> Self {
Viewport { size, offset: CalculatedPoint { x: 0, y: 0 } }
}
pub fn includes_point(&self, point: &ViewportPoint) -> bool {
point.x >= 0 && point.y >= 0
&& point.x < self.size.width as i64 && point.y < self.size.height as i64
}
pub fn visibility(&self, point: &ViewportPoint, area: &Area) -> ViewportVisibility {
let left_clip = if point.x < 0 { point.x.abs() } else { 0 } as u64;
let top_clip = if point.y < 0 { point.y.abs() } else { 0 } as u64;
let end_x = point.x + area.width as i64;
let right_clip = if end_x > self.size.width as i64 {
end_x - self.size.width as i64
} else { 0 } as u64;
let end_y = point.y + area.height as i64;
let bottom_clip = if end_y > self.size.height as i64 {
end_y - self.size.height as i64
} else { 0 } as u64;
if top_clip == 0 && bottom_clip == 0 && left_clip == 0 && right_clip == 0 {
ViewportVisibility::Visible
} else {
ViewportVisibility::Clipped {
invisible: end_x + (area.width as i64) < 0 || end_y + (area.height as i64) < 0
|| point.x > self.size.width as i64 || point.y > self.size.height as i64,
top_clip, bottom_clip, left_clip, right_clip
}
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ViewportPoint {
pub x: i64,
pub y: i64,
}
impl ViewportPoint {
pub fn dx(&self, delta: i64) -> ViewportPoint {
ViewportPoint { x: self.x + delta, y: self.y }
}
pub fn dy(&self, delta: i64) -> ViewportPoint {
ViewportPoint { x: self.x, y: self.y + delta }
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum ViewportVisibility {
Visible,
Clipped {
invisible: bool,
top_clip: Dimension,
bottom_clip: Dimension,
left_clip: Dimension,
right_clip: Dimension,
},
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ViewportGlyph {
pub glyph: SizedGlyph,
pub point: ViewportPoint,
pub visibility: ViewportVisibility,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Area {
pub width: Dimension,
pub height: Dimension,
}
impl Area {
pub fn new(width: Dimension, height: Dimension) -> Area {
Area { width, height }
}
pub fn square(size: Dimension) -> Area {
Area { width: size, height: size }
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Glyph {
Digit { number: u8 },
Point,
FunctionName { function: Function },
Comma,
Variable { name: char },
Add,
Subtract,
Multiply,
Divide,
Fraction { inner_width: Dimension },
LeftParenthesis { inner_height: Dimension },
RightParenthesis { inner_height: Dimension },
Sqrt { inner_area: Area },
Cursor { height: Dimension },
Placeholder,
}
impl From<Token> for Glyph {
fn from(token: Token) -> Self {
match token {
Token::Add => Glyph::Add,
Token::Subtract => Glyph::Subtract,
Token::Multiply => Glyph::Multiply,
Token::Divide => Glyph::Divide,
Token::Digit(d) => Glyph::Digit { number: d },
Token::Point => Glyph::Point,
Token::Variable(c) => Glyph::Variable { name: c },
}
}
}
impl Glyph {
pub fn to_sized(self, renderer: &mut impl Renderer, size_reduction_level: u32) -> SizedGlyph {
SizedGlyph::from_glyph(self, renderer, size_reduction_level)
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct SizedGlyph {
pub glyph: Glyph,
pub area: Area,
pub size_reduction_level: u32,
}
impl SizedGlyph {
pub fn from_glyph(glyph: Glyph, renderer: &mut impl Renderer, size_reduction_level: u32) -> Self {
SizedGlyph {
glyph,
area: renderer.size(glyph, size_reduction_level),
size_reduction_level,
}
}
}
#[derive(Clone, Debug)]
pub struct LayoutBlock {
pub glyphs: Vec<(SizedGlyph, CalculatedPoint)>,
pub baseline: Dimension,
pub area: Area,
pub special: LayoutBlockSpecial,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
pub struct LayoutBlockSpecial {
pub baseline_merge_with_high_precedence: bool,
pub superscript: bool,
}
pub enum MergeBaseline {
SelfAsBaseline,
OtherAsBaseline,
}
impl LayoutBlock {
pub fn empty() -> LayoutBlock {
LayoutBlock { glyphs: vec![], baseline: 0, area: Area::new(0, 0), special: LayoutBlockSpecial::default() }
}
pub fn new(glyphs: Vec<(SizedGlyph, CalculatedPoint)>, baseline: Dimension) -> Self {
let area = Self::area(&glyphs);
Self {
glyphs,
baseline,
area,
special: LayoutBlockSpecial::default(),
}
}
pub fn update_area(self) -> Self {
Self {
area: Self::area(&self.glyphs),
..self
}
}
pub fn from_glyph(renderer: &mut impl Renderer, glyph: Glyph, properties: LayoutComputationProperties) -> LayoutBlock {
let glyph = glyph.to_sized(renderer, properties.size_reduction_level);
LayoutBlock {
glyphs: vec![(glyph, CalculatedPoint { x: 0, y: 0 })],
baseline: glyph.area.height / 2,
area: glyph.area,
special: LayoutBlockSpecial::default(),
}
}
fn area(glyphs: &[(SizedGlyph, CalculatedPoint)]) -> Area {
let mut width = 0;
let mut height = 0;
for (glyph, point) in glyphs {
let size = glyph.area;
let ex = point.x + size.width;
let ey = point.y + size.height;
if ex > width { width = ex }
if ey > height { height = ey }
}
Area { width, height }
}
pub fn offset(&self, dx: Dimension, dy: Dimension) -> LayoutBlock {
LayoutBlock {
glyphs: self.glyphs
.iter()
.map(|(g, p)| (*g, p.dx(dx as i64).dy(dy as i64)))
.collect(),
baseline: self.baseline + dy,
..*self
}.update_area()
}
pub fn merge_along_baseline(&self, other: &LayoutBlock) -> LayoutBlock {
if other.special.superscript {
let base = self.offset(0, other.area.height);
return base.merge_in_place(other, MergeBaseline::SelfAsBaseline);
}
let (lesser_baselined, greater_baselined) = if self.baseline < other.baseline {
(self, other)
} else {
(other, self)
};
let baseline_difference = greater_baselined.baseline - lesser_baselined.baseline;
let glyphs =
lesser_baselined.glyphs
.iter()
.cloned()
.map(|(g, p)| (g, p.dy(baseline_difference as i64)))
.chain(greater_baselined.glyphs.iter().cloned())
.collect::<Vec<_>>();
LayoutBlock::new(glyphs, greater_baselined.baseline)
}
pub fn merge_along_vertical_centre(&self, other: &LayoutBlock, baseline: MergeBaseline) -> LayoutBlock {
let self_centre = self.area.width / 2;
let other_centre = other.area.width / 2;
let (thinner, thinner_centre, wider, wider_centre) = if self_centre < other_centre {
(self, self_centre, other, other_centre)
} else {
(other, other_centre, self, self_centre)
};
let centre_difference = wider_centre - thinner_centre;
let glyphs =
thinner.glyphs
.iter()
.cloned()
.map(|(g, p)| (g, p.dx(centre_difference as i64)))
.chain(wider.glyphs.iter().cloned())
.collect::<Vec<_>>();
LayoutBlock::new(glyphs, match baseline {
MergeBaseline::SelfAsBaseline => self.baseline,
MergeBaseline::OtherAsBaseline => other.baseline,
})
}
pub fn merge_in_place(&self, other: &LayoutBlock, baseline: MergeBaseline) -> LayoutBlock {
let glyphs =
self.glyphs
.iter()
.cloned()
.chain(other.glyphs.iter().cloned())
.collect::<Vec<_>>();
LayoutBlock::new(glyphs, match baseline {
MergeBaseline::SelfAsBaseline => self.baseline,
MergeBaseline::OtherAsBaseline => other.baseline,
})
}
pub fn move_right_of_other(&self, other: &LayoutBlock) -> LayoutBlock {
self.offset(other.area.width, 0)
}
pub fn move_below_other(&self, other: &LayoutBlock) -> LayoutBlock {
self.offset(0, other.area.height)
}
pub fn layout_horizontal(layouts: &[LayoutBlock]) -> LayoutBlock where Self: Sized
{
let mut block = LayoutBlock::empty();
let mut layouts = layouts.to_vec();
let mut i_offset = 0;
for (i, layout) in layouts.clone().into_iter().enumerate() {
let i = i - i_offset;
if layout.special.baseline_merge_with_high_precedence {
if let Some(layout_to_left) = layouts.get(i - 1) {
let layout_to_left = layout_to_left.clone();
layouts.drain((i - 1)..=i);
layouts.insert(i - 1, layout_to_left.merge_along_baseline(
&layout.move_right_of_other(&layout_to_left)
));
i_offset += 1;
}
}
}
for layout in layouts {
block = block.merge_along_baseline(
&layout.move_right_of_other(&block),
);
}
block
}
pub fn for_viewport(&self, viewport: Option<&Viewport>) -> Vec<ViewportGlyph> {
self.glyphs
.iter()
.map(|(g, p)| {
let viewport_point = p.to_viewport_point(viewport);
ViewportGlyph {
glyph: *g,
point: viewport_point,
visibility: if let Some(viewport) = viewport {
viewport.visibility(&viewport_point, &g.area)
} else {
ViewportVisibility::Visible
},
}
})
.collect::<Vec<_>>()
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct LayoutComputationProperties {
pub size_reduction_level: u32
}
impl Default for LayoutComputationProperties {
fn default() -> Self {
LayoutComputationProperties {
size_reduction_level: 0,
}
}
}
impl LayoutComputationProperties {
pub fn reduce_size(self) -> Self {
Self { size_reduction_level: self.size_reduction_level + 1, ..self }
}
}
pub trait Layoutable {
fn layout(&self, renderer: &mut impl Renderer, path: Option<&mut NavPathNavigator>, properties: LayoutComputationProperties) -> LayoutBlock;
}
pub trait Renderer {
fn size(&mut self, glyph: Glyph, size_reduction_level: u32) -> Area;
fn init(&mut self, size: Area);
fn draw(&mut self, glyph: ViewportGlyph);
fn layout(&mut self, root: &impl Layoutable, path: Option<&mut NavPathNavigator>, properties: LayoutComputationProperties) -> LayoutBlock where Self: Sized {
root.layout(self, path, properties)
}
fn draw_all(&mut self, root: &impl Layoutable, path: Option<&mut NavPathNavigator>, viewport: Option<&Viewport>) -> LayoutBlock where Self: Sized {
let layout = self.layout(root, path, LayoutComputationProperties::default());
self.draw_all_by_layout(&layout, viewport);
layout
}
fn draw_all_by_layout(&mut self, layout: &LayoutBlock, viewport: Option<&Viewport>) where Self: Sized {
let area = if let Some(v) = viewport {
v.size
} else {
layout.area
};
let viewport_glyphs = layout.for_viewport(viewport);
self.init(area);
for glyph in viewport_glyphs {
self.draw(glyph);
}
}
fn cursor_visibility(&mut self, root: &impl Layoutable, path: &mut NavPathNavigator, viewport: Option<&Viewport>) -> ViewportVisibility where Self: Sized {
let layout = self.layout(root, Some(path), LayoutComputationProperties::default());
let viewport_glyphs = layout.for_viewport(viewport);
for glyph in viewport_glyphs {
if let ViewportGlyph { glyph: SizedGlyph { glyph: Glyph::Cursor { .. }, .. }, visibility, .. } = glyph {
return visibility
}
}
panic!("cursor was not rendered");
}
fn square_root_padding(&self) -> u64 { 0 }
}