Skip to content

Commit

Permalink
Merge pull request #57 from jonmmease/jonmmease/hide_certain_text_labels
Browse files Browse the repository at this point in the history
Skip text and rule instances that have null color
  • Loading branch information
jonmmease authored Feb 1, 2024
2 parents a148d72 + 67c3a43 commit dd8a110
Show file tree
Hide file tree
Showing 8 changed files with 7,160 additions and 19 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6,934 changes: 6,934 additions & 0 deletions avenger-vega-test-data/vega-scenegraphs/text/lasagna_plot.sg.json

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
134 changes: 134 additions & 0 deletions avenger-vega-test-data/vega-specs/text/lasagna_plot.vg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"background": "white",
"padding": 5,
"width": 300,
"height": 100,
"style": "cell",
"data": [
{
"name": "source_0",
"url": "https://cdn.jsdelivr.net/npm/[email protected]/data/stocks.csv",
"format": {"type": "csv", "parse": {"date": "date"}, "delimiter": ","},
"transform": [
{"type": "filter", "expr": "(datum.symbol !== 'GOOG')"},
{
"field": "date",
"type": "timeunit",
"units": ["year", "month", "date"],
"as": ["yearmonthdate_date", "yearmonthdate_date_end"]
},
{
"type": "aggregate",
"groupby": ["yearmonthdate_date", "symbol"],
"ops": ["sum"],
"fields": ["price"],
"as": ["sum_price"]
},
{
"type": "filter",
"expr": "isValid(datum[\"sum_price\"]) && isFinite(+datum[\"sum_price\"])"
}
]
}
],
"marks": [
{
"name": "marks",
"type": "rect",
"style": ["rect"],
"from": {"data": "source_0"},
"encode": {
"update": {
"fill": {"scale": "color", "field": "sum_price"},
"description": {
"signal": "\"Time: \" + (timeFormat(datum[\"yearmonthdate_date\"], '%Y')) + \"; symbol: \" + (isValid(datum[\"symbol\"]) ? datum[\"symbol\"] : \"\"+datum[\"symbol\"]) + \"; Price: \" + (format(datum[\"sum_price\"], \"\"))"
},
"x": {"scale": "x", "field": "yearmonthdate_date"},
"width": {"signal": "max(0.25, bandwidth('x'))"},
"y": {"scale": "y", "field": "symbol"},
"height": {"signal": "max(0.25, bandwidth('y'))"}
}
}
}
],
"scales": [
{
"name": "x",
"type": "band",
"domain": {
"data": "source_0",
"field": "yearmonthdate_date",
"sort": true
},
"range": [0, {"signal": "width"}],
"paddingInner": 0,
"paddingOuter": 0
},
{
"name": "y",
"type": "band",
"domain": {"data": "source_0", "field": "symbol", "sort": true},
"range": [0, {"signal": "height"}],
"paddingInner": 0,
"paddingOuter": 0
},
{
"name": "color",
"type": "linear",
"domain": {"data": "source_0", "field": "sum_price"},
"range": "heatmap",
"interpolate": "hcl",
"zero": false
}
],
"axes": [
{
"scale": "x",
"orient": "bottom",
"grid": false,
"title": "Time",
"format": "%Y",
"labelAngle": 0,
"labelOverlap": false,
"formatType": "time",
"labelBaseline": "top",
"tickMinStep": {
"signal": "datetime(2001, 0, 2, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
},
"encode": {
"labels": {
"update": {
"fill": [
{
"test": "month(datum.value) == 1 && date(datum.value) == 1",
"value": "black"
},
{"value": null}
]
}
},
"ticks": {
"update": {
"stroke": [
{
"test": "month(datum.value) == 1 && date(datum.value) == 1",
"value": "black"
},
{"value": null}
]
}
}
},
"zindex": 1
},
{"scale": "y", "orient": "left", "grid": false, "zindex": 1}
],
"legends": [
{
"title": "Price",
"fill": "color",
"gradientLength": {"signal": "clamp(height, 64, 200)"}
}
]
}
22 changes: 14 additions & 8 deletions avenger-vega/src/marks/rule.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::error::AvengerVegaError;
use crate::marks::mark::{VegaMarkContainer, VegaMarkItem};
use crate::marks::values::{CssColorOrGradient, StrokeDashSpec};
use crate::marks::values::{CssColorOrGradient, MissingNullOrValue, StrokeDashSpec};
use avenger::marks::mark::SceneMark;
use avenger::marks::rule::RuleMark;
use avenger::marks::value::{ColorOrGradient, EncodingValue, Gradient, StrokeCap};
Expand All @@ -13,7 +13,7 @@ pub struct VegaRuleItem {
pub y: Option<f32>,
pub x2: Option<f32>,
pub y2: Option<f32>,
pub stroke: Option<CssColorOrGradient>,
pub stroke: MissingNullOrValue<CssColorOrGradient>,
pub stroke_width: Option<f32>,
pub stroke_cap: Option<StrokeCap>,
pub stroke_opacity: Option<f32>,
Expand Down Expand Up @@ -48,20 +48,25 @@ impl VegaMarkContainer<VegaRuleItem> {
let mut zindex = Vec::<i32>::new();
let mut gradients = Vec::<Gradient>::new();

let mut len: usize = 0;
// For each item, append explicit values to corresponding vector
for item in &self.items {
if item.stroke.is_null() {
// Skip rules with stroke set to explicit null value (not just missing)
continue;
}
if let Some(v) = item.stroke.as_option() {
let opacity = item.stroke_opacity.unwrap_or(1.0) * item.opacity.unwrap_or(1.0);
stroke.push(v.to_color_or_grad(opacity, &mut gradients)?);
}

let x = item.x.unwrap_or(0.0);
let y = item.y.unwrap_or(0.0);
x0.push(x);
y0.push(y);
x1.push(item.x2.unwrap_or(x));
y1.push(item.y2.unwrap_or(y));

if let Some(v) = &item.stroke {
let opacity = item.stroke_opacity.unwrap_or(1.0) * item.opacity.unwrap_or(1.0);
stroke.push(v.to_color_or_grad(opacity, &mut gradients)?);
}

if let Some(s) = item.stroke_width {
stroke_width.push(s);
}
Expand All @@ -77,10 +82,11 @@ impl VegaMarkContainer<VegaRuleItem> {
if let Some(v) = item.zindex {
zindex.push(v);
}

len += 1;
}

// Override values with vectors
let len = self.items.len();
mark.len = len as u32;

if x0.len() == len {
Expand Down
29 changes: 19 additions & 10 deletions avenger-vega/src/marks/text.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::AvengerVegaError;
use crate::marks::mark::{VegaMarkContainer, VegaMarkItem};
use crate::marks::values::MissingNullOrValue;
use avenger::marks::mark::SceneMark;
use avenger::marks::text::{
FontStyleSpec, FontWeightSpec, TextAlignSpec, TextBaselineSpec, TextMark,
Expand All @@ -20,7 +21,7 @@ pub struct VegaTextItem {
pub baseline: Option<TextBaselineSpec>,
pub dx: Option<f32>,
pub dy: Option<f32>,
pub fill: Option<String>,
pub fill: MissingNullOrValue<String>,
pub opacity: Option<f32>,
pub fill_opacity: Option<f32>,
pub font: Option<String>,
Expand Down Expand Up @@ -62,7 +63,20 @@ impl VegaMarkContainer<VegaTextItem> {
let mut limit = Vec::<f32>::new();
let mut zindex = Vec::<i32>::new();

let mut len: usize = 0;
for item in &self.items {
// When fill is set to null literal (not just missing) we skip the
// text item all together
if item.fill.is_null() {
continue;
}
if let Some(v) = item.fill.as_option() {
let c = csscolorparser::parse(v)?;
let opacity =
c.a as f32 * item.fill_opacity.unwrap_or(1.0) * item.opacity.unwrap_or(1.0);
color.push([c.r as f32, c.g as f32, c.b as f32, opacity])
}

x.push(item.x.unwrap_or(0.0) + item.dx.unwrap_or(0.0));
y.push(item.y.unwrap_or(0.0) + item.dy.unwrap_or(0.0));
text.push(match item.text.clone() {
Expand All @@ -83,13 +97,6 @@ impl VegaMarkContainer<VegaTextItem> {
angle.push(v);
}

if let Some(v) = &item.fill {
let c = csscolorparser::parse(v)?;
let opacity =
c.a as f32 * item.fill_opacity.unwrap_or(1.0) * item.opacity.unwrap_or(1.0);
color.push([c.r as f32, c.g as f32, c.b as f32, opacity])
}

if let Some(v) = item.dx {
dx.push(v);
}
Expand Down Expand Up @@ -121,12 +128,14 @@ impl VegaMarkContainer<VegaTextItem> {
if let Some(v) = item.zindex {
zindex.push(v);
}

len += 1;
}

// Override values with vectors
let len = self.items.len();
// Update len
mark.len = len as u32;

// Override values with vectors
if x.len() == len {
mark.x = EncodingValue::Array { values: x };
}
Expand Down
59 changes: 58 additions & 1 deletion avenger-vega/src/marks/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::error::AvengerVegaError;
use avenger::marks::value::{
ColorOrGradient, Gradient, GradientStop, LinearGradient, RadialGradient,
};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::Cow;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -135,3 +135,60 @@ impl CssGradientStop {
})
}
}

/// Helper struct that will not drop null values on round trip (de)serialization
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum MissingNullOrValue<V> {
#[default]
Missing,
Null,
Value(V),
}

impl<V> MissingNullOrValue<V> {
pub fn is_missing(&self) -> bool {
matches!(self, MissingNullOrValue::Missing)
}

pub fn is_null(&self) -> bool {
matches!(self, MissingNullOrValue::Null)
}

pub fn as_option(&self) -> Option<&V> {
match self {
MissingNullOrValue::Missing | MissingNullOrValue::Null => None,
MissingNullOrValue::Value(v) => Some(v),
}
}
}

impl<V> From<Option<V>> for MissingNullOrValue<V> {
fn from(opt: Option<V>) -> MissingNullOrValue<V> {
match opt {
Some(v) => MissingNullOrValue::Value(v),
None => MissingNullOrValue::Null,
}
}
}

impl<'de, V: Deserialize<'de>> Deserialize<'de> for MissingNullOrValue<V> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Option::deserialize(deserializer).map(Into::into)
}
}

impl<V: Serialize> Serialize for MissingNullOrValue<V> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
MissingNullOrValue::Missing => None::<Option<String>>.serialize(serializer),
MissingNullOrValue::Null => serde_json::Value::Null.serialize(serializer),
MissingNullOrValue::Value(v) => v.serialize(serializer),
}
}
}
1 change: 1 addition & 0 deletions avenger-wgpu/tests/test_image_baselines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ mod test_image_baselines {
case("text", "bar_axis_labels", 0.01),
case("text", "text_rotation", 0.015),
case("text", "letter_scatter", 0.012),
case("text", "lasagna_plot", 0.01),
// vl-convert doesn't support emoji at all
case("text", "emoji", 2.0),
Expand Down

0 comments on commit dd8a110

Please sign in to comment.