Tweaking TeraMath

msteiger

Active Member
Contributor
World
Architecture
Logistics
As the name inclines, this is about creating a math library for Terasology.

You can find it here:
https://github.com/MovingBlocks/TeraMath

Jenkins: http://jenkins.movingblocks.net/job/TeraMath/

Artifactory: http://artifactory.movingblocks.net/artifactory/simple/terasology-snapshot-local/org/terasology/TeraMath/0.1.0-SNAPSHOT

Gradle:
Code:
compile(group: 'org.terasology', name: 'TeraMath', version: '+')
The library currently contains most of Vecmath's functionality. Please check it out and let us know what you think of it.

There were already several threads in the past discussing a dedicated math library:

http://forum.movingblocks.net/threads/teramath.205/
http://forum.movingblocks.net/threads/proposal-moving-math-stuff-to-an-independent-repository.925/

Please share your thoughts, requirements and of course: code!
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
The "nice" thing about math libraries is everyone uses them and has an opinion on them, so they can be quite the target for bike shedding and argument. It might be good to jot down a few principles, so that they can be pointed to when contention arises.

My initial comments are:
- I prefer the name Vector to Point. That is largely bikeshedding, I know. It is slightly more accurate in a maths sense though, since you cannot add and subtract "points" (or if you did, the result would be a vector) - so vector is the more correct name for something that has those methods. Even more so when it comes to dot and cross products. In a mathematically rigorous sense you would have both classes, with rules regarding how they are used, but that is inappropriate for a games math library.
- ipol() would generally be called lerp(), as it is a linear interpolation (not to confuse it with slerp - spherical interpolation, or any of the non-linear interpolation methods)
- I assume you are experimenting with different ways of structuring with constant points and so forth, so I won't comment on that right now (you know my thoughts from the earlier thread).
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
I think that Point and Vector could both have a right to exist, but yes, this is debatable.

About the constant variants: I tried to respect your and my ideas and this is what came out of it.
There's a read-only base class you can use as a parameter and be sure that it won't be changed (unless it is cast to the mutable version).
If you need to return a point/vector you can explicitly use the constant version (for members) or mutable points if you don't care (i.e. local variables).
This should cover all relevant use cases..

One interopability question remains: should new ConstantVector(1,1).equals(new MutableVector(1,1)) be true or false? I'd go for false, but this would lead to
myVector(1,1).equals(otherVector(1,1)) evaluate to false

There should also be converters to and from AWT and vecmath I guess..
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
If you need to return a point/vector you can explicitly use the constant version (for members) or mutable points if you don't care (i.e. local variables).
I would actually just use the base class for returning members. Again, this is sacrificing perfect safety for performance, but this isn't an unusual pattern in Java.

I think you can save yourself some work and maintenance burden by dropping all the methods from Point2cd other than those defined in Tuple2d. We should be encouraging the use of the mutable type for calculations anyway (performance) - they'ld be better off just converting a Point2cd to a Point2md and working from there, since every action will be creating new objects anyway. This would then suggest that Point2cd would only be used for instantiation, and the result always stored in a Point2d.

Here are some use cases around how I would see the types being used, which may be useful to discuss:

1. Constants - Would instantiate as Point2cd and store as Point2d. Guarantees immutability.
Code:
    public static final Point2d ONE = new Point2cd(1, 1, 1);
 
    public void doingSomeCalculation() {
        Point2md a = ...;
        ...
        a.add(ONE);
    }
2. Field - return as Point2d. While this doesn't guarantee immutability, it doesn't expose the mutability so the consumer is breaking contract by casting and changing it. If you really wanted to be sure and performance wasn't a consideration, you could copy it into a new Point2cd, but you would still return it as a Point2d.
Code:
    private Point2md value = new Point2md(1, 2, 3);
 
    public void setValue(Point2d newVal) {
        value.set(newVal);
    }
 
    public Point2d getValue() {
        return value;
    }
3. Doing a calculation (note, favor returning Point2md because it is a new value anyway, and the user can make use of it however they like):
Code:
public Point2md calculate(Point2d v1, Point2d v2, ...) {
    Point2md result = new Point2md(v1).add(v2).mult(v3);
    return result;
}
or a more general pattern for performance, allowing you to populate an existing Point2md:
Code:
public Point2md calculate(Point2d v1, Point2d v2, ...) {
    return calculateInto(new Point2md(), v1, v2, ...);
}
 
public Point2md calculateInto(Point2md result, Point2d v1, Point2d v2, ...) {
    return result.set(v1).add(v2).mult(v3);
}
Basically, I don't think Point2cd would come up to often, and even when it did you would probably just instantiate it and store as a Point2d. So you might as well save yourself some effort and keep it simple.

Some really picky thoughts around names, sorry:
  • Perhaps the mutable implementation could be called Vector2d. It is the main thing that will be instantiated and used, so it should have the simple, clear name. (It would be nice to avoid alphabet soup names)
  • The interface then would be Point2d perhaps - or Tuple2d?
  • The constant implementation could be Point2dConst/Tuple2dConst/ConstantTuple2d? Basically, I don't think the c or m are particularly clear. The longer name is fine in this case because you wouldn't generally have variables of that type, just instantiate it and store it in a Point2d.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Interesting thoughts - I updated the code base to integrate them.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Oh, other thought:

In addition to the standard getX() and setX(double) methods, possibly:

Code:
void x(double newX);
double x();
Just for really concise usage. Or maybe just the getter version - since the setter aligns nicely with addX/subX/multX/divX(missing?).

I've checked out the library now, so you can look forward to some PRs I guess. :)
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Because creating all these math classes will get a bit repetitive (double and float implementations will be nearly the same, as will short/int/long implementations, and there is sizable crossover between the two) I've started a little investigation into template generation tools. StringTemplate 4 looks like a good pick - the doco is a little sparse but I got something working in a half an hour or so.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Sounds great. I had a similar idea, but wasn't sure if it's worth the effort compared to copy&paste + search&rename. Using a proper template engine is surely more elegant.

A colleague pointed out that having an explicit representation of Point could be beneficial. For example, vecmaths Matrix4d has different transform() implementations for Point3d and Vector3d. The 4th component is 1 for Point and 0 for Vector. This could be easily fixed by using Vector4d, though. Still, I would leave Point for now and add it later if it ever becomes necessary.
  • Goals for version 1.0: Implement/convert all relevant code from vecmath, so that vecmath can be removed from the list of external deps.
  • Goals for version X: Transfer math (geometry + helpers) from TS projects to have a single location of basic functionality (Rect2i, Region3i, etc)
I think a few groups of classes do not need to be ported: AxisAngle, Color, GMatrix/GVector/Point/TexCoord
This leaves Matrix, Vector, Quaternion.
Do you agree with that? I created github issues and a milestone for that.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Cervator did some magic to make TeraMath part of the Jenkins project family. Thank you for that! See the first post for setup details.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
I will start a branch of Terasology shortly to investigate switching to TeraMath. Unfortunately I won't be able to eliminate VecMath completely because TeraBullet uses it.

The one other thing I may investigate is object pools to avoid some object churn where math objects are frequently created.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Any news on that issue? I'm experimenting with rectangle/region at the moment, but this is beyond version 1.0.
I would like to put the code in a separate branch and wait with the merge until 1.0 is finalized (i.e. can replace vecmath).
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
I did start the branch, but got distracted with the vertical chunking sorry. I'll return to this after the chunking is done now. Basically the replacement will be a huge amount of work.

Should be noted that unless someone is willing to work through tera-bullet and replace vecmath in it, Terasology will end up depending on both vecmath and teramath.
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
Refreshing an earlier stray thought: How about the idea of doing something with https://github.com/ReactPhysics/React or another lib, if jBullet is doomed? React is a Java port by the Spout team of a C++ lib. I don't see vecmath in there although it appears to have some of its own internal math. Could probably shuffle around some stuff if needed. Or wrap Bullet ourselves, several options have come up in the past.

Unsure which is less work / better potential
 

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps

msteiger

Active Member
Contributor
World
Architecture
Logistics
Immortius: let me know if I can help with the port of TeraMath. Maybe some replacing pattern can be identified? eclipse also has several sophisticated refactoring tools such as "Encapsulate Field" which converts a member access to a getter or setter call. This feature could be quite useful.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
Will do. Basically I don't even want to begin again until the vertical chunking work is done, since there was a fair bit of math usage involved and I want to avoid merge conflicts.

I probably chose a bad place to start with the replacement last time - Vector4f - since I immediately hit a mess of Vector4f/Color(slick)/Color(awt)/Color4f usages that needed to be sorted out. Sometimes a Vector4f is not a Vector4f.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Progress! I implemented all methods that TS required, but were not present in TeraMath before.
For now, I renamed the packages to javax.vecmath and made all fields public to comply with VecMath.

https://github.com/msteiger/TeraMath/commit/201a20a103486e681ca6f2c77517ba3acde36194

I also made a few changes to the TS code base to comply with the new VecMath-look-alike branch of TeraMath (replace Tuple with Vector)

https://github.com/msteiger/Terasology/tree/teramath

AxisAngle4f is still missing - iirc we decided not to implement it and use Quat4f instead. Still, I got it down to 4 compiler errors.

A major open issue is that TeraBullet is still using VecMath - mixing it in resulted in a big mess for me.
As @Immortius already pointed out, JBullet is no longer maintained and has been superceded by the libgdx bullet wrapper.It uses the libgdx classes:

https://github.com/libgdx/libgdx/wiki/Bullet Wrapper - Using the wrapper#common-classes

I therefore suggest to upgrade TeraBullet to use the libgdx wrapper instead of JBullet first.
 

msteiger

Active Member
Contributor
World
Architecture
Logistics
Even more progress! I added the missing AxisAngle4f from the old Vecmath code base and fixed remaining compiler errors.

Everything that requires Bullet still uses vecmath, but it's completely internalized now. This should also enable us to switch to libgdx bullet in the near future.

I will fire out a PR for TeraMath and Terasology within the next days. There's probably a bit of bug fixing to do until it's back up to speed ...
 
Top