!!!###!!!title=grammar-element——VisActor/VGrammar tutorial documents!!!###!!!!!!###!!!description=VGrammar visual scenes consist of grammar elements with different meanings.Currently, VGrammar supports grammar elements including:- Semaphore Signal- Data Data- Mapping Scale- Coordinate System Coordinate- Graphic MarkIn addition, developers can register their own grammar elements through the `registerGrammar` interface.!!!###!!!

Grammar Elements

VGrammar visual scenes consist of grammar elements with different meanings.

Currently, VGrammar supports grammar elements including:

  • Semaphore Signal
  • Data Data
  • Mapping Scale
  • Coordinate System Coordinate
  • Graphic Mark

In addition, developers can register their own grammar elements through the registerGrammar interface.

Semaphore Signal

Semaphore (signal) grammar elements describe a variable in the visual scene. Signals can be simple values or generated by specific calculation logic, and dynamically affect the calculation of the visual scene.

VGrammar provides some built-in semaphores, including:

  • width & height: Width and height of the visual scene
  • viewWidth & viewHeight: Width and height excluding padding
  • viewBox: Bounding box excluding padding
  • padding: Inner padding of the visual scene
  • autoFit: Whether the visual scene adapts to the container width and height

Spec form Signal element usage example:

{
  signals: [{
    id: 'threshold',
    value: 40
  }],
  data: [{
    id: 'data',
    values: [
      { type: 'A', value: 22 },
      { type: 'B', value: 45 },
      { type: 'C', value: 77 },
      { type: 'D', value: 31 }
    ],
    // apply signal to data transform
    transform: [
      {
        type: 'filter',
        callback: (datum, params) => datum.value >= params.threshold,
      }
    ],
    // declare the reference to signal
    dependency: 'threshold'
  }]
}

API form Signal element usage example:

const threshold = view.signal(40);
const data = view
  .data([
    { type: 'A', value: 22 },
    { type: 'B', value: 45 },
    { type: 'C', value: 77 },
    { type: 'D', value: 31 }
  ])
  .transform([
    {
      type: 'filter',
      callback: (datum, params) => datum.value >= params.threshold
    }
  ])
  .depend(threshold);

Data Data

Data (data) grammar elements are used to manage a set of data, and VGrammar provides built-in data transformations to support data analysis logic.

In VGrammar visualization scenes, graphic element rendering is based on data-driven logic; except for special components and Group graphics, other graphic types are based on data elements to perform corresponding data mapping (data join) calculations.

Spec form Data element usage example:

{
  data: [{
    id: 'data',
    values: [
      { type: 'A', value: 22 },
      { type: 'B', value: 45 },
      { type: 'C', value: 77 },
      { type: 'D', value: 31 }
    ],
    transform: [
      {
        type: 'filter',
        callback: (datum) => datum.value >= 40
      }
    ]
  }],
  marks: [{
    type: 'rect',
    // apply data join to mark
    from: { data: 'data' }
  }]
}

API form Data element usage example:

const data = view
  .data([
    { type: 'A', value: 22 },
    { type: 'B', value: 45 },
    { type: 'C', value: 77 },
    { type: 'D', value: 31 }
  ])
  .transform([
    {
      type: 'filter',
      callback: datum => datum.value >= 40
    }
  ]);
// apply data join to mark
const bar = view.mark('rect', view.rootMark).join({ data: 'data' });

Mapping Scale

Mapping (scale) grammar element is responsible for mapping data to visual channels, such as mapping ['A', 'B'] data to color channel ['red', 'blue'].

Currently, VGrammar provides several different types of Scale grammar elements, such as LinearScale linear mapping, OrdinalScale sequential mapping, etc.

Developers can freely choose the mapping method, configure mapping parameters, and apply the declared scale element to the visual encoding calculation of the mark:

Spec form Scale element usage example:

{
  scales: [{
    id: 'colorScale',
    type: 'ordinal',
    domain: ['A', 'B'],
    range: ['red', 'blue']
  }],
  marks: [{
    type: 'rect',
    encode: {
      update: {
        // apply visual scale to mark
        fill: { scale: 'colorScale', field: 'type' }
      }
    }
  }]
}

API form Scale element usage example:

const scale = view.scale('ordinal').domain(['A', 'B']).range(['red', 'blue']);

// apply visual scale to mark
const bar = view.mark('rect', view.rootMark).encode('fill', { scale: 'colorScale', field: 'type' });

Coordinate System Coordinate

Coordinate (coordinate) grammar elements describe specific coordinate system transformation logic, converting coordinate positions from the canvas coordinate system to the target coordinate system.

Coordinate system functions include conversion between different coordinate system categories (such as Cartesian coordinate system to polar coordinate system conversion) and additional coordinate system transformation logic (such as translation, rotation, scaling, transposition). Semantic graphic elements adapt their presentation based on their corresponding coordinate system.

Currently, VGrammar provides two types of coordinate systems:

  • Cartesian coordinate system: The Cartesian coordinate system provided by VGrammar consists of two perpendicular x and y axes.
  • Polar coordinate system: The polar coordinate system provided by VGrammar consists of angle theta and radius r.

Spec form Coordinate element usage example:

{
  coordinates: [
    {
      id: 'polar',
      type: 'polar',
      origin: [200, 200],
      transpose: true
    }
  ],
  marks: [{
    type: 'interval',
    // apply coordinate to mark
    coordinate: 'polar'
  }]
}

API form Coordinate element usage example:

const polar = view.coordinate('polar').origin([200, 200]).transpose(true);
const interval = view.mark('interval', view.rootMark).coordinate(polar);

Graphic Mark

Graphic (mark) grammar elements describe a group of graphical elements with the same data mapping and visual encoding logic. Developers can create data-driven graphics content through mark elements.

Spec form Mark element usage example:

{
  marks: [
    {
      type: 'rect',
      from: { data: 'data' },
      encode: {
        update: {
          x: { scale: 'xscale', field: 'type' },
          width: { scale: 'xscale', band: 1 },
          y: { scale: 'yscale', field: 'value' },
          y1: 200,
          fill: '#1890ff'
        }
      }
    }
  ],
}

API form Mark element usage example:

const bar = view
  .mark('rect', view.rootMark)
  .join(data)
  .encode({
    x: { scale: 'xscale', field: 'type' },
    width: { scale: 'xscale', band: 1 },
    y: { scale: 'yscale', field: 'value' },
    y1: 200,
    fill: '#1890ff'
  });

Inter-reference of grammar elements

All grammar elements can reference each other

In Spec mode, grammar elements can reference another grammar element through the dependency configuration:

{
  "data": [
    {
      "id": "table",
      "values": [{ "a": 0, "b": 2 }]
    }
  ],

  "scales": [
    {
      "id": "colorScale",
      "type": "ordinal",
      "dependency": ["table"],
      "domain": (scale, params) => {
        const data = params.table;

        return data.map(entry => 'a');
      },
      "range": ["red", "blue"]
    }
  ]
}

In API mode, you can use the depend API to reference another grammar element:

const tableData = view.data([{ a: 0, b: 2 }]).id('table');

const colorScale = view
  .scale('ordinal')
  .depend('table')
  .domain((scale, params) => {
    const data = params.table;

    return data.map(entry => 'a');
  })
  .range(['red', 'blue']);

Note that each grammar element provides semantic configurations to depend on specific grammar elements; the effect and depend(), dependency are consistent, and when declaring specific configurations using custom callbacks, all dependent grammar elements can be accessed through their globally unique id;

const tableData = view
  .data([
    { cat: 'A', value: 2 },
    { cat: 'B', value: 2 }
  ])
  .id('table');

const colorScale = view.scale('ordinal').id('xScale').domain({ data: 'table', field: 'cat' }).range(['red', 'blue']);

const mark = view.mark('rect', view.rootMark);