Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

permissions:
contents: read
pull-requests: write

jobs:
Format:
Expand Down Expand Up @@ -81,4 +82,9 @@ jobs:

- name: Test
run: |
go test -v ./...
go test -coverprofile=cover.out ./tests/... -coverpkg=./pkg/...,.,./internal/...

- name: Coverage Report
uses: Jannik-Hm/go-test-coverage-report@v1.1
with:
coverprofile: cover.out
19 changes: 16 additions & 3 deletions pkg/MeshTypes/mesh.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package MeshTypes

import (
"fmt"
)

type Mesh struct {
Triangles []*Triangle
}
Expand Down Expand Up @@ -31,8 +35,13 @@ func (obj *Mesh) RotateAndTranslate(translationMatrix Matrix) {
}

func (obj *Mesh) calculateBoundingBox() Vector {
min := Vector{}
max := Vector{}
// init with first triangle to prohibit 0 values being min or max
if len(obj.Triangles) == 0 || obj.Triangles[0] == nil || obj.Triangles[0].V0 == nil {
return Vector{}
}
min := obj.Triangles[0].V0.Position
max := obj.Triangles[0].V0.Position

for _, triangle := range obj.Triangles {
min = triangle.V0.Position.Min(&min)
max = triangle.V0.Position.Max(&max)
Expand All @@ -50,8 +59,11 @@ func (obj *Mesh) calculateBoundingBox() Vector {
}
}

func (obj *Mesh) ScaleToDimensions(desiredSize *Vector) {
func (obj *Mesh) ScaleToDimensions(desiredSize *Vector) error {
actual := obj.calculateBoundingBox()
if actual.X == 0 && actual.Y == 0 && actual.Z == 0 {
return fmt.Errorf("invalid Mesh with 0 dimension")
}
scaling := desiredSize.Div(actual)
scaledVectors := make(map[*Vertex]struct{})
for _, triangle := range obj.Triangles {
Expand All @@ -68,4 +80,5 @@ func (obj *Mesh) ScaleToDimensions(desiredSize *Vector) {
scaledVectors[triangle.V2] = struct{}{}
}
}
return nil
}
5 changes: 4 additions & 1 deletion pkg/file_handlers/3ds.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ func Load3DS(fileData *[]byte, desiredSize *MeshTypes.Vector) (*MeshTypes.Mesh,
mesh := &MeshTypes.Mesh{Triangles: triangles}

if desiredSize != nil {
mesh.ScaleToDimensions(desiredSize)
err := mesh.ScaleToDimensions(desiredSize)
if err != nil {
return nil, err
}
}

return mesh, nil
Expand Down
67 changes: 67 additions & 0 deletions tests/MeshTypes/matrix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package MeshTypes_Test

import (
"reflect"
"testing"

"github.com/Patch2PDF/GDTF-Mesh-Reader/pkg/MeshTypes"
)

func TestIdentityMatrix(t *testing.T) {
want := MeshTypes.Matrix{
X00: 1, X01: 0, X02: 0, X03: 0,
X10: 0, X11: 1, X12: 0, X13: 0,
X20: 0, X21: 0, X22: 1, X23: 0,
X30: 0, X31: 0, X32: 0, X33: 1,
}
if !reflect.DeepEqual(MeshTypes.IdentityMatrix(), want) {
t.Errorf(`IdentityMatrix() Output does not match`)
}
}

func TestMatrixMul(t *testing.T) {
a := MeshTypes.Matrix{
X00: 1, X01: 2, X02: 3, X03: 4,
X10: 5, X11: 6, X12: 7, X13: 8,
X20: 9, X21: 10, X22: 11, X23: 12,
X30: 13, X31: 14, X32: 15, X33: 16,
}
b := MeshTypes.Matrix{
X00: 17, X01: 18, X02: 19, X03: 20,
X10: 21, X11: 22, X12: 23, X13: 24,
X20: 25, X21: 26, X22: 27, X23: 28,
X30: 29, X31: 30, X32: 31, X33: 32,
}
want := MeshTypes.Matrix{
X00: 250, X01: 260, X02: 270, X03: 280,
X10: 618, X11: 644, X12: 670, X13: 696,
X20: 986, X21: 1028, X22: 1070, X23: 1112,
X30: 1354, X31: 1412, X32: 1470, X33: 1528,
}
if !reflect.DeepEqual(a.Mul(b), want) {
t.Errorf(`Matrix Multiplication Output does not match`)
}
}

func TestMulPosition(t *testing.T) {
a := MeshTypes.Matrix{
X00: 1, X01: 2, X02: 3, X03: 4,
X10: 5, X11: 6, X12: 7, X13: 8,
X20: 9, X21: 10, X22: 11, X23: 12,
X30: 13, X31: 14, X32: 15, X33: 16,
}
b := MeshTypes.Vector{
X: 17,
Y: 18,
Z: 19,
}
want := MeshTypes.Vector{
X: 114,
Y: 334,
Z: 554,
}

if !reflect.DeepEqual(a.MulPosition(b), want) {
t.Errorf(`Matrix Vector Multiplication Output does not match`)
}
}
210 changes: 210 additions & 0 deletions tests/MeshTypes/mesh_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package MeshTypes_Test

import (
"math/rand"
"reflect"
"testing"

"github.com/Patch2PDF/GDTF-Mesh-Reader/pkg/MeshTypes"
)

func RandomTriangle() *MeshTypes.Triangle {
return &MeshTypes.Triangle{
V0: RandomVertex(),
V1: RandomVertex(),
V2: RandomVertex(),
}
}

func RandomVertex() *MeshTypes.Vertex {
normal := RandomVector()
return &MeshTypes.Vertex{
Position: RandomVector(),
Normal: &normal,
}
}

func RandomVector() MeshTypes.Vector {
return MeshTypes.Vector{
X: rand.Float64(),
Y: rand.Float64(),
Z: rand.Float64(),
}
}

func TestAddTriangle(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{RandomTriangle()},
}
b := RandomTriangle()
copy := a.Copy()
copy.AddTriangle(b)
if !reflect.DeepEqual(copy, MeshTypes.Mesh{Triangles: []*MeshTypes.Triangle{a.Triangles[0], b}}) {
t.Errorf("Mesh AddTriangle() Output does not match expected")
}
}

func TestMeshCopy(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{RandomTriangle()},
}
copy := a.Copy()
if !(reflect.DeepEqual(a, copy) && &a != &copy) {
t.Errorf("Mesh Copy() Output does not match expected")
}
}

func TestAddMesh(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{RandomTriangle()},
}
b := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{RandomTriangle()},
}
copy := a.Copy()
result := copy.Add(&b)
if !reflect.DeepEqual(*result, MeshTypes.Mesh{Triangles: []*MeshTypes.Triangle{a.Triangles[0], b.Triangles[0]}}) {
t.Errorf("Mesh Add() Output does not match expected")
}
}

func TestRotateAndTranslate(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{RandomTriangle()},
}
translationMatrix := MeshTypes.Matrix{
X00: 1, X01: -1, X02: 5, X03: -20,
X10: 1, X11: 2, X12: -3, X13: 10,
X20: 1, X21: 5, X22: 3, X23: -5,
X30: 1, X31: 6, X32: -6, X33: 4,
}
result := a.Copy()
result.RotateAndTranslate(translationMatrix)
want := a.Copy()
for _, triangle := range want.Triangles {
triangle.V0.Position = translationMatrix.MulPosition(triangle.V0.Position) // safe to use as func is tested in another place
triangle.V1.Position = translationMatrix.MulPosition(triangle.V1.Position)
triangle.V2.Position = translationMatrix.MulPosition(triangle.V2.Position)
}
if !reflect.DeepEqual(result, want) {
t.Errorf("Mesh RotateAndTranslate() Output does not match expected")
}
}

func TestScaleToDimensions(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{
{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: -1, Y: 1, Z: -1}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 1, Y: -3, Z: 1}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 1, Y: 1, Z: 0}},
},
},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
result := a.Copy()
result.ScaleToDimensions(&desiredSize)
want := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{
{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: -2, Y: 1, Z: -4}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 2, Y: -3, Z: 4}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 2, Y: 1, Z: 0}},
},
},
}
if !reflect.DeepEqual(result, want) {
t.Errorf("Mesh ScaleToDimensions() Output does not match expected")
}
}

func TestScaleToDimensionsEmptyMesh(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
err := a.ScaleToDimensions(&desiredSize)
if err == nil {
t.Errorf("Mesh ScaleToDimensions() did not return error on faulty mesh")
}
}

func TestScaleToDimensionsNullTriangle(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{nil},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
err := a.ScaleToDimensions(&desiredSize)
if err == nil {
t.Errorf("Mesh ScaleToDimensions() did not return error on faulty mesh")
}
}

func TestScaleToDimensionsNullVertex(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{
{
V0: nil,
V1: nil,
V2: nil,
},
},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
err := a.ScaleToDimensions(&desiredSize)
if err == nil {
t.Errorf("Mesh ScaleToDimensions() did not return error on faulty mesh")
}
}

func TestScaleToDimensions0Size(t *testing.T) {
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{
{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
},
},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
err := a.ScaleToDimensions(&desiredSize)
if err == nil {
t.Errorf("Mesh ScaleToDimensions() did not return error on faulty mesh")
}
}

func TestScaleToDimensionsSameVertex(t *testing.T) {
triangle := MeshTypes.Triangle{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 0, Y: 0, Z: 0}},
}
a := MeshTypes.Mesh{
Triangles: []*MeshTypes.Triangle{
&triangle,
{
V0: triangle.V0,
V1: triangle.V1,
V2: triangle.V2,
},
},
}
desiredSize := MeshTypes.Vector{
X: 4, Y: 4, Z: 8,
}
err := a.ScaleToDimensions(&desiredSize)
if err == nil {
t.Errorf("Mesh ScaleToDimensions() did not return error on faulty mesh")
}
}
33 changes: 33 additions & 0 deletions tests/MeshTypes/triangle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package MeshTypes_Test

import (
"reflect"
"testing"

"github.com/Patch2PDF/GDTF-Mesh-Reader/pkg/MeshTypes"
)

func TestTriangleNormal(t *testing.T) {
a := MeshTypes.Triangle{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 1, Y: 4, Z: 3}, Normal: &MeshTypes.Vector{X: 4, Y: 5, Z: 6}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 4, Y: 8, Z: 10}, Normal: &MeshTypes.Vector{X: 10, Y: 11, Z: 12}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 13, Y: 14, Z: 15}, Normal: &MeshTypes.Vector{X: 16, Y: 17, Z: 18}},
}
want := MeshTypes.Vector{X: -0.39436910666014113, Y: 0.8604416872584897, Z: -0.3226656327219336}
result := a.Normal()
if !reflect.DeepEqual(result, want) {
t.Error("Triangle Normal() returned value does not match expected value")
}
}

func TestTriangleCopy(t *testing.T) {
a := MeshTypes.Triangle{
V0: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 1, Y: 2, Z: 3}, Normal: &MeshTypes.Vector{X: 4, Y: 5, Z: 6}},
V1: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 7, Y: 8, Z: 9}, Normal: &MeshTypes.Vector{X: 10, Y: 11, Z: 12}},
V2: &MeshTypes.Vertex{Position: MeshTypes.Vector{X: 13, Y: 14, Z: 15}, Normal: &MeshTypes.Vector{X: 16, Y: 17, Z: 18}},
}
copy := a.Copy()
if !(reflect.DeepEqual(copy, a) && a.V0 != copy.V0 && a.V1 != copy.V1 && a.V2 != copy.V2) {
t.Error("Triangle Copy() returned value does not match expected value")
}
}
Loading
Loading