/// <summary> /// Create a new spring with given parameter /// </summary> /// <param name="p1">the first particle attached to the spring</param> /// <param name="p2">the second particle attached to the spring</param> public Spring(Particle p1, Particle p2) { this.p1 = p1; this.p2 = p2; restLength = 10.0; tension = 0.1; damping = 0.1; isDead = false; }
/// <summary> /// Reclaims a Particle, adding it to the object pool for recycling /// <param name="p">the Particle to Reclaim</param> /// </summary> protected static void reclaimParticle(Particle p) { if (_ppool.Count < objectPoolLimit) { _ppool.Push(p); } }
/// <summary> /// Adds a spring to the simulation /// </summary> /// <param name="p1">the first Particle attached to the spring</param> /// <param name="p2">the second Particle attached to the spring</param> /// <param name="restLength">the rest length of the spring</param> /// <param name="tenstion">the tension of the spring</param> /// <param name="damping">the damping (friction) co-efficient of the spring</param> /// <returns>the added spring</returns> public Spring AddSpring(Particle p1, Particle p2, double restLength, double tension, double damping) { var edge = new Spring(p1, p2) { restLength = restLength, tension = tension, damping = damping }; p1.degree++; p2.degree++; Springs.Add(edge); return edge; }
private void BuildParticles() { double maxProjectFileCount = 0; foreach (Project p in _projectManager.Projects) { if (p.Files > maxProjectFileCount) { maxProjectFileCount = p.Files; } } Random random = new Random(DateTime.Now.Millisecond); Dictionary<string, Particle<NodeRenderer, LineRenderer>> particleDictionary = new Dictionary<string, Particle<NodeRenderer, LineRenderer>>(); //create each particle foreach (Project p in _projectManager.Projects) { NodeRenderer renderer = new NodeRenderer(p.Name, ((p.Files / maxProjectFileCount) * MAX_RADIUS) + MIN_RADIUS); Particle<NodeRenderer, LineRenderer> particle = new Particle<NodeRenderer, LineRenderer>(DEFAULT_MAX_MASS, DEFAULT_REPELLANT_FORCE, _simulation.ForceDissipationFunction); particle.InitPosition(random.Next(RenderPanel.Width), random.Next(RenderPanel.Height)); _simulation.AddParticle(particle, renderer); particleDictionary.Add(p.Name,particle); } //connect the particle dependancies foreach (Project p in _projectManager.Projects) { foreach (string projectName in p.Dependancies) { //if this has any projects that are not loaded, create a defualt project to link to if (!particleDictionary.ContainsKey(projectName)) { NodeRenderer renderer = new NodeRenderer(projectName, MIN_RADIUS); Particle<NodeRenderer, LineRenderer> particle = new Particle<NodeRenderer, LineRenderer>(DEFAULT_MAX_MASS, DEFAULT_REPELLANT_FORCE, _simulation.ForceDissipationFunction); particle.InitPosition(random.Next(RenderPanel.Width), random.Next(RenderPanel.Height)); _simulation.AddParticle(particle, renderer); particleDictionary.Add(projectName, particle); } particleDictionary[ p.Name].AddConnection(particleDictionary[projectName], new LineRenderer(particleDictionary[projectName].MetaData.Radius), DEFAULT_SHORT_RESTING_LENGTH, DEFAULT_SPRING_CONSTANT); } } }
private bool isSameLocation(Particle p1, Particle p2) { return (Math.Abs(p1.x - p2.x) < _eps && Math.Abs(p1.y - p2.y) < _eps); }
private void insertHelper(Particle p, QuadTreeNode n, double x1, double y1, double x2, double y2) { // determine split double sx = (x1+x2)/2; double sy = (y1+y2)/2; int c = (p.x >= sx ? 1 : 0) + (p.y >= sy ? 2 : 0); // update bounds if (c==1 || c==3) x1 = sx; else x2 = sx; if (c>1) y1 = sy; else y2 = sy; // update children QuadTreeNode cn; if (c == 0) { if (n.c1==null) n.c1 = QuadTreeNode.Particle(); cn = n.c1; } else if (c == 1) { if (n.c2==null) n.c2 = QuadTreeNode.Particle(); cn = n.c2; } else if (c == 2) { if (n.c3==null) n.c3 = QuadTreeNode.Particle(); cn = n.c3; } else { if (n.c4==null) n.c4 = QuadTreeNode.Particle(); cn = n.c4; } n.hasChildren = true; insert(p,cn,x1,y1,x2,y2); }
// -- Helpers --------------------------------------------------------- private void insert(Particle p, QuadTreeNode n, double x1, double y1, double x2, double y2) { // ignore particles with NaN coordinates if (double.IsNaN(p.x) || double.IsNaN(p.y)) return; // try to insert Particle p at Particle 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.p != null ) { // n contains 1 Particle if ( isSameLocation(n.p, p) ) { // recurse insertHelper(p,n,x1,y1,x2,y2); } else { // divide Particle v = n.p; n.p = null; insertHelper(v,n,x1,y1,x2,y2); insertHelper(p,n,x1,y1,x2,y2); } } else { // n is empty, add p as leaf n.p = p; } }
private void forces(Particle p, QuadTreeNode n, double x1, double y1, double x2, double y2) { Random rnd = new Random(); double f = 0; double dx = n.cx - p.x; double dy = n.cy - p.y; double dd = Math.Sqrt(dx*dx + dy*dy); bool max = _max > 0 && dd > _max; if (dd == 0) { // add direction when needed dx = _eps * (0.5 - rnd.NextDouble()); dy = _eps * (0.5 - rnd.NextDouble()); } // 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.p != p) || ((x2-x1)/dd < _t) ) { if ( max ) return; // either only 1 Particle or we meet criteria // for Barnes-Hut approximation, so calc force dd = dd<_min ? _min : dd; f = _g * p.mass * n.mass / (dd * dd * dd); p.fx += f*dx; p.fy += f*dy; } else if ( n.hasChildren ) { // recurse for more accurate calculation double sx = (x1+x2)/2; double sy = (y1+y2)/2; if (n.c1 != null) forces(p, n.c1, x1, y1, sx, sy); if (n.c2 != null) forces(p, n.c2, sx, y1, x2, sy); if (n.c3 != null) forces(p, n.c3, x1, sy, sx, y2); if (n.c4 != null) forces(p, n.c4, sx, sy, x2, y2); if ( max ) return; if ( n.p != null && n.p != p ) { dd = dd<_min ? _min : dd; f = _g * p.mass * n.p.mass / (dd*dd*dd); p.fx += f*dx; p.fy += f*dy; } } }
/// <summary> /// 多角形のエッジを追加する /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="style"></param> /// <returns></returns> Spring AddPolygonEdge(Particle p1, Particle p2, string style) { var spring = sim.AddSpring(p1, p2, 100, 0.1, 0.1); var group = new TransformGroup(); var scale = new ScaleTransform(); var rotate = new RotateTransform(); var translate = new TranslateTransform(); group.Children.Add(scale); group.Children.Add(rotate); group.Children.Add(translate); p1.PropertyChanged += (s, e) => { var dx = p2.x - p1.x; var dy = p2.y - p1.y; var d = Math.Sqrt(dx * dx + dy * dy); var angle = Math.Atan2(dy, dx) / Math.PI * 180; scale.ScaleX = d / 100.0; rotate.Angle = angle; translate.X = p1.x; translate.Y = p1.y; }; p2.PropertyChanged += (s, e) => { var dx = p2.x - p1.x; var dy = p2.y - p1.y; var d = Math.Sqrt(dx * dx + dy * dy); var angle = Math.Atan2(dy, dx) / Math.PI * 180; scale.ScaleX = d / 100; rotate.Angle = angle; translate.X = p1.x; translate.Y = p1.y; }; Polygon poly = new Polygon() { Points = new PointCollection(){ new Point(0,1), new Point(100,0), new Point(0,-1) }, RenderTransform = group, Style = Resources[style] as Style }; LayoutRoot.Children.Insert(0, poly); return spring; }
/// <summary> /// 線のエッジを追加する /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <returns></returns> Spring AddLineEdge(Particle p1, Particle p2) { var s = sim.AddSpring(p1, p2, 10, 0.1, 0.1); Line line = new Line() { Fill = new SolidColorBrush(Colors.Brown), Stroke = new SolidColorBrush(Colors.Brown), }; line.SetBinding(Line.X1Property, new Binding("x") { Source = p1, Mode = BindingMode.TwoWay }); line.SetBinding(Line.Y1Property, new Binding("y") { Source = p1, Mode = BindingMode.TwoWay }); line.SetBinding(Line.X2Property, new Binding("x") { Source = p2, Mode = BindingMode.TwoWay }); line.SetBinding(Line.Y2Property, new Binding("y") { Source = p2, Mode = BindingMode.TwoWay }); LayoutRoot.Children.Insert(0, line); return s; }