From c8ca77ad65e9c0b31c6bd5289de0c1d332c06e63 Mon Sep 17 00:00:00 2001 From: Emile Date: Thu, 7 Mar 2019 16:14:12 +0100 Subject: subdivided the project into multiple logical compartments --- backend/backend.go | 7 + backend/calc.go | 189 ++++++++++++++++++++ backend/contains.go | 23 +++ backend/delete.go | 47 +++++ backend/gen.go | 55 ++++++ backend/get.go | 478 +++++++++++++++++++++++++++++++++++++++++++++++++++ backend/init.go | 44 +++++ backend/insert.go | 213 +++++++++++++++++++++++ backend/is.go | 23 +++ backend/new.go | 48 ++++++ backend/subdivide.go | 40 +++++ backend/update.go | 179 +++++++++++++++++++ 12 files changed, 1346 insertions(+) create mode 100644 backend/backend.go create mode 100644 backend/calc.go create mode 100644 backend/contains.go create mode 100644 backend/delete.go create mode 100644 backend/gen.go create mode 100644 backend/get.go create mode 100644 backend/init.go create mode 100644 backend/insert.go create mode 100644 backend/is.go create mode 100644 backend/new.go create mode 100644 backend/subdivide.go create mode 100644 backend/update.go diff --git a/backend/backend.go b/backend/backend.go new file mode 100644 index 0000000..24fc1a4 --- /dev/null +++ b/backend/backend.go @@ -0,0 +1,7 @@ +package backend + +import "database/sql" + +var ( + db *sql.DB +) diff --git a/backend/calc.go b/backend/calc.go new file mode 100644 index 0000000..4ad9ce6 --- /dev/null +++ b/backend/calc.go @@ -0,0 +1,189 @@ +package backend + +import ( + "database/sql" + "git.darknebu.la/GalaxySimulator/structs" + "log" + "math" +) + +// 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, galaxyIndex int64, 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("[db_actions] Getting the root ID") + rootID := getRootNodeID(galaxyIndex) + log.Println("[db_actions] Done getting the root ID") + + log.Printf("[db_actions] Calculating the forces acting on the star %v", star) + force := CalcAllForcesNode(star, rootID, theta) + log.Printf("[db_actions] Done calculating the forces acting on the star %v", star) + log.Printf("[db_actions] 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 { + log.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 + + nodeWidth := getBoxWidth(nodeID) + + if nodeID != 0 { + log.Println("[theta] Calculating localtheta(star, node)") + log.Printf("[theta] node with: %f", nodeWidth) + localTheta = calcTheta(star, nodeID) + log.Printf("[theta] Done calculating localtheta: %v", localTheta) + } + + // recurse deeper into the tree + if localTheta < theta { + log.Println("[ ] localtheta < theta") + + } else { + log.Println("[ ] localtheta > theta") + + log.Printf("[ ] Iterating over subtrees") + var subtreeIDs [4]int64 + subtreeIDs = getSubtreeIDs(nodeID) + for i, subtreeID := range subtreeIDs { + log.Printf("Subtree: %d\t ID: %d", i, subtreeID) + + if subtreeID != 0 { + subtreeStarId := getStarID(subtreeID) + if subtreeStarId != 0 { + var localStar = GetStar(subtreeStarId) + log.Printf("subtree %d star: %v", i, localStar) + if localStar != star { + log.Println("Not even the original star, calculating forces...") + var force = calcForce(localStar, star) + forceX += force.X + forceY += force.Y + } + } + var force = CalcAllForcesNode(star, subtreeID, theta) + log.Printf("force: %v", force) + forceX += force.X + forceY += force.Y + } + } + + } + + //// dont't recurse deeper into the tree + //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 + // + //// recurse deeper into the tree + //} else { + // log.Printf("localTheta > theta") + // // iterate over all subtrees and add the forces acting through them + // var subtreeIDs [4]int64 + // subtreeIDs = getSubtreeIDs(nodeID) + // for i, subtreeID := range subtreeIDs { + // fmt.Printf("Subtree: %d", i) + // + // // don't recurse into + // if subtreeID != 0 { + // var force = CalcAllForcesNode(star, subtreeID, theta) + // log.Printf("force: %v", force) + // forceX += force.X + // forceY += force.Y + // } + // } + //} + log.Println("---------------------------------------") + 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 +} + +// calcForce calculates the force the star s1 is acting on s2. +// The force acting is returned in Newtons. +func calcForce(s1 structs.Star2D, s2 structs.Star2D) structs.Vec2 { + log.Println("+++++++++++++++++++++++++") + log.Printf("s1: %v", s1) + log.Printf("s2: %v", 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)) + log.Printf("combined mass: %f", combinedMass) + log.Printf("distance: %f", distance) + + var scalar float64 = G * ((combinedMass) / math.Pow(distance, 2)) + log.Printf("scalar: %f", scalar) + + // 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/backend/contains.go b/backend/contains.go new file mode 100644 index 0000000..a4fb83d --- /dev/null +++ b/backend/contains.go @@ -0,0 +1,23 @@ +package backend + +import ( + "fmt" + "log" +) + +// containsStar returns true if the node with the given id contains a star and returns false if not. +func containsStar(id int64) bool { + var starID int64 + + query := fmt.Sprintf("SELECT star_id FROM nodes WHERE node_id=%d", id) + err := db.QueryRow(query).Scan(&starID) + if err != nil { + log.Fatalf("[ E ] containsStar query: %v\n\t\t\t query: %s\n", err, query) + } + + if starID != 0 { + return true + } + + return false +} diff --git a/backend/delete.go b/backend/delete.go new file mode 100644 index 0000000..bc463dd --- /dev/null +++ b/backend/delete.go @@ -0,0 +1,47 @@ +package backend + +import ( + "database/sql" + "fmt" + "log" +) + +// deleteAll Stars deletes all the rows in the stars table +func DeleteAllStars(database *sql.DB) { + db = database + // build the query creating a new node + query := "DELETE FROM stars WHERE TRUE" + + // execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] deleteAllStars query: %v\n\t\t\t query: %s\n", err, query) + } +} + +// deleteAll Stars deletes all the rows in the nodes table +func DeleteAllNodes(database *sql.DB) { + db = database + // build the query creating a new node + query := "DELETE FROM nodes WHERE TRUE" + + // execute the query + _, err := db.Query(query) + if err != nil { + log.Fatalf("[ E ] deleteAllStars query: %v\n\t\t\t query: %s\n", err, query) + } +} + +// removeStarFromNode removes the star from the node with the given ID +func removeStarFromNode(nodeID int64) { + // build the query + query := fmt.Sprintf("UPDATE nodes SET star_id=0 WHERE node_id=%d", nodeID) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] removeStarFromNode query: %v\n\t\t\t query: %s\n", err, query) + } +} diff --git a/backend/gen.go b/backend/gen.go new file mode 100644 index 0000000..6e95aeb --- /dev/null +++ b/backend/gen.go @@ -0,0 +1,55 @@ +package backend + +import ( + "database/sql" + "fmt" + "log" +) + +// genForestTree generates a forest representation of the tree with the given index +func GenForestTree(database *sql.DB, index int64) string { + db = database + rootNodeID := getRootNodeID(index) + return genForestTreeNode(rootNodeID) +} + +// genForestTreeNodes returns a sub-representation of a given node in forest format +func genForestTreeNode(nodeID int64) string { + var returnString string + + // get the subnode ids + var subnode [4]int64 + + query := fmt.Sprintf("SELECT subnode[1], subnode[2], subnode[3], subnode[4] FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&subnode[0], &subnode[1], &subnode[2], &subnode[3]) + if err != nil { + log.Fatalf("[ E ] updateTotalMassNode query: %v\n\t\t\t query: %s\n", err, query) + } + + returnString += "[" + + // iterate over all subnodes updating their total masses + for _, subnodeID := range subnode { + if subnodeID != 0 { + centerOfMass := getCenterOfMass(nodeID) + mass := getNodeTotalMass(nodeID) + returnString += fmt.Sprintf("%.0f %.0f %.0f", centerOfMass.X, centerOfMass.Y, mass) + returnString += genForestTreeNode(subnodeID) + } else { + if getStarID(nodeID) != 0 { + coords := getStarCoordinates(nodeID) + starID := getStarID(nodeID) + mass := getStarMass(starID) + returnString += fmt.Sprintf("[%.0f %.0f %.0f]", coords.X, coords.Y, mass) + } else { + returnString += fmt.Sprintf("[0 0]") + } + // break, this stops a star from being counted multiple (4) times + break + } + } + + returnString += "]" + + return returnString +} diff --git a/backend/get.go b/backend/get.go new file mode 100644 index 0000000..93be930 --- /dev/null +++ b/backend/get.go @@ -0,0 +1,478 @@ +package backend + +import ( + "database/sql" + "fmt" + "git.darknebu.la/GalaxySimulator/structs" + "log" + "strconv" +) + +// getBoxWidth gets the width of the box from the node width the given id +func getBoxWidth(nodeID int64) float64 { + var boxWidth float64 + + query := fmt.Sprintf("SELECT box_width FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&boxWidth) + if err != nil { + log.Fatalf("[ E ] getBoxWidth query: %v\n\t\t\t query: %s\n", err, query) + } + + return boxWidth +} + +// getTimestepNode gets the timestep of the current node +func getTimestepNode(nodeID int64) int64 { + var timestep int64 + + query := fmt.Sprintf("SELECT timestep FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(×tep) + if err != nil { + log.Fatalf("[ E ] getTimeStep query: %v\n\t\t\t query: %s\n", err, query) + } + + return timestep +} + +// getBoxWidth gets the center of the box from the node width the given id +func getBoxCenter(nodeID int64) []float64 { + var boxCenterX, boxCenterY []uint8 + + query := fmt.Sprintf("SELECT box_center[1], box_center[2] FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&boxCenterX, &boxCenterY) + if err != nil { + log.Fatalf("[ E ] getBoxCenter query: %v\n\t\t\t query: %s\n", err, query) + } + + x, parseErr := strconv.ParseFloat(string(boxCenterX), 64) + y, parseErr := strconv.ParseFloat(string(boxCenterX), 64) + + if parseErr != nil { + log.Fatalf("[ E ] parse boxCenter: %v\n\t\t\t query: %s\n", err, query) + log.Fatalf("[ E ] parse boxCenter: (%f, %f)\n", x, y) + } + + boxCenterFloat := []float64{x, y} + + return boxCenterFloat +} + +// getMaxTimestep gets the maximal timestep from the nodes table +func getMaxTimestep() float64 { + var maxTimestep float64 + + query := fmt.Sprintf("SELECT max(timestep) FROM nodes") + err := db.QueryRow(query).Scan(&maxTimestep) + if err != nil { + log.Fatalf("[ E ] getMaxTimestep query: %v\n\t\t\t query: %s\n", err, query) + } + + return maxTimestep +} + +// getStarID returns the id of the star inside of the node with the given ID +func getStarID(nodeID int64) int64 { + // get the star id from the node + var starID int64 + query := fmt.Sprintf("SELECT star_id FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&starID) + if err != nil { + log.Fatalf("[ E ] getStarID id query: %v\n\t\t\t query: %s\n", err, query) + } + + return starID +} + +// getNodeDepth returns the depth of the given node in the tree +func getNodeDepth(nodeID int64) int64 { + // build the query + query := fmt.Sprintf("SELECT depth FROM nodes WHERE node_id=%d", nodeID) + + var depth int64 + + // Execute the query + err := db.QueryRow(query).Scan(&depth) + if err != nil { + log.Fatalf("[ E ] getNodeDepth query: %v \n\t\t\t query: %s\n", err, query) + } + + return depth +} + +// quadrant returns the quadrant into which the given star belongs +func quadrant(star structs.Star2D, nodeID int64) int64 { + // get the center of the node the star is in + center := getBoxCenter(nodeID) + centerX := center[0] + centerY := center[1] + + if star.C.X > centerX { + if star.C.Y > centerY { + // North East condition + return 1 + } + // South East condition + return 3 + } + + if star.C.Y > centerY { + // North West condition + return 0 + } + // South West condition + return 2 +} + +// getQuadrantNodeID returns the id of the requested child-node +// Example: if a parent has four children and quadrant 0 is requested, the function returns the north east child id +func getQuadrantNodeID(parentNodeID int64, quadrant int64) int64 { + var a, b, c, d []uint8 + + // 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", parentNodeID) + err := db.QueryRow(query).Scan(&a, &b, &c, &d) + if err != nil { + log.Fatalf("[ E ] getQuadrantNodeID star query: %v \n\t\t\tquery: %s\n", err, query) + } + + returnA, _ := strconv.ParseInt(string(a), 10, 64) + returnB, _ := strconv.ParseInt(string(b), 10, 64) + returnC, _ := strconv.ParseInt(string(c), 10, 64) + returnD, _ := strconv.ParseInt(string(d), 10, 64) + + switch quadrant { + case 0: + return returnA + case 1: + return returnB + case 2: + return returnC + case 3: + return returnD + } + + return -1 +} + +// GetStar returns the star with the given ID from the stars table +func GetStar(starID int64) structs.Star2D { + var x, y, vx, vy, m float64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT x, y, vx, vy, m FROM stars WHERE star_id=%d", starID) + err := db.QueryRow(query).Scan(&x, &y, &vx, &vy, &m) + if err != nil { + log.Fatalf("[ E ] GetStar query: %v \n\t\t\tquery: %s\n", err, query) + } + + star := structs.Star2D{ + C: structs.Vec2{ + X: x, + Y: y, + }, + V: structs.Vec2{ + X: vx, + Y: vy, + }, + M: m, + } + + return star +} + +// getStarIDTimestep returns the timestep the given starID is currently inside of +func GetStarIDTimestep(starID int64) int64 { + var timestep int64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT timestep FROM nodes WHERE star_id=%d", starID) + err := db.QueryRow(query).Scan(×tep) + if err != nil { + log.Fatalf("[ E ] GetStar query: %v \n\t\t\tquery: %s\n", err, query) + } + + return timestep +} + +// getStarMass returns the mass if the star with the given ID +func getStarMass(starID int64) float64 { + var mass float64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT m FROM stars WHERE star_id=%d", starID) + err := db.QueryRow(query).Scan(&mass) + if err != nil { + log.Fatalf("[ E ] getStarMass query: %v \n\t\t\tquery: %s\n", err, query) + } + + return mass +} + +// getNodeTotalMass returns the total mass of the node with the given ID and its children +func getNodeTotalMass(nodeID int64) float64 { + var mass float64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT total_mass FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&mass) + if err != nil { + log.Fatalf("[ E ] getStarMass query: %v \n\t\t\tquery: %s\n", err, query) + } + + return mass +} + +// getListOfStarsGo returns the list of stars in go struct format +func GetListOfStarsGo(database *sql.DB) []structs.Star2D { + db = database + // build the query + query := fmt.Sprintf("SELECT * FROM stars") + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] removeStarFromNode query: %v\n\t\t\t query: %s\n", err, query) + } + + var starList []structs.Star2D + + // iterate over the returned rows + for rows.Next() { + + var starID int64 + var x, y, vx, vy, m float64 + scanErr := rows.Scan(&starID, &x, &y, &vx, &vy, &m) + if scanErr != nil { + log.Fatalf("[ E ] scan error: %v", scanErr) + } + + star := structs.Star2D{ + C: structs.Vec2{ + X: x, + Y: y, + }, + V: structs.Vec2{ + X: vx, + Y: vy, + }, + M: m, + } + + starList = append(starList, star) + } + + return starList +} + +// GetListOfStarIDs returns a list of all star ids in the stars table +func GetListOfStarIDs(db *sql.DB) []int64 { + // build the query + query := fmt.Sprintf("SELECT star_id FROM stars") + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] GetListOfStarIDs query: %v\n\t\t\t query: %s\n", err, query) + } + + var starIDList []int64 + + // iterate over the returned rows + for rows.Next() { + + var starID int64 + scanErr := rows.Scan(&starID) + if scanErr != nil { + log.Fatalf("[ E ] scan error: %v", scanErr) + } + + starIDList = append(starIDList, starID) + } + + return starIDList +} + +// GetListOfStarIDs returns a list of all star ids in the stars table with the given timestep +func GetListOfStarIDsTimestep(db *sql.DB, timestep int64) []int64 { + // build the query + query := fmt.Sprintf("SELECT star_id FROM nodes WHERE star_id<>0 AND timestep=%d", timestep) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] GetListOfStarIDsTimestep query: %v\n\t\t\t query: %s\n", err, query) + } + + var starIDList []int64 + + // iterate over the returned rows + for rows.Next() { + + var starID int64 + scanErr := rows.Scan(&starID) + if scanErr != nil { + log.Fatalf("[ E ] scan error: %v", scanErr) + } + + starIDList = append(starIDList, starID) + } + + return starIDList +} + +// getListOfStarsCsv returns an array of strings containing the coordinates of all the stars in the stars table +func GetListOfStarsCsv(db *sql.DB) []string { + // build the query + query := fmt.Sprintf("SELECT * FROM stars") + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] getListOfStarsCsv query: %v\n\t\t\t query: %s\n", err, query) + } + + var starList []string + + // iterate over the returned rows + for rows.Next() { + + var starID int64 + var x, y, vx, vy, m float64 + scanErr := rows.Scan(&starID, &x, &y, &vx, &vy, &m) + if scanErr != nil { + log.Fatalf("[ E ] scan error: %v", scanErr) + } + + row := fmt.Sprintf("%d, %f, %f, %f, %f, %f", starID, x, y, vx, vy, m) + starList = append(starList, row) + } + + return starList +} + +// getListOfStarsTreeCsv returns an array of strings containing the coordinates of all the stars in the given tree +func GetListOfStarsTree(database *sql.DB, treeindex int64) []structs.Star2D { + db = database + + // build the query + query := fmt.Sprintf("SELECT * FROM stars WHERE star_id IN(SELECT star_id FROM nodes WHERE timestep=%d)", treeindex) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] removeStarFromNode query: %v\n\t\t\t query: %s\n", err, query) + } + + var starList []structs.Star2D + + // iterate over the returned rows + for rows.Next() { + + var starID int64 + var x, y, vx, vy, m float64 + scanErr := rows.Scan(&starID, &x, &y, &vx, &vy, &m) + if scanErr != nil { + log.Fatalf("[ E ] scan error: %v", scanErr) + } + + star := structs.Star2D{ + C: structs.Vec2{ + X: x, + Y: y, + }, + V: structs.Vec2{ + X: vx, + Y: vy, + }, + M: m, + } + + starList = append(starList, star) + } + + return starList +} + +// getRootNodeID gets a tree index and returns the nodeID of its root node +func getRootNodeID(index int64) int64 { + var nodeID int64 + + log.Printf("Preparing query with the root id %d", index) + query := fmt.Sprintf("SELECT node_id FROM nodes WHERE root_id=%d", index) + log.Printf("Sending query") + err := db.QueryRow(query).Scan(&nodeID) + if err != nil { + log.Fatalf("[ E ] getRootNodeID query: %v\n\t\t\t query: %s\n", err, query) + } + log.Printf("Done Sending query") + + return nodeID +} + +// getCenterOfMass returns the center of mass of the given nodeID +func getCenterOfMass(nodeID int64) structs.Vec2 { + + var CenterOfMass [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(&CenterOfMass[0], &CenterOfMass[1]) + if err != nil { + log.Fatalf("[ E ] getCenterOfMass query: %v \n\t\t\tquery: %s\n", err, query) + } + + 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 +func getStarCoordinates(nodeID int64) structs.Vec2 { + var Coordinates [2]float64 + + starID := getStarID(nodeID) + + // get the star from the stars table + query := fmt.Sprintf("SELECT x, y FROM stars WHERE star_id=%d", starID) + err := db.QueryRow(query).Scan(&Coordinates[0], &Coordinates[1]) + if err != nil { + log.Fatalf("[ E ] getStarCoordinates query: %v \n\t\t\tquery: %s\n", err, query) + } + + fmt.Printf("%v\n", Coordinates) + + return structs.Vec2{X: Coordinates[0], Y: Coordinates[1]} +} + +// 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 +} diff --git a/backend/init.go b/backend/init.go new file mode 100644 index 0000000..0e8303e --- /dev/null +++ b/backend/init.go @@ -0,0 +1,44 @@ +package backend + +import ( + "database/sql" + "log" +) + +func InitStarsTable(db *sql.DB) { + query := `CREATE TABLE public.stars +( + star_id bigint NOT NULL DEFAULT nextval('stars_star_id_seq'::regclass), + x numeric, + y numeric, + vx numeric, + vy numeric, + m numeric +) +` + _, err := db.Exec(query) + if err != nil { + log.Fatalf("[ E ] InitNodesTable query: %v \n\t\t\tquery: %s\n", err, query) + } +} + +func InitNodesTable(db *sql.DB) { + query := `CREATE TABLE public.nodes + ( + node_id bigint NOT NULL DEFAULT nextval('nodes_node_id_seq'::regclass), + box_width numeric NOT NULL, + total_mass numeric NOT NULL, + depth integer, + star_id bigint NOT NULL, + root_id bigint NOT NULL, + isleaf boolean, + box_center numeric[] NOT NULL, + center_of_mass numeric[] NOT NULL, + subnodes bigint[] NOT NULL + ) +` + _, err := db.Exec(query) + if err != nil { + log.Fatalf("[ E ] InitNodesTable query: %v \n\t\t\tquery: %s\n", err, query) + } +} diff --git a/backend/insert.go b/backend/insert.go new file mode 100644 index 0000000..ab527a5 --- /dev/null +++ b/backend/insert.go @@ -0,0 +1,213 @@ +package backend + +import ( + "database/sql" + "encoding/csv" + "fmt" + "git.darknebu.la/GalaxySimulator/structs" + "io" + "io/ioutil" + "log" + "strconv" + "strings" + "time" +) + +// insertStar inserts the given star into the stars table and the nodes table tree +func InsertStar(database *sql.DB, star structs.Star2D, index int64) int64 { + db = database + start := time.Now() + + log.Printf("Inserting the star %v into the tree with the index %d", star, index) + + // insert the star into the stars table + starID := insertIntoStars(star) + + // get the root node id + query := fmt.Sprintf("select case when exists (select node_id from nodes where root_id=%d) then (select node_id from nodes where root_id=%d) else -1 end;", index, index) + var id int64 + err := db.QueryRow(query).Scan(&id) + + // if there are no rows in the result set, create a new tree + if err != nil { + log.Fatalf("[ E ] Get root node id query: %v\n\t\t\t query: %s\n", err, query) + } + + if id == -1 { + NewTree(db, 1000) + id = getRootNodeID(index) + } + + log.Printf("Node id of the root node %d: %d", id, index) + + // insert the star into the tree (using it's ID) starting at the root + insertIntoTree(starID, id) + elapsedTime := time.Since(start) + log.Printf("\t\t\t\t\t %s", elapsedTime) + return starID +} + +// insertIntoStars inserts the given star into the stars table +func insertIntoStars(star structs.Star2D) int64 { + // unpack the star + x := star.C.X + y := star.C.Y + vx := star.V.X + vy := star.V.Y + m := star.M + + // build the request query + query := fmt.Sprintf("INSERT INTO stars (x, y, vx, vy, m) VALUES (%f, %f, %f, %f, %f) RETURNING star_id", x, y, vx, vy, m) + + // execute the query + var starID int64 + err := db.QueryRow(query).Scan(&starID) + if err != nil { + log.Fatalf("[ E ] insert query: %v\n\t\t\t query: %s\n", err, query) + } + + return starID +} + +// insert into tree inserts the given star into the tree starting at the node with the given node id +func insertIntoTree(starID int64, nodeID int64) { + //starRaw := GetStar(starID) + //nodeCenter := getBoxCenter(nodeID) + //nodeWidth := getBoxWidth(nodeID) + //log.Printf("[ ] \t Inserting star %v into the node (c: %v, w: %v)", starRaw, nodeCenter, nodeWidth) + + // There exist four cases: + // | Contains a Star | Does not Contain a Star | + // ------------------ + --------------- + ----------------------- + + // Node is a Leaf | Impossible | insert into node | + // | | subdivide | + // ------------------ + --------------- + ----------------------- + + // Node is not a Leaf | insert preexist | insert into the subtree | + // | insert new | | + // ------------------ + --------------- + ----------------------- + + + // get the node with the given nodeID + // find out if the node contains a star or not + containsStar := containsStar(nodeID) + + // find out if the node is a leaf + isLeaf := isLeaf(nodeID) + + // if the node is a leaf and contains a star + // subdivide the tree + // insert the preexisting star into the correct subtree + // insert the new star into the subtree + if isLeaf == true && containsStar == true { + //log.Printf("Case 1, \t %v \t %v", nodeWidth, nodeCenter) + subdivide(nodeID) + //tree := printTree(nodeID) + + // Stage 1: Inserting the blocking star + blockingStarID := getStarID(nodeID) // get the id of the star blocking the node + blockingStar := GetStar(blockingStarID) // get the actual star + blockingStarQuadrant := quadrant(blockingStar, nodeID) // find out in which quadrant it belongs + quadrantNodeID := getQuadrantNodeID(nodeID, blockingStarQuadrant) // get the nodeID of that quadrant + insertIntoTree(blockingStarID, quadrantNodeID) // insert the star into that node + removeStarFromNode(nodeID) // remove the blocking star from the node it was blocking + + // Stage 1: Inserting the actual star + star := GetStar(starID) // get the actual star + starQuadrant := quadrant(star, nodeID) // find out in which quadrant it belongs + quadrantNodeID = getQuadrantNodeID(nodeID, starQuadrant) // get the nodeID of that quadrant + insertIntoTree(starID, nodeID) + } + + // if the node is a leaf and does not contain a star + // insert the star into the node and subdivide it + if isLeaf == true && containsStar == false { + //log.Printf("Case 2, \t %v \t %v", nodeWidth, nodeCenter) + directInsert(starID, nodeID) + } + + // if the node is not a leaf and contains a star + // insert the preexisting star into the correct subtree + // insert the new star into the subtree + if isLeaf == false && containsStar == true { + //log.Printf("Case 3, \t %v \t %v", nodeWidth, nodeCenter) + // Stage 1: Inserting the blocking star + blockingStarID := getStarID(nodeID) // get the id of the star blocking the node + blockingStar := GetStar(blockingStarID) // get the actual star + blockingStarQuadrant := quadrant(blockingStar, nodeID) // find out in which quadrant it belongs + quadrantNodeID := getQuadrantNodeID(nodeID, blockingStarQuadrant) // get the nodeID of that quadrant + insertIntoTree(blockingStarID, quadrantNodeID) // insert the star into that node + removeStarFromNode(nodeID) // remove the blocking star from the node it was blocking + + // Stage 1: Inserting the actual star + star := GetStar(blockingStarID) // get the actual star + starQuadrant := quadrant(star, nodeID) // find out in which quadrant it belongs + quadrantNodeID = getQuadrantNodeID(nodeID, starQuadrant) // get the nodeID of that quadrant + insertIntoTree(starID, nodeID) + } + + // if the node is not a leaf and does not contain a star + // insert the new star into the according subtree + if isLeaf == false && containsStar == false { + //log.Printf("Case 4, \t %v \t %v", nodeWidth, nodeCenter) + star := GetStar(starID) // get the actual star + starQuadrant := quadrant(star, nodeID) // find out in which quadrant it belongs + quadrantNodeID := getQuadrantNodeID(nodeID, starQuadrant) // get the if of that quadrant + insertIntoTree(starID, quadrantNodeID) // insert the star into that quadrant + } +} + +// directInsert inserts the star with the given ID into the given node inside of the given database +func directInsert(starID int64, nodeID int64) { + // build the query + query := fmt.Sprintf("UPDATE nodes SET star_id=%d WHERE node_id=%d", starID, nodeID) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] directInsert query: %v\n\t\t\t query: %s\n", err, query) + } +} + +// insertList inserts all the stars in the given .csv into the stars and nodes table +func InsertList(database *sql.DB, filename string) { + db = database + // open the file + content, readErr := ioutil.ReadFile(filename) + if readErr != nil { + panic(readErr) + } + + in := string(content) + reader := csv.NewReader(strings.NewReader(in)) + + // insert all the stars into the db + for { + record, err := reader.Read() + if err == io.EOF { + log.Println("EOF") + break + } + if err != nil { + log.Println("insertListErr") + panic(err) + } + + x, _ := strconv.ParseFloat(record[0], 64) + y, _ := strconv.ParseFloat(record[1], 64) + + star := structs.Star2D{ + C: structs.Vec2{ + X: x / 100000, + Y: y / 100000, + }, + V: structs.Vec2{ + X: 0, + Y: 0, + }, + M: 1000, + } + + fmt.Printf("Inserting (%f, %f)\n", star.C.X, star.C.Y) + InsertStar(db, star, 1) + } +} diff --git a/backend/is.go b/backend/is.go new file mode 100644 index 0000000..01bc747 --- /dev/null +++ b/backend/is.go @@ -0,0 +1,23 @@ +package backend + +import ( + "fmt" + "log" +) + +// isLeaf returns true if the node with the given id is a leaf +func isLeaf(nodeID int64) bool { + var isLeaf bool + + query := fmt.Sprintf("SELECT COALESCE(isleaf, FALSE) FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&isLeaf) + if err != nil { + log.Fatalf("[ E ] isLeaf query: %v\n\t\t\t query: %s\n", err, query) + } + + if isLeaf == true { + return true + } + + return false +} diff --git a/backend/new.go b/backend/new.go new file mode 100644 index 0000000..615a2e7 --- /dev/null +++ b/backend/new.go @@ -0,0 +1,48 @@ +package backend + +import ( + "database/sql" + "fmt" + "log" +) + +// newTree creates a new tree with the given width +func NewTree(database *sql.DB, width float64) { + db = database + + log.Printf("Creating a new tree with a width of %f", width) + + // get the current max root id + query := fmt.Sprintf("SELECT COALESCE(max(root_id), 0) FROM nodes") + var currentMaxRootID int64 + err := db.QueryRow(query).Scan(¤tMaxRootID) + if err != nil { + log.Fatalf("[ E ] max root id query: %v\n\t\t\t query: %s\n", err, query) + } + + // build the query creating a new node + query = fmt.Sprintf("INSERT INTO nodes (box_width, root_id, box_center, depth, isleaf, timestep) VALUES (%f, %d, '{0, 0}', 0, TRUE, %d)", width, currentMaxRootID+1, currentMaxRootID+1) + + // execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] insert new node query: %v\n\t\t\t query: %s\n", err, query) + } +} + +// newNode Inserts a new node into the database with the given parameters +func newNode(x float64, y float64, width float64, depth int64, timestep int64) int64 { + // build the query creating a new node + query := fmt.Sprintf("INSERT INTO nodes (box_center, box_width, depth, isleaf, timestep) VALUES ('{%f, %f}', %f, %d, TRUE, %d) RETURNING node_id", x, y, width, depth, timestep) + + var nodeID int64 + + // execute the query + err := db.QueryRow(query).Scan(&nodeID) + if err != nil { + log.Fatalf("[ E ] newNode query: %v\n\t\t\t query: %s\n", err, query) + } + + return nodeID +} diff --git a/backend/subdivide.go b/backend/subdivide.go new file mode 100644 index 0000000..a30fbe4 --- /dev/null +++ b/backend/subdivide.go @@ -0,0 +1,40 @@ +package backend + +import ( + "fmt" + "log" +) + +// subdivide subdivides the given node creating four child nodes +func subdivide(nodeID int64) { + boxWidth := getBoxWidth(nodeID) + boxCenter := getBoxCenter(nodeID) + originalDepth := getNodeDepth(nodeID) + timestep := getTimestepNode(nodeID) + log.Printf("Subdividing %d, setting the timestep to %d", nodeID, timestep) + + // calculate the new positions + newPosX := boxCenter[0] + (boxWidth / 2) + newPosY := boxCenter[1] + (boxWidth / 2) + newNegX := boxCenter[0] - (boxWidth / 2) + newNegY := boxCenter[1] - (boxWidth / 2) + newWidth := boxWidth / 2 + + // create new news with those positions + newNodeIDA := newNode(newPosX, newPosY, newWidth, originalDepth+1, timestep) + newNodeIDB := newNode(newPosX, newNegY, newWidth, originalDepth+1, timestep) + newNodeIDC := newNode(newNegX, newPosY, newWidth, originalDepth+1, timestep) + newNodeIDD := newNode(newNegX, newNegY, newWidth, originalDepth+1, timestep) + + // Update the subtrees of the parent node + + // build the query + query := fmt.Sprintf("UPDATE nodes SET subnode='{%d, %d, %d, %d}', isleaf=FALSE, timestep=%d WHERE node_id=%d", newNodeIDA, newNodeIDB, newNodeIDC, newNodeIDD, timestep, nodeID) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] subdivide query: %v\n\t\t\t query: %s\n", err, query) + } +} diff --git a/backend/update.go b/backend/update.go new file mode 100644 index 0000000..d51b115 --- /dev/null +++ b/backend/update.go @@ -0,0 +1,179 @@ +package backend + +import ( + "database/sql" + "fmt" + "git.darknebu.la/GalaxySimulator/structs" + "log" +) + +// updateTotalMass gets a tree index and returns the nodeID of the trees root node +func UpdateTotalMass(database *sql.DB, index int64) { + db = database + rootNodeID := getRootNodeID(index) + log.Printf("RootID: %d", rootNodeID) + updateTotalMassNode(rootNodeID) +} + +// updateTotalMassNode updates the total mass of the given node +func updateTotalMassNode(nodeID int64) float64 { + var totalmass float64 + + // get the subnode ids + var subnode [4]int64 + + query := fmt.Sprintf("SELECT subnode[1], subnode[2], subnode[3], subnode[4] FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&subnode[0], &subnode[1], &subnode[2], &subnode[3]) + if err != nil { + log.Fatalf("[ E ] updateTotalMassNode query: %v\n\t\t\t query: %s\n", err, query) + } + // TODO: implement the getSubtreeIDs(nodeID) []int64 {...} function + // iterate over all subnodes updating their total masses + for _, subnodeID := range subnode { + fmt.Println("----------------------------") + fmt.Printf("SubdnodeID: %d\n", subnodeID) + if subnodeID != 0 { + totalmass += updateTotalMassNode(subnodeID) + } else { + // get the starID for getting the star mass + starID := getStarID(nodeID) + fmt.Printf("StarID: %d\n", starID) + if starID != 0 { + mass := getStarMass(starID) + log.Printf("starID=%d \t mass: %f", starID, mass) + totalmass += mass + } + + // break, this stops a star from being counted multiple (4) times + break + } + fmt.Println("----------------------------") + } + + query = fmt.Sprintf("UPDATE nodes SET total_mass=%f WHERE node_id=%d", totalmass, nodeID) + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] insert total_mass query: %v\n\t\t\t query: %s\n", err, query) + } + + fmt.Printf("nodeID: %d \t totalMass: %f\n", nodeID, totalmass) + + return totalmass +} + +// updateCenterOfMass recursively updates the center of mass of all the nodes starting at the node with the given +// root index +func UpdateCenterOfMass(database *sql.DB, index int64) { + db = database + rootNodeID := getRootNodeID(index) + log.Printf("RootID: %d", rootNodeID) + updateCenterOfMassNode(rootNodeID) +} + +// updateCenterOfMassNode updates the center of mass of the node with the given nodeID recursively +// center of mass := ((x_1 * m) + (x_2 * m) + ... + (x_n * m)) / m +func updateCenterOfMassNode(nodeID int64) structs.Vec2 { + fmt.Println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") + + var centerOfMass structs.Vec2 + + // get the subnode ids + var subnode [4]int64 + var starID int64 + + query := fmt.Sprintf("SELECT subnode[1], subnode[2], subnode[3], subnode[4], star_id FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&subnode[0], &subnode[1], &subnode[2], &subnode[3], &starID) + if err != nil { + log.Fatalf("[ E ] updateCenterOfMassNode query: %v\n\t\t\t query: %s\n", err, query) + } + + // if the nodes does not contain a star but has children, update the center of mass + if subnode != ([4]int64{0, 0, 0, 0}) { + log.Println("[ ] recursing deeper") + + // define variables storing the values of the subnodes + var totalMass float64 + var centerOfMassX float64 + var centerOfMassY float64 + + // iterate over all the subnodes and calculate the center of mass of each node + for _, subnodeID := range subnode { + subnodeCenterOfMass := updateCenterOfMassNode(subnodeID) + + if subnodeCenterOfMass.X != 0 && subnodeCenterOfMass.Y != 0 { + fmt.Printf("SubnodeCenterOfMass: (%f, %f)\n", subnodeCenterOfMass.X, subnodeCenterOfMass.Y) + subnodeMass := getNodeTotalMass(subnodeID) + totalMass += subnodeMass + + centerOfMassX += subnodeCenterOfMass.X * subnodeMass + centerOfMassY += subnodeCenterOfMass.Y * subnodeMass + } + } + + // calculate the overall center of mass of the subtree + centerOfMass = structs.Vec2{ + X: centerOfMassX / totalMass, + Y: centerOfMassY / totalMass, + } + + // else, use the star as the center of mass (this can be done, because of the rule defining that there + // can only be one star in a cell) + } else { + log.Println("[ ] using the star in the node as the center of mass") + log.Printf("[ ] NodeID: %v", nodeID) + starID := getStarID(nodeID) + + if starID == 0 { + log.Println("[ ] StarID == 0...") + centerOfMass = structs.Vec2{ + X: 0, + Y: 0, + } + } else { + log.Printf("[ ] NodeID: %v", starID) + star := GetStar(starID) + centerOfMassX := star.C.X + centerOfMassY := star.C.Y + centerOfMass = structs.Vec2{ + X: centerOfMassX, + Y: centerOfMassY, + } + } + } + + // build the query + query = fmt.Sprintf("UPDATE nodes SET center_of_mass='{%f, %f}' WHERE node_id=%d", centerOfMass.X, centerOfMass.Y, nodeID) + + // Execute the query + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] update center of mass query: %v\n\t\t\t query: %s\n", err, query) + } + + fmt.Printf("[ ] CenterOfMass: (%f, %f)\n", centerOfMass.X, centerOfMass.Y) + + return centerOfMass +} + +// updateStarForce updates the force acting on the star +func updateStarForce(db *sql.DB, starID int64, force structs.Vec2) structs.Star2D { + + star := GetStar(starID) + newStar := structs.Star2D{ + structs.Vec2{star.C.X, star.C.Y}, + structs.Vec2{force.X, force.Y}, + star.M, + } + + // updated the stars Force + query := fmt.Sprintf("UPDATE stars SET vx=%f, vy=%f WHERE star_id=%d", force.X, force.Y, starID) + rows, err := db.Query(query) + defer rows.Close() + if err != nil { + log.Fatalf("[ E ] updateStarForce query: %v\n\t\t\t query: %s\n", err, query) + } + + return newStar +} -- cgit 1.4.1