JOML-CI / JOML

A Java math library for OpenGL rendering calculations
MIT License
728 stars 104 forks source link

Optimize Vector Multiplication? #337

Closed Speiger closed 1 year ago

Speiger commented 1 year ago

JOML is really optimized, and I really enjoy using it and learning from it.

But i noticed one thing with Matrix Multiplications on vectors. While the Matrix has so many properties, they are actually never used with Vectors. https://github.com/JOML-CI/JOML/blob/8773210868b66e8e0fdb96284bd2124df3d77d59/src/main/java/org/joml/Vector4f.java#L940

Since the Matrix has Properties: Such as "Identity" and "Translation". Why not make use of them: If the matrix is a Identity, do nothing. if the matrix is a translation only then only translate it.

This would reduce operations a LOT:

This would also optimize game engines a lot, minecraft too, since they do a TON of Matrix multiplications on the CPU in the first place, and having a ton of them reduced to identity or translation ONLY. That would be awesome!

Edit: After reading more about the flags. Only Affine is really used, the others don't really indicate what they are.

httpdigest commented 1 year ago

Thanks for suggesting! I've done some benchmarking to assess both the benefits as well as the possible performance degradation (from people actually using generic matrices with unknown properties in which case the multiple if checks checking for the various matrix properties will add unnecessary overhead) and I think the benefits likely outweigh the disadvantages. The next JOML 1.10.6-SNAPSHOT will have this performance optimization in place.

In addition, the "generic" and specific multiplication methods will be exposed on the vector classes such that if people know what they are dealing with, they can directly use the most optimal method available.

Speiger commented 1 year ago

Thank you for the quick answer :) I hope this will make JOML faster.

And that sounds like a good plan.

Side topic. Or more specifically a question. Is it normal that the scale is lost in a matrix when rotation is applied? I noticed this behavior in my matrix implementation and though: Ok maybe i have a bug and lets try JOML, since you have a lot more knowledge, and found out that it also happens here too.

Is that normal?

httpdigest commented 1 year ago

Is it normal that the scale is lost in a matrix when rotation is applied? I noticed this behavior in my matrix implementation.

This is a very weird coincidence, and thanks for mentioning this: JOML actually has a serious bug in this regard. :D When you write the following: new Matrix4f.scale(2).rotateXYZ(angleX, angleY, angleZ, new Matrix4f()); then the matrix indeed only contains the rotation and scaling is reset to 1. The buggy code line is this: https://github.com/JOML-CI/JOML/blob/8773210868b66e8e0fdb96284bd2124df3d77d59/src/main/java/org/joml/Matrix4f.java#L5317 But, of course, this should not be the case, and will be fixed with the next 1.10.6-SNAPSHOT. Thanks for mentioning it! :)

Speiger commented 1 year ago

Eh you missunderstood me. My example was this:

Matrix4f mat = new Matrix4f();
for(int i = 0;i<100;i++) {
   mat.rotateX(22F); //Rotate by 22Degree a few hundred times, that includes quaternions or Eula
}
mat.getScale(new Vector3f()); The scale will be all 0 or in some cases 1 on one specific axis.

And there was no scale applied at all, the scale information will be just lost when you rotate a matrix, in that sense that you can not know the scale itself at any point.

httpdigest commented 1 year ago

rotate methods do not rotate by degrees. All angles in all methods are radians, not degrees. See: https://github.com/JOML-CI/JOML/wiki/Common-Pitfalls#angles-are-always-in-radians And in your specific example, the queried scale (by mat.getScale(...)) will be (1, 1, 1), so correct.

httpdigest commented 1 year ago

If you have a situation that results in Matrix4f.getScale(...) returning unexpected results, it would be good to have a reproducible example. One situation this might happen is accumulated floating point rounding errors that over time ultimately degenerate the matrix element values. You should not really operate solely on a matrix many thousands of times, as rounding errors will accumulate in that case.

Speiger commented 1 year ago

rotate methods do not rotate by degrees. All angles in all methods are radians, not degrees.

This was just a example i came up with on the fly the "bug/rounding errors" test i did was a few days/weeks ago.

And in your specific example, the queried scale (by mat.getScale(...)) will be (1, 1, 1), so correct.

Ok let me test it.

You should not really operate solely on a matrix many thousands of times, as rounding errors will accumulate in that case.

Yeah if the rounding errors are small that is fine. The issue is that they are not that small then you think. What i found out now is that YOMLs rotation logic only loses scale if the Rotation (angle, X, Y, Z) if any non "angle" variable is not exactly 1.

This is the code example:

public void test() {
        Vector3f vector = new Vector3f();
        Matrix4f jom = new Matrix4f();
        for(int i = 0;i<100;i++) {
            jom.rotate((float)Math.toRadians(12), 1F, 0F, 0F).getScale(vector);
            System.out.println("Index="+i+", Scale="+vector);
        }
    }

The output:

ClickMe

``` Index=0, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=1, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=2, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=3, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=4, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=5, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=6, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=7, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=8, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=9, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=10, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=11, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=12, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=13, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=14, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=15, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=16, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=17, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=18, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=19, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=20, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=21, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=22, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=23, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=24, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=25, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=26, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=27, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=28, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=29, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=30, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=31, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=32, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=33, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=34, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=35, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=36, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=37, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=38, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=39, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=40, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=41, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=42, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=43, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=44, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=45, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=46, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=47, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=48, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=49, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=50, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=51, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=52, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=53, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=54, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=55, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=56, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=57, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=58, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=59, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=60, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=61, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=62, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=63, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=64, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=65, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=66, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=67, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=68, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=69, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=70, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=71, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=72, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=73, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=74, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=75, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=76, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=77, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=78, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=79, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=80, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=81, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=82, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=83, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=84, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=85, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=86, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=87, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=88, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=89, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=90, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=91, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=92, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=93, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=94, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=95, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=96, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=97, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=98, Scale=( 1,000E+0 1,000E+0 1,000E+0) Index=99, Scale=( 1,000E+0 1,000E+0 1,000E+0) ```

Now this is the output if the code looks like this. (Changed 1F to 0.1F)

public void test() {
        Vector3f vector = new Vector3f();
        Matrix4f jom = new Matrix4f();
        for(int i = 0;i<100;i++) {
            jom.rotate((float)Math.toRadians(12), 0.1F, 0F, 0F).getScale(vector);
            System.out.println("Index="+i+", Scale="+vector);
        }
    }

The output!

ClickMe

``` Index=0, Scale=( 9,784E-1 9,784E-1 9,784E-1) Index=1, Scale=( 9,572E-1 9,572E-1 9,572E-1) Index=2, Scale=( 9,365E-1 9,365E-1 9,365E-1) Index=3, Scale=( 9,162E-1 9,162E-1 9,162E-1) Index=4, Scale=( 8,964E-1 8,964E-1 8,964E-1) Index=5, Scale=( 8,770E-1 8,770E-1 8,770E-1) Index=6, Scale=( 8,580E-1 8,581E-1 8,581E-1) Index=7, Scale=( 8,395E-1 8,395E-1 8,395E-1) Index=8, Scale=( 8,213E-1 8,213E-1 8,213E-1) Index=9, Scale=( 8,036E-1 8,036E-1 8,036E-1) Index=10, Scale=( 7,862E-1 7,862E-1 7,862E-1) Index=11, Scale=( 7,692E-1 7,692E-1 7,692E-1) Index=12, Scale=( 7,525E-1 7,525E-1 7,525E-1) Index=13, Scale=( 7,362E-1 7,363E-1 7,363E-1) Index=14, Scale=( 7,203E-1 7,203E-1 7,203E-1) Index=15, Scale=( 7,047E-1 7,048E-1 7,048E-1) Index=16, Scale=( 6,895E-1 6,895E-1 6,895E-1) Index=17, Scale=( 6,746E-1 6,746E-1 6,746E-1) Index=18, Scale=( 6,600E-1 6,600E-1 6,600E-1) Index=19, Scale=( 6,457E-1 6,457E-1 6,457E-1) Index=20, Scale=( 6,317E-1 6,318E-1 6,318E-1) Index=21, Scale=( 6,181E-1 6,181E-1 6,181E-1) Index=22, Scale=( 6,047E-1 6,047E-1 6,047E-1) Index=23, Scale=( 5,916E-1 5,916E-1 5,916E-1) Index=24, Scale=( 5,788E-1 5,788E-1 5,788E-1) Index=25, Scale=( 5,663E-1 5,663E-1 5,663E-1) Index=26, Scale=( 5,540E-1 5,541E-1 5,541E-1) Index=27, Scale=( 5,421E-1 5,421E-1 5,421E-1) Index=28, Scale=( 5,303E-1 5,304E-1 5,304E-1) Index=29, Scale=( 5,189E-1 5,189E-1 5,189E-1) Index=30, Scale=( 5,076E-1 5,077E-1 5,077E-1) Index=31, Scale=( 4,966E-1 4,967E-1 4,967E-1) Index=32, Scale=( 4,859E-1 4,859E-1 4,859E-1) Index=33, Scale=( 4,754E-1 4,754E-1 4,754E-1) Index=34, Scale=( 4,651E-1 4,651E-1 4,651E-1) Index=35, Scale=( 4,550E-1 4,551E-1 4,551E-1) Index=36, Scale=( 4,452E-1 4,452E-1 4,452E-1) Index=37, Scale=( 4,356E-1 4,356E-1 4,356E-1) Index=38, Scale=( 4,261E-1 4,262E-1 4,262E-1) Index=39, Scale=( 4,169E-1 4,170E-1 4,170E-1) Index=40, Scale=( 4,079E-1 4,079E-1 4,079E-1) Index=41, Scale=( 3,991E-1 3,991E-1 3,991E-1) Index=42, Scale=( 3,904E-1 3,905E-1 3,905E-1) Index=43, Scale=( 3,820E-1 3,820E-1 3,820E-1) Index=44, Scale=( 3,737E-1 3,738E-1 3,738E-1) Index=45, Scale=( 3,656E-1 3,657E-1 3,657E-1) Index=46, Scale=( 3,577E-1 3,578E-1 3,578E-1) Index=47, Scale=( 3,500E-1 3,500E-1 3,500E-1) Index=48, Scale=( 3,424E-1 3,425E-1 3,425E-1) Index=49, Scale=( 3,350E-1 3,351E-1 3,351E-1) Index=50, Scale=( 3,278E-1 3,278E-1 3,278E-1) Index=51, Scale=( 3,207E-1 3,207E-1 3,207E-1) Index=52, Scale=( 3,137E-1 3,138E-1 3,138E-1) Index=53, Scale=( 3,070E-1 3,070E-1 3,070E-1) Index=54, Scale=( 3,003E-1 3,004E-1 3,004E-1) Index=55, Scale=( 2,938E-1 2,939E-1 2,939E-1) Index=56, Scale=( 2,875E-1 2,875E-1 2,875E-1) Index=57, Scale=( 2,812E-1 2,813E-1 2,813E-1) Index=58, Scale=( 2,752E-1 2,752E-1 2,752E-1) Index=59, Scale=( 2,692E-1 2,692E-1 2,692E-1) Index=60, Scale=( 2,634E-1 2,634E-1 2,634E-1) Index=61, Scale=( 2,577E-1 2,577E-1 2,577E-1) Index=62, Scale=( 2,521E-1 2,521E-1 2,521E-1) Index=63, Scale=( 2,467E-1 2,467E-1 2,467E-1) Index=64, Scale=( 2,413E-1 2,414E-1 2,414E-1) Index=65, Scale=( 2,361E-1 2,361E-1 2,361E-1) Index=66, Scale=( 2,310E-1 2,310E-1 2,310E-1) Index=67, Scale=( 2,260E-1 2,260E-1 2,260E-1) Index=68, Scale=( 2,211E-1 2,211E-1 2,211E-1) Index=69, Scale=( 2,163E-1 2,164E-1 2,164E-1) Index=70, Scale=( 2,116E-1 2,117E-1 2,117E-1) Index=71, Scale=( 2,071E-1 2,071E-1 2,071E-1) Index=72, Scale=( 2,026E-1 2,026E-1 2,026E-1) Index=73, Scale=( 1,982E-1 1,982E-1 1,982E-1) Index=74, Scale=( 1,939E-1 1,939E-1 1,939E-1) Index=75, Scale=( 1,897E-1 1,898E-1 1,898E-1) Index=76, Scale=( 1,856E-1 1,856E-1 1,856E-1) Index=77, Scale=( 1,816E-1 1,816E-1 1,816E-1) Index=78, Scale=( 1,777E-1 1,777E-1 1,777E-1) Index=79, Scale=( 1,738E-1 1,739E-1 1,739E-1) Index=80, Scale=( 1,701E-1 1,701E-1 1,701E-1) Index=81, Scale=( 1,664E-1 1,664E-1 1,664E-1) Index=82, Scale=( 1,628E-1 1,628E-1 1,628E-1) Index=83, Scale=( 1,593E-1 1,593E-1 1,593E-1) Index=84, Scale=( 1,558E-1 1,559E-1 1,559E-1) Index=85, Scale=( 1,524E-1 1,525E-1 1,525E-1) Index=86, Scale=( 1,491E-1 1,492E-1 1,492E-1) Index=87, Scale=( 1,459E-1 1,460E-1 1,460E-1) Index=88, Scale=( 1,428E-1 1,428E-1 1,428E-1) Index=89, Scale=( 1,397E-1 1,397E-1 1,397E-1) Index=90, Scale=( 1,367E-1 1,367E-1 1,367E-1) Index=91, Scale=( 1,337E-1 1,337E-1 1,337E-1) Index=92, Scale=( 1,308E-1 1,308E-1 1,308E-1) Index=93, Scale=( 1,280E-1 1,280E-1 1,280E-1) Index=94, Scale=( 1,252E-1 1,252E-1 1,252E-1) Index=95, Scale=( 1,225E-1 1,225E-1 1,225E-1) Index=96, Scale=( 1,198E-1 1,199E-1 1,199E-1) Index=97, Scale=( 1,173E-1 1,173E-1 1,173E-1) Index=98, Scale=( 1,147E-1 1,147E-1 1,147E-1) Index=99, Scale=( 1,122E-1 1,123E-1 1,123E-1) ```

As you can see the moment you don't provide 1F to the rotation axis it will just lose all scale. UNLESS i misunderstood what XYZ do. I always thought XYZ said How much you wanted to rotate on each axis. (rotate((float)Math.toRadians(36), 0.9F, 0.1F, 0.5F); In other words 90% X rotation, 10% Y rotation, 50% Z Rotation)

httpdigest commented 1 year ago

Yes, this is expected, by design and thoroughly documented as JavaDoc on the respective methods:

The axis described by the three components needs to be a unit vector.

The axis vector in a rotate() method must be normalized/unit. So, you cannot rotate using the axis (0.1f, 0, 0) as that vector is not unit.

I always thought XYZ said How much you wanted to rotate on each axis.

No, the second, third and fourth parameter of https://javadoc.io/doc/org.joml/joml/latest/org/joml/Matrix4f.html#rotate(float,float,float,float) define the axis of rotation. Not the Euler angles around X, Y, and Z.

If you want to rotate using Euler angles there are the methods rotateX, rotateY, rotateZ and also rotateXYZ, rotateYXZ and rotateZYX.

Speiger commented 1 year ago

So then why allow floats in the first place instead of boolean flags/enum?

Also sorry if i ask to much. I am just trying to understand this ^^

httpdigest commented 1 year ago

So then why allow floats in the first place instead of boolean flags?

You can specify the axis of the rotation using floats (or a 3d vector using an overload of the rotate() method that takes the axis as a vector). Obviously, you can use other axes other than the primary axes (1, 0, 0), (0, 1, 0) and (0, 0, 1) (for example (0.7071f, 0.7071f, 0)). It's just that the axis needs to be unit/normalized.

Speiger commented 1 year ago

Ok, so anything but the primary axis will mess with the scale. Since when i tried with: 0.1, 0.5, 0.4 or 0.5, 0.5, 0 it would break the scale too.

httpdigest commented 1 year ago

Ok, so anything but the primary axis will mess with the scale. Since when i tried with: 0.1, 0.5, 0.4 or 0.5, 0.5, 0 it would break the scale too.

As I said above: The rotation axis needs to be unit (normalized - have a length of one). Any axis is fine, so long as its length is 1.

If you use a Vector3f to represent the axis, you can invoke .normalize() to ensure this.

Since when i tried with: 0.1, 0.5, 0.4 or 0.5, 0.5, 0 it would break the scale too.

Yes, since none of these two axes have a length of 1.

Speiger commented 1 year ago

If you use a Vector3f to represent the axis, you can invoke .normalize() to ensure this.

Ok yeah i feel really dumb..... Just tried that and it worked.

Found also out that i need to fix the rounding issues in "MY implementation" since yours is still a lot more accurate but that is something i can work with :)

Anyways, thank you for this I think algebra class? It gave me a greater understanding of stuff i didn't really understand before. (Clearly)

Again thank you, and sorry for the wasted time i caused ^^"

httpdigest commented 1 year ago

Sure, no problem. All fine. :)

httpdigest commented 1 year ago

One might now ask, why rotate(angle, <axis>) has this restriction of needing a normalized/unit axis in the first place, singe e.g. the old-school glRotatef(angle, <axis>) OpenGL method did not (and probably other vector libraries also do not). The reason here is to avoid the otherwise necessary Math.sqrt() and division operation in order to normalize the axis inside of JOML's method and have the client/user in control of normalizing the axis, since when the user comes with a normalized/unit axis in the first place, there is no need to do the renormalization in the JOML method (again).

So, it was a consideration between performance vs. least-surprise/astonishment in API design, in the favor of performance. This problem could probably have been solved better by providing two different methods (one which always does the normalization and one which expects the axis to be normalized in the first place).

Speiger commented 1 year ago

Ok, i was until now under the assumption this was the same in all implementations out there. Thanks for the clarification.

Speiger commented 1 year ago

@httpdigest you ran into a small pitfall yourself right now. The Translation Property can not exist without the Affine Property the way you have written the "GuesstimateProperties" function. It checks first if the translation is not present, and after that it checks if the rotation/scale was removed if that was the case then translation flag is being set.

A fix for that would be: Move the translation & identity check out of the affine check. After that move the identity check out of the translation check and use the set properties to check if affine & translation were set and then check for the rest of what identity requires.

(Yeah i have done this optimization already once)

httpdigest commented 1 year ago

I don't know what you mean.

The Translation Property can not exist without the Affine Property the way you have written the "GuesstimateProperties" function.

If by "GuesstimateProperties" you mean the determineProperties() method in the matrix classes, then, it is true that whenever a matrix only contains a translation, then it follows that it is of course also an affine transformation.

Therefore, we first check for affinity (because that has the least restrictions). After determining affinity, we can then proceed to check if it is only a translation (more restrictions) or even identity (the most amount of restrictions). I am not sure what's wrong with that.

Speiger commented 1 year ago

I was about to write a bunch of things. And then i double checked and i am dumb... Sorry... (I got your m3x and mx3 mixed up)

httpdigest commented 1 year ago

Alright, no problem. Please keep checking for possible bugs/issues. I really appreciate your efforts!