Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: spatial manipulation matrices #1797

Open
soypat opened this issue May 8, 2022 · 8 comments
Open

proposal: spatial manipulation matrices #1797

soypat opened this issue May 8, 2022 · 8 comments

Comments

@soypat
Copy link
Contributor

soypat commented May 8, 2022

Background

Most (if not all) 3D renderers use spatial transformation matrices. For example, here is the documentation of three.js' Matrix4 type which allows for rotations and translations. Similarly, the corresponding matrix for 2D rotation+translation is a 3x3 matrix (three.js link).

Today there is a 3x3 spatial matrix implementation, the r3.Mat type. This type was thought of for use as a rotation tensor, which describes a complete rotation in 3D space, without translation. This is useful for studying rigid-body dynamics of arbitrarily oriented frames in space, such as is the case with planes and drones. However, its functionality is a far cry from a Matrix4 type, which brings rotation and translation into the equation, something which is crucial for 3D rendering.

These matrices have been implemented in sdfx by Jason Harris in Go: https://github.com/soypat/sdf/blob/main/matrix.go. The "important" methods this matrix should have for the correct functioning of the sdf package are as follows for the Matrix4 type:

  • func NewMatrix4(data []float64) Matrix4
  • func (a Matrix4) MulPosition(b r3.Vec) r3.Vec Worth mentioning three.js calls this "applying". Maybe that is a more apt name ApplyPosition
  • func (a Matrix4) Inverse() Matrix4
  • func (a Matrix4) Det() float64
  • func (a Matrix4) MulBox(box r3.Box) r3.Box rotates/translates a 2d bounding box and resizes for axis-alignment. Maybe rename to ApplyBox
  • func (a Matrix4) Scale(k float64) Matrix4 multiplies all values by k and returns new matrix

Matrix3 should have corresponding methods of the same name too for r2 types.

Proposal

Add a similar-to Matrix3 and Matrix4 types somewhere in the spatial package. My guess of the package name where it belongs is at best as good as yours (r2, mat2, mat?) .

Will Matrix3 use r3.Mat, or should these methods be implemented for r3.Mat? It seems as though the latter would mix too many responsibilities in one type i.e. a matrix that can be used for 3D rotations or 2D spatial transformations.

Another idea is to add these types as r3.Transform (Matrix4). The possible downside to this is that if r2.Transform wants to use the underlying r3.Mat type then there could be a future import cycle waiting to happen (I figure it would be more natural for r3 to import r2).

  • Could breaking the r3.Mat type be entertained to figure out a elegant way to solve this?
  • Or maybe the r3.Mat logic could be moved to an internal package that can be shared between r2 and r3.
  • Or just implement the above methods without typical matrix methods for now.

The code shown above uses "direct" types- they should probably be pointers to the Matrix though (?).

I am in no hurry to implement these so this proposal can serve as a floor to discuss the idea.

Zero value

The zero value of a Transformation matrix should be the Identity matrix, ideally.

Potential impact of proposal

3D renderer libraries are happy with gonum!

@soypat
Copy link
Contributor Author

soypat commented May 8, 2022

I went out of my way to create a minimal quasi-working example of what the Matrix3 type could look like: https://github.com/soypat/sdf/blob/main/internal/d2/transform.go

@kortschak
Copy link
Member

I'm OK with this in principle. It would be worth looking into how gioui does tranforms since they have worked on efficient implementations that could be generalised to the cases here.

@soypat
Copy link
Contributor Author

soypat commented May 15, 2022

gioui.org's affine transformation matrix.
https://git.sr.ht/~eliasnaur/gio/tree/0e2e02a66237/f32/affine.go#L12 (2D)
Here's my take on it:
https://github.com/soypat/go-play3d/blob/main/transform.go (3D)

Edit: although after looking at gioui's methods, I'm bound to change quite a few things.

@soypat
Copy link
Contributor Author

soypat commented May 29, 2022

@kortschak Ran into a conundrum adding shear/skew. I have the following signature for Compose and AddShear

func ComposeAffine(translate, scale Vec, q Rotation, sxy, sxz, syx, syz, szx, szy float64) Affine

func (a Affine) AddShear(origin Vec, sxy, sxz, syx, syz, szx, szy float64) Affine

Feel like the verbosity with all these floats might be too much to comfortably use these functions.

// Hard to decipher which number corresponds to which shear plane/component.
a := ComposeAffine(r3.Vec{}, r3.Vec{}, r3.Rotation{}, 1, .2, 0, 0, math.Pi, 6) 

This could be solved by adding a Shear type that has Sxx, Sxy... fields. This Shear type could also implement Transformer.

@soypat
Copy link
Contributor Author

soypat commented May 29, 2022

Adding XX, YY, ZZ fields to Shear type would then allow scaling functionality. Maybe this type could be called Warp? This would further simplify ComposeAffine:

func ComposeAffine(translate Vec, scaleShear Warp, q Rotation) Affine

identityAffine := ComposeAffine(Vec{}, Warp{}, Rotation{}) 

The cool thing about this is that now the zero values of all arguments would yield the identity transform!

@soypat
Copy link
Contributor Author

soypat commented May 29, 2022

Another question: What does shear XY do?
Does it:

  1. Shear on plane X, in direction Y. Similar to how tensors are represented in solid and fluid mechanics. In this example a vector Vec{Z:z} -> Vec{Z:z, Y: z*w.XY}. I'm biased towards this representation since I've studied these fields in depth.
  2. Shear position X in direction Y. This is how three.js does things. Vec{X:x} -> Vec{X:x, Y: x*w.XY}. Might be more intuitive to most users.

@quillaja
Copy link

I'm not sure how much this applies to the conversation, but as far as math libraries for 2D and 3D rendering (ie opengl), I would suggest looking at, copying, or just using the excellent mathgl library instead of reinventing it.

@soypat
Copy link
Contributor Author

soypat commented Jun 19, 2022

All respect to mathgl library, it's got nearly everything I need and I've used it several times in the past. However, it's not quite my cup of tea with respect to what a good API should look like. I find gonum has hit the nail on the head by sectioning vector operations as package level functions resulting in readable vector operations, something that has burned me in the past with the sdfx library as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants