[whatwg] Canvas transform() and matrix element notation

Brendan Kenny bckenny at gmail.com
Mon Jul 19 15:19:45 PDT 2010


On Mon, Jul 19, 2010 at 3:54 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
> On 7/19/10 4:13 PM, David Flanagan wrote:
>>
>> The spec describes the transform() method as follows:
>>
>>> The transform(m11, m12, m21, m22, dx, dy) method must multiply the
>>> current transformation matrix with the matrix described by:
>>>
>>> m11 m21 dx
>>> m12 m22 dy
>>> 0 0 1
>>
>> The first number in these argument names is the column number and the
>> second is the row number.
>
> I agree that this is somewhat weird at first glance, but it seems to be not
> uncommon for graphics libraries.  For example, for cairo the call
>
>  cairo_matrix_init(m, a, b, c, d, e, f);
>
> creates a matrix which represents the affine transformation [1]:
>
>  x_new = a*x + c*y + e;
>  y_new = b*x + d*y + f;
>
> As another example, in CoreGraphics CGAffineTransformMake(a, b, c, d, e, f)
> [2] produces a trasformation that does [3]:
>
>  x_new = a*x + c*y + e;
>  y_new = b*x + d*y + f;
>
> I think the issue here is that these graphics libraries think in terms of
> row vectors and right-multiplication by transformation matrices; see the
> nice matrices written out in the CoreGraphics documentation.  In that
> context the component specification order is in fact the "sensible" one.
>
> In practice, I assume the initial canvas implementation just called into
> CoreGraphics directly, hence the treatment of the argument order.  And now
> that's the argument order we have.
>
>> 2) Java's java.awt.geom.AffineTransform class also lists the row index
>> first, as in the following javadoc excerpt:
>>
>>> [ x']   [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
>>> [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
>>> [ 1 ]   [ 0    0   1 ] [ 1 ] [ 1 ]
>
> While true, if you look at the way one constructs an AffineTransform by
> passing in the matrix entries [4]:
>
>  AffineTransform(double m00, double m10, double m01, double m11,
>                  double m02, double m12)
>
> which is worse than the CoreGraphics setup, even: the convention is the same
> but the example is misleading, unlike the CoreGraphics example with a row
> vector.
>
>> It would be nice if this spec was not inconsistent with other usage.
>
> It seems that it's not, if "other usage" means "other things in the computer
> world that use affine transformation matrices"...
>
> I do think the spec could benefit from an example akin to the one in the
> CoreGraphics documentation.
>
> By the way, 100% agreed that the multiplication order needs to be defined
> here.
>
> -Boris
>
> [1] http://www.cairographics.org/manual/cairo-matrix.html#cairo-matrix-init
>
> [2]
> http://developer.apple.com/mac/library/documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html#//apple_ref/c/func/CGAffineTransformMake
>
> [3]
> http://developer.apple.com/mac/library/documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html#//apple_ref/doc/c_ref/CGAffineTransform

I think the problem is not the parameter order but the parameter
names. For instance, you creates matrices in OpenGL in the same way,
specified as a list of entries with the translation vector at the end,
even though it (ostensibly) uses column-vectors [1].

Looking at that last CoreGraphics link, it seems like the current
names are an artifact of a row-vector matrix format (in which 'b' *is*
m12) that is transposed for external exposure in the browser, but
retains the same entry indexing. The row- vs column-vector dispute is
an ancient one, but I can't think of anyone that refers to an entry of
a matrix by [column, row].

> Though I would prefer to define it in terms of transformation order (which one
> takes place first), not left-multiplication vs right-multiplication, since the behavior
> of the latter depends on whether we're considering the matrices as
> right-multipliers onto row vectors or left-multipliers onto column vectors.

This would definitely be ideal, but transformation creation would also
have to be agnostic for it to work. For 2d, transform(a,b,c,d,e,f)
would work well, and is conveniently the naming used by SVG and CSS 2d
transformations. 3d is harder (and less pertinent here), but I'm a fan
of the 'm1' through 'm16' approach, where those values match the order
used in the transform(...) method.

Also see [1] for some (nearly 20 years old) insight into the confusion
these distinctions can cause. Matrix-notation independence for the
win.

[1] http://steve.hollasch.net/cgindex/math/matrix/column-vec.html



More information about the whatwg mailing list