diff --git a/sg2d-vega/src/marks/arc.rs b/sg2d-vega/src/marks/arc.rs index 918a353..0ce9e1c 100644 --- a/sg2d-vega/src/marks/arc.rs +++ b/sg2d-vega/src/marks/arc.rs @@ -29,7 +29,7 @@ pub struct VegaArcItem { impl VegaMarkItem for VegaArcItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Init mark with scalar defaults let mut mark = ArcMark { clip: self.clip, @@ -56,8 +56,8 @@ impl VegaMarkContainer { // For each item, append explicit values to corresponding vector for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); if let Some(s) = item.start_angle { start_angle.push(s); diff --git a/sg2d-vega/src/marks/area.rs b/sg2d-vega/src/marks/area.rs index d598d47..a16bc9b 100644 --- a/sg2d-vega/src/marks/area.rs +++ b/sg2d-vega/src/marks/area.rs @@ -29,7 +29,7 @@ pub struct VegaAreaItem { impl VegaMarkItem for VegaAreaItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let stroke_cap = first.and_then(|item| item.stroke_cap).unwrap_or_default(); @@ -80,13 +80,13 @@ impl VegaMarkContainer { let mut defined = Vec::::new(); for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); if let Some(v) = &item.x2 { - x2.push(*v + origin[0]); + x2.push(*v); } if let Some(v) = &item.y2 { - y2.push(*v + origin[1]); + y2.push(*v); } if let Some(v) = item.defined { defined.push(v); diff --git a/sg2d-vega/src/marks/group.rs b/sg2d-vega/src/marks/group.rs index 7db121b..83b1ebd 100644 --- a/sg2d-vega/src/marks/group.rs +++ b/sg2d-vega/src/marks/group.rs @@ -18,48 +18,47 @@ pub struct VegaGroupItem { impl VegaMarkItem for VegaGroupItem {} impl VegaGroupItem { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { - let new_origin = [self.x + origin[0], self.y + origin[1]]; + pub fn to_scene_graph(&self) -> Result { let mut marks: Vec = Vec::new(); for item in &self.items { let item_marks: Vec<_> = match item { VegaMark::Group(group) => group .items .iter() - .map(|item| Ok(SceneMark::Group(item.to_scene_graph(new_origin)?))) + .map(|item| Ok(SceneMark::Group(item.to_scene_graph()?))) .collect::, VegaSceneGraphError>>()?, VegaMark::Rect(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Rule(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Symbol(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Text(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Arc(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Path(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Shape(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Line(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Area(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Trail(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } VegaMark::Image(mark) => { - vec![mark.to_scene_graph(new_origin)?] + vec![mark.to_scene_graph()?] } }; marks.extend(item_marks); diff --git a/sg2d-vega/src/marks/image.rs b/sg2d-vega/src/marks/image.rs index 7abc32d..71cba7d 100644 --- a/sg2d-vega/src/marks/image.rs +++ b/sg2d-vega/src/marks/image.rs @@ -35,7 +35,7 @@ fn default_true() -> bool { impl VegaMarkItem for VegaImageItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { let name = self .name .clone() @@ -58,8 +58,8 @@ impl VegaMarkContainer { let fetcher = make_image_fetcher()?; for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); align.push(item.align); baseline.push(item.baseline); diff --git a/sg2d-vega/src/marks/line.rs b/sg2d-vega/src/marks/line.rs index d6f4c4f..44dcbab 100644 --- a/sg2d-vega/src/marks/line.rs +++ b/sg2d-vega/src/marks/line.rs @@ -26,7 +26,7 @@ pub struct VegaLineItem { impl VegaMarkItem for VegaLineItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let stroke_width = first.and_then(|item| item.stroke_width).unwrap_or(1.0); @@ -65,8 +65,8 @@ impl VegaMarkContainer { let mut defined = Vec::::new(); for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); if let Some(v) = item.defined { defined.push(v); } diff --git a/sg2d-vega/src/marks/path.rs b/sg2d-vega/src/marks/path.rs index ca4ebcd..a5533e2 100644 --- a/sg2d-vega/src/marks/path.rs +++ b/sg2d-vega/src/marks/path.rs @@ -33,7 +33,7 @@ pub struct VegaPathItem { impl VegaMarkItem for VegaPathItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let first_has_stroke = first.map(|item| item.stroke.is_some()).unwrap_or(false); @@ -94,8 +94,8 @@ impl VegaMarkContainer { PathTransform::scale(item.scale_x.unwrap_or(1.0), item.scale_y.unwrap_or(1.0)) .then_rotate(Angle::degrees(item.angle.unwrap_or(0.0))) .then_translate(Vector2D::new( - item.x.unwrap_or(0.0) + origin[0], - item.y.unwrap_or(0.0) + origin[1], + item.x.unwrap_or(0.0), + item.y.unwrap_or(0.0), )), ) } @@ -117,7 +117,7 @@ impl VegaMarkContainer { mark.transform = EncodingValue::Array { values: transform }; } else { mark.transform = EncodingValue::Scalar { - value: PathTransform::translation(origin[0], origin[1]), + value: PathTransform::identity(), } } if zindex.len() == len { diff --git a/sg2d-vega/src/marks/rect.rs b/sg2d-vega/src/marks/rect.rs index d8ae086..db18530 100644 --- a/sg2d-vega/src/marks/rect.rs +++ b/sg2d-vega/src/marks/rect.rs @@ -28,7 +28,7 @@ pub struct VegaRectItem { impl VegaMarkItem for VegaRectItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { let mut mark = RectMark { clip: self.clip, ..Default::default() @@ -52,8 +52,8 @@ impl VegaMarkContainer { // For each item, append explicit values to corresponding vector for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); if let Some(v) = item.width { width.push(v); } diff --git a/sg2d-vega/src/marks/rule.rs b/sg2d-vega/src/marks/rule.rs index dcc464f..f52c5c3 100644 --- a/sg2d-vega/src/marks/rule.rs +++ b/sg2d-vega/src/marks/rule.rs @@ -25,7 +25,7 @@ pub struct VegaRuleItem { impl VegaMarkItem for VegaRuleItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Init mark with scalar defaults let mut mark = RuleMark { clip: self.clip, @@ -49,10 +49,10 @@ impl VegaMarkContainer { // For each item, append explicit values to corresponding vector for item in &self.items { - x0.push(item.x + origin[0]); - y0.push(item.y + origin[1]); - x1.push(item.x2.unwrap_or(item.x) + origin[0]); - y1.push(item.y2.unwrap_or(item.y) + origin[1]); + x0.push(item.x); + y0.push(item.y); + x1.push(item.x2.unwrap_or(item.x)); + y1.push(item.y2.unwrap_or(item.y)); if let Some(v) = &item.stroke { let opacity = item.stroke_opacity.unwrap_or(1.0) * item.opacity.unwrap_or(1.0); diff --git a/sg2d-vega/src/marks/shape.rs b/sg2d-vega/src/marks/shape.rs index 22a3f39..7072099 100644 --- a/sg2d-vega/src/marks/shape.rs +++ b/sg2d-vega/src/marks/shape.rs @@ -28,7 +28,7 @@ pub struct VegaShapeItem { impl VegaMarkItem for VegaShapeItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let first_has_stroke = first.map(|item| item.stroke.is_some()).unwrap_or(false); @@ -79,8 +79,8 @@ impl VegaMarkContainer { // Build transform if item.x.is_some() || item.y.is_some() { transform.push(PathTransform::translation( - item.x.unwrap_or(0.0) + origin[0], - item.y.unwrap_or(0.0) + origin[1], + item.x.unwrap_or(0.0), + item.y.unwrap_or(0.0), )) } @@ -101,7 +101,7 @@ impl VegaMarkContainer { mark.transform = EncodingValue::Array { values: transform }; } else { mark.transform = EncodingValue::Scalar { - value: PathTransform::translation(origin[0], origin[1]), + value: PathTransform::identity(), } } if zindex.len() == len { diff --git a/sg2d-vega/src/marks/symbol.rs b/sg2d-vega/src/marks/symbol.rs index 36860ca..03d29ff 100644 --- a/sg2d-vega/src/marks/symbol.rs +++ b/sg2d-vega/src/marks/symbol.rs @@ -37,7 +37,7 @@ pub struct VegaSymbolItem { impl VegaMarkItem for VegaSymbolItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let first_shape = first @@ -64,14 +64,9 @@ impl VegaMarkContainer { clip: false, len: 2, x: EncodingValue::Array { - values: vec![ - origin[0] + item.x - width / 2.0, - origin[0] + item.x + width / 2.0, - ], - }, - y: EncodingValue::Scalar { - value: origin[1] + item.y, + values: vec![item.x - width / 2.0, item.x + width / 2.0], }, + y: EncodingValue::Scalar { value: item.y }, stroke, stroke_width: item.stroke_width.unwrap_or(1.0), stroke_cap: item.stroke_cap.unwrap_or_default(), @@ -132,8 +127,8 @@ impl VegaMarkContainer { // For each item, append explicit values to corresponding vector for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); let base_opacity = item.opacity.unwrap_or(1.0); if let Some(v) = &item.fill { diff --git a/sg2d-vega/src/marks/text.rs b/sg2d-vega/src/marks/text.rs index 3f5827f..f005b9a 100644 --- a/sg2d-vega/src/marks/text.rs +++ b/sg2d-vega/src/marks/text.rs @@ -33,7 +33,7 @@ pub struct VegaTextItem { impl VegaMarkItem for VegaTextItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Init mark with scalar defaults let mut mark = TextMark { clip: self.clip, @@ -61,8 +61,8 @@ impl VegaMarkContainer { let mut zindex = Vec::::new(); for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); text.push(item.text.clone()); if let Some(v) = item.align { diff --git a/sg2d-vega/src/marks/trail.rs b/sg2d-vega/src/marks/trail.rs index 50dc16c..f840dac 100644 --- a/sg2d-vega/src/marks/trail.rs +++ b/sg2d-vega/src/marks/trail.rs @@ -21,7 +21,7 @@ pub struct VegaTrailItem { impl VegaMarkItem for VegaTrailItem {} impl VegaMarkContainer { - pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result { + pub fn to_scene_graph(&self) -> Result { // Get shape of first item and use that for all items for now let first = self.items.first(); let mut gradients = Vec::::new(); @@ -50,8 +50,8 @@ impl VegaMarkContainer { let mut defined = Vec::::new(); for item in &self.items { - x.push(item.x + origin[0]); - y.push(item.y + origin[1]); + x.push(item.x); + y.push(item.y); if let Some(v) = item.size { size.push(v); } diff --git a/sg2d-vega/src/scene_graph.rs b/sg2d-vega/src/scene_graph.rs index 84f65bf..6b88615 100644 --- a/sg2d-vega/src/scene_graph.rs +++ b/sg2d-vega/src/scene_graph.rs @@ -18,13 +18,14 @@ impl VegaSceneGraph { .scenegraph .items .iter() - .map(|group| group.to_scene_graph(self.origin)) + .map(|group| group.to_scene_graph()) .collect::, VegaSceneGraphError>>()?; Ok(SceneGraph { groups, width: self.width, height: self.height, + origin: self.origin, }) } } diff --git a/sg2d-wgpu/src/canvas.rs b/sg2d-wgpu/src/canvas.rs index eebbc1f..f42c4c5 100644 --- a/sg2d-wgpu/src/canvas.rs +++ b/sg2d-wgpu/src/canvas.rs @@ -23,6 +23,7 @@ use crate::marks::rule::RuleShader; use crate::marks::symbol::SymbolShader; use sg2d::marks::arc::ArcMark; use sg2d::marks::area::AreaMark; +use sg2d::marks::group::GroupBounds; use sg2d::marks::image::ImageMark; use sg2d::marks::line::LineMark; use sg2d::marks::path::PathMark; @@ -76,87 +77,155 @@ pub trait Canvas { fn sample_count(&self) -> u32; - fn add_arc_mark(&mut self, mark: &ArcMark) -> Result<(), Sg2dWgpuError> { + fn add_arc_mark( + &mut self, + mark: &ArcMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Instanced(InstancedMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(ArcShader::from_arc_mark(mark, self.dimensions())), + Box::new(ArcShader::from_arc_mark( + mark, + self.dimensions(), + group_bounds, + )), ))); Ok(()) } - fn add_path_mark(&mut self, mark: &PathMark) -> Result<(), Sg2dWgpuError> { + fn add_path_mark( + &mut self, + mark: &PathMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(PathShader::from_path_mark(mark, self.dimensions())?), + Box::new(PathShader::from_path_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_line_mark(&mut self, mark: &LineMark) -> Result<(), Sg2dWgpuError> { + fn add_line_mark( + &mut self, + mark: &LineMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(PathShader::from_line_mark(mark, self.dimensions())?), + Box::new(PathShader::from_line_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_trail_mark(&mut self, mark: &TrailMark) -> Result<(), Sg2dWgpuError> { + fn add_trail_mark( + &mut self, + mark: &TrailMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(PathShader::from_trail_mark(mark, self.dimensions())?), + Box::new(PathShader::from_trail_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_area_mark(&mut self, mark: &AreaMark) -> Result<(), Sg2dWgpuError> { + fn add_area_mark( + &mut self, + mark: &AreaMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(PathShader::from_area_mark(mark, self.dimensions())?), + Box::new(PathShader::from_area_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_symbol_mark(&mut self, mark: &SymbolMark) -> Result<(), Sg2dWgpuError> { + fn add_symbol_mark( + &mut self, + mark: &SymbolMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Instanced(InstancedMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(SymbolShader::from_symbol_mark(mark, self.dimensions())?), + Box::new(SymbolShader::from_symbol_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_rect_mark(&mut self, mark: &RectMark) -> Result<(), Sg2dWgpuError> { + fn add_rect_mark( + &mut self, + mark: &RectMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Instanced(InstancedMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(RectShader::from_rect_mark(mark, self.dimensions())), + Box::new(RectShader::from_rect_mark( + mark, + self.dimensions(), + group_bounds, + )), ))); Ok(()) } - fn add_rule_mark(&mut self, mark: &RuleMark) -> Result<(), Sg2dWgpuError> { + fn add_rule_mark( + &mut self, + mark: &RuleMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Instanced(InstancedMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(RuleShader::from_rule_mark(mark, self.dimensions())), + Box::new(RuleShader::from_rule_mark( + mark, + self.dimensions(), + group_bounds, + )), ))); Ok(()) } - fn add_text_mark(&mut self, mark: &TextMark) -> Result<(), Sg2dWgpuError> { + fn add_text_mark( + &mut self, + mark: &TextMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { cfg_if::cfg_if! { if #[cfg(feature = "text-glyphon")] { self.add_mark_renderer(MarkRenderer::Text(TextMarkRenderer::new( @@ -166,6 +235,7 @@ pub trait Canvas { self.dimensions(), self.sample_count(), mark, + group_bounds, ))); Ok(()) } else { @@ -174,51 +244,69 @@ pub trait Canvas { } } - fn add_image_mark(&mut self, mark: &ImageMark) -> Result<(), Sg2dWgpuError> { + fn add_image_mark( + &mut self, + mark: &ImageMark, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new( self.device(), self.texture_format(), self.sample_count(), - Box::new(ImageShader::from_image_mark(mark, self.dimensions())?), + Box::new(ImageShader::from_image_mark( + mark, + self.dimensions(), + group_bounds, + )?), ))); Ok(()) } - fn add_group_mark(&mut self, group: &SceneGroup) -> Result<(), Sg2dWgpuError> { + fn add_group_mark( + &mut self, + group: &SceneGroup, + group_bounds: GroupBounds, + ) -> Result<(), Sg2dWgpuError> { + // Compute new group bounds + let group_bounds = GroupBounds { + x: group_bounds.x + group.bounds.x, + y: group_bounds.y + group.bounds.y, + ..group.bounds + }; for mark in &group.marks { match mark { SceneMark::Arc(mark) => { - self.add_arc_mark(mark)?; + self.add_arc_mark(mark, group_bounds)?; } SceneMark::Symbol(mark) => { - self.add_symbol_mark(mark)?; + self.add_symbol_mark(mark, group_bounds)?; } SceneMark::Rect(mark) => { - self.add_rect_mark(mark)?; + self.add_rect_mark(mark, group_bounds)?; } SceneMark::Rule(mark) => { - self.add_rule_mark(mark)?; + self.add_rule_mark(mark, group_bounds)?; } SceneMark::Path(mark) => { - self.add_path_mark(mark)?; + self.add_path_mark(mark, group_bounds)?; } SceneMark::Line(mark) => { - self.add_line_mark(mark)?; + self.add_line_mark(mark, group_bounds)?; } SceneMark::Trail(mark) => { - self.add_trail_mark(mark)?; + self.add_trail_mark(mark, group_bounds)?; } SceneMark::Area(mark) => { - self.add_area_mark(mark)?; + self.add_area_mark(mark, group_bounds)?; } SceneMark::Text(mark) => { - self.add_text_mark(mark)?; + self.add_text_mark(mark, group_bounds)?; } SceneMark::Image(mark) => { - self.add_image_mark(mark)?; + self.add_image_mark(mark, group_bounds)?; } SceneMark::Group(group) => { - self.add_group_mark(group)?; + self.add_group_mark(group, group_bounds)?; } } } @@ -230,8 +318,14 @@ pub trait Canvas { self.clear_mark_renderer(); // Add marks + let group_bounds = GroupBounds { + x: scene_graph.origin[0], + y: scene_graph.origin[1], + width: None, + height: None, + }; for group in &scene_graph.groups { - self.add_group_mark(group)?; + self.add_group_mark(group, group_bounds)?; } Ok(()) diff --git a/sg2d-wgpu/src/marks/arc.rs b/sg2d-wgpu/src/marks/arc.rs index d0209aa..e80c813 100644 --- a/sg2d-wgpu/src/marks/arc.rs +++ b/sg2d-wgpu/src/marks/arc.rs @@ -3,6 +3,7 @@ use crate::marks::gradient::{build_gradients_image, to_color_or_gradient_coord}; use crate::marks::instanced_mark::{InstancedMarkBatch, InstancedMarkShader}; use itertools::izip; use sg2d::marks::arc::ArcMark; +use sg2d::marks::group::GroupBounds; use std::f32::consts::TAU; use std::mem; use wgpu::{Extent3d, VertexBufferLayout}; @@ -11,16 +12,27 @@ use wgpu::{Extent3d, VertexBufferLayout}; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct ArcUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl ArcUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -153,7 +165,11 @@ pub struct ArcShader { } impl ArcShader { - pub fn from_arc_mark(mark: &ArcMark, dimensions: CanvasDimensions) -> Self { + pub fn from_arc_mark( + mark: &ArcMark, + dimensions: CanvasDimensions, + group_bounds: GroupBounds, + ) -> Self { let (instances, img, texture_size) = ArcInstance::from_spec(mark); let batches = vec![InstancedMarkBatch { instances_range: 0..instances.len() as u32, @@ -177,7 +193,7 @@ impl ArcShader { ], indices: vec![0, 1, 2, 0, 2, 3], instances, - uniform: ArcUniform::new(dimensions), + uniform: ArcUniform::new(dimensions, group_bounds, mark.clip), batches, texture_size, shader: format!( diff --git a/sg2d-wgpu/src/marks/arc.wgsl b/sg2d-wgpu/src/marks/arc.wgsl index 6d7da41..82a6211 100644 --- a/sg2d-wgpu/src/marks/arc.wgsl +++ b/sg2d-wgpu/src/marks/arc.wgsl @@ -1,8 +1,10 @@ // Vertex shader struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, // for 16 byte alignment + clip: f32, }; @group(0) @binding(0) @@ -48,7 +50,11 @@ fn vs_main( instance: InstanceInput, ) -> VertexOutput { var out: VertexOutput; - out.position = instance.position; + + // Compute absolute position + let position = instance.position + chart_uniforms.origin; + + out.position = position; out.start_angle = instance.start_angle; out.end_angle = instance.end_angle; out.outer_radius = instance.outer_radius; @@ -62,8 +68,8 @@ fn vs_main( let buffer = 0.5; let half_stroke = out.stroke_width / 2.0; let buffered_radius = instance.outer_radius + half_stroke + buffer; - let x = 2.0 * (model.position[0] * buffered_radius + instance.position[0]) / chart_uniforms.size[0] - 1.0; - let y = 2.0 * (model.position[1] * buffered_radius + (chart_uniforms.size[1] - instance.position[1])) / chart_uniforms.size[1] - 1.0; + let x = 2.0 * (model.position[0] * buffered_radius + position[0]) / chart_uniforms.size[0] - 1.0; + let y = 2.0 * (model.position[1] * buffered_radius + (chart_uniforms.size[1] - position[1])) / chart_uniforms.size[1] - 1.0; out.clip_position = vec4(x, y, 0.0, 1.0); diff --git a/sg2d-wgpu/src/marks/image.rs b/sg2d-wgpu/src/marks/image.rs index b683453..ecb6bd0 100644 --- a/sg2d-wgpu/src/marks/image.rs +++ b/sg2d-wgpu/src/marks/image.rs @@ -3,6 +3,7 @@ use crate::error::Sg2dWgpuError; use crate::marks::basic_mark::{BasicMarkBatch, BasicMarkShader}; use etagere::Size; use itertools::izip; +use sg2d::marks::group::GroupBounds; use sg2d::marks::image::ImageMark; use sg2d::marks::value::{ImageAlign, ImageBaseline}; use wgpu::{Extent3d, FilterMode, VertexBufferLayout}; @@ -11,16 +12,27 @@ use wgpu::{Extent3d, FilterMode, VertexBufferLayout}; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct ImageUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl ImageUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -63,6 +75,7 @@ impl ImageShader { pub fn from_image_mark( mark: &ImageMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { let mut verts: Vec = Vec::new(); let mut indices: Vec = Vec::new(); @@ -240,7 +253,7 @@ impl ImageShader { Ok(Self { verts, indices, - uniform: ImageUniform::new(dimensions), + uniform: ImageUniform::new(dimensions, group_bounds, mark.clip), smooth: mark.smooth, batches, texture_size, diff --git a/sg2d-wgpu/src/marks/image.wgsl b/sg2d-wgpu/src/marks/image.wgsl index 8d8625d..56bd225 100644 --- a/sg2d-wgpu/src/marks/image.wgsl +++ b/sg2d-wgpu/src/marks/image.wgsl @@ -1,7 +1,9 @@ struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, + clip: f32, }; @group(0) @binding(0) @@ -23,9 +25,13 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; out.tex_coords = model.tex_coords; + + // Compute absolute position + let position = model.position + chart_uniforms.origin; + let normalized_pos = vec2( - 2.0 * model.position[0] / chart_uniforms.size[0] - 1.0, - 2.0 * (chart_uniforms.size[1] - model.position[1]) / chart_uniforms.size[1] - 1.0, + 2.0 * position[0] / chart_uniforms.size[0] - 1.0, + 2.0 * (chart_uniforms.size[1] - position[1]) / chart_uniforms.size[1] - 1.0, ); out.clip_position = vec4(normalized_pos, 0.0, 1.0); return out; diff --git a/sg2d-wgpu/src/marks/path.rs b/sg2d-wgpu/src/marks/path.rs index ca1e09e..2b477ce 100644 --- a/sg2d-wgpu/src/marks/path.rs +++ b/sg2d-wgpu/src/marks/path.rs @@ -13,6 +13,7 @@ use lyon::path::builder::WithSvg; use lyon::path::path::BuilderImpl; use lyon::path::{AttributeIndex, LineCap, LineJoin, Path}; use sg2d::marks::area::{AreaMark, AreaOrientation}; +use sg2d::marks::group::GroupBounds; use sg2d::marks::line::LineMark; use sg2d::marks::path::PathMark; use sg2d::marks::trail::TrailMark; @@ -23,16 +24,27 @@ use wgpu::{Extent3d, VertexBufferLayout}; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct PathUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl PathUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -78,6 +90,7 @@ impl PathShader { pub fn from_path_mark( mark: &PathMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { let (gradients_image, texture_size) = build_gradients_image(&mark.gradients); @@ -141,7 +154,7 @@ impl PathShader { Ok(Self { verts, indices, - uniform: PathUniform::new(dimensions), + uniform: PathUniform::new(dimensions, group_bounds, mark.clip), batches: vec![BasicMarkBatch { indices_range, image: gradients_image, @@ -160,6 +173,7 @@ impl PathShader { pub fn from_area_mark( mark: &AreaMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { // Handle gradients: let (gradients_image, texture_size) = build_gradients_image(&mark.gradients); @@ -265,7 +279,7 @@ impl PathShader { Ok(Self { verts: buffers.vertices, indices: buffers.indices, - uniform: PathUniform::new(dimensions), + uniform: PathUniform::new(dimensions, group_bounds, mark.clip), batches: vec![BasicMarkBatch { indices_range, image: gradients_image, @@ -284,6 +298,7 @@ impl PathShader { pub fn from_line_mark( mark: &LineMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { let (gradients_image, texture_size) = build_gradients_image(&mark.gradients); let mut defined_paths: Vec = Vec::new(); @@ -408,7 +423,7 @@ impl PathShader { Ok(Self { verts, indices, - uniform: PathUniform::new(dimensions), + uniform: PathUniform::new(dimensions, group_bounds, mark.clip), batches: vec![BasicMarkBatch { indices_range, image: gradients_image, @@ -427,6 +442,7 @@ impl PathShader { pub fn from_trail_mark( mark: &TrailMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { let (gradients_image, texture_size) = build_gradients_image(&mark.gradients); @@ -489,7 +505,7 @@ impl PathShader { Ok(Self { verts: buffers.vertices, indices: buffers.indices, - uniform: PathUniform::new(dimensions), + uniform: PathUniform::new(dimensions, group_bounds, mark.clip), batches: vec![BasicMarkBatch { indices_range, image: gradients_image, diff --git a/sg2d-wgpu/src/marks/path.wgsl b/sg2d-wgpu/src/marks/path.wgsl index b9a6a04..affb3f1 100644 --- a/sg2d-wgpu/src/marks/path.wgsl +++ b/sg2d-wgpu/src/marks/path.wgsl @@ -1,7 +1,9 @@ struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, // for 16 byte alignment + clip: f32, }; @group(0) @binding(0) @@ -28,14 +30,17 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; + // Compute absolute position + let position = model.position + chart_uniforms.origin; + // Compute vertex coordinates - let x = 2.0 * model.position[0] / chart_uniforms.size[0] - 1.0; - let y = 2.0 * (chart_uniforms.size[1] - model.position[1]) / chart_uniforms.size[1] - 1.0; + let x = 2.0 * position[0] / chart_uniforms.size[0] - 1.0; + let y = 2.0 * (chart_uniforms.size[1] - position[1]) / chart_uniforms.size[1] - 1.0; out.clip_position = vec4(x, y, 0.0, 1.0); out.color = model.color; - out.top_left = model.top_left * chart_uniforms.scale; - out.bottom_right = model.bottom_right * chart_uniforms.scale; + out.top_left = (model.top_left + chart_uniforms.origin) * chart_uniforms.scale; + out.bottom_right = (model.bottom_right + chart_uniforms.origin) * chart_uniforms.scale; return out; } diff --git a/sg2d-wgpu/src/marks/rect.rs b/sg2d-wgpu/src/marks/rect.rs index ef40f3e..295162a 100644 --- a/sg2d-wgpu/src/marks/rect.rs +++ b/sg2d-wgpu/src/marks/rect.rs @@ -2,6 +2,7 @@ use crate::canvas::CanvasDimensions; use crate::marks::gradient::{build_gradients_image, to_color_or_gradient_coord}; use crate::marks::instanced_mark::{InstancedMarkBatch, InstancedMarkShader}; use itertools::izip; +use sg2d::marks::group::GroupBounds; use sg2d::marks::rect::RectMark; use wgpu::{Extent3d, VertexBufferLayout}; @@ -15,16 +16,27 @@ pub const GRADIENT_TEXTURE_WIDTH: u32 = 256; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct RectUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl RectUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -115,7 +127,11 @@ pub struct RectShader { } impl RectShader { - pub fn from_rect_mark(mark: &RectMark, dimensions: CanvasDimensions) -> Self { + pub fn from_rect_mark( + mark: &RectMark, + dimensions: CanvasDimensions, + group_bounds: GroupBounds, + ) -> Self { let (instances, img, texture_size) = RectInstance::from_spec(mark); let batches = vec![InstancedMarkBatch { @@ -142,7 +158,7 @@ impl RectShader { instances, batches, texture_size, - uniform: RectUniform::new(dimensions), + uniform: RectUniform::new(dimensions, group_bounds, mark.clip), shader: format!( "{}\n{}", include_str!("rect.wgsl"), diff --git a/sg2d-wgpu/src/marks/rect.wgsl b/sg2d-wgpu/src/marks/rect.wgsl index 0a6e76a..1649a33 100644 --- a/sg2d-wgpu/src/marks/rect.wgsl +++ b/sg2d-wgpu/src/marks/rect.wgsl @@ -1,8 +1,10 @@ // Vertex shader struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, // for 16 byte alignment + clip: f32, }; @group(0) @binding(0) @@ -53,19 +55,22 @@ fn vs_main( let corner_radius = min(instance.corner_radius, instance.height / 2.0); out.corner_radius = corner_radius; + // Compute absolute position + let position = instance.position + chart_uniforms.origin; + // Compute corner points in fragment shader coordinates let half_stroke = instance.stroke_width / 2.0; let width_height = vec2(instance.width, instance.height); - out.outer_top_left = (instance.position - half_stroke) * chart_uniforms.scale; - out.outer_bottom_right = (instance.position + width_height + half_stroke) * chart_uniforms.scale; + out.outer_top_left = (position - half_stroke) * chart_uniforms.scale; + out.outer_bottom_right = (position + width_height + half_stroke) * chart_uniforms.scale; // Compute corner radius center points in fragment shader coordinates - out.inner_top_left = (instance.position + corner_radius) * chart_uniforms.scale; - out.inner_bottom_right = (instance.position + width_height - corner_radius) * chart_uniforms.scale; + out.inner_top_left = (position + corner_radius) * chart_uniforms.scale; + out.inner_bottom_right = (position + width_height - corner_radius) * chart_uniforms.scale; // Compute vertex coordinates - let x = 2.0 * (model.position[0] * (instance.width + instance.stroke_width) + instance.position[0] - half_stroke) / chart_uniforms.size[0] - 1.0; - let y = 2.0 * (model.position[1] * (instance.height + instance.stroke_width) + (chart_uniforms.size[1] - instance.position[1] - instance.height - half_stroke)) / chart_uniforms.size[1] - 1.0; + let x = 2.0 * (model.position[0] * (instance.width + instance.stroke_width) + position[0] - half_stroke) / chart_uniforms.size[0] - 1.0; + let y = 2.0 * (model.position[1] * (instance.height + instance.stroke_width) + (chart_uniforms.size[1] - position[1] - instance.height - half_stroke)) / chart_uniforms.size[1] - 1.0; out.clip_position = vec4(x, y, 0.0, 1.0); return out; } diff --git a/sg2d-wgpu/src/marks/rule.rs b/sg2d-wgpu/src/marks/rule.rs index ee37b7c..1773886 100644 --- a/sg2d-wgpu/src/marks/rule.rs +++ b/sg2d-wgpu/src/marks/rule.rs @@ -3,6 +3,7 @@ use crate::marks::gradient::{build_gradients_image, to_color_or_gradient_coord}; use crate::marks::instanced_mark::{InstancedMarkBatch, InstancedMarkShader}; use image::DynamicImage; use itertools::izip; +use sg2d::marks::group::GroupBounds; use sg2d::marks::rule::RuleMark; use sg2d::marks::value::StrokeCap; use wgpu::{Extent3d, VertexBufferLayout}; @@ -11,16 +12,27 @@ use wgpu::{Extent3d, VertexBufferLayout}; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct RuleUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl RuleUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -190,7 +202,11 @@ pub struct RuleShader { } impl RuleShader { - pub fn from_rule_mark(mark: &RuleMark, dimensions: CanvasDimensions) -> Self { + pub fn from_rule_mark( + mark: &RuleMark, + dimensions: CanvasDimensions, + group_bounds: GroupBounds, + ) -> Self { let (instances, gradient_image, texture_size) = RuleInstance::from_spec(mark); let batches = vec![InstancedMarkBatch { instances_range: 0..instances.len() as u32, @@ -213,7 +229,7 @@ impl RuleShader { ], indices: vec![0, 1, 2, 0, 2, 3], instances, - uniform: RuleUniform::new(dimensions), + uniform: RuleUniform::new(dimensions, group_bounds, mark.clip), batches, texture_size, shader: format!( diff --git a/sg2d-wgpu/src/marks/rule.wgsl b/sg2d-wgpu/src/marks/rule.wgsl index 9a2b42c..1dc25d1 100644 --- a/sg2d-wgpu/src/marks/rule.wgsl +++ b/sg2d-wgpu/src/marks/rule.wgsl @@ -2,8 +2,10 @@ struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, // for 16 byte alignment + clip: f32, }; @group(0) @binding(0) @@ -47,8 +49,8 @@ fn vs_main( out.color = instance.stroke; var width: f32 = instance.stroke_width; - var p0: vec2 = vec2(instance.x0, instance.y0); - var p1: vec2 = vec2(instance.x1, instance.y1); + var p0: vec2 = vec2(instance.x0, instance.y0) + chart_uniforms.origin; + var p1: vec2 = vec2(instance.x1, instance.y1) + chart_uniforms.origin; let mid = (p0 + p1) / 2.0; var len: f32 = distance(p0, p1); if (instance.stroke_cap == STROKE_CAP_ROUND) { @@ -62,7 +64,7 @@ fn vs_main( p1 += p0p1_norm * (width / 2.0); } - let should_anitalias = instance.stroke_cap == STROKE_CAP_ROUND || (instance.x0 != instance.x1 && instance.y0 != instance.y1); + let should_anitalias = instance.stroke_cap == STROKE_CAP_ROUND || (p0[0] != p1[0] && p0[1] != p1[1]); if (should_anitalias) { // Add anti-aliasing buffer for rules with rounded caps and all diagonal rules. // Non-round rules that are vertical or horizontal don't get anti-aliasing. diff --git a/sg2d-wgpu/src/marks/symbol.rs b/sg2d-wgpu/src/marks/symbol.rs index 44a40ef..4b10f4e 100644 --- a/sg2d-wgpu/src/marks/symbol.rs +++ b/sg2d-wgpu/src/marks/symbol.rs @@ -8,6 +8,7 @@ use lyon::lyon_tessellation::{ }; use lyon::tessellation::geometry_builder::VertexBuffers; use lyon::tessellation::{FillOptions, FillTessellator, StrokeOptions, StrokeTessellator}; +use sg2d::marks::group::GroupBounds; use sg2d::marks::symbol::{SymbolMark, SymbolShape}; use wgpu::{Extent3d, VertexBufferLayout}; @@ -19,16 +20,27 @@ const CIRCLE_KIND: u32 = 2; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct SymbolUniform { pub size: [f32; 2], + pub origin: [f32; 2], + pub group_size: [f32; 2], pub scale: f32, - _pad: [f32; 1], // Pad to 16 bytes + pub clip: f32, } impl SymbolUniform { - pub fn new(dimensions: CanvasDimensions) -> Self { + pub fn new(dimensions: CanvasDimensions, group_bounds: GroupBounds, clip: bool) -> Self { Self { size: dimensions.size, scale: dimensions.scale, - _pad: [0.0], + origin: [group_bounds.x, group_bounds.y], + group_size: [ + group_bounds.width.unwrap_or(0.0), + group_bounds.height.unwrap_or(0.0), + ], + clip: if clip && group_bounds.width.is_some() && group_bounds.height.is_some() { + 1.0 + } else { + 0.0 + }, } } } @@ -131,6 +143,7 @@ impl SymbolShader { pub fn from_symbol_mark( mark: &SymbolMark, dimensions: CanvasDimensions, + group_bounds: GroupBounds, ) -> Result { let shapes = &mark.shapes; let has_stroke = mark.stroke_width.is_some(); @@ -208,7 +221,7 @@ impl SymbolShader { verts, indices, instances, - uniform: SymbolUniform::new(dimensions), + uniform: SymbolUniform::new(dimensions, group_bounds, mark.clip), batches, texture_size, shader: format!( diff --git a/sg2d-wgpu/src/marks/symbol.wgsl b/sg2d-wgpu/src/marks/symbol.wgsl index 5b3beff..9823c91 100644 --- a/sg2d-wgpu/src/marks/symbol.wgsl +++ b/sg2d-wgpu/src/marks/symbol.wgsl @@ -1,8 +1,10 @@ // Vertex shader struct ChartUniform { size: vec2, + origin: vec2, + group_size: vec2, scale: f32, - _pad: f32, // for 16 byte alignment + clip: f32, }; @group(0) @binding(0) @@ -67,12 +69,15 @@ fn vs_main( let size_scale = sqrt(instance.size); + // Compute absolute position + let position = instance.position + chart_uniforms.origin; + // Compute scenegraph x and y coordinates let angle_rad = PI * instance.angle / 180.0; let rot = mat2x2(cos(angle_rad), -sin(angle_rad), sin(angle_rad), cos(angle_rad)); let rotated_pos = rot * model.position; - let sg_x = rotated_pos[0] * size_scale + instance.position[0]; - let sg_y = rotated_pos[1] * size_scale + (chart_uniforms.size[1] - instance.position[1]); + let sg_x = rotated_pos[0] * size_scale + position[0]; + let sg_y = rotated_pos[1] * size_scale + (chart_uniforms.size[1] - position[1]); let pos = vec2(sg_x, sg_y); if (model.kind == 0u) { @@ -119,8 +124,8 @@ fn vs_main( // Compute circle center in fragment shader coordinates out.center = vec2( - instance.position[0] * chart_uniforms.scale, - instance.position[1] * chart_uniforms.scale, + position[0] * chart_uniforms.scale, + position[1] * chart_uniforms.scale, ); // Compute radius in fragment shader coordinates diff --git a/sg2d-wgpu/src/marks/text.rs b/sg2d-wgpu/src/marks/text.rs index 294b308..e95a611 100644 --- a/sg2d-wgpu/src/marks/text.rs +++ b/sg2d-wgpu/src/marks/text.rs @@ -4,6 +4,7 @@ use glyphon::{ TextAtlas, TextBounds, TextRenderer, Weight, }; use itertools::izip; +use sg2d::marks::group::GroupBounds; use sg2d::marks::text::{ FontStyleSpec, FontWeightNameSpec, FontWeightSpec, TextAlignSpec, TextBaselineSpec, TextMark, }; @@ -30,7 +31,12 @@ pub struct TextInstance { } impl TextInstance { - pub fn iter_from_spec(mark: &TextMark) -> impl Iterator + '_ { + pub fn iter_from_spec( + mark: &TextMark, + group_bounds: GroupBounds, + ) -> impl Iterator + '_ { + let group_x = group_bounds.x; + let group_y = group_bounds.y; izip!( mark.text_iter(), mark.x_iter(), @@ -48,7 +54,7 @@ impl TextInstance { mark.limit_iter(), ) .map( - |( + move |( text, x, y, @@ -66,7 +72,7 @@ impl TextInstance { )| { TextInstance { text: text.clone(), - position: [*x, *y], + position: [*x + group_x, *y + group_y], color: *color, align: *align, angle: *angle, @@ -91,6 +97,7 @@ pub struct TextMarkRenderer { pub text_renderer: TextRenderer, pub instances: Vec, pub dimensions: CanvasDimensions, + pub group_bounds: GroupBounds, } impl TextMarkRenderer { @@ -101,8 +108,9 @@ impl TextMarkRenderer { dimensions: CanvasDimensions, sample_count: u32, mark: &TextMark, + group_bounds: GroupBounds, ) -> Self { - let instances = TextInstance::iter_from_spec(mark).collect::>(); + let instances = TextInstance::iter_from_spec(mark, group_bounds).collect::>(); let font_system = FontSystem::new(); let cache = SwashCache::new(); let mut atlas = TextAtlas::new(device, queue, texture_format); @@ -124,6 +132,7 @@ impl TextMarkRenderer { text_renderer, dimensions, instances, + group_bounds, } } diff --git a/sg2d/src/marks/group.rs b/sg2d/src/marks/group.rs index 38587dc..7b17738 100644 --- a/sg2d/src/marks/group.rs +++ b/sg2d/src/marks/group.rs @@ -9,6 +9,17 @@ pub struct GroupBounds { pub height: Option, } +impl Default for GroupBounds { + fn default() -> Self { + Self { + x: 0.0, + y: 0.0, + width: None, + height: None, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SceneGroup { pub bounds: GroupBounds, diff --git a/sg2d/src/scene_graph.rs b/sg2d/src/scene_graph.rs index 4f82171..5af20ba 100644 --- a/sg2d/src/scene_graph.rs +++ b/sg2d/src/scene_graph.rs @@ -6,4 +6,5 @@ pub struct SceneGraph { pub groups: Vec, pub width: f32, pub height: f32, + pub origin: [f32; 2], }