private static FieldOctree <GravityCell> BuildField(MapOctree mapSnapshot, double gravConstant) { // Dig through the tree, and construct my tree with partially populated leaves (mass but no force) List <GravityCell> allLeavesList = new List <GravityCell>(); FieldOctree <GravityCell> retVal = BuildField_Node(allLeavesList, null, mapSnapshot); GravityCell[] allLeaves = allLeavesList.ToArray(); Vector3D[] forces = new Vector3D[allLeaves.Length]; // Calculate the force of gravity for each cell //TODO: Individual far away cells shouldn't need to be processed individually, use their parent instead BuildField_Forces(allLeaves, forces, gravConstant); // Sort on token to make things easier for the swapper SortedList <long, int> indicesByToken = new SortedList <long, int>(); for (int cntr = 0; cntr < allLeaves.Length; cntr++) { indicesByToken.Add(allLeaves[cntr].Token, cntr); } // Inject the forces into the leaves BuildField_SwapLeaves(retVal, indicesByToken, forces); // Exit Function return(retVal); }
private void Timer_Tick(object sender, EventArgs e) { _timer.IsEnabled = false; DateTime startTime = DateTime.UtcNow; MapOctree snapshot = _map.LatestSnapshot; if (snapshot == null || (_lastSnapshotToken != null && snapshot.Token == _lastSnapshotToken.Value)) { // There is nothing to do, rig the timer to try again later ScheduleNextTick(startTime); return; } _lastSnapshotToken = snapshot.Token; double gravConstant = this.GravitationalConstant; // Build the field on a different thread var task = Task.Factory.StartNew(() => { _fieldRoot = BuildField(snapshot, gravConstant); }); // After the tree is built, schedule the next build from within this current thread task.ContinueWith(resultTask => { // Schedule the next snapshot ScheduleNextTick(startTime); }, TaskScheduler.FromCurrentSynchronizationContext()); }
private static void BuildField_SwapLeaves(FieldOctree <GravityCell> node, SortedList <long, int> indicesByToken, Vector3D[] forces) { if (node.IsLeaf) { // Find the index into forces int index = indicesByToken[node.Leaf.Token]; // Swap it out node.Leaf = new GravityCell(node.Leaf.Token, node.Leaf.Mass, node.Leaf.Position, forces[index]); } else { // Every child is non null, so just recurse with each one BuildField_SwapLeaves(node.X0_Y0_Z0, indicesByToken, forces); BuildField_SwapLeaves(node.X0_Y0_Z1, indicesByToken, forces); BuildField_SwapLeaves(node.X0_Y1_Z0, indicesByToken, forces); BuildField_SwapLeaves(node.X0_Y1_Z1, indicesByToken, forces); BuildField_SwapLeaves(node.X1_Y0_Z0, indicesByToken, forces); BuildField_SwapLeaves(node.X1_Y0_Z1, indicesByToken, forces); BuildField_SwapLeaves(node.X1_Y1_Z0, indicesByToken, forces); BuildField_SwapLeaves(node.X1_Y1_Z1, indicesByToken, forces); } }
private static void GetLeaves_Recurse(List <GravityCell> returnList, FieldOctree <GravityCell> node) { if (node == null) { return; } if (node.IsLeaf) { returnList.Add(node.Leaf); } else { // Recurse GetLeaves_Recurse(returnList, node.X0_Y0_Z0); GetLeaves_Recurse(returnList, node.X0_Y0_Z1); GetLeaves_Recurse(returnList, node.X0_Y1_Z0); GetLeaves_Recurse(returnList, node.X0_Y1_Z1); GetLeaves_Recurse(returnList, node.X1_Y0_Z0); GetLeaves_Recurse(returnList, node.X1_Y0_Z1); GetLeaves_Recurse(returnList, node.X1_Y1_Z0); GetLeaves_Recurse(returnList, node.X1_Y1_Z1); } }
private static FieldOctree <GravityCell> BuildField_Node(List <GravityCell> allLeaves, MapOctree[] ancestors, MapOctree node) { #region Add up mass double massOfNode = 0d; if (node.Items != null) { foreach (MapObjectInfo item in node.Items) { massOfNode += item.Mass; } } #endregion if (!node.HasChildren) { #region Leaf // Exit Function GravityCell cell = new GravityCell(BuildField_NodeSprtAncestorMass(ancestors, node.MinRange, node.MaxRange) + massOfNode, node.CenterPoint); allLeaves.Add(cell); return(new FieldOctree <GravityCell>(node.MinRange, node.MaxRange, node.CenterPoint, cell)); #endregion } // Make the return node FieldOctree <GravityCell> retVal = new FieldOctree <GravityCell>(node.MinRange, node.MaxRange, node.CenterPoint); #region Build ancestor arrays // Create new arrays that hold this node's values (to pass to the children) MapOctree[] ancestorsNew = null; if (ancestors == null) { // This is the root ancestorsNew = new MapOctree[] { node }; } else { // This is a middle ancestor ancestorsNew = new MapOctree[ancestors.Length + 1]; Array.Copy(ancestors, ancestorsNew, ancestors.Length); ancestorsNew[ancestorsNew.Length - 1] = node; } #endregion #region Add the children //NOTE: The map's octree will leave children null if there are no items in them. But this tree always has all 8 children if (node.X0_Y0_Z0 == null) { Point3D childMin = new Point3D(node.MinRange.X, node.MinRange.Y, node.MinRange.Z); Point3D childMax = new Point3D(node.CenterPoint.X, node.CenterPoint.Y, node.CenterPoint.Z); retVal.X0_Y0_Z0 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X0_Y0_Z0 = BuildField_Node(allLeaves, ancestorsNew, node.X0_Y0_Z0); } if (node.X0_Y0_Z1 == null) { Point3D childMin = new Point3D(node.MinRange.X, node.MinRange.Y, node.CenterPoint.Z); Point3D childMax = new Point3D(node.CenterPoint.X, node.CenterPoint.Y, node.MaxRange.Z); retVal.X0_Y0_Z1 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X0_Y0_Z1 = BuildField_Node(allLeaves, ancestorsNew, node.X0_Y0_Z1); } if (node.X0_Y1_Z0 == null) { Point3D childMin = new Point3D(node.MinRange.X, node.CenterPoint.Y, node.MinRange.Z); Point3D childMax = new Point3D(node.CenterPoint.X, node.MaxRange.Y, node.CenterPoint.Z); retVal.X0_Y1_Z0 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X0_Y1_Z0 = BuildField_Node(allLeaves, ancestorsNew, node.X0_Y1_Z0); } if (node.X0_Y1_Z1 == null) { Point3D childMin = new Point3D(node.MinRange.X, node.CenterPoint.Y, node.CenterPoint.Z); Point3D childMax = new Point3D(node.CenterPoint.X, node.MaxRange.Y, node.MaxRange.Z); retVal.X0_Y1_Z1 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X0_Y1_Z1 = BuildField_Node(allLeaves, ancestorsNew, node.X0_Y1_Z1); } if (node.X1_Y0_Z0 == null) { Point3D childMin = new Point3D(node.CenterPoint.X, node.MinRange.Y, node.MinRange.Z); Point3D childMax = new Point3D(node.MaxRange.X, node.CenterPoint.Y, node.CenterPoint.Z); retVal.X1_Y0_Z0 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X1_Y0_Z0 = BuildField_Node(allLeaves, ancestorsNew, node.X1_Y0_Z0); } if (node.X1_Y0_Z1 == null) { Point3D childMin = new Point3D(node.CenterPoint.X, node.MinRange.Y, node.CenterPoint.Z); Point3D childMax = new Point3D(node.MaxRange.X, node.CenterPoint.Y, node.MaxRange.Z); retVal.X1_Y0_Z1 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X1_Y0_Z1 = BuildField_Node(allLeaves, ancestorsNew, node.X1_Y0_Z1); } if (node.X1_Y1_Z0 == null) { Point3D childMin = new Point3D(node.CenterPoint.X, node.CenterPoint.Y, node.MinRange.Z); Point3D childMax = new Point3D(node.MaxRange.X, node.MaxRange.Y, node.CenterPoint.Z); retVal.X1_Y1_Z0 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X1_Y1_Z0 = BuildField_Node(allLeaves, ancestorsNew, node.X1_Y1_Z0); } if (node.X1_Y1_Z1 == null) { Point3D childMin = new Point3D(node.CenterPoint.X, node.CenterPoint.Y, node.CenterPoint.Z); Point3D childMax = new Point3D(node.MaxRange.X, node.MaxRange.Y, node.MaxRange.Z); retVal.X1_Y1_Z1 = BuildField_Node(allLeaves, BuildField_NodeSprtAncestorMass(ancestors, childMin, childMax) + massOfNode, childMin, childMax); } else { retVal.X1_Y1_Z1 = BuildField_Node(allLeaves, ancestorsNew, node.X1_Y1_Z1); } #endregion // Exit Function return(retVal); }
public T GetLeaf(Point3D position) { if (!Math3D.IsInside_AABB(_minRange, _maxRange, position)) { // Let a tree node throw an exception. The container of the entire tree can decide whether to return null or an exception if a request // is out of bounds of the entire tree. throw new ArgumentOutOfRangeException("The position is not inside this tree"); } if (_isLeaf) { return(this.Leaf); } // Figure out which child to look in //NOTE: Because the children can be swapped out at any momemnt, I have to get that instance, and then check if that instance is null (can't // double call this.Child) FieldOctree <T> child = null; if (position.X <= _centerPoint.X && position.Y <= _centerPoint.Y && position.Z <= _centerPoint.Z) // 0 { child = this.X0_Y0_Z0; } else if (position.X <= _centerPoint.X && position.Y <= _centerPoint.Y && position.Z > _centerPoint.Z) // 1 { child = this.X0_Y0_Z1; } else if (position.X <= _centerPoint.X && position.Y > _centerPoint.Y && position.Z <= _centerPoint.Z) // 2 { child = this.X0_Y1_Z0; } else if (position.X <= _centerPoint.X && position.Y > _centerPoint.Y && position.Z > _centerPoint.Z) // 3 { child = this.X0_Y1_Z1; } else if (position.X > _centerPoint.X && position.Y <= _centerPoint.Y && position.Z <= _centerPoint.Z) // 4 { child = this.X1_Y0_Z0; } else if (position.X > _centerPoint.X && position.Y <= _centerPoint.Y && position.Z > _centerPoint.Z) // 5 { child = this.X1_Y0_Z1; } else if (position.X > _centerPoint.X && position.Y > _centerPoint.Y && position.Z <= _centerPoint.Z) // 6 { child = this.X1_Y1_Z0; } else //if (position.X > _centerPoint.X && position.Y > _centerPoint.Y && position.Z > _centerPoint.Z) // 7 { child = this.X1_Y1_Z1; } if (child == null) { return(null); } // Recurse return(child.GetLeaf(position)); }