At first glance, it may seem overly complicated and some attributes seem unnecessary, but there is a reason why we did it this way.
We basically support two models, the simple model and the advanced model. In both models, the combination of attributes ultimately results in a single affine transformation, which is a combination of translations (i.e. offsets), rotations, scaling, reflections and shears.
In the simple model, the following attributes apply: x, y, rotation, scale, shear and offset (transform must be null). And in the advanced model only these attributes apply: x, y and transform (see Transform class.) When a transform is specified (i.e. not null), we switch to the advanced model and ignore rotation, scale, shear and offset. The simple model should suffice in most cases. Only when you want to manipulate the transform directly would you use the advanced model.
In the simple model, we apply the attributes in the following order:
- x, y - moves the node within the parent context
- rotation - rotates the node around its (x,y) position
- scale - scales the node about the (x,y) position. Different scale factors can be provided for the X and Y directions. Negative scale factors can be used to mirror (a.k.a reflect) a node.
- shear - shears the node about the (x,y) position. Different shear factors can be provided for the X and Y directions.
- offset - moves the node by a second offset (dx,dy)
In the advanced mode, we first apply the (x,y) offset, followed by the transform.
Note that strictly speaking we didn't need the x and y attributes in the advanced model, and could have used just a transform, but we found it easier to separate out the x and y attributes for certain use cases. We also didn't really need the offset in the simple model, but it allows us to easily rotate/scale or shear the node about an arbitrary point.