From 8f1f924a65d66bc51570d48ea78fcf4894c6ff6b Mon Sep 17 00:00:00 2001 From: hanemile Date: Tue, 22 Jan 2019 19:08:05 +0100 Subject: Started writing alot of tests --- quadtree.go | 51 +- quadtree_test.go | 1603 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1635 insertions(+), 19 deletions(-) create mode 100644 quadtree_test.go diff --git a/quadtree.go b/quadtree.go index 07bbe73..8d74804 100644 --- a/quadtree.go +++ b/quadtree.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "log" "math" + "os" "os/exec" ) @@ -42,7 +43,7 @@ func NewNode(bounadry BoundingBox) *Node { } // Subdivide the tree -func (n *Node) subdivide() { +func (n *Node) Subdivide() { // define new values defining the new BoundaryBoxes newBoundaryWidth := n.Boundry.Width / 2 @@ -89,7 +90,7 @@ func (n *Node) Insert(star Star2D) error { // if the node does not all ready have child nodes, subdivide it if n.Subtrees == ([4]*Node{}) { - n.subdivide() + n.Subdivide() } // Insert the blocking star into it's subtree @@ -205,40 +206,51 @@ func (n Node) GetAllStars() []Star2D { return listOfNodes } -// CalcCenterOfMass calculates the center of mass for every node in the treee -func (n *Node) CalcCenterOfMass() Vec2 { - fmt.Printf("[CalcCenterOfMass] %v\n", n.Boundry.Width) +// CalcCenterOfMass calculates the center of mass for every node in the tree +func (n *Node) calcCenterOfMass() Vec2 { - if n.Boundry.Width < 10 { - return Vec2{} - } + nominatorX := 0.0 + denominatorX := 0.0 + + nominatorY := 0.0 + denominatorY := 0.0 // if the subtrees are not empty if n.Subtrees != ([4]*Node{}) { + for _, star := range n.Subtrees { + fmt.Println(star) + } for i := 0; i < len(n.Subtrees); i++ { - // get the center of mass for the subtree - subtreeCenterOfMassX := n.Subtrees[i].CalcCenterOfMass().X - subtreeCenterOfMassY := n.Subtrees[i].CalcCenterOfMass().Y - - // get the totalmass of the subtree - subtreeTotalMass := n.Subtrees[i].TotalMass + nominatorX += n.Subtrees[i].calcCenterOfMass().X * n.Subtrees[i].TotalMass + denominatorX += n.Subtrees[i].TotalMass - // calculate the center of mass of both - n.CenterOfMass.X = subtreeCenterOfMassX * subtreeTotalMass - n.CenterOfMass.Y = subtreeCenterOfMassY * subtreeTotalMass + nominatorY += n.Subtrees[i].calcCenterOfMass().Y * n.Subtrees[i].TotalMass + denominatorY += n.Subtrees[i].TotalMass } } if n.Star != (Star2D{}) { n.CenterOfMass = n.Star.C + fmt.Println(n.Star) } - n.CenterOfMass.X = n.CenterOfMass.X / n.TotalMass - n.CenterOfMass.Y = n.CenterOfMass.Y / n.TotalMass + comX := nominatorX / denominatorX + comY := nominatorY / denominatorY + fmt.Printf("nomX: %f \t denomX: %f\n", nominatorX, denominatorX) + fmt.Printf("nomY: %f \t denomY: %f\n", nominatorY, denominatorY) + + n.CenterOfMass = Vec2{comX, comY} return n.CenterOfMass } +func (n *Node) CalcCenterOfMass() Vec2 { + tree := n.GenForestTree(n) + fmt.Println(tree) + centerOfMass := n.calcCenterOfMass() + return centerOfMass +} + // CalcTotalMass calculates the total mass for every node in the tree func (n *Node) CalcTotalMass() float64 { @@ -260,6 +272,7 @@ func (n *Node) CalcTotalMass() float64 { // CalcAllForces calculates the force acting in between the given star and all the other stars using the given theta. // It gets all the other stars from the root node it is called on func (n Node) CalcAllForces(star Star2D, theta float64) Vec2 { + log.SetOutput(os.Stderr) // initialize a variable storing the overall force var localForce Vec2 = Vec2{} diff --git a/quadtree_test.go b/quadtree_test.go new file mode 100644 index 0000000..76ee7c4 --- /dev/null +++ b/quadtree_test.go @@ -0,0 +1,1603 @@ +package structs + +import ( + "fmt" + "reflect" + "testing" +) + +// The example below creates a new root node at (0, 0) with the given width. +// The width given in this case is 100. +func ExampleNewRoot() { + root := NewRoot(100) + fmt.Printf("%v\n", root) + // Output: &{{{0 0} 100} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} +} + +func TestNewRoot(t *testing.T) { + type args struct { + BoundingBoxWidth float64 + } + tests := []struct { + name string + args args + want *Node + }{ + { + name: "New root at (0, 0) with a width of 100", + args: args{ + BoundingBoxWidth: 100, + }, + want: &Node{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewRoot(tt.args.BoundingBoxWidth); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewRoot() = %v, want %v", got, tt.want) + } + }) + } +} + +// The example below creates a new node using the given bounding box +func ExampleNewNode() { + newNode := NewNode(BoundingBox{ + Center: Vec2{ + X: 25, + Y: 25, + }, + Width: 50, + }) + fmt.Printf("%v\n", newNode) + // Output: &{{{25 25} 50} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} +} + +func TestNewNode(t *testing.T) { + type args struct { + bounadry BoundingBox + } + tests := []struct { + name string + args args + want *Node + }{ + { + name: "Return a new node", + args: args{ + bounadry: BoundingBox{ + Center: Vec2{ + X: 3, + Y: 15, + }, + Width: 100, + }, + }, + want: &Node{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 3, + Y: 15, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewNode(tt.args.bounadry); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNode() = %v, want %v", got, tt.want) + } + }) + } +} + +// The example below subdivides the node it is called on. +// The function inserts four pointers pointing to other nodes into the subtree array representing the quadrants. +// +// The Code below prints the four subtrees that where generated: +func ExampleNode_Subdivide() { + root := NewRoot(100) + root.Subdivide() + for i := 0; i < 4; i++ { + fmt.Printf("%v\n", root.Subtrees[i]) + } + // Output: + // &{{{-25 25} 50} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} + // &{{{25 25} 50} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} + // &{{{-25 -25} 50} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} + // &{{{25 -25} 50} {0 0} 0 0 {{0 0} {0 0} 0} [ ]} +} + +func TestNode_Subdivide(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + tests := []struct { + name string + fields fields + }{ + { + name: "Subdivide a tree from [100] to [[50][50][50][50]]", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{ + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + n.Subdivide() + }) + } +} + +// Insert a star into a tree. +// If the star cannot be inserted (e.g. recursion depth too deep), Insert() returns an error +func ExampleNode_Insert() { + + // Initialize a tree and a star + root := NewRoot(100) + star := Star2D{ + C: Vec2{ + X: 12, + Y: 34, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + } + + // insert the star into the tree + err := root.Insert(star) + + // handle potential errors + if err != nil { + panic(err) + } + + fmt.Printf("%v", root) + + // Output: + // Direct insert of (12.000000, 34.000000) + // &{{{0 0} 100} {0 0} 0 0 {{12 34} {0 0} 0} [ ]} +} + +// Insert two stars that are very close to each other into the tree. +// A problem arises: the tree has to be subdivided so often, that the insert function +// raises a recursion depth error +func ExampleNode_Insert_error() { + + // Initialize a tree and two + root := NewRoot(100) + star1 := Star2D{ + C: Vec2{ + X: 5, + Y: 5, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + } + star2 := Star2D{ + C: Vec2{ + X: 5.000000000000001, + Y: 5.000000000000001, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + } + + // insert the first star into the tree + err := root.Insert(star1) + // handle potential errors + if err != nil { + panic(err) + } + + // insert the second star into the tree + err = root.Insert(star2) + // handle potential errors + if err != nil { + panic(err) + } + + // Output: + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Direct insert of (5.000000, 5.000000) + // Could not insert star (5.000000, 5.000000) (recursion limit reached) + // + // Could not insert star (5.000000, 5.000000) (recursion limit reached) +} + +func TestNode_Insert(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + type args struct { + star Star2D + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "Inserting a single star into a previously empty galaxy", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + }, + wantErr: false, + }, + { + name: "Inserting a single star into a galaxy all ready containing a star", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 2, + Y: 3, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + star: Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + }, + wantErr: false, + }, + { + name: "Inserting a single star onto the boundary limit", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + star: Star2D{ + C: Vec2{ + X: 25, + Y: 25, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 20, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if err := n.Insert(tt.args.star); (err != nil) != tt.wantErr { + t.Errorf("Node.Insert() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// Generate a tree using the LaTeX forest tree notation +// This is a minimal example using only a root node +func ExampleNode_GenForestTree() { // Create a new root + root := NewRoot(100) + root.Star = Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 20, + } + + // generate the tree + forestTree := root.GenForestTree(root) + fmt.Println(forestTree) + // Output: + // [10 20[][][][]] +} + +// Generate a tree using the LaTeX forest tree notation. +// This is an example displaying a bigger tree. +func ExampleNode_GenForestTree_deepTree() { + // Create a new root + root := NewRoot(100) + + // Subdivide the root multiple times + root.Subdivide() + root.Subtrees[1].Subdivide() + + // Insert a star into the tree + root.Subtrees[1].Star = Star2D{ + C: Vec2{ + X: 20, + Y: 30, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 20, + } + + // Generate the tree + forestTree := root.GenForestTree(root) + fmt.Println(forestTree) + // Output: + // [[[][][][]][20 30[[][][][]][[][][][]][[][][][]][[][][][]]][[][][][]][[][][][]]] +} + +func TestNode_GenForestTree(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + type args struct { + node *Node + } + tests := []struct { + name string + fields fields + args args + want string + }{ + { + name: "Create a forest from a single node with four subnodes", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + node: &Node{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 100, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + want: "[[][][][]]", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if got := n.GenForestTree(tt.args.node); got != tt.want { + t.Errorf("Node.GenForestTree() = %v, want %v", got, tt.want) + } + }) + } +} + +// Draws the tree to a pdf using lualatex for building the pdf. +// (Luatex is used, because pdflatex apparently cannot handle such deep recursion depths) +func ExampleNode_DrawTreeLaTeX() { + // create a new root node + root := NewRoot(100) + + // write the LaTeX to out.tex and build the tex using luatex + root.DrawTreeLaTeX("out.tex") + // Output: +} + +func TestNode_DrawTreeLaTeX(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + type args struct { + outpath string + } + tests := []struct { + name string + fields fields + args args + }{ + { + name: "Draw a tree using latex", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 0, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + outpath: "tree.tex", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + n.DrawTreeLaTeX(tt.args.outpath) + }) + } +} + +// GetAllStars gets all the stars from a selected tree. +// In the example below, an empty node is generated, subdivided and two stars are inserted into it. +// Then, a list of all stars (the two that were previously inserted) gets generated and printed. +func ExampleNode_GetAllStars() { + // Define a new root node + root := NewRoot(100) + + // Subdivide the root + root.Subdivide() + + // Insert two stars into the tree + root.Subtrees[1].Star = Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + } + root.Subtrees[3].Star = Star2D{ + C: Vec2{ + X: 30, + Y: 40, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + } + + // Get the stars from the tree + starsList := root.GetAllStars() + + // Print all the stars in the list + for _, star := range starsList { + fmt.Println(star) + } + // Output: + // {{10 20} {0 0} 0} + // {{30 40} {0 0} 0} +} + +func TestNode_GetAllStars(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + tests := []struct { + name string + fields fields + want []Star2D + }{ + { + name: "", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 0, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{ + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 30, + Y: 40, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 50, + Y: 60, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 70, + Y: 80, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + }, + want: []Star2D{ + { + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + { + C: Vec2{ + X: 30, + Y: 40, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + { + C: Vec2{ + X: 50, + Y: 60, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + { + C: Vec2{ + X: 70, + Y: 80, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if got := n.GetAllStars(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.GetAllStars() = %v, want %v", got, tt.want) + } + }) + } +} + +// CalcCenterOfMass calculates the center of mass of the node it is called on. +// In the example below, the Tree contains two stars with equal mass (10): (20, 30) and (-20, -30). +func ExampleNode_CalcCenterOfMass() { + root := NewRoot(100) + + root.Subdivide() + + star1 := NewStar2D(Vec2{0, 0}, Vec2{0, 0}, 10) + star2 := NewStar2D(Vec2{3, 3}, Vec2{0, 0}, 10) + + // Insert the stars into the tree + // There will be no error handling here, we'll assume that everything goes right.. + _ = root.Insert(star1) + _ = root.Insert(star2) + + root.CalcTotalMass() + + centerOfMass := root.CalcCenterOfMass() + fmt.Println(centerOfMass) + // Output: +} + +func TestNode_CalcCenterOfMass(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + tests := []struct { + name string + fields fields + want Vec2 + }{ + { + name: "Center of mass inbetween ", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 0, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{ + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 30, + Y: 40, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 50, + Y: 60, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 70, + Y: 80, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + }, + want: Vec2{ + X: 0, + Y: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if got := n.CalcCenterOfMass(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.CalcCenterOfMass() = %v, want %v", got, tt.want) + } + }) + } +} + +// +func ExampleNode_CalcTotalMass() { + +} + +func TestNode_CalcTotalMass(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + tests := []struct { + name string + fields fields + want float64 + }{ + { + name: "Calculating the total mass two stars", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 0, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{ + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 10, + Y: 20, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 42, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: 25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: -25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 30, + Y: 40, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 24, + }, + Subtrees: [4]*Node{}, + }, + { + Boundry: BoundingBox{ + Center: Vec2{ + X: 25, + Y: -25, + }, + Width: 50, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + }, + }, + want: 66, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if got := n.CalcTotalMass(); got != tt.want { + t.Errorf("Node.CalcTotalMass() = %v, want %v", got, tt.want) + } + }) + } +} + +// +func ExampleNode_CalcAllForces() { + +} + +func TestNode_CalcAllForces(t *testing.T) { + type fields struct { + Boundry BoundingBox + CenterOfMass Vec2 + TotalMass float64 + Depth int + Star Star2D + Subtrees [4]*Node + } + type args struct { + star Star2D + theta float64 + } + tests := []struct { + name string + fields fields + args args + want Vec2 + }{ + { + name: "single star", + fields: fields{ + Boundry: BoundingBox{ + Center: Vec2{ + X: 0, + Y: 0, + }, + Width: 0, + }, + CenterOfMass: Vec2{ + X: 0, + Y: 0, + }, + TotalMass: 0, + Depth: 0, + Star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + Subtrees: [4]*Node{}, + }, + args: args{ + star: Star2D{ + C: Vec2{ + X: 0, + Y: 0, + }, + V: Vec2{ + X: 0, + Y: 0, + }, + M: 0, + }, + theta: 0, + }, + want: Vec2{ + X: 0, + Y: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := Node{ + Boundry: tt.fields.Boundry, + CenterOfMass: tt.fields.CenterOfMass, + TotalMass: tt.fields.TotalMass, + Depth: tt.fields.Depth, + Star: tt.fields.Star, + Subtrees: tt.fields.Subtrees, + } + if got := n.CalcAllForces(tt.args.star, tt.args.theta); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.CalcAllForces() = %v, want %v", got, tt.want) + } + }) + } +} + +// +func ExampleCalcForce() { + +} + +func TestCalcForce(t *testing.T) { + type args struct { + s1 Star2D + s2 Star2D + } + tests := []struct { + name string + args args + want Vec2 + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalcForce(tt.args.s1, tt.args.s2); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CalcForce() = %v, want %v", got, tt.want) + } + }) + } +} -- cgit 1.4.1