uwdata / vega

Vega Development Staging
https://vega.github.io/
BSD 3-Clause "New" or "Revised" License
58 stars 15 forks source link

Reference to calculated fields in reactive geometry #37

Closed marcprux closed 9 years ago

marcprux commented 9 years ago

It is possible to reference the "width" and "height" fields of a mark in a referencing mark that is defined only using "x", "y", "x2", and "y2", as follows:

{
  "padding": "strict",
  "marks": [
    {
      "name": "mark_name",
      "type": "rect",
      "properties": {
        "update": {
          "x": { "group": "width", "mult": 0.2 },
          "y": { "group": "height", "mult": 0.2 },
          "x2": { "group": "width", "mult": 0.5 },
          "y2": { "group": "height", "mult": 0.5 },
          "opacity": { "value": 0.5 },
          "fill": { "value": "#9955AA" }
        }
      }
    },
    {
      "type": "rect",
      "from": { "mark": "mark_name" },
      "properties": {
        "update": {
          "x": { "field": "x2" },
          "y": { "field": "y2" },
          "width": { "field": "width", "mult": 0.75 },
          "height": { "field": "height", "mult": 1.0 },
          "fill": { "value": "#55BC82" },
          "opacity": { "value": 0.5 }
        }
      }
    }
  ]
}

However, it isn't possible to reference "x2" and "y2" of a mark that is defined only by "x", "y", "width", and "height". The following spec doesn't lay out the second mark relative to the first one:

{
  "padding": "strict",
  "marks": [
    {
      "name": "mark_name",
      "type": "rect",
      "properties": {
        "update": {
          "x": { "group": "width", "mult": 0.2 },
          "y": { "group": "height", "mult": 0.2 },
          "width": { "group": "width", "mult": 0.5 },
          "height": { "group": "height", "mult": 0.5 },
          "opacity": { "value": 0.5 },
          "fill": { "value": "#9955AA" }
        }
      }
    },
    {
      "type": "rect",
      "from": { "mark": "mark_name" },
      "properties": {
        "update": {
          "x": { "field": "x2" },
          "y": { "field": "y2" },
          "width": { "field": "width", "mult": 0.75 },
          "height": { "field": "height", "mult": 1.0 },
          "fill": { "value": "#55BC82" },
          "opacity": { "value": 0.5 }
        }
      }
    }
  ]
}

It would be especially useful to be able to define mark1 with just "x" and "width" and then define mark2 as being relative to mark1's "x2", since it would allow doing things like positioning text just to the right of a mark while still permitting the original mark to be defined just by "x" and "width".

arvind commented 9 years ago

I'm wary of always calculating x, x2, width and y, y2, height regardless of which two are specified for performance reasons. Our current set up only calculates additional properties when necessary for the renderer (i.e., as you pointed out, when x, x2 or y, y2 are specified).

The use-case you described is still expressible using transforms, like so:

"from": { 
  "mark": "mark_name",
  "transform": [{"type": "filter", "field": "x2", "expr": "datum.x + datum.width"}]
},