Skip to content

Commit

Permalink
Merge pull request #31 from jonmmease/jonmmease/trail_mark
Browse files Browse the repository at this point in the history
Add trail mark
  • Loading branch information
jonmmease authored Jan 20, 2024
2 parents 8c5c272 + 61e0aad commit a3ddb51
Show file tree
Hide file tree
Showing 16 changed files with 10,152 additions and 2 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4,598 changes: 4,598 additions & 0 deletions sg2d-vega-test-data/vega-scenegraphs/trail/trail_stocks.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.
5,158 changes: 5,158 additions & 0 deletions sg2d-vega-test-data/vega-scenegraphs/trail/trail_stocks_opacity.sg.json

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions sg2d-vega-test-data/vega-specs/trail/trail_stocks.vg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"background": "white",
"padding": 5,
"width": 400,
"height": 400,
"style": "cell",
"config": {"style": {"cell": {"stroke": "transparent"}}},
"data": [
{
"name": "source_0",
"url": "data/stocks.csv",
"format": {"type": "csv", "parse": {"date": "date"}, "delimiter": ","}
}
],
"marks": [
{
"name": "pathgroup",
"type": "group",
"from": {
"facet": {
"name": "faceted_path_main",
"data": "source_0",
"groupby": ["symbol"]
}
},
"encode": {
"update": {
"width": {"field": {"group": "width"}},
"height": {"field": {"group": "height"}}
}
},
"marks": [
{
"name": "marks",
"type": "trail",
"style": ["trail"],
"sort": {"field": "datum[\"date\"]"},
"from": {"data": "faceted_path_main"},
"encode": {
"update": {
"fill": {"scale": "color", "field": "symbol"},
"description": {
"signal": "\"date: \" + (timeFormat(datum[\"date\"], '%b %d, %Y')) + \"; price: \" + (format(datum[\"price\"], \"\")) + \"; symbol: \" + (isValid(datum[\"symbol\"]) ? datum[\"symbol\"] : \"\"+datum[\"symbol\"])"
},
"x": {"scale": "x", "field": "date"},
"y": {"scale": "y", "field": "price"},
"size": {"scale": "size", "field": "price"},
"defined": {
"signal": "isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
}
}
}
}
]
}
],
"scales": [
{
"name": "x",
"type": "time",
"domain": {"data": "source_0", "field": "date"},
"range": [0, {"signal": "width"}]
},
{
"name": "y",
"type": "linear",
"domain": {"data": "source_0", "field": "price"},
"range": [{"signal": "height"}, 0],
"nice": true,
"zero": true
},
{
"name": "color",
"type": "ordinal",
"domain": {"data": "source_0", "field": "symbol", "sort": true},
"range": "category"
},
{
"name": "size",
"type": "linear",
"domain": {"data": "source_0", "field": "price"},
"range": [1, 8],
"zero": true
}
]
}
89 changes: 89 additions & 0 deletions sg2d-vega-test-data/vega-specs/trail/trail_stocks_opacity.vg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "Stock prices of 5 Tech Companies over Time.",
"background": "white",
"padding": 5,
"width": 400,
"height": 400,
"style": "cell",
"config": {"style": {"cell": {"stroke": "transparent"}}},
"data": [
{
"name": "source_0",
"url": "data/stocks.csv",
"format": {"type": "csv", "parse": {"date": "date"}, "delimiter": ","}
}
],
"marks": [
{
"name": "pathgroup",
"type": "group",
"from": {
"facet": {
"name": "faceted_path_main",
"data": "source_0",
"groupby": ["symbol"]
}
},
"encode": {
"update": {
"width": {"field": {"group": "width"}},
"height": {"field": {"group": "height"}}
}
},
"marks": [
{
"name": "marks",
"type": "trail",
"style": ["trail"],
"sort": {"field": "datum[\"date\"]"},
"from": {"data": "faceted_path_main"},
"encode": {
"update": {
"fill": {"scale": "color", "field": "symbol"},
"fillOpacity": {"value": 0.6},
"description": {
"signal": "\"date: \" + (timeFormat(datum[\"date\"], '%b %d, %Y')) + \"; price: \" + (format(datum[\"price\"], \"\")) + \"; symbol: \" + (isValid(datum[\"symbol\"]) ? datum[\"symbol\"] : \"\"+datum[\"symbol\"])"
},
"x": {"scale": "x", "field": "date"},
"y": {"scale": "y", "field": "price"},
"size": {"scale": "size", "field": "price"},
"defined": {
"signal": "isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
}
}
}
}
]
}
],
"scales": [
{
"name": "x",
"type": "time",
"domain": {"data": "source_0", "field": "date"},
"range": [0, {"signal": "width"}]
},
{
"name": "y",
"type": "linear",
"domain": {"data": "source_0", "field": "price"},
"range": [{"signal": "height"}, 0],
"nice": true,
"zero": true
},
{
"name": "color",
"type": "ordinal",
"domain": {"data": "source_0", "field": "symbol", "sort": true},
"range": "category"
},
{
"name": "size",
"type": "linear",
"domain": {"data": "source_0", "field": "price"},
"range": [1, 8],
"zero": true
}
]
}
3 changes: 3 additions & 0 deletions sg2d-vega/src/marks/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ impl VegaGroupItem {
VegaMark::Area(mark) => {
vec![mark.to_scene_graph(new_origin)?]
}
VegaMark::Trail(mark) => {
vec![mark.to_scene_graph(new_origin)?]
}
_ => {
println!("Mark type not yet supported: {:?}", item);
continue;
Expand Down
3 changes: 2 additions & 1 deletion sg2d-vega/src/marks/mark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::marks::rule::VegaRuleItem;
use crate::marks::shape::VegaShapeItem;
use crate::marks::symbol::VegaSymbolItem;
use crate::marks::text::VegaTextItem;
use crate::marks::trail::VegaTrailItem;
use serde::{Deserialize, Serialize};

pub trait VegaMarkItem {}
Expand All @@ -27,7 +28,7 @@ pub enum VegaMark {
Shape(VegaMarkContainer<VegaShapeItem>),
Symbol(VegaMarkContainer<VegaSymbolItem>),
Text(VegaMarkContainer<VegaTextItem>),
Trail,
Trail(VegaMarkContainer<VegaTrailItem>),
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions sg2d-vega/src/marks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pub mod rule;
pub mod shape;
pub mod symbol;
pub mod text;
pub mod trail;
80 changes: 80 additions & 0 deletions sg2d-vega/src/marks/trail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::error::VegaSceneGraphError;
use crate::marks::mark::{VegaMarkContainer, VegaMarkItem};
use serde::{Deserialize, Serialize};
use sg2d::marks::mark::SceneMark;
use sg2d::marks::trail::TrailMark;
use sg2d::value::EncodingValue;

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VegaTrailItem {
pub x: f32,
pub y: f32,
pub defined: Option<bool>,
pub size: Option<f32>,
pub fill: Option<String>,
pub fill_opacity: Option<f32>,
pub opacity: Option<f32>,
}

impl VegaMarkItem for VegaTrailItem {}

impl VegaMarkContainer<VegaTrailItem> {
pub fn to_scene_graph(&self, origin: [f32; 2]) -> Result<SceneMark, VegaSceneGraphError> {
// Get shape of first item and use that for all items for now
let first = self.items.first();

// Parse stroke color (Note, vega uses "fill" for the trail, but we use stroke
let mut stroke = [0.0, 0.0, 0.0, 1.0];
if let Some(item) = &first {
if let Some(stroke_css) = &item.fill {
let c = csscolorparser::parse(stroke_css)?;
let base_opacity = item.opacity.unwrap_or(1.0);
let stroke_opacity = c.a as f32 * item.fill_opacity.unwrap_or(1.0) * base_opacity;
stroke = [c.r as f32, c.g as f32, c.b as f32, stroke_opacity]
}
}

let mut mark = TrailMark {
clip: self.clip,
stroke,
..Default::default()
};

// Init vector for each encoding channel
let mut x = Vec::<f32>::new();
let mut y = Vec::<f32>::new();
let mut size = Vec::<f32>::new();
let mut defined = Vec::<bool>::new();

for item in &self.items {
x.push(item.x + origin[0]);
y.push(item.y + origin[1]);
if let Some(v) = item.size {
size.push(v);
}
if let Some(v) = item.defined {
defined.push(v);
}
}

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

if x.len() == len {
mark.x = EncodingValue::Array { values: x };
}
if y.len() == len {
mark.y = EncodingValue::Array { values: y };
}
if size.len() == len {
mark.size = EncodingValue::Array { values: size };
}
if defined.len() == len {
mark.defined = EncodingValue::Array { values: defined };
}

Ok(SceneMark::Trail(mark))
}
}
15 changes: 15 additions & 0 deletions sg2d-wgpu/src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use sg2d::marks::arc::ArcMark;
use sg2d::marks::area::AreaMark;
use sg2d::marks::line::LineMark;
use sg2d::marks::path::PathMark;
use sg2d::marks::trail::TrailMark;
use sg2d::{
marks::group::SceneGroup, marks::mark::SceneMark, marks::rect::RectMark, marks::rule::RuleMark,
marks::symbol::SymbolMark, marks::text::TextMark, scene_graph::SceneGraph,
Expand Down Expand Up @@ -93,6 +94,17 @@ pub trait Canvas {
Ok(())
}

fn add_trail_mark(&mut self, mark: &TrailMark) -> Result<(), Sg2dWgpuError> {
self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new(
self.device(),
*self.uniform(),
self.texture_format(),
self.sample_count(),
Box::new(PathShader::from_trail_mark(mark)?),
)));
Ok(())
}

fn add_area_mark(&mut self, mark: &AreaMark) -> Result<(), Sg2dWgpuError> {
self.add_mark_renderer(MarkRenderer::Basic(BasicMarkRenderer::new(
self.device(),
Expand Down Expand Up @@ -181,6 +193,9 @@ pub trait Canvas {
SceneMark::Line(mark) => {
self.add_line_mark(mark)?;
}
SceneMark::Trail(mark) => {
self.add_trail_mark(mark)?;
}
SceneMark::Area(mark) => {
self.add_area_mark(mark)?;
}
Expand Down
Loading

0 comments on commit a3ddb51

Please sign in to comment.