deadsy / sdfx

A simple CAD package using signed distance functions
MIT License
518 stars 52 forks source link

Initialize a 4x4 matrix i.e. `sdf.M44` #69

Closed Megidd closed 1 year ago

Megidd commented 1 year ago

Problem

There is no public method to declare a new instance of sdf.M44 with the initialization of its values.

The fields of the sdf.M44 are all private:

// M44 is a 4x4 matrix.
type M44 struct {
    x00, x01, x02, x03 float64
    x10, x11, x12, x13 float64
    x20, x21, x22, x23 float64
    x30, x31, x32, x33 float64
}

Why

The reason that I'm running into this problem is that I need to create a Quaternion type:


import (
    "math"

    "github.com/deadsy/sdfx/sdf"
    v3 "github.com/deadsy/sdfx/vec/v3"
)

// Quaternion represents a quaternion.
type Quaternion struct {
    W, X, Y, Z float64
}

// FromVectors creates a quaternion representing the rotation from vector a to vector b.
func FromVectors(a, b v3.Vec) Quaternion {
    v := a.Cross(b)
    w := math.Sqrt(a.Length2()*b.Length2()) + a.Dot(b)
    q := Quaternion{W: w, X: v.X, Y: v.Y, Z: v.Z}
    return q.Normalize()
}

// Normalize normalizes the quaternion.
func (q Quaternion) Normalize() Quaternion {
    length := math.Sqrt(q.W*q.W + q.X*q.X + q.Y*q.Y + q.Z*q.Z)
    return Quaternion{W: q.W / length, X: q.X / length, Y: q.Y / length, Z: q.Z / length}
}

// ToMatrix converts the quaternion to a 4x4 matrix.
func (q Quaternion) ToMatrix() sdf.M44 {
    xx := q.X * q.X
    yy := q.Y * q.Y
    zz := q.Z * q.Z
    xy := q.X * q.Y
    xz := q.X * q.Z
    yz := q.Y * q.Z
    wx := q.W * q.X
    wy := q.W * q.Y
    wz := q.W * q.Z

    return sdf.M44{
        1 - 2*(yy+zz), 2 * (xy - wz), 2 * (xz + wy), 0,
        2 * (xy + wz), 1 - 2*(xx+zz), 2 * (yz - wx), 0,
        2 * (xz - wy), 2 * (yz + wx), 1 - 2*(xx+yy), 0,
        0, 0, 0, 1,
    }
}

But, the last statement cannot be done, I mean:

    return sdf.M44{
        1 - 2*(yy+zz), 2 * (xy - wz), 2 * (xz + wy), 0,
        2 * (xy + wz), 1 - 2*(xx+zz), 2 * (yz - wx), 0,
        2 * (xz - wy), 2 * (yz + wx), 1 - 2*(xx+yy), 0,
        0, 0, 0, 1,
    }

There is no way to initialize a sdf.M44, as far as I understand. Am I right? :roll_eyes:

deadsy commented 1 year ago

What about just making "Quaternion" an object within the sdf package itself? ie- it would have access to private members within M44.

A couple of other points...

deadsy commented 1 year ago

btw - looking at M22/33/44 references I think the argument could be made to split off matrix operations into their own package. Maybe Quaternions could join them there.

Megidd commented 1 year ago

What about just making "Quaternion" an object within the sdf package itself?

... Maybe Quaternions could join them there.

Eventually, I ended up using the Quaternion provided by this Go pkg rather than my own custom Quaternion logic:

https://github.com/go-gl/mathgl

https://github.com/go-gl/mathgl/blob/e426c0894fa41bc41ac04704eef3ac4011b19ecf/mgl32/quat.go#L32

Data types need to be converted of course :slightly_smiling_face:

Megidd commented 1 year ago
  • Doing m[i][j] = k is probably nicer than doing m.Set(i,j,k)

btw - looking at M22/33/44 references I think the argument could be made to split off matrix operations into their own package...

I guess to do the above points, heavy modifications are needed. Probably affecting many other things. Maybe a heavy restructuring needs rigorous testing. A bit risky maybe :thinking:

  • What about M22 and M33?

I'm going to modify I modified the PR so that the same Get and Set methods are provided for M22 and M33 too :heavy_check_mark: Just a simple way to have access to matrix values for now.

deadsy commented 1 year ago

So are you doing 16 set's to initialise the M44 for your quaternion stuff? Not very efficient... What about:

func NewM44(x []float64) M44

which initialises based on up to 16 values out of x

E.g.

m := sdf.NewM33([]float64{ 0,1,2, 3,4,5, 6,7,8, })

deadsy commented 1 year ago

I'd be curious to benchmark differences between type built with arrays vs type built with named elements.

E.g.

M44 as [16]float64 M44 as struct{x00,x01,x02..... float64}

I guess they should be the same.

The former representation makes any sort of indexed access very simple. The latter representations is good for 2d/3d vectors where you can do v.X, v.Y, v.Z

But for a matrix maybe I should convert the representation to a x[i] form internally.

Megidd commented 1 year ago

So are you doing 16 set's to initialise the M44 for your quaternion stuff? Not very efficient... What about:

func NewM44(x []float64) M44

which initialises based on up to 16 values out of x

E.g.

m := sdf.NewM33([]float64{ 0,1,2, 3,4,5, 6,7,8, })

Makes sense. I'm going to modify I modified the PR accordingly :+1:

Megidd commented 1 year ago

I'd be curious to benchmark differences between type built with arrays vs type built with named elements.

E.g.

M44 as [16]float64 M44 as struct{x00,x01,x02..... float64}

I guess they should be the same.

The former representation makes any sort of indexed access very simple. The latter representations is good for 2d/3d vectors where you can do v.X, v.Y, v.Z

But for a matrix maybe I should convert the representation to a x[i] form internally.

https://poe.com/s/GArFor42nHhIhRKhvBVb :robot:

deadsy commented 1 year ago

Funny. TIL: ChatGPTs opinion is worth about as much as the opinion of any rando on the internet, in fact it's probably an amalgam of the opinion of said randos.