Skip to content

Instantly share code, notes, and snippets.

@seliopou
Last active December 15, 2015 16:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seliopou/5287587 to your computer and use it in GitHub Desktop.
Save seliopou/5287587 to your computer and use it in GitHub Desktop.
A discussion of the API behavior of d3-transform.

d3-transform chaining

An unnecessarily thorough analysis

The Methods of d3.selection

When you look at the behavior of the methods on a d3.selection, they fall into one of two categories:

  • those that create a new selection, e.g., .filter(), .exit(), .enter(), .append(), etc.; and
  • those that mutate the existing selection, e.g., .attr(), .style(), on(), etc.

Question: Are these categories disjoint and exhaustive?

Note that for the latter category, the order in which they are applied in a method chain is irrelevant as long as the arguments are provied as values, or functions that do not read from the this variable. If the values that these functions return only depend on the data associated with the selection and not the selection itself, you can permute them as you please.

Furthermore, if you call, for example, .on('click') multiple times with a different callback function, the last callback function will be the only one that your code will register with the selection. In other words, the latter category have a getter-setter behavior. When you use them as method-chanined objects, they are typically only used as setters. In this case youc annot change order of an .on('click') call relative other .on('click') calls without changing the behavior of yoru program.

The Differences with transform

This is not the case for the .transform() class of methods that you are proposing. In particular .translate() and .scale() do not commute. Not only that but the "last one wins" behavior that of getter-setters does not apply in this case. Multiple calls to translate() should be cumulative.

For these reasons, I don't think it's a good idea to include them as methods on d3.selection. Rather, they should be their own object, with their own semantics and rules for composition.

Proposed API

/* Creates an identity transform */
var tid = d3.transform()
/* Creates a transform of the given kind, given kind-specific parameters. Kinds
 * include: rotate, scale, matrix, translate, etc.
 */
var t2 = d3.transform('<kind>', args*)
/* Right-composes the transformation specified by the arguments of transform
 * with `t`.
 */
t.transform('<kind>', args*)
/* Right-composes the transformation `t2` with `t1`.*/
t1.transform(t2)

The result of all calls to transform() should be a function that, when applied, will produce a string that can be set to the transform attribute of an SVG element.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment