WIP: Replace primitives based on hover
This commit is contained in:
@@ -5,31 +5,32 @@ use std::{
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BoundsTree<U: Default + Clone + Debug> {
|
||||
#[derive(Debug)]
|
||||
pub struct BoundsTree<U, T>
|
||||
where
|
||||
U: Default + Clone + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
root: Option<usize>,
|
||||
nodes: Vec<Node<U>>,
|
||||
nodes: Vec<Node<U, T>>,
|
||||
stack: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<U> BoundsTree<U>
|
||||
impl<U, T> BoundsTree<U, T>
|
||||
where
|
||||
U: Clone + Debug + PartialOrd + Add<U, Output = U> + Sub<Output = U> + Half + Default,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
BoundsTree::default()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.root = None;
|
||||
self.nodes.clear();
|
||||
self.stack.clear();
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, new_bounds: Bounds<U>) -> u32 {
|
||||
pub fn insert(&mut self, new_bounds: Bounds<U>, payload: T) -> u32 {
|
||||
// If the tree is empty, make the root the new leaf.
|
||||
if self.root.is_none() {
|
||||
let new_node = self.push_leaf(new_bounds, 1);
|
||||
let new_node = self.push_leaf(new_bounds, payload, 1);
|
||||
self.root = Some(new_node);
|
||||
return 1;
|
||||
}
|
||||
@@ -84,7 +85,7 @@ where
|
||||
}
|
||||
|
||||
let ordering = max_intersecting_ordering + 1;
|
||||
let new_node = self.push_leaf(new_bounds, ordering);
|
||||
let new_node = self.push_leaf(new_bounds, payload, ordering);
|
||||
let new_parent = self.push_internal(sibling, new_node);
|
||||
|
||||
// If there was an old parent, we need to update its children indices.
|
||||
@@ -147,8 +148,12 @@ where
|
||||
max_ordering
|
||||
}
|
||||
|
||||
fn push_leaf(&mut self, bounds: Bounds<U>, order: u32) -> usize {
|
||||
self.nodes.push(Node::Leaf { bounds, order });
|
||||
fn push_leaf(&mut self, bounds: Bounds<U>, payload: T, order: u32) -> usize {
|
||||
self.nodes.push(Node::Leaf {
|
||||
bounds,
|
||||
payload,
|
||||
order,
|
||||
});
|
||||
self.nodes.len() - 1
|
||||
}
|
||||
|
||||
@@ -167,26 +172,39 @@ where
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn node(&self, index: usize) -> &Node<U> {
|
||||
fn node(&self, index: usize) -> &Node<U, T> {
|
||||
&self.nodes[index]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn node_mut(&mut self, index: usize) -> &mut Node<U> {
|
||||
fn node_mut(&mut self, index: usize) -> &mut Node<U, T> {
|
||||
&mut self.nodes[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
pub struct Primitive<U: Clone + Default + Debug> {
|
||||
bounds: Bounds<U>,
|
||||
order: u32,
|
||||
impl<U, T> Default for BoundsTree<U, T>
|
||||
where
|
||||
U: Default + Clone + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
fn default() -> Self {
|
||||
BoundsTree {
|
||||
root: None,
|
||||
nodes: Vec::new(),
|
||||
stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Node<U: Clone + Default + Debug> {
|
||||
#[derive(Debug, Clone)]
|
||||
enum Node<U, T>
|
||||
where
|
||||
U: Clone + Default + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
Leaf {
|
||||
bounds: Bounds<U>,
|
||||
payload: T,
|
||||
order: u32,
|
||||
},
|
||||
Internal {
|
||||
@@ -197,9 +215,10 @@ enum Node<U: Clone + Default + Debug> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<U> Node<U>
|
||||
impl<U, T> Node<U, T>
|
||||
where
|
||||
U: Clone + Default + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
fn bounds(&self) -> &Bounds<U> {
|
||||
match self {
|
||||
@@ -217,219 +236,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Point, Size};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::{fs, path::Path};
|
||||
|
||||
#[test]
|
||||
fn test_bounds_insertion_with_two_bounds() {
|
||||
let mut tree = BoundsTree::new();
|
||||
let bounds1 = Bounds {
|
||||
origin: Point { x: 0.0, y: 0.0 },
|
||||
size: Size {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
},
|
||||
};
|
||||
let bounds2 = Bounds {
|
||||
origin: Point { x: 5.0, y: 5.0 },
|
||||
size: Size {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Insert the first Bounds.
|
||||
assert_eq!(tree.insert(bounds1), 1);
|
||||
|
||||
// Insert the second Bounds, which overlaps with the first.
|
||||
assert_eq!(tree.insert(bounds2), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacent_bounds() {
|
||||
let mut tree = BoundsTree::new();
|
||||
let bounds1 = Bounds {
|
||||
origin: Point { x: 0.0, y: 0.0 },
|
||||
size: Size {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
},
|
||||
};
|
||||
let bounds2 = Bounds {
|
||||
origin: Point { x: 10.0, y: 0.0 },
|
||||
size: Size {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Insert the first bounds.
|
||||
assert_eq!(tree.insert(bounds1), 1);
|
||||
|
||||
// Insert the second bounds, which is adjacent to the first but not overlapping.
|
||||
assert_eq!(tree.insert(bounds2), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_iterations() {
|
||||
let max_bounds = 100;
|
||||
|
||||
let mut actual_intersections: Vec<usize> = Vec::new();
|
||||
for seed in 1..=1000 {
|
||||
// let seed = 44;
|
||||
let debug = false;
|
||||
if debug {
|
||||
let svg_path = Path::new("./svg");
|
||||
if svg_path.exists() {
|
||||
fs::remove_dir_all("./svg").unwrap();
|
||||
}
|
||||
fs::create_dir_all("./svg").unwrap();
|
||||
}
|
||||
|
||||
dbg!(seed);
|
||||
|
||||
let mut tree = BoundsTree::new();
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||
let mut expected_quads: Vec<Primitive<f32>> = Vec::new();
|
||||
|
||||
let mut insert_time = std::time::Duration::ZERO;
|
||||
|
||||
// Insert a random number of random Bounds into the tree.
|
||||
let num_bounds = rng.gen_range(1..=max_bounds);
|
||||
for quad_id in 0..num_bounds {
|
||||
let min_x: f32 = rng.gen_range(-100.0..100.0);
|
||||
let min_y: f32 = rng.gen_range(-100.0..100.0);
|
||||
let max_x: f32 = rng.gen_range(min_x..min_x + 50.0);
|
||||
let max_y: f32 = rng.gen_range(min_y..min_y + 50.0);
|
||||
let bounds = Bounds {
|
||||
origin: Point { x: min_x, y: min_y },
|
||||
size: Size {
|
||||
width: max_x - min_x,
|
||||
height: max_y - min_y,
|
||||
},
|
||||
};
|
||||
|
||||
let expected_ordering = expected_quads
|
||||
.iter()
|
||||
.filter_map(|quad| {
|
||||
(quad.bounds.origin.x < bounds.origin.x + bounds.size.width
|
||||
&& quad.bounds.origin.x + quad.bounds.size.width > bounds.origin.x
|
||||
&& quad.bounds.origin.y < bounds.origin.y + bounds.size.height
|
||||
&& quad.bounds.origin.y + quad.bounds.size.height > bounds.origin.y)
|
||||
.then_some(quad.order)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
+ 1;
|
||||
expected_quads.push(Primitive {
|
||||
bounds,
|
||||
order: expected_ordering,
|
||||
});
|
||||
if debug {
|
||||
println!("inserting {} with Bounds: {:?}", quad_id, bounds);
|
||||
draw_bounds(
|
||||
format!("./svg/expected_bounds_after_{}.svg", quad_id),
|
||||
&expected_quads,
|
||||
);
|
||||
}
|
||||
|
||||
// Insert the Bounds into the tree and collect intersections.
|
||||
actual_intersections.clear();
|
||||
let t0 = std::time::Instant::now();
|
||||
let actual_ordering = tree.insert(bounds);
|
||||
insert_time += t0.elapsed();
|
||||
assert_eq!(actual_ordering, expected_ordering);
|
||||
|
||||
if debug {
|
||||
tree.draw(format!("./svg/bounds_tree_after_{}.svg", quad_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_bounds(svg_path: impl AsRef<Path>, bounds: &[Primitive<f32>]) {
|
||||
let mut svg_content = String::from(
|
||||
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-100 -100 200 200" style="border:1px solid black;">"#,
|
||||
);
|
||||
|
||||
for quad in bounds {
|
||||
svg_content.push_str(&format!(
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:none;stroke:black;stroke-width:1" />"#,
|
||||
quad.bounds.origin.x,
|
||||
quad.bounds.origin.y,
|
||||
quad.bounds.size.width,
|
||||
quad.bounds.size.height
|
||||
));
|
||||
svg_content.push_str(&format!(
|
||||
r#"<text x="{}" y="{}" font-size="3" text-anchor="middle" alignment-baseline="central"></text>"#,
|
||||
quad.bounds.origin.x + quad.bounds.size.width / 2.0,
|
||||
quad.bounds.origin.y + quad.bounds.size.height / 2.0,
|
||||
));
|
||||
}
|
||||
|
||||
svg_content.push_str("</svg>");
|
||||
fs::write(svg_path, &svg_content).unwrap();
|
||||
}
|
||||
|
||||
impl BoundsTree<f32> {
|
||||
fn draw(&self, svg_path: impl AsRef<std::path::Path>) {
|
||||
let root_bounds = self.node(self.root.unwrap()).bounds();
|
||||
|
||||
let mut svg_content = format!(
|
||||
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="border:1px solid black;" viewBox="{} {} {} {}">"#,
|
||||
root_bounds.origin.x,
|
||||
root_bounds.origin.y,
|
||||
root_bounds.size.width,
|
||||
root_bounds.size.height
|
||||
);
|
||||
|
||||
fn draw_node(svg_content: &mut String, nodes: &[Node<f32>], index: usize) {
|
||||
match &nodes[index] {
|
||||
Node::Internal {
|
||||
bounds,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
svg_content.push_str(&format!(
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:rgba({},{},{},0.1);stroke:rgba({},{},{},1);stroke-width:1" />"#,
|
||||
bounds.origin.x,
|
||||
bounds.origin.y,
|
||||
bounds.size.width,
|
||||
bounds.size.height,
|
||||
(index * 50) % 255, // Red component
|
||||
(index * 120) % 255, // Green component
|
||||
(index * 180) % 255, // Blue component
|
||||
(index * 50) % 255, // Red stroke
|
||||
(index * 120) % 255, // Green stroke
|
||||
(index * 180) % 255 // Blue stroke
|
||||
));
|
||||
draw_node(svg_content, nodes, *left);
|
||||
draw_node(svg_content, nodes, *right);
|
||||
}
|
||||
Node::Leaf { bounds, .. } => {
|
||||
svg_content.push_str(&format!(
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:none;stroke:black;stroke-width:1" />"#,
|
||||
bounds.origin.x,
|
||||
bounds.origin.y,
|
||||
bounds.size.width,
|
||||
bounds.size.height
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(root) = self.root {
|
||||
draw_node(&mut svg_content, &self.nodes, root);
|
||||
}
|
||||
|
||||
svg_content.push_str("</svg>");
|
||||
std::fs::write(svg_path, &svg_content).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
point, AtlasTextureId, AtlasTile, Bounds, BoundsTree, ContentMask, Corners, Edges, EntityId,
|
||||
Hsla, Pixels, Point, ScaledPixels,
|
||||
Hsla, Pixels, Point, ScaledPixels, SharedString,
|
||||
};
|
||||
use collections::FxHashSet;
|
||||
use collections::{FxHashMap, FxHashSet, HashMap};
|
||||
use std::{fmt::Debug, iter::Peekable, slice};
|
||||
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
@@ -37,14 +37,14 @@ impl From<ViewId> for EntityId {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Scene {
|
||||
pub(crate) shadows: Vec<Shadow>,
|
||||
pub(crate) quads: Vec<Quad>,
|
||||
pub(crate) paths: Vec<Path<ScaledPixels>>,
|
||||
pub(crate) underlines: Vec<Underline>,
|
||||
pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
|
||||
pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
|
||||
pub(crate) shadows: PrimitiveSet<Shadow>,
|
||||
pub(crate) quads: PrimitiveSet<Quad>,
|
||||
pub(crate) paths: PrimitiveSet<Path<ScaledPixels>>,
|
||||
pub(crate) underlines: PrimitiveSet<Underline>,
|
||||
pub(crate) monochrome_sprites: PrimitiveSet<MonochromeSprite>,
|
||||
pub(crate) polychrome_sprites: PrimitiveSet<PolychromeSprite>,
|
||||
pub(crate) surfaces: Vec<Surface>,
|
||||
bounds_tree: BoundsTree<ScaledPixels>,
|
||||
bounds_tree: BoundsTree<ScaledPixels, PrimitiveIndex>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@@ -60,79 +60,217 @@ impl Scene {
|
||||
}
|
||||
|
||||
pub fn paths(&self) -> &[Path<ScaledPixels>] {
|
||||
&self.paths
|
||||
&self.paths.primitives
|
||||
}
|
||||
|
||||
pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
|
||||
BatchIterator {
|
||||
shadows: &self.shadows,
|
||||
shadows: &self.shadows.primitives,
|
||||
shadows_start: 0,
|
||||
shadows_iter: self.shadows.iter().peekable(),
|
||||
quads: &self.quads,
|
||||
shadows_iter: self.shadows.primitives.iter().peekable(),
|
||||
quads: &self.quads.primitives,
|
||||
quads_start: 0,
|
||||
quads_iter: self.quads.iter().peekable(),
|
||||
paths: &self.paths,
|
||||
quads_iter: self.quads.primitives.iter().peekable(),
|
||||
paths: &self.paths.primitives,
|
||||
paths_start: 0,
|
||||
paths_iter: self.paths.iter().peekable(),
|
||||
underlines: &self.underlines,
|
||||
paths_iter: self.paths.primitives.iter().peekable(),
|
||||
underlines: &self.underlines.primitives,
|
||||
underlines_start: 0,
|
||||
underlines_iter: self.underlines.iter().peekable(),
|
||||
monochrome_sprites: &self.monochrome_sprites,
|
||||
underlines_iter: self.underlines.primitives.iter().peekable(),
|
||||
monochrome_sprites: &self.monochrome_sprites.primitives,
|
||||
monochrome_sprites_start: 0,
|
||||
monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
|
||||
polychrome_sprites: &self.polychrome_sprites,
|
||||
monochrome_sprites_iter: self.monochrome_sprites.primitives.iter().peekable(),
|
||||
polychrome_sprites: &self.polychrome_sprites.primitives,
|
||||
polychrome_sprites_start: 0,
|
||||
polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
|
||||
polychrome_sprites_iter: self.polychrome_sprites.primitives.iter().peekable(),
|
||||
surfaces: &self.surfaces,
|
||||
surfaces_start: 0,
|
||||
surfaces_iter: self.surfaces.iter().peekable(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, primitive: impl Into<Primitive>) -> Option<u32> {
|
||||
let primitive = primitive.into();
|
||||
let clipped_bounds = primitive
|
||||
.bounds()
|
||||
.intersect(&primitive.content_mask().bounds);
|
||||
pub(crate) fn insert_shadow(
|
||||
&mut self,
|
||||
shadow: Shadow,
|
||||
hover: Option<Shadow>,
|
||||
group_hover: Option<(SharedString, Option<Shadow>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = shadow.bounds.intersect(&shadow.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(clipped_bounds);
|
||||
match primitive {
|
||||
Primitive::Shadow(mut shadow) => {
|
||||
shadow.order = order;
|
||||
self.shadows.push(shadow);
|
||||
}
|
||||
Primitive::Quad(mut quad) => {
|
||||
quad.order = order;
|
||||
self.quads.push(quad);
|
||||
}
|
||||
Primitive::Path(mut path) => {
|
||||
path.order = order;
|
||||
path.id = PathId(self.paths.len());
|
||||
self.paths.push(path);
|
||||
}
|
||||
Primitive::Underline(mut underline) => {
|
||||
underline.order = order;
|
||||
self.underlines.push(underline);
|
||||
}
|
||||
Primitive::MonochromeSprite(mut sprite) => {
|
||||
sprite.order = order;
|
||||
self.monochrome_sprites.push(sprite);
|
||||
}
|
||||
Primitive::PolychromeSprite(mut sprite) => {
|
||||
sprite.order = order;
|
||||
self.polychrome_sprites.push(sprite);
|
||||
}
|
||||
Primitive::Surface(mut surface) => {
|
||||
surface.order = order;
|
||||
self.surfaces.push(surface);
|
||||
}
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::Shadow,
|
||||
index: self.shadows.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.shadows
|
||||
.insert(Shadow { order, ..shadow }, hover, group_hover);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_quad(
|
||||
&mut self,
|
||||
quad: Quad,
|
||||
hover: Option<Quad>,
|
||||
group_hover: Option<(SharedString, Option<Quad>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = quad.bounds.intersect(&quad.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::Quad,
|
||||
index: self.quads.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.quads
|
||||
.insert(Quad { order, ..quad }, hover, group_hover);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_path(
|
||||
&mut self,
|
||||
path: Path<ScaledPixels>,
|
||||
hover: Option<Path<ScaledPixels>>,
|
||||
group_hover: Option<(SharedString, Option<Path<ScaledPixels>>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::Path,
|
||||
index: self.paths.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.paths
|
||||
.insert(Path { order, ..path }, hover, group_hover);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_underline(
|
||||
&mut self,
|
||||
underline: Underline,
|
||||
hover: Option<Underline>,
|
||||
group_hover: Option<(SharedString, Option<Underline>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = underline.bounds.intersect(&underline.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::Underline,
|
||||
index: self.underlines.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.underlines
|
||||
.insert(Underline { order, ..underline }, hover, group_hover);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_monochrome_sprite(
|
||||
&mut self,
|
||||
monochrome_sprite: MonochromeSprite,
|
||||
hover: Option<MonochromeSprite>,
|
||||
group_hover: Option<(SharedString, Option<MonochromeSprite>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = monochrome_sprite
|
||||
.bounds
|
||||
.intersect(&monochrome_sprite.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::MonochromeSprite,
|
||||
index: self.monochrome_sprites.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.monochrome_sprites.insert(
|
||||
MonochromeSprite {
|
||||
order,
|
||||
..monochrome_sprite
|
||||
},
|
||||
hover,
|
||||
group_hover,
|
||||
);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_polychrome_sprite(
|
||||
&mut self,
|
||||
polychrome_sprite: PolychromeSprite,
|
||||
hover: Option<PolychromeSprite>,
|
||||
group_hover: Option<(SharedString, Option<PolychromeSprite>)>,
|
||||
) -> Option<u32> {
|
||||
let clipped_bounds = polychrome_sprite
|
||||
.bounds
|
||||
.intersect(&polychrome_sprite.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::PolychromeSprite,
|
||||
index: self.polychrome_sprites.primitives.len(),
|
||||
},
|
||||
);
|
||||
self.polychrome_sprites.insert(
|
||||
PolychromeSprite {
|
||||
order,
|
||||
..polychrome_sprite
|
||||
},
|
||||
hover,
|
||||
group_hover,
|
||||
);
|
||||
Some(order)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_surface(&mut self, surface: Surface) -> Option<u32> {
|
||||
let clipped_bounds = surface.bounds.intersect(&surface.content_mask.bounds);
|
||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let order = self.bounds_tree.insert(
|
||||
clipped_bounds,
|
||||
PrimitiveIndex {
|
||||
kind: PrimitiveKind::Surface,
|
||||
index: self.surfaces.len(),
|
||||
},
|
||||
);
|
||||
self.surfaces.push(Surface { order, ..surface });
|
||||
Some(order)
|
||||
}
|
||||
|
||||
@@ -189,19 +327,101 @@ impl Scene {
|
||||
}
|
||||
|
||||
pub fn finish(&mut self) {
|
||||
self.shadows.sort_unstable_by_key(|shadow| shadow.order);
|
||||
self.quads.sort_unstable_by_key(|quad| quad.order);
|
||||
self.paths.sort_unstable_by_key(|path| path.order);
|
||||
self.shadows
|
||||
.primitives
|
||||
.sort_unstable_by_key(|shadow| shadow.order);
|
||||
self.quads
|
||||
.primitives
|
||||
.sort_unstable_by_key(|quad| quad.order);
|
||||
self.paths
|
||||
.primitives
|
||||
.sort_unstable_by_key(|path| path.order);
|
||||
self.underlines
|
||||
.primitives
|
||||
.sort_unstable_by_key(|underline| underline.order);
|
||||
self.monochrome_sprites
|
||||
.primitives
|
||||
.sort_unstable_by_key(|sprite| sprite.order);
|
||||
self.polychrome_sprites
|
||||
.primitives
|
||||
.sort_unstable_by_key(|sprite| sprite.order);
|
||||
self.surfaces.sort_unstable_by_key(|surface| surface.order);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PrimitiveSet<P> {
|
||||
pub(crate) primitives: Vec<P>,
|
||||
hovers: FxHashMap<usize, PrimitiveHover<P>>,
|
||||
}
|
||||
|
||||
impl<P> PrimitiveSet<P> {
|
||||
/// Returns the number of primitives in the set.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.primitives.len()
|
||||
}
|
||||
|
||||
/// Inserts a primitive into the set and associates hover and group hover information with it.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `primitive` - The primitive to insert.
|
||||
/// * `hover` - An optional hover state for the primitive.
|
||||
/// * `group_hover` - An optional group hover state for the primitive, with an associated shared string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use zed::SharedString;
|
||||
/// let mut primitive_set = PrimitiveSet::default();
|
||||
/// let primitive = Quad::default();
|
||||
/// let hover = Some(Quad::default());
|
||||
/// let group_hover = Some((SharedString::from("group"), Some(Quad::default())));
|
||||
///
|
||||
/// primitive_set.insert(primitive, hover, group_hover);
|
||||
/// ```
|
||||
fn insert(
|
||||
&mut self,
|
||||
primitive: P,
|
||||
hover: Option<P>,
|
||||
group_hover: Option<(SharedString, Option<P>)>,
|
||||
) {
|
||||
let index = self.primitives.len();
|
||||
self.primitives.push(primitive);
|
||||
if hover.is_some() || group_hover.is_some() {
|
||||
self.hovers
|
||||
.insert(index, PrimitiveHover { hover, group_hover });
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears all primitives and associated hover information from the set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # let mut primitive_set = PrimitiveSet::default();
|
||||
/// primitive_set.clear();
|
||||
/// assert!(primitive_set.len() == 0);
|
||||
/// ```
|
||||
fn clear(&mut self) {
|
||||
self.primitives.clear();
|
||||
self.hovers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Default for PrimitiveSet<P> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
primitives: Vec::new(),
|
||||
hovers: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PrimitiveHover<P> {
|
||||
hover: Option<P>,
|
||||
group_hover: Option<(SharedString, Option<P>)>,
|
||||
}
|
||||
|
||||
struct BatchIterator<'a> {
|
||||
shadows: &'a [Shadow],
|
||||
shadows_start: usize,
|
||||
@@ -399,40 +619,10 @@ pub(crate) enum PrimitiveKind {
|
||||
Surface,
|
||||
}
|
||||
|
||||
pub(crate) enum Primitive {
|
||||
Shadow(Shadow),
|
||||
Quad(Quad),
|
||||
Path(Path<ScaledPixels>),
|
||||
Underline(Underline),
|
||||
MonochromeSprite(MonochromeSprite),
|
||||
PolychromeSprite(PolychromeSprite),
|
||||
Surface(Surface),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub fn bounds(&self) -> &Bounds<ScaledPixels> {
|
||||
match self {
|
||||
Primitive::Shadow(shadow) => &shadow.bounds,
|
||||
Primitive::Quad(quad) => &quad.bounds,
|
||||
Primitive::Path(path) => &path.bounds,
|
||||
Primitive::Underline(underline) => &underline.bounds,
|
||||
Primitive::MonochromeSprite(sprite) => &sprite.bounds,
|
||||
Primitive::PolychromeSprite(sprite) => &sprite.bounds,
|
||||
Primitive::Surface(surface) => &surface.bounds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
|
||||
match self {
|
||||
Primitive::Shadow(shadow) => &shadow.content_mask,
|
||||
Primitive::Quad(quad) => &quad.content_mask,
|
||||
Primitive::Path(path) => &path.content_mask,
|
||||
Primitive::Underline(underline) => &underline.content_mask,
|
||||
Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
|
||||
Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
|
||||
Primitive::Surface(surface) => &surface.content_mask,
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
struct PrimitiveIndex {
|
||||
kind: PrimitiveKind,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -478,12 +668,6 @@ impl PartialOrd for Quad {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Quad> for Primitive {
|
||||
fn from(quad: Quad) -> Self {
|
||||
Primitive::Quad(quad)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Underline {
|
||||
@@ -509,12 +693,6 @@ impl PartialOrd for Underline {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Underline> for Primitive {
|
||||
fn from(underline: Underline) -> Self {
|
||||
Primitive::Underline(underline)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Shadow {
|
||||
@@ -541,12 +719,6 @@ impl PartialOrd for Shadow {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Shadow> for Primitive {
|
||||
fn from(shadow: Shadow) -> Self {
|
||||
Primitive::Shadow(shadow)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MonochromeSprite {
|
||||
@@ -574,12 +746,6 @@ impl PartialOrd for MonochromeSprite {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MonochromeSprite> for Primitive {
|
||||
fn from(sprite: MonochromeSprite) -> Self {
|
||||
Primitive::MonochromeSprite(sprite)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PolychromeSprite {
|
||||
@@ -609,12 +775,6 @@ impl PartialOrd for PolychromeSprite {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PolychromeSprite> for Primitive {
|
||||
fn from(sprite: PolychromeSprite) -> Self {
|
||||
Primitive::PolychromeSprite(sprite)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Surface {
|
||||
pub view_id: ViewId,
|
||||
@@ -638,12 +798,6 @@ impl PartialOrd for Surface {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Surface> for Primitive {
|
||||
fn from(surface: Surface) -> Self {
|
||||
Primitive::Surface(surface)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PathId(pub(crate) usize);
|
||||
|
||||
@@ -792,12 +946,6 @@ impl PartialOrd for Path<ScaledPixels> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Path<ScaledPixels>> for Primitive {
|
||||
fn from(path: Path<ScaledPixels>) -> Self {
|
||||
Primitive::Path(path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PathVertex<P: Clone + Default + Debug> {
|
||||
|
||||
@@ -666,17 +666,21 @@ impl<'a> ElementContext<'a> {
|
||||
let mut shadow_bounds = bounds;
|
||||
shadow_bounds.origin += shadow.offset;
|
||||
shadow_bounds.dilate(shadow.spread_radius);
|
||||
window.next_frame.scene.insert(Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
pad: 0,
|
||||
});
|
||||
window.next_frame.scene.insert_shadow(
|
||||
Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,17 +693,21 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
});
|
||||
window.next_frame.scene.insert_quad(
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint the given `Path` into the scene for the next frame at the current z-index.
|
||||
@@ -712,7 +720,10 @@ impl<'a> ElementContext<'a> {
|
||||
path.color = color.into();
|
||||
path.view_id = view_id.into();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(path.scale(scale_factor));
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.insert_path(path.scale(scale_factor), None, None);
|
||||
}
|
||||
|
||||
/// Paint an underline into the scene for the next frame at the current z-index.
|
||||
@@ -736,16 +747,20 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
wavy: style.wavy,
|
||||
});
|
||||
window.next_frame.scene.insert_underline(
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
/// Paint a strikethrough into the scene for the next frame at the current z-index.
|
||||
@@ -765,16 +780,20 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: false,
|
||||
});
|
||||
window.next_frame.scene.insert_underline(
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: false,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
/// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
|
||||
@@ -822,15 +841,19 @@ impl<'a> ElementContext<'a> {
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
});
|
||||
window.next_frame.scene.insert_monochrome_sprite(
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -877,17 +900,21 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
|
||||
window.next_frame.scene.insert(PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
tile,
|
||||
grayscale: false,
|
||||
pad: 0,
|
||||
});
|
||||
window.next_frame.scene.insert_polychrome_sprite(
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
tile,
|
||||
grayscale: false,
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -920,15 +947,19 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
});
|
||||
window.next_frame.scene.insert_monochrome_sprite(
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -956,17 +987,21 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
tile,
|
||||
grayscale,
|
||||
pad: 0,
|
||||
});
|
||||
window.next_frame.scene.insert_polychrome_sprite(
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
tile,
|
||||
grayscale,
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -978,7 +1013,7 @@ impl<'a> ElementContext<'a> {
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(crate::Surface {
|
||||
window.next_frame.scene.insert_surface(crate::Surface {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
|
||||
Reference in New Issue
Block a user