Affine transforms - matrix algebra
Pavel Safrata
pavel.safrata at oracle.com
Wed Jul 11 13:17:40 PDT 2012
Hi Kirill,
what is the use-case? I suspect that if you want the raw matrix to do
some math with it we are rather missing some operations doing the math
directly.
Thanks,
Pavel
On 11.7.2012 21:35, Kirill.Prazdnikov wrote:
> Hi Pavel,
>
> I`m not sure, but many time times I found the following conversions
> as very useful:
>
> public float [] asArray(float data[12]);
> public Affine(float data[]);
>
> along with 12-arguments ctor.
>
> Thanks
> -Kirill
>
> On 7/11/2012 6:57 PM, Pavel Safrata wrote:
>> Hi Martin,
>> thank you for your input.
>>
>> On 10.7.2012 18:36, Martin Desruisseaux wrote:
>>> Hello Pavel
>>>
>>> Many thanks for taking care of this task!
>>>
>>>
>>> Le 10/07/12 17:00, Pavel Safrata a écrit :
>>>> On Transform class:
>>>> public Transform getConcatenation(Transform transform) //
>>>> multiplication
>>>> public Transform getInverse() throws
>>>> NoninvertibleTransformException // negation
>>>> public Transform copy()
>>> Sound fine to me, while I'm not sure why a 'copy' method instead
>>> than overriding the 'clone()' method?
>>
>> Good point, we can use clone().
>>
>>>
>>>
>>>> Constructors:
>>>> public Affine(Transform transform)
>>>> public Affine(double mxx, double mxy, double mxz, double tx,
>>>> double myx, double myy, double myz, double ty,
>>>> double mzx, double mzy, double mzz, double tz)
>>> Look fine.
>>>
>>>
>>>> Setters of the entire matrix:
>>>> (...snip...)
>>> I don't know for JavaFX, but in my experience with Java2D, I wasn't
>>> using the setter methods often, except 'setToIdentity' and
>>> 'setToTransform'. For example rather than invoking
>>> 'setToTranslation', I strongly push our developers to use
>>> 'translate' (or 'concatWithTranslation' in this proposal) instead.
>>> If a developer really wants the functionality of 'setToTranslation',
>>> he can get it by invoking 'setToIdentity()' followed by
>>> 'concatWithTranslation'. Or yet better, 'setToTransform(...)'
>>> instead than 'setToIdentity' with the coefficients of some previous
>>> state that the user saved.
>>
>> I don't insist on having the setters, anybody wants them?
>>
>>>
>>> The rational is that in many cases, the affine transform is already
>>> initialized to some important value. For example in Java2D,
>>> AffineTransform is initialized to the transform from 'dot' to
>>> whatever units the underlying device uses. When rendering on screen,
>>> this is the identity transform. But when printing, this is something
>>> different that depends on the printer resolution. In GIS
>>> applications, it depends on the zoom level. Other applications may
>>> use magnifier glass over some areas. Because the initial transform
>>> is often (but not always) the identity one, developers with limited
>>> experience with affine transforms often use 'setTranslate' or
>>> 'setScale' in situations where they should really use 'translate' or
>>> 'scale', and do not realize their bug before late in the development
>>> process. For this reason, I would be inclined to discourage every
>>> setter methods except 'setToIdentity()' and 'setToTransform'.
>>> Keeping in mind that it is often easier to fill a hole later than to
>>> fix something broken, I think it would be safer to leave out all
>>> other setter methods for now, and revisit later if experience show
>>> that they are really needed.
>>>
>>
>> While I don't think our Affine class will ever have varying initial
>> values I'm ok with keeping only setToIdentity() and setToTransform()
>> if there is no demand for the other setters right now.
>>
>>>
>>>> Operations on the matrix (modifying it in place):
>>>> (...snip...)
>>> Sound good, minus the unfortunate 'concatWith*' naming :-(.
>>>
>>>
>>>> Instead of "concatWithTranslation" it would be more natural to use
>>>> just "translate" (and similarly for the others), but unfortunately
>>>> these method names are already taken by the static factory methods
>>>> on Transform class. This is unpleasant but we need to be backward
>>>> compatible so we have to introduce different names. We'll be happy
>>>> to hear better naming suggestions than the concatWith* (which is
>>>> pretty descriptive I think but not really nice).
>>> Hard to say... A consistency with "preTranslate" would be nice, but
>>> "postTranslate" doesn't look very nice... What about the following?
>>>
>>> * Rename "preTranslate" as "appendTranslation"
>>> * Rename "concatWithTranslation" as "prependTranslation"
>>>
>>> The "preConcatenate(Tx)" name in Java2D was actually misleading to
>>> some developers, because it works as if points were first
>>> transformed by the original transform, then transformed by 'Tx'.
>>> Maybe "appendTranslation(Tx)" would make clear that the translation
>>> is applied after the original transform. This would also make
>>> operation order clearer. The following code using Java2D API:
>>>
>>> tr.translate(...)
>>> tr.scale(...)
>>> tr.rotate(...)
>>>
>>> must be read from bottom to up: it is as if points were rotated
>>> first, then scaled, then translated. So maybe the above proposition
>>> would make that more obvious:
>>>
>>> tr.prependTranslation(....)
>>> tr.prependScale(...)
>>> tr.prependRotate(...)
>>> tr.appendTranslation(...) // Just for fun.
>>>
>>
>> These names indeed sound way better.
>>
>> If we are going this way, shouldn't we do also this?
>> * Rename 'concatenate' as 'prepend'
>> * Rename 'preConcatenate' as 'append'
>> * Rename 'getConcatenation' as .. well .. 'getPrependage' :-)
>>
>> As a black-box transformation composing this makes sense. As a matrix
>> algebra, it may be also confusing: tA.append(tB) means matrix
>> multiplication 'B x A', does it sound good? Maybe yes, I'm not sure.
>> Anyway, so far the best proposal I think.
>>
>>>
>>>> Would you want static factory methods on Affine (creating Affine
>>>> instances with the simple transforms)?
>>> I don't think it is necessary. I found the static factory methods of
>>> Java2D AffineTransform rarely used.
>>>
>>>> Would it be important to you whether or not matrix changes are
>>>> atomic? If you call one of the methods that modify the entire
>>>> matrix, can be listeners for each member called immediately as the
>>>> members are set, or do they need to be called after all the members
>>>> are updated?
>>> I don't have experience in this area. But naively, it seems to we
>>> that it would be better to be notified only after the full matrix
>>> has been updated...
>>
>> Yes, I would also pick this option. It's just that it would mean
>> writing much more complicated (and a bit less effective) code so the
>> question is whether or not you think it is important.
>>
>> Thanks,
>> Pavel
>>
>>>
>>> Regards,
>>>
>>> Martin
>>>
>>
>>
>
More information about the openjfx-dev
mailing list