about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--db_actions.go167
-rw-r--r--db_actions_test.go73
2 files changed, 238 insertions, 2 deletions
diff --git a/db_actions.go b/db_actions.go
index d93728b..1cadf77 100644
--- a/db_actions.go
+++ b/db_actions.go
@@ -25,6 +25,7 @@ import (
 	"io"
 	"io/ioutil"
 	"log"
+	"math"
 	"strconv"
 	"strings"
 	"time"
@@ -907,8 +908,8 @@ func getCenterOfMass(nodeID int64) structs.Vec2 {
 	return structs.Vec2{X: CenterOfMass[0], Y: CenterOfMass[1]}
 }
 
-// getStarCoordinates gets the star coordinates of a star using a given nodeID. It returns a vector describing the
-// coordinates
+// getStarCoordinates gets the star coordinates of a star using a given nodeID.
+// It returns a vector describing the coordinates
 func getStarCoordinates(nodeID int64) structs.Vec2 {
 	var Coordinates [2]float64
 
@@ -925,3 +926,165 @@ func getStarCoordinates(nodeID int64) structs.Vec2 {
 
 	return structs.Vec2{X: Coordinates[0], Y: Coordinates[1]}
 }
+
+// CalcAllForces calculates all the forces acting on the given star.
+// The theta value it receives is used by the Barnes-Hut algorithm to determine what
+// stars to include into the calculations
+func CalcAllForces(database *sql.DB, star structs.Star2D, theta float64) structs.Vec2 {
+	db = database
+
+	// calculate all the forces and add them to the list of all forces
+	// this is done recursively
+	// first of all, get the root id
+	log.Println("getting the root ID")
+	rootID := getRootNodeID(1)
+	log.Println("done getting the root ID")
+
+	log.Printf("Calculating the forces acting on the star %v", star)
+	force := CalcAllForcesNode(star, rootID, theta)
+	log.Printf("Done calculating the forces acting on the star %v", star)
+	log.Printf("Force: %v", force)
+
+	return force
+}
+
+// calcAllForces nodes calculates the forces in between a sta	log.Printf("Calculating the forces acting on the star %v", star)r and a node and returns the overall force
+// TODO: implement the calcForce(star, centerOfMass) {...} function
+// TODO: implement the getSubtreeIDs(nodeID) []int64 {...} function
+func CalcAllForcesNode(star structs.Star2D, nodeID int64, theta float64) structs.Vec2 {
+	fmt.Println("-----------------------------------------------------------")
+	log.Printf("NodeID: %d \t star: %v \t theta: %f \t nodeboxwidth: %f", nodeID, star, theta, getBoxWidth(nodeID))
+	var forceX float64
+	var forceY float64
+	var localTheta float64
+
+	if nodeID != 0 {
+		log.Println("Calculating the localtheta")
+		localTheta = calcTheta(star, nodeID)
+		log.Printf("Done calculating theta: %v", localTheta)
+	}
+
+	if localTheta < theta {
+		log.Printf("localTheta < theta")
+		var force structs.Vec2
+
+		// if the nodeID is not zero, use the center of mass as the other star
+		if nodeID != 0 {
+			pseudoStarCoodinates := getCenterOfMass(nodeID)
+			PseudoStar := structs.Star2D{
+				C: structs.Vec2{
+					X: pseudoStarCoodinates.X,
+					Y: pseudoStarCoodinates.Y,
+				},
+				V: structs.Vec2{
+					X: 0,
+					Y: 0,
+				},
+				M: 1000,
+			}
+			log.Printf("PseudoStar: %v", PseudoStar)
+			force = calcForce(star, PseudoStar)
+
+			// else, use the star in the node as the other star
+		} else {
+			if getStarID(nodeID) != 0 {
+				var pseudoStar = getStar(getStarID(nodeID))
+				force = calcForce(star, pseudoStar)
+			}
+		}
+
+		forceX = force.X
+		forceY = force.X
+	} else {
+		log.Printf("localTheta > theta")
+		// iterate over all subtrees and add the forces acting through them
+		var subtreeIDs [4]int64
+		subtreeIDs = getSubtreeIDs(nodeID)
+		for _, subtreeID := range subtreeIDs {
+
+			// don't recurse into
+			if subtreeID != 0 {
+				var force = CalcAllForcesNode(star, subtreeID, theta)
+				log.Printf("force: %v", force)
+				forceX += force.X
+				forceY += force.Y
+			}
+		}
+	}
+	return structs.Vec2{forceX, forceY}
+}
+
+// calcTheta calculates the theat for a given star and a node
+func calcTheta(star structs.Star2D, nodeID int64) float64 {
+	d := getBoxWidth(nodeID)
+	r := distance(star, nodeID)
+	theta := d / r
+	return theta
+}
+
+// calculate the distance in between the star and the node with the given ID
+func distance(star structs.Star2D, nodeID int64) float64 {
+	var starX float64 = star.C.X
+	var starY float64 = star.C.Y
+	var node structs.Vec2 = getNodeCenterOfMass(nodeID)
+	var nodeX float64 = node.X
+	var nodeY float64 = node.Y
+
+	var tmpX = math.Pow(starX-nodeX, 2)
+	var tmpY = math.Pow(starY-nodeY, 2)
+
+	var distance float64 = math.Sqrt(tmpX + tmpY)
+	return distance
+}
+
+// getNodeCenterOfMass returns the center of mass of the node with the given ID
+func getNodeCenterOfMass(nodeID int64) structs.Vec2 {
+	var Coordinates [2]float64
+
+	// get the star from the stars table
+	query := fmt.Sprintf("SELECT center_of_mass[1], center_of_mass[2] FROM nodes WHERE node_id=%d", nodeID)
+	err := db.QueryRow(query).Scan(&Coordinates[0], &Coordinates[1])
+	if err != nil {
+		log.Fatalf("[ E ] getNodeCenterOfMass query: %v \n\t\t\tquery: %s\n", err, query)
+	}
+
+	return structs.Vec2{X: Coordinates[0], Y: Coordinates[1]}
+}
+
+// getSubtreeIDs returns the id of the subtrees of the nodeID
+func getSubtreeIDs(nodeID int64) [4]int64 {
+
+	var subtreeIDs [4]int64
+
+	// get the star from the stars table
+	query := fmt.Sprintf("SELECT subnode[1], subnode[2], subnode[3], subnode[4] FROM nodes WHERE node_id=%d", nodeID)
+	err := db.QueryRow(query).Scan(&subtreeIDs[0], &subtreeIDs[1], &subtreeIDs[2], &subtreeIDs[3])
+	if err != nil {
+		log.Fatalf("[ E ] getSubtreeIDs query: %v \n\t\t\tquery: %s\n", err, query)
+	}
+
+	return subtreeIDs
+}
+
+func calcForce(s1 structs.Star2D, s2 structs.Star2D) structs.Vec2 {
+	log.Println("+++++++++++++++++++++++++")
+	log.Printf("Calculating the force acting inbetween %f and %f", s1, s2)
+	G := 6.6726 * math.Pow(10, -11)
+
+	// calculate the force acting
+	var combinedMass float64 = s1.M * s2.M
+	var distance float64 = math.Sqrt(math.Pow(math.Abs(s1.C.X-s2.C.X), 2) + math.Pow(math.Abs(s1.C.Y-s2.C.Y), 2))
+
+	var scalar float64 = G * ((combinedMass) / math.Pow(distance, 2))
+
+	// define a unit vector pointing from s1 to s2
+	var vector structs.Vec2 = structs.Vec2{s2.C.X - s1.C.X, s2.C.Y - s1.C.Y}
+	var UnitVector structs.Vec2 = structs.Vec2{vector.X / distance, vector.Y / distance}
+
+	// multiply the vector with the force to get a vector representing the force acting
+	var force structs.Vec2 = UnitVector.Multiply(scalar)
+	log.Println("+++++++++++++++++++++++++")
+
+	// return the force exerted on s1 by s2
+	return force
+}
diff --git a/db_actions_test.go b/db_actions_test.go
new file mode 100644
index 0000000..7b96426
--- /dev/null
+++ b/db_actions_test.go
@@ -0,0 +1,73 @@
+// db_actions defines actions on the database
+// Copyright (C) 2019 Emile Hansmaennel
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+package db_actions
+
+import (
+	"database/sql"
+	"reflect"
+	"testing"
+
+	"git.darknebu.la/GalaxySimulator/structs"
+	_ "github.com/lib/pq"
+)
+
+func TestCalcAllForces(t *testing.T) {
+	// define a database
+	db = ConnectToDB()
+	db.SetMaxOpenConns(75)
+
+	type args struct {
+		database *sql.DB
+		star     structs.Star2D
+		theta    float64
+	}
+	tests := []struct {
+		name string
+		args args
+		want structs.Vec2
+	}{
+		{
+			name: "force acting on a single star",
+			args: args{
+				database: db,
+				star: structs.Star2D{
+					C: structs.Vec2{
+						X: 100,
+						Y: 100,
+					},
+					V: structs.Vec2{
+						X: 0,
+						Y: 0,
+					},
+					M: 1000,
+				},
+				theta: 0.5,
+			},
+			want: structs.Vec2{
+				X: 0,
+				Y: 0,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := CalcAllForces(tt.args.database, tt.args.star, tt.args.theta); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("CalcAllForces() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}