# Affine transforms - matrix algebra

Pavel Safrata pavel.safrata at oracle.com
Wed Aug 15 01:13:17 PDT 2012

```Hi Jim,

On 14.8.2012 20:50, Jim Graham wrote:
> Hi Pavel,
>
> On 8/14/2012 12:41 AM, Pavel Safrata wrote:
>>> Also, in many cases we provide:
>>>
>>> Foo Foo.operate(Foo_operand, Foo_result_container)
>>>
>>> so that code can reuse objects. I think that makes sense here as well.
>>
>> I generally agree but Point3D is immutable so it's not possible here.
>
> D'oh!  You learn something new every day.  My apologies as I'm not
> familiar with the FX API versions of these objects - I tend to work on
> the implementation and we have internal versions of many of these
> objects with different design constraints...

Don't worry, I'm supposed to be familiar with the API and also do find
myself surprised time to time..

>
>>>> public double getAngle(Point3D)
>>>> // Gets angle between the two points when observed from the origin
>>>> // (angle between vectors)
>>>
>>> What about the static variant "getAngle(po, p1, p2)"?
>>
>> Again the immutable point..
>
> Not an issue.  My suggestion was for a getAngle() method that took 3
> input points for calculating the angle "p1 -> po -> p2" and returning
> it.  None of those arguments was for storing the result.
>
> For that matter we could consider "angle(p1, p2)" which returned the
> angle  of one corner of the triangle "this, p1, p2", but the question
> is which of those 3 points should be used as the vertex of the
> triangle for maximum utility and maximum "being able to remember which
> 2 of the 3 points are in the argument list". Barring some obvious and
> easily remembered way of specing that method I'm suggesting a static
> method that requires you to provide all 3 points (see above) and then
> it is easy enough to either specify that the "origin/vertex" point is
> the first one since it is the most important one, or perhaps it should
> be the middle one so that the argument list is in the order that reads
> along the angle?

Oh, got it. I think it is obvious which point is the most important one.
For the static method you propose two different argument orders, whereas
for the dynamic method it would be easy to remember that 'this' is the
most important point. I also slightly dislike the idea of having dynamic
and static methods of the same name in one class, I'd rather see the
static method in some utility class then. Anyway, I like the idea and I
vote for the angle(p1, p2) which would compute angle between the two
given points observed from this point.

>
>>> I think we discussed it before, but do we want "void transform(p1,
>>> presult)"?
>>
>> I don't think we discussed this before, but you know by now, point is
>> immutable :-)
>
> OK, I deserve that... :(
>
>>>> public double[] toArray(MatrixType type)
>>>> // Returns flattened matrix specified by type
>>>> public double[] toArray(MatrixType type, double[] array)
>>>> // Similar to the above, just uses the passed array if it is big
>>>> enough
>>>> public double[] getRow(MatrixType type, int row)
>>>> public double[] getRow(MatrixType type, int row, double[] array)
>>>> public double[] getColumn(MatrixType type, int column)
>>>> public double[] getColumn(MatrixType type, int column, double[] array)
>>>> // Similar to toAarray returning the particular tuples
>>>> // throw IndexOutOfBounds if index out of the range specified by type
>>>
>>> Would there be a "getMatrixType"?
>>
>> The transform has no matrix type. You can request any matrix type from
>> any transform.
>
> A given transform may only have some elements, though, so at any given
> time it may have non-default elements for its 2x3 positions even
> though it might return full 4x4 values if you ask it.  If you are
> wondering how complex an operation that has arbitrary Transforms
> specified will be then you have to assume the worst, that they are all
> 4x4 transforms. The is2D() method would help for optimizing the 2D 2x3
> only case, and that may be the most important case in the end.  This
> could be a more general form of that convenience method, though.  It
> would be important to make sure it is well understood that it is
> returning "the current state of the matrix elements".

The is2D/is3D gives you all of the information. We have only affine
transforms, so we never utilize the full matrix. You can ask for it for
convenience of your computations, but the last row always contains only
0s and the final 1. So if is2D()==true all the significant values are
stored in the 2x3 matrix, otherwise in the 3x4 matrix.

>
>>> I hope someone involved more in FX events can review this.
>>
>> I discussed this with Lubo who is our greatest event expert :-)
>
> Yay...
>
>>>> public void appendRotation(double theta)
>>>> public void appendRotation(double theta, double pivotX, double pivotY)
>>>> public void appendRotation(double theta,
>>>> double pivotX, double pivotY, double pivotZ,
>>>> double axisX, double axisY, double axisZ)
>>>> public void appendRotation(double theta,
>>>> double pivotX, double pivotY, double pivotZ,
>>>> Point3D axis)
>>>> public void appendShear(double shx, double shy)
>>>> public void appendShear(double shx, double shy, double pivotX, double
>>>> pivotY)
>>>
>>> The appendRotation(... Point3D axis) stands out. Should we have
>>> variants for all of the pivotXYZ methods that take a Point2D/3D?
>>
>> Pivot is always represented as three doubles, even with rotation.
>> Point3D can be used only for the axis (which is unique property to
>> rotation) and the reason is that for the orthogonal rotations you can
>> use Rotate's X_AXIS, Y_AXIS and Z_AXIS.
>
> I see.  I am aware of the convenience values for the axis, and that
> makes the Point3D for axis doubly useful, but is that a reason not to
> consider variants that use Point3D for pivot as well?  (One reason I
> can think is that the overloads might run into each other if we aren't
> careful, but if we can find a sensible overload set then it would be
> nice to allow specifying the "pivot point" with an actual point, no?)...

Yes, it sounds useful. I just hope we don't over-complicate the class.

public void appendScale(double sx, double sy, Point2D pivot)
public void appendScale(double sx, double sy, double sz, Point3D pivot)
public void appendRotation(double theta, Point2D pivot)
public void appendRotation(double theta, Point3D pivot, double
axisX, double axisY, double axisZ)
public void appendRotation(double theta, Point3D pivot, Point3D axis)
public void appendShear(double shx, double shy, Poin2D pivot)

and the same set for prepend.

Thanks,
Pavel

>
>             ...jim

```