From 287792fcf5264234b26ca83091b6cde90988f50e Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Wed, 10 Jan 2024 20:14:27 -0500 Subject: [PATCH] Support zindex encoding channel for ordering mark items --- .../symbol/zindex_circles.dims.json | 6 ++ .../symbol/zindex_circles.png | Bin 0 -> 11702 bytes .../symbol/zindex_circles.sg.json | 82 ++++++++++++++++++ .../vega-specs/symbol/zindex_circles.vg.json | 36 ++++++++ sg2d-vega/src/marks/rect.rs | 10 +++ sg2d-vega/src/marks/rule.rs | 11 +++ sg2d-vega/src/marks/symbol.rs | 10 +++ sg2d-vega/src/marks/text.rs | 11 +++ sg2d-wgpu/tests/test_image_baselines.rs | 1 + sg2d/src/marks/rect.rs | 13 +-- sg2d/src/marks/rule.rs | 19 ++-- sg2d/src/marks/symbol.rs | 15 ++-- sg2d/src/marks/text.rs | 37 ++++---- sg2d/src/value.rs | 11 ++- 14 files changed, 227 insertions(+), 35 deletions(-) create mode 100644 sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.dims.json create mode 100644 sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.png create mode 100644 sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.sg.json create mode 100644 sg2d-vega-test-data/vega-specs/symbol/zindex_circles.vg.json diff --git a/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.dims.json b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.dims.json new file mode 100644 index 0000000..b6a2711 --- /dev/null +++ b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.dims.json @@ -0,0 +1,6 @@ +{ + "width": 115, + "height": 100, + "origin_x": 15, + "origin_y": 0 +} \ No newline at end of file diff --git a/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.png b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.png new file mode 100644 index 0000000000000000000000000000000000000000..a184a5cc1996700a0df586bc36b8ece4914b6131 GIT binary patch literal 11702 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 literal 0 HcmV?d00001 diff --git a/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.sg.json b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.sg.json new file mode 100644 index 0000000..1705341 --- /dev/null +++ b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.sg.json @@ -0,0 +1,82 @@ +{ + "marktype": "group", + "name": "root", + "role": "frame", + "interactive": true, + "clip": false, + "items": [ + { + "items": [ + { + "marktype": "symbol", + "role": "mark", + "interactive": true, + "clip": false, + "items": [ + { + "zindex": 3, + "x": 30, + "y": 40, + "fill": "red", + "stroke": "black", + "strokeWidth": 3, + "size": 600, + "shape": "circle", + "angle": 0 + }, + { + "zindex": 1, + "x": 30, + "y": 40, + "fill": "blue", + "stroke": "black", + "strokeWidth": 3, + "size": 1200, + "shape": "circle", + "angle": 30 + }, + { + "zindex": 0, + "x": 10, + "y": 40, + "fill": "green", + "stroke": "black", + "strokeWidth": 3, + "size": 1800, + "shape": "circle", + "angle": 90 + }, + { + "zindex": 2, + "x": 50, + "y": 40, + "fill": "aqua", + "stroke": "black", + "strokeWidth": 3, + "size": 2200, + "shape": "circle", + "angle": 120 + }, + { + "zindex": 0, + "x": 30, + "y": 40, + "fill": "pink", + "stroke": "black", + "strokeWidth": 3, + "size": 2800, + "shape": "circle", + "angle": 180 + } + ], + "zindex": 0 + } + ], + "x": 0, + "y": 0, + "width": 100, + "height": 100 + } + ], + "zindex": 0 +} \ No newline at end of file diff --git a/sg2d-vega-test-data/vega-specs/symbol/zindex_circles.vg.json b/sg2d-vega-test-data/vega-specs/symbol/zindex_circles.vg.json new file mode 100644 index 0000000..e7c5e68 --- /dev/null +++ b/sg2d-vega-test-data/vega-specs/symbol/zindex_circles.vg.json @@ -0,0 +1,36 @@ +{ + "width": 100, + "height": 100, + "background": "white", + "data": [ + { + "name": "data_1", + "values": [ + {"x": 30, "z": 3, "angle": 0, "c": "red", "size": 600}, + {"x": 30, "z": 1, "angle": 30, "c": "blue", "size": 1200}, + {"x": 10, "z": 0, "angle": 90, "c": "green", "size": 1800}, + {"x": 50, "z": 2, "angle": 120, "c": "aqua", "size": 2200}, + {"x": 30, "z": 0, "angle": 180, "c": "pink", "size": 2800} + ] + } + ], + "marks": [ + { + "type": "symbol", + "from": {"data": "data_1"}, + "encode": { + "update": { + "x": {"field": "x"}, + "y": {"value": 40}, + "zindex": {"field": "z"}, + "shape": {"value": "circle"}, + "fill": {"field": "c"}, + "stroke": {"value": "black"}, + "strokeWidth": {"value": 3}, + "size": {"field": "size"}, + "angle": {"field": "angle"} + } + } + } + ] +} \ No newline at end of file diff --git a/sg2d-vega/src/marks/rect.rs b/sg2d-vega/src/marks/rect.rs index be9940a..ca25f9b 100644 --- a/sg2d-vega/src/marks/rect.rs +++ b/sg2d-vega/src/marks/rect.rs @@ -16,6 +16,7 @@ pub struct VegaRectItem { pub y2: Option, pub fill: Option, pub fill_opacity: Option, + pub zindex: Option, } impl VegaMarkItem for VegaRectItem {} @@ -37,6 +38,7 @@ impl VegaMarkContainer { let mut width = Vec::::new(); let mut height = Vec::::new(); let mut fill = Vec::<[f32; 3]>::new(); + let mut zindex = Vec::::new(); // For each item, append explicit values to corresponding vector for item in &self.items { @@ -52,6 +54,9 @@ impl VegaMarkContainer { let c = csscolorparser::parse(v)?; fill.push([c.r as f32, c.g as f32, c.b as f32]) } + if let Some(v) = item.zindex { + zindex.push(v); + } } // Override values with vectors @@ -73,6 +78,11 @@ impl VegaMarkContainer { if fill.len() == len { mark.fill = EncodingValue::Array { values: fill }; } + if zindex.len() == len { + let mut indices: Vec = (0..len).collect(); + indices.sort_by_key(|i| zindex[*i]); + mark.indices = Some(indices); + } Ok(SceneMark::Rect(mark)) } diff --git a/sg2d-vega/src/marks/rule.rs b/sg2d-vega/src/marks/rule.rs index fa21362..4c71612 100644 --- a/sg2d-vega/src/marks/rule.rs +++ b/sg2d-vega/src/marks/rule.rs @@ -15,6 +15,7 @@ pub struct VegaRuleItem { pub stroke: Option, pub stroke_width: Option, pub stroke_cap: Option, + pub zindex: Option, } impl VegaMarkItem for VegaRuleItem {} @@ -38,6 +39,7 @@ impl VegaMarkContainer { let mut stroke = Vec::<[f32; 3]>::new(); let mut stroke_width = Vec::::new(); let mut stroke_cap = Vec::::new(); + let mut zindex = Vec::::new(); // For each item, append explicit values to corresponding vector for item in &self.items { @@ -58,6 +60,10 @@ impl VegaMarkContainer { if let Some(s) = item.stroke_cap { stroke_cap.push(s); } + + if let Some(v) = item.zindex { + zindex.push(v); + } } // Override values with vectors @@ -87,6 +93,11 @@ impl VegaMarkContainer { if stroke_cap.len() == len { mark.stroke_cap = EncodingValue::Array { values: stroke_cap }; } + if zindex.len() == len { + let mut indices: Vec = (0..len).collect(); + indices.sort_by_key(|i| zindex[*i]); + mark.indices = Some(indices); + } Ok(SceneMark::Rule(mark)) } diff --git a/sg2d-vega/src/marks/symbol.rs b/sg2d-vega/src/marks/symbol.rs index 4947b90..13ee1a5 100644 --- a/sg2d-vega/src/marks/symbol.rs +++ b/sg2d-vega/src/marks/symbol.rs @@ -23,6 +23,7 @@ pub struct VegaSymbolItem { pub stroke_width: Option, pub stroke_opacity: Option, pub angle: Option, + pub zindex: Option, } impl VegaMarkItem for VegaSymbolItem {} @@ -67,6 +68,7 @@ impl VegaMarkContainer { let mut stroke = Vec::<[f32; 4]>::new(); let mut stroke_width = Vec::::new(); let mut angle = Vec::::new(); + let mut zindex = Vec::::new(); // For each item, append explicit values to corresponding vector for item in &self.items { @@ -99,6 +101,9 @@ impl VegaMarkContainer { if let Some(v) = item.angle { angle.push(v); } + if let Some(v) = item.zindex { + zindex.push(v); + } } // Override values with vectors @@ -123,6 +128,11 @@ impl VegaMarkContainer { if angle.len() == len { mark.angle = EncodingValue::Array { values: angle }; } + if zindex.len() == len { + let mut indices: Vec = (0..len).collect(); + indices.sort_by_key(|i| zindex[*i]); + mark.indices = Some(indices); + } Ok(SceneMark::Symbol(mark)) } diff --git a/sg2d-vega/src/marks/text.rs b/sg2d-vega/src/marks/text.rs index 4e2fc8d..08e2013 100644 --- a/sg2d-vega/src/marks/text.rs +++ b/sg2d-vega/src/marks/text.rs @@ -26,6 +26,7 @@ pub struct VegaTextItem { pub font_weight: Option, pub font_style: Option, pub limit: Option, + pub zindex: Option, } impl VegaMarkItem for VegaTextItem {} @@ -57,6 +58,7 @@ impl VegaMarkContainer { let mut font_weight = Vec::::new(); let mut font_style = Vec::::new(); let mut limit = Vec::::new(); + let mut zindex = Vec::::new(); for item in &self.items { x.push(item.x + origin[0]); @@ -111,6 +113,10 @@ impl VegaMarkContainer { if let Some(v) = item.limit { limit.push(v); } + + if let Some(v) = item.zindex { + zindex.push(v); + } } // Override values with vectors @@ -164,6 +170,11 @@ impl VegaMarkContainer { if limit.len() == len { mark.limit = EncodingValue::Array { values: limit }; } + if zindex.len() == len { + let mut indices: Vec = (0..len).collect(); + indices.sort_by_key(|i| zindex[*i]); + mark.indices = Some(indices); + } Ok(SceneMark::Text(Box::new(mark))) } } diff --git a/sg2d-wgpu/tests/test_image_baselines.rs b/sg2d-wgpu/tests/test_image_baselines.rs index 4de98db..af2252b 100644 --- a/sg2d-wgpu/tests/test_image_baselines.rs +++ b/sg2d-wgpu/tests/test_image_baselines.rs @@ -37,6 +37,7 @@ mod test_image_baselines { case("symbol", "wind_vector", 0.0015), case("symbol", "wedge_angle", 0.001), case("symbol", "wedge_stroke_angle", 0.001), + case("symbol", "zindex_circles", 0.001), case("rule", "wide_rule_axes", 0.0001), case("text", "bar_axis_labels", 0.025) )] diff --git a/sg2d/src/marks/rect.rs b/sg2d/src/marks/rect.rs index 2168e35..51c3de5 100644 --- a/sg2d/src/marks/rect.rs +++ b/sg2d/src/marks/rect.rs @@ -12,27 +12,29 @@ pub struct RectMark { pub width: EncodingValue, pub height: EncodingValue, pub fill: EncodingValue<[f32; 3]>, + pub indices: Option>, } impl RectMark { pub fn x_iter(&self) -> Box + '_> { - self.x.as_iter(self.len as usize) + self.x.as_iter(self.len as usize, self.indices.as_ref()) } pub fn y_iter(&self) -> Box + '_> { - self.y.as_iter(self.len as usize) + self.y.as_iter(self.len as usize, self.indices.as_ref()) } pub fn width_iter(&self) -> Box + '_> { - self.width.as_iter(self.len as usize) + self.width.as_iter(self.len as usize, self.indices.as_ref()) } pub fn height_iter(&self) -> Box + '_> { - self.height.as_iter(self.len as usize) + self.height + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn fill_iter(&self) -> Box + '_> { - self.fill.as_iter(self.len as usize) + self.fill.as_iter(self.len as usize, self.indices.as_ref()) } } @@ -49,6 +51,7 @@ impl Default for RectMark { fill: EncodingValue::Scalar { value: [0.0, 0.0, 0.0], }, + indices: None, } } } diff --git a/sg2d/src/marks/rule.rs b/sg2d/src/marks/rule.rs index fca8cd7..008f903 100644 --- a/sg2d/src/marks/rule.rs +++ b/sg2d/src/marks/rule.rs @@ -14,29 +14,33 @@ pub struct RuleMark { pub stroke: EncodingValue<[f32; 3]>, pub stroke_width: EncodingValue, pub stroke_cap: EncodingValue, + pub indices: Option>, } impl RuleMark { pub fn x0_iter(&self) -> Box + '_> { - self.x0.as_iter(self.len as usize) + self.x0.as_iter(self.len as usize, self.indices.as_ref()) } pub fn y0_iter(&self) -> Box + '_> { - self.y0.as_iter(self.len as usize) + self.y0.as_iter(self.len as usize, self.indices.as_ref()) } pub fn x1_iter(&self) -> Box + '_> { - self.x1.as_iter(self.len as usize) + self.x1.as_iter(self.len as usize, self.indices.as_ref()) } pub fn y1_iter(&self) -> Box + '_> { - self.y1.as_iter(self.len as usize) + self.y1.as_iter(self.len as usize, self.indices.as_ref()) } pub fn stroke_iter(&self) -> Box + '_> { - self.stroke.as_iter(self.len as usize) + self.stroke + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn stroke_width_iter(&self) -> Box + '_> { - self.stroke_width.as_iter(self.len as usize) + self.stroke_width + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn stroke_cap_iter(&self) -> Box + '_> { - self.stroke_cap.as_iter(self.len as usize) + self.stroke_cap + .as_iter(self.len as usize, self.indices.as_ref()) } } @@ -57,6 +61,7 @@ impl Default for RuleMark { stroke_cap: EncodingValue::Scalar { value: StrokeCap::Butt, }, + indices: None, } } } diff --git a/sg2d/src/marks/symbol.rs b/sg2d/src/marks/symbol.rs index af5c07a..dda659a 100644 --- a/sg2d/src/marks/symbol.rs +++ b/sg2d/src/marks/symbol.rs @@ -15,29 +15,31 @@ pub struct SymbolMark { pub size: EncodingValue, pub stroke: EncodingValue<[f32; 4]>, pub angle: EncodingValue, + pub indices: Option>, } impl SymbolMark { pub fn x_iter(&self) -> Box + '_> { - self.x.as_iter(self.len as usize) + self.x.as_iter(self.len as usize, self.indices.as_ref()) } pub fn y_iter(&self) -> Box + '_> { - self.y.as_iter(self.len as usize) + self.y.as_iter(self.len as usize, self.indices.as_ref()) } pub fn fill_iter(&self) -> Box + '_> { - self.fill.as_iter(self.len as usize) + self.fill.as_iter(self.len as usize, self.indices.as_ref()) } pub fn size_iter(&self) -> Box + '_> { - self.size.as_iter(self.len as usize) + self.size.as_iter(self.len as usize, self.indices.as_ref()) } pub fn stroke_iter(&self) -> Box + '_> { - self.stroke.as_iter(self.len as usize) + self.stroke + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn angle_iter(&self) -> Box + '_> { - self.angle.as_iter(self.len as usize) + self.angle.as_iter(self.len as usize, self.indices.as_ref()) } } @@ -59,6 +61,7 @@ impl Default for SymbolMark { value: [0.0, 0.0, 0.0, 0.0], }, angle: EncodingValue::Scalar { value: 0.0 }, + indices: None, } } } diff --git a/sg2d/src/marks/text.rs b/sg2d/src/marks/text.rs index 4e791c2..0d44f27 100644 --- a/sg2d/src/marks/text.rs +++ b/sg2d/src/marks/text.rs @@ -22,53 +22,59 @@ pub struct TextMark { pub font_weight: EncodingValue, pub font_style: EncodingValue, pub limit: EncodingValue, + pub indices: Option>, } impl TextMark { pub fn text_iter(&self) -> Box + '_> { - self.text.as_iter(self.len as usize) + self.text.as_iter(self.len as usize, self.indices.as_ref()) } pub fn x_iter(&self) -> Box + '_> { - self.x.as_iter(self.len as usize) + self.x.as_iter(self.len as usize, self.indices.as_ref()) } pub fn y_iter(&self) -> Box + '_> { - self.y.as_iter(self.len as usize) + self.y.as_iter(self.len as usize, self.indices.as_ref()) } pub fn align_iter(&self) -> Box + '_> { - self.align.as_iter(self.len as usize) + self.align.as_iter(self.len as usize, self.indices.as_ref()) } pub fn baseline_iter(&self) -> Box + '_> { - self.baseline.as_iter(self.len as usize) + self.baseline + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn opacity_iter(&self) -> Box + '_> { - self.opacity.as_iter(self.len as usize) + self.opacity + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn angle_iter(&self) -> Box + '_> { - self.angle.as_iter(self.len as usize) + self.angle.as_iter(self.len as usize, self.indices.as_ref()) } pub fn color_iter(&self) -> Box + '_> { - self.color.as_iter(self.len as usize) + self.color.as_iter(self.len as usize, self.indices.as_ref()) } pub fn dx_iter(&self) -> Box + '_> { - self.dx.as_iter(self.len as usize) + self.dx.as_iter(self.len as usize, self.indices.as_ref()) } pub fn dy_iter(&self) -> Box + '_> { - self.dx.as_iter(self.len as usize) + self.dx.as_iter(self.len as usize, self.indices.as_ref()) } pub fn font_iter(&self) -> Box + '_> { - self.font.as_iter(self.len as usize) + self.font.as_iter(self.len as usize, self.indices.as_ref()) } pub fn font_size_iter(&self) -> Box + '_> { - self.font_size.as_iter(self.len as usize) + self.font_size + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn font_weight_iter(&self) -> Box + '_> { - self.font_weight.as_iter(self.len as usize) + self.font_weight + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn font_style_iter(&self) -> Box + '_> { - self.font_style.as_iter(self.len as usize) + self.font_style + .as_iter(self.len as usize, self.indices.as_ref()) } pub fn limit_iter(&self) -> Box + '_> { - self.limit.as_iter(self.len as usize) + self.limit.as_iter(self.len as usize, self.indices.as_ref()) } } @@ -107,6 +113,7 @@ impl Default for TextMark { value: FontStyleSpec::Normal, }, limit: EncodingValue::Scalar { value: 0.0 }, + indices: None, } } } diff --git a/sg2d/src/value.rs b/sg2d/src/value.rs index a3541a3..cbe6878 100644 --- a/sg2d/src/value.rs +++ b/sg2d/src/value.rs @@ -8,10 +8,17 @@ pub enum EncodingValue { } impl EncodingValue { - pub fn as_iter(&self, scalar_len: usize) -> Box + '_> { + pub fn as_iter<'a>( + &'a self, + scalar_len: usize, + indices: Option<&'a Vec>, + ) -> Box + '_> { match self { EncodingValue::Scalar { value } => Box::new(std::iter::repeat(value).take(scalar_len)), - EncodingValue::Array { values } => Box::new(values.iter()), + EncodingValue::Array { values } => match indices { + None => Box::new(values.iter()), + Some(indices) => Box::new(indices.iter().map(|i| &values[*i])), + }, } } }