Add some other ideas to geom.

This commit is contained in:
Josh Deprez 2021-12-10 15:34:02 +11:00
parent 010c0a662c
commit 41cffbec5b
2 changed files with 214 additions and 0 deletions

105
geom/intfloat.go Normal file
View file

@ -0,0 +1,105 @@
/*
Copyright 2021 Josh Deprez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package geom
import (
"fmt"
"math"
)
// IntFloat represents a number as an integer part plus a fractional part.
// This can represent reals in the int range with decent precision.
type IntFloat struct {
I int
F float64
}
// ToIntFloat converts a float64 directly into an IntFloat.
func ToIntFloat(f float64) IntFloat {
return IntFloat{I: 0, F: f}.Canon()
}
// Canon returns a value equal to x, in canonical form (0 ≤ F < 1).
// Each possible value only has one canonical form.
func (x IntFloat) Canon() IntFloat {
i, f := math.Modf(x.F)
if f < 0 {
i--
f = 1 + f
}
return IntFloat{I: x.I + int(i), F: f}
}
// Float converts the value into a float64.
func (x IntFloat) Float() float64 {
return float64(x.I) + x.F
}
func (x IntFloat) String() string {
return fmt.Sprintf("%d + %f", x.I, x.F)
}
// Lt reports x < y. x and y must be in canonical form for the
// comparison to be meaningful.
func (x IntFloat) Lt(y IntFloat) bool {
if x.I == y.I {
return x.F < y.F
}
return x.I < y.I
}
// Gt reports x > y. x and y must be in canonical form for the
// comparison to be meaningful.
func (x IntFloat) Gt(y IntFloat) bool {
if x.I == y.I {
return x.F > y.F
}
return x.I > y.I
}
// Add returns x+y (not canonicalised).
func (x IntFloat) Add(y IntFloat) IntFloat {
return IntFloat{I: x.I + y.I, F: x.F + y.F}
}
// Neg returns -x (not canonicalised).
func (x IntFloat) Neg() IntFloat {
return IntFloat{I: -x.I, F: -x.F}
}
// Sub returns x-y (not canonicalised).
func (x IntFloat) Sub(y IntFloat) IntFloat {
return IntFloat{I: x.I - y.I, F: x.F - y.F}
}
// Mul returns x*y, canonicalised.
func (x IntFloat) Mul(y IntFloat) IntFloat {
return IntFloat{
I: x.I * y.I,
F: float64(x.I)*y.F + x.F*float64(y.I) + x.F*y.F,
}.Canon()
}
// Inv returns 1/x, canonicalised.
func (x IntFloat) Inv() IntFloat {
return ToIntFloat(1 / x.Float())
}
// Div returns x/y, canonicalised.
func (x IntFloat) Div(y IntFloat) IntFloat {
return ToIntFloat(x.Float() / y.Float())
}

View file

@ -181,3 +181,112 @@ func (a RatMatrix3) Concat(b RatMatrix3) RatMatrix3 {
},
}
}
// Matrix3x4 implements a 3x4 matrix with floating-point values.
type Matrix3x4 [3][4]float64
// IdentityMatrix3x4 is the identity matrix for Matrix3x4.
var IdentityMatrix3x4 = Matrix3x4{
0: [4]float64{0: 1},
1: [4]float64{1: 1},
2: [4]float64{2: 1},
}
// Apply applies the matrix to the vector v.
func (a Matrix3x4) Apply(v Float3) Float3 {
return Float3{
X: a[0][0]*v.X + a[0][1]*v.Y + a[0][2]*v.Z + a[0][3],
Y: a[1][0]*v.X + a[1][1]*v.Y + a[1][2]*v.Z + a[1][3],
Z: a[2][0]*v.X + a[2][1]*v.Y + a[2][2]*v.Z + a[2][3],
}
}
// Mul multiplies the whole matrix by a scalar.
func (a Matrix3x4) Mul(r float64) Matrix3x4 {
a[0][0] *= r
a[0][1] *= r
a[0][2] *= r
a[0][3] *= r
a[1][0] *= r
a[1][1] *= r
a[1][2] *= r
a[1][3] *= r
a[2][0] *= r
a[2][1] *= r
a[2][2] *= r
a[2][3] *= r
return a
}
// Translation returns the translation component of the matrix (last column)
// i.e. what you get if you Apply the matrix to the zero vector.
func (a Matrix3x4) Translation() Float3 {
return Float3{X: a[0][3], Y: a[1][3], Z: a[2][3]}
}
// Adjugate returns the adjugate of the 3x3 submatrix.
func (a Matrix3x4) Adjugate() Matrix3x4 {
return Matrix3x4{
0: [4]float64{
0: a[1][1]*a[2][2] - a[1][2]*a[2][1],
1: a[0][2]*a[2][1] - a[0][1]*a[2][2],
2: a[0][1]*a[1][2] - a[0][2]*a[1][1],
},
1: [4]float64{
0: a[1][2]*a[2][0] - a[1][0]*a[2][2],
1: a[0][0]*a[2][2] - a[0][2]*a[2][0],
2: a[0][2]*a[1][0] - a[0][0]*a[1][2],
},
2: [4]float64{
0: a[1][0]*a[2][1] - a[1][1]*a[2][0],
1: a[0][1]*a[2][0] - a[0][0]*a[2][1],
2: a[0][0]*a[1][1] - a[0][1]*a[1][0],
},
}
}
// Inverse returns the inverse of the matrix.
func (a Matrix3x4) Inverse() (Matrix3x4, error) {
adj := a.Adjugate()
det := a[0][0]*adj[0][0] + a[0][1]*adj[1][0] + a[0][2]*adj[2][0]
if det == 0 {
return Matrix3x4{}, errSingularMatrix
}
inv := adj.Mul(1 / det) // the inverse of the 3x3 submatrix.
// The affine transformation T consists of a square 3x3 submatrix A and
// a translation vector b. We now have A^{-1} (inv).
// T(x) = Ax + b
// ⇒ T(x) - b = Ax
// ⇒ A^{-1}(T(x) - b)) = x
// ⇒ A^{-1}T(x) - A^{-1}b = x (by linearity of A^{-1})
// ∴ T^{-1}(y) = A^{-1}y - A^{-1}b.
ainvb := inv.Apply(a.Translation())
a[0][3] -= ainvb.X
a[1][3] -= ainvb.Y
a[2][3] -= ainvb.Z
return inv, nil
}
// Concat returns the matrix equivalent to applying matrix a and then b.
func (a Matrix3x4) Concat(b Matrix3x4) Matrix3x4 {
return Matrix3x4{
0: [4]float64{
a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0],
a[0][0]*b[0][1] + a[0][1]*b[1][1] + a[0][2]*b[2][1],
a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]*b[2][2],
a[0][0]*b[0][3] + a[0][1]*b[1][3] + a[0][3]*b[2][3],
},
1: [4]float64{
a[1][0]*b[0][0] + a[1][1]*b[1][0] + a[1][2]*b[2][0],
a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1],
a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]*b[2][2],
a[1][0]*b[0][3] + a[1][1]*b[1][3] + a[1][3]*b[2][3],
},
2: [4]float64{
a[2][0]*b[0][0] + a[2][1]*b[1][0] + a[2][2]*b[2][0],
a[2][0]*b[0][1] + a[2][1]*b[1][1] + a[2][2]*b[2][1],
a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2],
a[2][0]*b[0][3] + a[2][1]*b[1][3] + a[2][3]*b[2][3],
},
}
}