This is Charming1. For Charming2, go to charmingjs.org. This is v1. For v2, go to charmingjs.org.

Flow

Binding data to shapes.

flow.data([data])

Returns a new flow that contains this specified data. The data is specified for each group in this flow.

If the specified data is an array of arbitrary values(e.g. number of objects), sets [data] as the group of this flow.

const group = app.append(cm.group, {
  width: app.prop("width") / 3,
  height: app.prop("heigh") / 3,
});

group.data([1, 2, 3]);

If the flow has multiple groups(such as flow.data followed by app.data), then data should typically be specified as a function. The function will be evaluated for each group in order, being passed the group’s parent datum(d), the group index(i), all the groups(data) and this flow(flow).

app.data(matrix).data((d, i, data, flow) => {});

For example, to draw a matrix of characters in a terminal:

const matrix = [
  [" +", "-", "+ "],
  [" |", cm.wch("🚀"), "| "],
  [" +", "-", "+ "],
];

app
  .data(matrix)
  .append(cm.group, { y: (_, i) => i })
  .data((d) => d)
  .append(cm.point, {
    y: 0,
    x: (_, i) => i,
    stroke: (d) => cm.cfb(d),
  });

If no argument is specified, return groups of this app:

app.data(); // [...]

flow.datum(value)

Like flow.data, except receives a value instead of an array of data.

flow.datum(1);

// Equivalent
flow.data([1]);

flow.process(process, options)

Processes the data of this flow with the specified process function receiving the specified options, returning a flow with the processed data. It provides a convenient mechanism to manipulate data before calling flow.append to bind it with shapes.

For example, to draw a particle system with two shape types:

const groups = app
  .data(particles)
  .process(cm.push, createParticle)
  .process(cm.eachRight, removeDied)
  .process(cm.each, decay)
  .process(cm.each, move);

groups.process(cm.filter, isCircle).append(cm.circle, {});
groups.process(cm.filter, isSquare).append(cm.rect, {});

See the respective process function for details.

flow.append(shape[, options])

Appends and binds shapes with the data of this flow, returning a flow with shapes. Shapes are created by the specified shape function with the specified options.

Shape function interprets attribute values and invokes the renderer of this flow to draw shapes. See the respective shape function for details. Each shape’s options are typically specified as a object with corresponding attribute name.

For each attribute, if the value is constant, all the shapes are given the same attribute value; otherwise, if the value is a function, it is evaluated for each datum, in order, being passed the current datum(d), the current index(i), the data(data) and this flow(flow). The function’s return value is then used to set each shapes’ attribute.

const flow = app.data([1, 2, 3]);

flow.append(cm.circle, {
  x: (d) => d * 100,
  y: (d) => d * 100,
  fill: "red",
});

flow.transform(transform, options)

Transforms shapes’ attribute values with the specified transform function receiving the specified options, returning a flow with the transformed attribute values. It provides a convenient mechanism to manipulate attribute values after calling flow.append to binding data with shapes.

For example, to map abstract values produced by Math.sin into visual values, drawing a sine wave:

app
  .data(cm.range(50, cm.TWO_PI))
  .append(cm.circle, {
    x: (d) => d,
    y: (d) => Math.sin(d),
    r: 20,
    fill: "rgba(175, 175, 175, 0.5)",
    stroke: "#000",
    strokeWidth: 1,
  })
  .transform(cm.mapPosition);

See the respective transform function for details.

flow.call(callback[, …argument])

Like app.call, except calls on this flow.

function scaleRadius(flow) {
  flow.transform(cm.mapAttrs, {
    r: { range: [10, 15] },
  });
}

app
  .data([1, 2, 3, 4])
  .append(cm.circle, {
    x: 100,
    y: 100,
    r: (d) => d,
    fill: "steelblue",
  })
  .call(scaleRadius);

flow.app()

Returns this app. It helps define some pure functions relaying some properties of this app.

function scale(d, i, data, flow) {
  const app = flow.app();
  const width = app.prop("width");
  return d * width;
}

app.data([0.1, 0.2, 0.3]).process(cm.map, scale);