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 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 static Brush GetBrush(MapOctree node, Func <MapOctree, double> getValue, double valueMult, Color color) { const double MAXOPACITY = .25; //if (node.Items.Any(o => o.MapObject is ShipPlayer)) //{ // return new SolidColorBrush(UtilityWPF.ColorFromHex("600000FF")); //} // Add up the resources double resourceValue = getValue(node); if (resourceValue.IsNearZero()) { return(null); } double area = (node.MaxRange.X - node.MinRange.X) * (node.MaxRange.Y - node.MinRange.Y); double opacity = (resourceValue * valueMult) / area; if (opacity > 1) { opacity = 1; } Color colorFinal = UtilityWPF.AlphaBlend(color, Colors.Transparent, opacity * MAXOPACITY); return(new SolidColorBrush(colorFinal)); }
/// <summary> /// This fires in an arbitrary thread. It looks at what's in the map, and builds instructions that will be run in the main thread /// (adds/removes) /// </summary> public void Update_AnyThread(double elapsedTime) { MapOctree snapshot = _map.LatestSnapshot; if (snapshot == null) { return; } IEnumerable <MapObjectInfo> allItems = snapshot.GetItems(); //_map.GetAllItems(true) //TODO: May want to use this to get disposed items // Look for too few/many ChangeInstruction[] asteroids = ExamineAsteroids(allItems, _boundry); ChangeInstruction[] minerals = ExamineMinerals(allItems, _boundry, _mineralTypesByValue); // Store these instructions for the main thread to do if (asteroids != null || minerals != null) { ChangeInstruction[] instructions = UtilityCore.ArrayAdd(asteroids, minerals); if (instructions.Length > MAXCHANGES) { instructions = UtilityCore.RandomOrder(instructions, MAXCHANGES).ToArray(); } _instructions = instructions; } }
public ResourcesVisual(MapOctree snapshot, Func<MapOctree, double> getValue, Color color, Transform transform) { //const double STANDARDAREA = 1000000; const double STANDARDAREA = 750000; //TODO: Use this to get a normalization multiplier double totalArea = (snapshot.MaxRange.X - snapshot.MinRange.X) * (snapshot.MaxRange.Y - snapshot.MinRange.Y); double valueMult = totalArea / STANDARDAREA; _visual = new DrawingVisual(); using (DrawingContext dc = _visual.RenderOpen()) { foreach (MapOctree node in snapshot.Descendants(o => o.Children)) { if (node.Items == null) { continue; } // Get the color Brush brush = GetBrush(node, getValue, valueMult, color); if (brush == null) { continue; } // Define the rectangle Point min = transform.Transform(new Point(node.MinRange.X, -node.MinRange.Y)); // need to negate Y Point max = transform.Transform(new Point(node.MaxRange.X, -node.MaxRange.Y)); double x = min.X; double y = max.Y; // can't use min, because Y is backward double width = max.X - min.X; double height = Math.Abs(max.Y - min.Y); // Y is all flipped around // Fill the box dc.DrawRectangle(brush, null, new Rect(x, y, width, height)); } } }
public ResourcesVisual(MapOctree snapshot, Func <MapOctree, double> getValue, Color color, Transform transform) { //const double STANDARDAREA = 1000000; const double STANDARDAREA = 750000; //TODO: Use this to get a normalization multiplier double totalArea = (snapshot.MaxRange.X - snapshot.MinRange.X) * (snapshot.MaxRange.Y - snapshot.MinRange.Y); double valueMult = totalArea / STANDARDAREA; _visual = new DrawingVisual(); using (DrawingContext dc = _visual.RenderOpen()) { foreach (MapOctree node in snapshot.Descendants(o => o.Children)) { if (node.Items == null) { continue; } // Get the color Brush brush = GetBrush(node, getValue, valueMult, color); if (brush == null) { continue; } // Define the rectangle Point min = transform.Transform(new Point(node.MinRange.X, -node.MinRange.Y)); // need to negate Y Point max = transform.Transform(new Point(node.MaxRange.X, -node.MaxRange.Y)); double x = min.X; double y = max.Y; // can't use min, because Y is backward double width = max.X - min.X; double height = Math.Abs(max.Y - min.Y); // Y is all flipped around // Fill the box dc.DrawRectangle(brush, null, new Rect(x, y, width, height)); } } }
private Point3D[] GetNearbyObjects(Point3D center, double radius) { //TODO: filter what they can drag toward //TODO: frequent requests should reuse results of a prev call MapOctree snapshot = _map.LatestSnapshot; if (snapshot != null) { // The snapshot is designed for this kind of request, so use it return(snapshot.GetItems(center, radius). Where(o => o.MapObject != _selectedItem.Item). // don't return the item behing dragged Select(o => o.Position).ToArray()); } double radSquared = radius * radius; // Do a brute force scan of all objects return(_map.GetAllItems(). Where(o => o != _selectedItem.Item). // don't return the item behing dragged Select(o => o.PhysicsBody.Position). Where(o => (center - o).LengthSquared <= radSquared). ToArray()); }
private Tuple<MapObjectInfo, double, ForceSettings_Initial>[] GetNeighbors(MapOctree snapshot, Point3D position) { // Get nearby items, sort by distance double searchRadius = this.SearchRadius; var initial = snapshot.GetItems(position, searchRadius). Where(o => o.Token != this.Token). Select(o => Tuple.Create(o, (o.Position - position).LengthSquared)). OrderBy(o => o.Item2). ToArray(); // Get chase settings for each item //NOTE: This needs to be done after sorting on distance, because bots will have different settings if too many are actively chased var retVal = new List<Tuple<MapObjectInfo, double, ForceSettings_Initial>>(); int chaseNeighborCount = this.ChaseNeighborCount; int currentNeighborCount = 0; foreach (var item in initial) { ForceSettings_Initial chaseProps = GetForceSetting_Initial(ref currentNeighborCount, item.Item1, chaseNeighborCount); if (chaseProps != null) { retVal.Add(Tuple.Create(item.Item1, item.Item2, chaseProps)); } } return retVal.ToArray(); }
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); }
private static Brush GetBrush(MapOctree node, Func<MapOctree, double> getValue, double valueMult, Color color) { const double MAXOPACITY = .25; //if (node.Items.Any(o => o.MapObject is ShipPlayer)) //{ // return new SolidColorBrush(UtilityWPF.ColorFromHex("600000FF")); //} // Add up the resources double resourceValue = getValue(node); if (resourceValue.IsNearZero()) { return null; } double area = (node.MaxRange.X - node.MinRange.X) * (node.MaxRange.Y - node.MinRange.Y); double opacity = (resourceValue * valueMult) / area; if (opacity > 1) { opacity = 1; } Color colorFinal = UtilityWPF.AlphaBlend(color, Colors.Transparent, opacity * MAXOPACITY); return new SolidColorBrush(colorFinal); }
private void DrawAsteroidsMinerals(Transform transform, bool shouldPopulateCanvas) { const double MINERALMULT = 100d; const double ASTEROIDMULT = .25d; MapOctree snapshot = _map.LatestSnapshot; if (snapshot == null) { return; } if (snapshot.X0_Y0_Z0 == null && snapshot.X0_Y0_Z1 == null && snapshot.X0_Y1_Z0 == null && snapshot.X0_Y1_Z1 == null && snapshot.X1_Y0_Z0 == null && snapshot.X1_Y0_Z1 == null && snapshot.X1_Y1_Z0 == null && snapshot.X1_Y1_Z1 == null) { // This was happening because build snapshot wasn't throwing out disposed objects return; } #region GetValue delegates // These get the value of the items in that node. The value will be divided by the area of the node to figure out opacity. // So the value/area should be 1 when the node is rich in that resource Func <MapOctree, double> getMineralValue = new Func <MapOctree, double>((node) => { Mineral[] minerals = node.Items. Where(o => o.MapObject is Mineral). Select(o => (Mineral)o.MapObject). ToArray(); if (minerals.Length == 0) { return(0d); } else { return(minerals.Sum(o => Convert.ToDouble(o.Credits) * MINERALMULT)); } }); Func <MapOctree, double> getAsteroidValue = new Func <MapOctree, double>((node) => { Asteroid[] asteroids = node.Items. Where(o => o.MapObject is Asteroid). Select(o => (Asteroid)o.MapObject). ToArray(); if (asteroids.Length == 0) { return(0d); } else { return(asteroids.Sum(o => o.PhysicsBody.Mass * ASTEROIDMULT)); } }); #endregion // Create the visuals _mineralVisual = new ResourcesVisual(snapshot, getMineralValue, Colors.Lime, transform); _asteroidVisual = new ResourcesVisual(snapshot, getAsteroidValue, Colors.LightGray, transform); // Show a random one (the timer will swap them every tick) if (shouldPopulateCanvas) { _isShowingMineral = StaticRandom.NextBool(); canvasMap.Children.Insert(0, _isShowingMineral ? _mineralVisual : _asteroidVisual); } #region Get Stats //double width = snapshot.MaxRange.X - snapshot.MinRange.X; //double height = snapshot.MaxRange.Y - snapshot.MinRange.Y; //double diagonal = Math3D.Avg(width, height) * Math.Sqrt(2); //double area = width * height; //double mineralValue = snapshot.Descendants(o => o.Children).Where(o => o.Items != null).Sum(o => getMineralValue(o)); //double asteroidValue = snapshot.Descendants(o => o.Children).Where(o => o.Items != null).Sum(o => getAsteroidValue(o)); //string stats = ""; //stats += string.Format("width\t{0}\r\n", width); //stats += string.Format("height\t{0}\r\n", height); //stats += string.Format("diagonal\t{0}\r\n", diagonal); //stats += string.Format("area\t{0}\r\n", area); //stats += string.Format("minerals\t{0}\r\n", mineralValue); //stats += string.Format("asteroids\t{0}\r\n", asteroidValue); //stats += "\r\n"; //stats += string.Format("min/diag\t{0}\r\n", mineralValue / diagonal); //stats += string.Format("ast/diag\t{0}\r\n", asteroidValue / diagonal); //stats += string.Format("min/area\t{0}\r\n", mineralValue / area); //stats += string.Format("ast/area\t{0}\r\n", asteroidValue / area); //Clipboard.SetText(stats); #endregion }