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 0000000..a184a5c Binary files /dev/null and b/sg2d-vega-test-data/vega-scenegraphs/symbol/zindex_circles.png differ 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])), + }, } } }