Affine transforms - matrix algebra

Kirill.Prazdnikov kirill.prazdnikov at oracle.com
Wed Jul 11 16:20:14 PDT 2012


Hi Pavel,

My typical use-case was to transfer matrix data between JavaFX and 
different libraries like 3D or physics.
Use of one array with 12 (16) elements is by far simpler then passing 12 
(16) arguments.
And performance of one getter is better then 16 separate martix elements 
getters.

Thanks
   -Kirill

On 7/12/2012 12:17 AM, Pavel Safrata wrote:
> 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