/// <summary> /// Create a new Spring instance ///<param name="fi1"> the first ForceItem endpoint</param> /// <param name="fi2"> the second ForceItem endpoint </param> /// <param name="k">the spring tension co-efficient </param> /// <param name="len">the spring's resting length </param> /// </summary> public Spring(ForceItem fi1, ForceItem fi2, float k, float len) { item1 = fi1; item2 = fi2; coeff = k; length = len; }
/// <summary> /// Inserts the specified force. /// </summary> /// <param name="p">The p.</param> /// <param name="n">The n.</param> /// <param name="x1">The x1.</param> /// <param name="y1">The y1.</param> /// <param name="x2">The x2.</param> /// <param name="y2">The y2.</param> private void Insert(ForceItem p, QuadTreeNode n, float x1, float y1, float x2, float y2) { // try to Insert particle p at node n in the quadtree // by construction, each leaf will contain either 1 or 0 particles if (n.hasChildren) { // n contains more than 1 particle InsertHelper(p, n, x1, y1, x2, y2); } else if (n.value != null) { // n contains 1 particle if (IsSameLocation(n.value, p)) { InsertHelper(p, n, x1, y1, x2, y2); } else { ForceItem v = n.value; n.value = null; InsertHelper(v, n, x1, y1, x2, y2); InsertHelper(p, n, x1, y1, x2, y2); } } else { // n is empty, so is a leaf n.value = p; } }
/// <summary> /// Determines whether the two force are at the same location. /// </summary> /// <param name="f1">The f1.</param> /// <param name="f2">The f2.</param> /// <returns> /// <c>true</c> if [is same location] [the specified f1]; otherwise, <c>false</c>. /// </returns> private static bool IsSameLocation(ForceItem f1, ForceItem f2) { float dx = Math.Abs(f1.Location[0] - f2.Location[0]); float dy = Math.Abs(f1.Location[1] - f2.Location[1]); return(dx < 0.01 && dy < 0.01); }
/// <summary> /// Inserts helper method. /// </summary> /// <param name="p">The p.</param> /// <param name="n">The n.</param> /// <param name="x1">The x1.</param> /// <param name="y1">The y1.</param> /// <param name="x2">The x2.</param> /// <param name="y2">The y2.</param> private void InsertHelper(ForceItem p, QuadTreeNode n, float x1, float y1, float x2, float y2) { float x = p.Location[0], y = p.Location[1]; float splitx = (x1 + x2) / 2; float splity = (y1 + y2) / 2; int i = (x >= splitx ? 1 : 0) + (y >= splity ? 2 : 0); // create new child node, if necessary if (n.children[i] == null) { n.children[i] = factory.GetQuadTreeNode(); n.hasChildren = true; } // update bounds if (i == 1 || i == 3) { x1 = splitx; } else { x2 = splitx; } if (i > 1) { y1 = splity; } else { y2 = splity; } // recurse Insert(p, n.children[i], x1, y1, x2, y2); }
/// <summary> /// Checks a ForceItem to make sure its values are all valid numbers(i.e., not NaNs). /// </summary> /// <param name="item">The item to check.</param> /// <returns> /// <c>true</c> if the specified item is valid; otherwise, <c>false</c>. /// </returns> public static bool isValid(ForceItem item) { return (!(float.IsNaN(item.location[0]) || float.IsNaN(item.location[1]) || float.IsNaN(item.plocation[0]) || float.IsNaN(item.plocation[1]) || float.IsNaN(item.velocity[0]) || float.IsNaN(item.velocity[1]) || float.IsNaN(item.force[0]) || float.IsNaN(item.force[1]))); }
/** * Calculates the force vector acting on the given item. * @param item the ForceItem for which to compute the force */ public override void GetForce(ForceItem item) { try { ForceHelper(item, root, xMin, yMin, xMax, yMax); } catch (StackOverflowException e) { // TODO: safe to remove? Trace.WriteLine(e.Message); } }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2"> the second endpoint of the spring</param> /// <param name="coeff">the spring coefficient</param> /// <param name="length">the spring length</param> /// <returns> the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2, float coeff, float length) { if (item1 == null || item2 == null) { throw new ArgumentException("ForceItems must be non-null"); } Spring s = Spring.Factory.getSpring(item1, item2, coeff, length); springs.Add(s); return(s); }
/// <summary> /// Inserts an item into the quadtree. /// </summary> /// <param name="item"> the ForceItem to add.</param> public void Insert(ForceItem item) { // Insert item into the quadtrees try { Insert(item, root, xMin, yMin, xMax, yMax); } catch (StackOverflowException e) { // TODO: safe to remove? Trace.WriteLine(e.Message); } }
/// <summary> /// Get a Spring instance and set it to the given parameters. /// </summary> public Spring getSpring(ForceItem f1, ForceItem f2, float k, float length) { if (springs.Count > 0) { Spring s = springs[springs.Count - 1]; springs.Remove(s); s.Item1 = f1; s.Item2 = f2; s.Coeff = k; s.Length = length; return(s); } else { return(new Spring(f1, f2, k, length)); } }
/// <summary> /// Gets the force. /// </summary> /// <param name="s">The s.</param> public override void GetForce(Spring s) { ForceItem item1 = s.Item1; ForceItem item2 = s.Item2; float length = (s.Length < 0 ? parms[SpringLength] : s.Length); float x1 = item1.Location[0], y1 = item1.Location[1]; float x2 = item2.Location[0], y2 = item2.Location[1]; float dx = x2 - x1, dy = y2 - y1; float r = (float)Math.Sqrt(dx * dx + dy * dy); if (r == 0.0) { dx = ((float)rnd.NextDouble() - 0.5f) / 50.0f; dy = ((float)rnd.NextDouble() - 0.5f) / 50.0f; r = (float)Math.Sqrt(dx * dx + dy * dy); } float d = r - length; float coeff = (s.Coeff < 0 ? parms[SpringCoeff] : s.Coeff) * d / r; item1.Force[0] += coeff * dx; item1.Force[1] += coeff * dy; item2.Force[0] += -coeff * dx; item2.Force[1] += -coeff * dy; }
/// <summary> /// Add a ForceItem to the simulation. /// </summary> /// <param name="item"> item the ForceItem to add.</param> public void addItem(ForceItem item) { mItems.Add(item); }
/// <summary> /// Returns the force acting on the given item. /// </summary> /// <param name="item"></param> public override void GetForce(ForceItem item) { item.Force[0] -= parms[DragCoeff] * item.Velocity[0]; item.Force[1] -= parms[DragCoeff] * item.Velocity[1]; }
/// <summary> /// Utility method. /// </summary> /// <param name="item">The item.</param> /// <param name="n">The n.</param> /// <param name="x1">The x1.</param> /// <param name="y1">The y1.</param> /// <param name="x2">The x2.</param> /// <param name="y2">The y2.</param> private void ForceHelper(ForceItem item, QuadTreeNode n, float x1, float y1, float x2, float y2) { float dx = n.com[0] - item.Location[0]; float dy = n.com[1] - item.Location[1]; float r = (float)Math.Sqrt(dx * dx + dy * dy); bool same = false; if (r == 0.0f) { // if items are in the exact same place, add some noise dx = Convert.ToSingle((rand.NextDouble() - 0.5D) / 50.0D); dy = Convert.ToSingle((rand.NextDouble() - 0.5D) / 50.0D); r = (float)Math.Sqrt(dx * dx + dy * dy); same = true; } bool minDist = parms[MIN_DISTANCE] > 0f && r > parms[MIN_DISTANCE]; // the Barnes-Hut approximation criteria is if the ratio of the // size of the quadtree box to the distance between the point and // the box's center of mass is beneath some threshold theta. if ((!n.hasChildren && n.value != item) || (!same && (x2 - x1) / r < parms[BARNES_HUT_THETA])) { if (minDist) { return; } // either only 1 particle or we meet criteria // for Barnes-Hut approximation, so calc force float v = parms[GRAVITATIONAL_CONST] * item.Mass * n.mass / (r * r * r); item.Force[0] += v * dx; item.Force[1] += v * dy; } else if (n.hasChildren) { // recurse for more accurate calculation float splitx = (x1 + x2) / 2; float splity = (y1 + y2) / 2; for (int i = 0; i < n.children.Length; i++) { if (n.children != null && n.children[i] != null) { ForceHelper(item, n.children[i], (i == 1 || i == 3 ? splitx : x1), (i > 1 ? splity : y1), (i == 1 || i == 3 ? x2 : splitx), (i > 1 ? y2 : splity)); } } if (minDist) { return; } if (n.value != null && n.value != item) { float v = parms[GRAVITATIONAL_CONST] * item.Mass * n.value.Mass / (r * r * r); item.Force[0] += v * dx; item.Force[1] += v * dy; } } }
/// <summary> /// Checks a ForceItem to make sure its values are all valid numbers(i.e., not NaNs). /// </summary> /// <param name="item">The item to check.</param> /// <returns> /// <c>true</c> if the specified item is valid; otherwise, <c>false</c>. /// </returns> public static bool isValid(ForceItem item) { return !(float.IsNaN(item.location[0]) || float.IsNaN(item.location[1]) || float.IsNaN(item.plocation[0]) || float.IsNaN(item.plocation[1]) || float.IsNaN(item.velocity[0]) || float.IsNaN(item.velocity[1]) || float.IsNaN(item.force[0]) || float.IsNaN(item.force[1])); }
/// <summary> /// Remove a ForceItem to the simulation. /// </summary> /// <param name="item">Item the ForceItem to remove.</param> /// <returns></returns> public bool removeItem(ForceItem item) { return(mItems.Remove(item)); }
/// <summary> ///Returns the force acting on the given item. /// </summary> public virtual void GetForce(ForceItem item) { throw new NotImplementedException("This class does not support this operation"); }
/// <summary> /// Utility method. /// </summary> /// <param name="item">The item.</param> /// <param name="n">The n.</param> /// <param name="x1">The x1.</param> /// <param name="y1">The y1.</param> /// <param name="x2">The x2.</param> /// <param name="y2">The y2.</param> private void ForceHelper(ForceItem item, QuadTreeNode n, float x1, float y1, float x2, float y2) { float dx = n.com[0] - item.Location[0]; float dy = n.com[1] - item.Location[1]; float r = (float)Math.Sqrt(dx * dx + dy * dy); bool same = false; if (r == 0.0f) { // if items are in the exact same place, add some noise dx = Convert.ToSingle((rand.NextDouble() - 0.5D) / 50.0D); dy = Convert.ToSingle((rand.NextDouble() - 0.5D) / 50.0D); r = (float)Math.Sqrt(dx * dx + dy * dy); same = true; } bool minDist = parms[MIN_DISTANCE] > 0f && r > parms[MIN_DISTANCE]; // the Barnes-Hut approximation criteria is if the ratio of the // size of the quadtree box to the distance between the point and // the box's center of mass is beneath some threshold theta. if ((!n.hasChildren && n.value != item) || (!same && (x2 - x1) / r < parms[BARNES_HUT_THETA])) { if (minDist) return; // either only 1 particle or we meet criteria // for Barnes-Hut approximation, so calc force float v = parms[GRAVITATIONAL_CONST] * item.Mass * n.mass / (r * r * r); item.Force[0] += v * dx; item.Force[1] += v * dy; } else if (n.hasChildren) { // recurse for more accurate calculation float splitx = (x1 + x2) / 2; float splity = (y1 + y2) / 2; for (int i = 0; i < n.children.Length; i++) { if (n.children != null && n.children[i] != null) { ForceHelper(item, n.children[i], (i == 1 || i == 3 ? splitx : x1), (i > 1 ? splity : y1), (i == 1 || i == 3 ? x2 : splitx), (i > 1 ? y2 : splity)); } } if (minDist) return; if (n.value != null && n.value != item) { float v = parms[GRAVITATIONAL_CONST] * item.Mass * n.value.Mass / (r * r * r); item.Force[0] += v * dx; item.Force[1] += v * dy; } } }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2">the second endpoint of the spring</param> /// <returns>the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2) { return(addSpring(item1, item2, -1.0F, -1.0F)); }
/// <summary> /// Inserts helper method. /// </summary> /// <param name="p">The p.</param> /// <param name="n">The n.</param> /// <param name="x1">The x1.</param> /// <param name="y1">The y1.</param> /// <param name="x2">The x2.</param> /// <param name="y2">The y2.</param> private void InsertHelper(ForceItem p, QuadTreeNode n, float x1, float y1, float x2, float y2) { float x = p.Location[0], y = p.Location[1]; float splitx = (x1 + x2) / 2; float splity = (y1 + y2) / 2; int i = (x >= splitx ? 1 : 0) + (y >= splity ? 2 : 0); // create new child node, if necessary if (n.children[i] == null) { n.children[i] = factory.GetQuadTreeNode(); n.hasChildren = true; } // update bounds if (i == 1 || i == 3) x1 = splitx; else x2 = splitx; if (i > 1) y1 = splity; else y2 = splity; // recurse Insert(p, n.children[i], x1, y1, x2, y2); }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2">the second endpoint of the spring</param> /// <param name="length">the spring length</param> /// <returns>the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2, float length) { return(addSpring(item1, item2, -1.0F, length)); }
/// <summary> /// Get a Spring instance and set it to the given parameters. /// </summary> public Spring getSpring(ForceItem f1, ForceItem f2, float k, float length) { if (springs.Count > 0) { Spring s = springs[springs.Count - 1]; springs.Remove(s); s.Item1 = f1; s.Item2 = f2; s.Coeff = k; s.Length = length; return s; } else { return new Spring(f1, f2, k, length); } }
/// <summary> /// Remove a ForceItem to the simulation. /// </summary> /// <param name="item">Item the ForceItem to remove.</param> /// <returns></returns> public bool removeItem(ForceItem item) { return mItems.Remove(item); }
/// <summary> /// Determines whether the two force are at the same location. /// </summary> /// <param name="f1">The f1.</param> /// <param name="f2">The f2.</param> /// <returns> /// <c>true</c> if [is same location] [the specified f1]; otherwise, <c>false</c>. /// </returns> private static bool IsSameLocation(ForceItem f1, ForceItem f2) { float dx = Math.Abs(f1.Location[0] - f2.Location[0]); float dy = Math.Abs(f1.Location[1] - f2.Location[1]); return (dx < 0.01 && dy < 0.01); }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2">the second endpoint of the spring</param> /// <returns>the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2) { return addSpring(item1, item2, -1.0F, -1.0F); }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2">the second endpoint of the spring</param> /// <param name="length">the spring length</param> /// <returns>the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2, float length) { return addSpring(item1, item2, -1.0F, length); }
/// <summary> /// Add a Spring to the simulation. /// </summary> /// <param name="item1">the first endpoint of the spring</param> /// <param name="item2"> the second endpoint of the spring</param> /// <param name="coeff">the spring coefficient</param> /// <param name="length">the spring length</param> /// <returns> the Spring added to the simulation</returns> public Spring addSpring(ForceItem item1, ForceItem item2, float coeff, float length) { if (item1 == null || item2 == null) throw new ArgumentException("ForceItems must be non-null"); Spring s = Spring.Factory.getSpring(item1, item2, coeff, length); springs.Add(s); return s; }