d3.jssvgcss-transformssvg-transforms

Why don't translate(${x},${y}) and translate(${-x},${-y}) cancel out?


This is a snippet from https://observablehq.com/@d3/non-contiguous-cartogram , which controls the transformation of the shape of states.

function transform(d, year) {
  const [x, y] = path.centroid(d);
  return `
    translate(${x},${y})
    scale(${Math.sqrt(data.get(d.id)[year])})
    translate(${-x},${-y})
  `;
}

Since x and y are constants, shouldn't translate(${x},${y}) and translate(${-x},${-y}) cancel out?

Further, why does this mechanism secure the centroid in its old position?


Solution

  • It is important to understand that SVG transformations are applied consecutively, i.e. order does matter. You cannot just add up numbers to consolidate a list of various transform definitions. In the words of the spec:

    The value of the ‘transform’ attribute is a <transform-list>, which is defined as a list of transform definitions, which are applied in the order provided.

    Each transform definition of that list manipulates the coordinate system that is the basis for all following transformations. Although, in your example, the translations are nominally of the same amount in opposing directions they do not cancel out because the scaling that happens in between changes the coordinate system along the way. Thus, the second translation does not cover the same distance as the first one.

    To understand why those transformations keep the centroid in place it is helpful to write them down a bit more formally. Given the centroid's coodinates (xc, yc) and the scaling factor k we can write them as:

    xxc + k (xxc)
    yyc + k (yyc)

    Every original point (x, y) is mapped to the centroid (first terms) and then moved outwards to its original position, yet it is only moved a scaled down portion of the original distance (second terms).

    Plugging the centroid itself into these rules shows the second terms cancelling out which keeps the centroid in its original place whereby centering the whole transformation on the centroid:

    xcxc + k (xcxc) = xc
    ycyc + k (ycyc) = yc