private void TestToStringWithCulture(CultureInfo culture) { CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = culture; try { string listSeparator = culture.TextInfo.ListSeparator; string decimalSeparator = culture.NumberFormat.NumberDecimalSeparator; var corner1 = new Vec2F(1.1f, 2.2f); var corner2 = new Vec2F(4.4f, 5.5f); var o = new Box2F(corner1, corner2); string s = o.ToString(null, null); TestToStringResults(o, s, listSeparator, decimalSeparator); string s2 = o.ToString(); Assert.AreEqual(s, s2); s = o.ToString("G", culture); TestToStringResults(o, s, listSeparator, decimalSeparator); s = o.ToString("R", culture); TestToStringResults(o, s, listSeparator, decimalSeparator); } finally { Thread.CurrentThread.CurrentCulture = originalCulture; } }
protected virtual double GetBoundingRadius( Digraph <Node, Edge> .GNode node) { Box2F bbox = node.Data.LayoutBBox; return(Math.Sqrt(bbox.W * bbox.W + bbox.H * bbox.H) / 2.0); }
private void Adjust() { Digraph <Node, Edge> .GNode closest; if (this.mDistances.Length < this.mNodeCount) { this.mDistances = new int[this.mNodeCount]; } for (int i = 0; i < this.mNodeCount; i++) { closest = this.mGraph.InternalNodeAt(i); //this.mNodes[i].Index = i; closest.Color = GraphColor.White; this.mDistances[i] = 0; } closest = null; Box2F bbox = this.mClusterNode == null ? this.BoundingBox : this.mClusterNode.LayoutBBox; while (closest == null || closest.TotalEdgeCount(false) == 0) { // get a random point in the container this.mGlobalX = (0.1 + 0.8 * this.mRnd.NextDouble()) * bbox.W + bbox.X; this.mGlobalY = (0.1 + 0.8 * this.mRnd.NextDouble()) * bbox.H + bbox.Y; // find the closest node to this random point closest = this.GetClosest(this.mGlobalX, this.mGlobalY); } // Adjust the nodes to the selected node this.AdjustNode(closest); }
public ISOMLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.mQueue = new Digraph <Node, Edge> .GNode[0]; this.mDistances = new int[0]; }
public bool Intersects(Box2F box) { if (!Box.Overlaps(box)) { return(false); } switch (Direction) { case SegmentDirection.Vertical: return(box.Min.X < Start.X && Start.X < box.Max.X); case SegmentDirection.Horizontal: return(box.Min.Y < Start.Y && Start.Y < box.Max.Y); case SegmentDirection.PositiveSlope: return(DifferentSides(box.TopLeft, box.BottomRight)); case SegmentDirection.NegativeSlope: return(DifferentSides(box.BottomLeft, box.TopRight)); default: throw new InvalidOperationException("Invalid box intersection direction enumeration"); } }
public SimpleTreeLayoutForCircles(CircleNodeScene scene, Box2F boundingBox) : base(scene.Graph, boundingBox) { this.mScene = scene; this.AdaptToSizeChanges = true; }
public FRLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.MaxIterations = 200; this.mNewXs = sEmptyCoords; this.mNewYs = sEmptyCoords; }
public SingleCircleLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.mEmbedCircle = new Digraph <Node, Edge> .GNode[0]; this.mAngles = new double[0]; this.mHalfSizes = new double[0]; }
/*/// <summary> * /// Tries to get the internal node in this layout algorithm's * /// <see cref="Graph"/> that corresponds to the root node set with * /// the <see cref="M:ARootedAlgorithm`1{Node}.SetRoot(Node)"/> * /// function. * /// </summary> * /// <returns>The internal node in this algorithm's graph that * /// corresponds to this algorithm's root value, or the first node in * /// the graph if there is corresponding node, or null if this * /// algorithm's graph is currently empty.</returns> * public Digraph<Node, Edge>.GNode TryGetGraphRoot() * { * if (this.mGraph.NodeCount == 0) * { * return null; * } * int index = this.HasRoot * ? this.mGraph.IndexOfNode(this.TryGetRoot()) : 0; * if (index < 0) * index = 0; * Digraph<Node, Edge>.GNode root * = this.mGraph.InternalNodeAt(index); * //root.Index = index; * return root; * }/* */ /// <summary> /// Shuffles all layout nodes (but not port nodes) in this layout /// algorithm's <see cref="Graph"/> to random positions with /// minimal intersections of their bounding boxes. /// </summary> /// <param name="immediate">Whether the positions of the layout nodes /// are set after all their new positions have been randomly set. /// </param><remarks> /// The random positions of the nodes are restricted to within the /// bounding box of this algorithm's <see cref="ClusterNode"/>, or /// within this algorithm's <see cref="BoundingBox"/> if the cluster /// node is null. /// </remarks> public void ShuffleNodes(bool immediate) { Box2F bbox = this.mClusterNode == null ? this.mBBox : this.mClusterNode.LayoutBBox; this.ShuffleNodesInternal(bbox.X, bbox.Y, bbox.W, bbox.H, immediate); }
public SCircleLayoutForCircles(CircleNodeScene scene, Box2F boundingBox) : base(scene.Graph, boundingBox) { this.mScene = scene; this.CenterX = boundingBox.X + boundingBox.W / 2; this.CenterY = boundingBox.Y + boundingBox.H / 2; this.AdaptToSizeChanges = true; }
public SimpleTreeLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { //this.Spring = new LayoutLinearSpring(); this.mDatas = new NodeData[0]; //this.mSizeWs = new float[0]; //this.mSizeHs = new float[0]; }
public void LearnNodePos(float x, float y, Box2F boundingBox) { double rad = Math.Sqrt(x * x + y * y); if (rad > this.mRadius) { this.mRadius = rad; } }
private void CalcParameters() { Box2F bbox = this.mClusterNode == null ? this.BoundingBox : this.mClusterNode.LayoutBBox; this.mK = (float)Math.Sqrt(bbox.W * bbox.H / this.mGraph.NodeCount); this.mInitTemp = Math.Min(bbox.W, bbox.H) / 10; this.UpdateParameters(); }
/// <summary> /// Gets the position on the border of the given bounding box based /// on its intersection with a ray starting at its center point with /// the given rotational <paramref name="angle"/>. /// </summary> /// <param name="bbox">The bounding box to intersect with /// a positioning ray.</param> /// <param name="angle">The angle of a positioning ray starting at /// the center of <paramref name="bbox"/>.</param> /// <returns>The intersection point of the given bounding box and /// a ray with the given <paramref name="angle"/> starting at the /// bounding box's center point.</returns> public static Vec2F GetBoundaryPosition(Box2F bbox, double angle) { double cx = bbox.X + bbox.W / 2.0; double cy = bbox.Y + bbox.H / 2.0; double dx = Math.Cos(angle); double dy = Math.Sin(angle); double t = Math.Min(Math.Abs(cx / dx), Math.Abs(cy / dy)); return(new Vec2F((float)(dx * t + cx), (float)(dy * t + cy))); }
public SCircleLayoutForCircles(CircleNodeScene scene, IClusterNode clusterNode) : base(scene.Graph, clusterNode) { this.mScene = scene; Box2F bbox = clusterNode.LayoutBBox; this.CenterX = bbox.X + bbox.W / 2; this.CenterY = bbox.Y + bbox.H / 2; this.AdaptToSizeChanges = true; }
private void TestToStringResults(Box2F o, string s, string listSeparator, string decimalSeparator) { string[] results = s.Split(new[] { listSeparator }, StringSplitOptions.RemoveEmptyEntries); Assert.AreEqual(results.Length, 4); foreach (string oneFloatString in results) { Assert.True(oneFloatString.Contains(decimalSeparator)); } Assert.AreEqual(float.Parse(results[0]), o.Min.X); Assert.AreEqual(float.Parse(results[1]), o.Min.Y); Assert.AreEqual(float.Parse(results[2]), o.Max.X); Assert.AreEqual(float.Parse(results[3]), o.Max.Y); }
public KKLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.mShortestPathsAlg = new BasicAllShortestPaths <Node, Edge>( new DijkstraShortestPath <Node, Edge>( graph, false, false)); this.mXs = new double[0]; this.mYs = new double[0]; this.mHidden = new bool[0]; this.MaxIterations = 200; }
public Vec2F AdjustNodePosition(float x, float y, Box2F bbox) { float d; Vec2F v = new Vec2F(x, y); d = bbox.W; v.X = Math.Max(x + bbox.X, this.mMinX) + d; v.X = Math.Min(v.X, this.mMaxX) - bbox.X - d; d = bbox.H; v.Y = Math.Max(y + bbox.Y, this.mMinY) + d; v.Y = Math.Min(v.Y, this.mMaxY) - bbox.Y - d; return(v); }
public RectGeom(Box2F boundingBox) { this.mX = boundingBox.X; this.mY = boundingBox.Y; this.mW = boundingBox.W; this.mH = boundingBox.H; this.mOffsetX = 0; this.mOffsetY = 0; this.bBBoxDirty = true; this.mBBoxX = this.mX; this.mBBoxY = this.mY; this.mBBoxW = this.mW; this.mBBoxH = this.mH; }
private BoxCollider CreateCollider(GameObject gameObject, List <Seg2F> edges) { Box2F box = Box2F.Combine(edges.Select(edge => edge.Box)); (float x, float z) = box.Center; float y = subsectorPlane.SectorPlane.Height; // Because we don't want some thin value being made even thinner, // we scale it up by the map unit size. This way we don't risk any // objects passing through it. float colliderThickness = PhysicsSystem.ColliderThickness * Constants.MapUnitInverse; BoxCollider collider = gameObject.AddComponent <BoxCollider>(); collider.center = new Vector3(x, y, z).MapUnit(); collider.size = new Vector3(box.Width, colliderThickness, box.Height).MapUnit(); return(collider); }
/// <summary> /// Creates a new layout algorithm instance to operate on the given /// <paramref name="graph"/> using the given /// <see cref="IClusterNode"/> instance to affect the positions /// of the graph's nodes.</summary> /// <param name="graph">The graph that this layout algorithm will /// operate on by setting the positions of its nodes.</param> /// <param name="clusterNode">The <see cref="IClusterNode"/> /// instance that affects the positions of the nodes in the /// <paramref name="graph"/>.</param> /// <seealso cref="Graph"/><seealso cref="ClusterNode"/> public LayoutAlgorithm(Digraph <Node, Edge> graph, IClusterNode clusterNode) : base(graph) { if (clusterNode == null) { throw new ArgumentNullException("clusterNode"); } this.mClusterNode = clusterNode; this.mBBox = new Box2F(0, 0, 0, 0); uint vers = graph.NodeVersion; this.mLastNVers = vers == 0 ? uint.MaxValue : vers - 1; vers = graph.EdgeVersion; this.mLastEVers = vers == 0 ? uint.MaxValue : vers - 1; this.mLastXs = new float[0]; this.mLastYs = new float[0]; }
/// <summary> /// Creates a new layout algorithm instance to operate on the given /// <paramref name="graph"/> using the given <see cref="RectangleF"/> /// instance to constrain the positions of the graph's nodes to /// within its boundaries. /// </summary> /// <param name="graph">The graph that this layout algorithm will /// operate on by setting the positions of its nodes.</param> /// <param name="boundingBox">The <see cref="RectangleF"/> instance /// that constrains the positions of the nodes in the /// <paramref name="graph"/> to within its boundaries.</param> public LayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph) { if (boundingBox == null) { throw new ArgumentNullException("boundingBox"); } this.mClusterNode = null; this.mBBox = boundingBox; uint vers = graph.NodeVersion; this.mLastNVers = vers == 0 ? uint.MaxValue : vers - 1; vers = graph.EdgeVersion; this.mLastEVers = vers == 0 ? uint.MaxValue : vers - 1; this.mLastXs = new float[0]; this.mLastYs = new float[0]; }
/// <summary> /// Shuffles all layout nodes (but not port nodes) in this layout /// algorithm's <see cref="Graph"/> to random positions with /// minimal intersections of their bounding boxes. /// </summary> /// <param name="bbox">A rectangle used to restrict the randomly set /// positions of the layout nodes to within its boundaries.</param> /// <param name="immediate">Whether the positions of the layout nodes /// are set after all their new positions have been randomly set. /// </param><remarks> /// The random positions of the nodes are restricted to within the /// bounding box of this algorithm's <see cref="ClusterNode"/>, or /// within this algorithm's <see cref="BoundingBox"/> if the cluster /// node is null. /// </remarks> public void ShuffleNodes(Box2F bbox, bool immediate) { if (this.mClusterNode == null) { bbox = Box2F.Intersect(bbox, this.mBBox); if (bbox.W == 0 || bbox.H == 0) { bbox = this.mBBox; } } else { bbox = Box2F.Intersect(bbox, this.mClusterNode.LayoutBBox); if (bbox.W == 0 || bbox.H == 0) { bbox = this.mClusterNode.LayoutBBox; } } this.ShuffleNodesInternal(bbox.X, bbox.Y, bbox.W, bbox.H, immediate); }
public void LearnNodePos(float x, float y, Box2F boundingBox) { }
private CircularLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { }
// TODO: Can the position copying from mXPositions and mYPositions // be reduced to the barycenter node that changes position and any nodes // which have been swapped on each iteration? protected override void PerformIteration(uint iteration) { int i, j; Node node; // copy positions into array // necessary each time because node positions can change outside // this algorithm, by constraining to bbox and by user code. for (i = 0; i < this.mNodeCount; i++) { if (!this.mHidden[i]) { node = this.mGraph.NodeAt(i); if (node.PositionFixed) { this.mXs[i] = node.X; this.mYs[i] = node.Y; } } } double nx, ny, deltaM, maxDeltaM = double.NegativeInfinity; int pm = -1; // get the 'p' with max delta_m for (i = 0; i < this.mNodeCount; i++) { if (!this.mHidden[i]) { node = this.mGraph.NodeAt(i); if (!node.PositionFixed) { deltaM = this.CalculateEnergyGradient(i); if (maxDeltaM < deltaM) { maxDeltaM = deltaM; pm = i; } } } } // TODO: is this needed? if (pm == -1) { this.Abort(); return; } // Calculate the delta_x & delta_y with the Newton-Raphson method // Upper-bound for the while (deltaM > epsilon) {...} cycle (100) for (i = 0; i < 100; i++) { this.CalcDeltaXY(pm); deltaM = this.CalculateEnergyGradient(pm); // real stop condition if (deltaM < double.Epsilon) { break; } } // Perform the bounding constraints normally done by base layout // algorithm. It needs to be done here so cached positions don't // need to be copied over on every single iteration, but still // allow the energy calculations to compensate for it. node = this.mGraph.NodeAt(pm); Box2F nbox = node.LayoutBBox; if (this.mClusterNode == null) { double d; Box2F bbox = this.BoundingBox; d = nbox.W; this.mXs[pm] = Math.Min(Math.Max(this.mXs[pm] + nbox.X, bbox.X) + d, bbox.Right) - nbox.X - d; d = nbox.H; this.mYs[pm] = Math.Min(Math.Max(this.mYs[pm] + nbox.Y, bbox.Y) + d, bbox.Bottom) - nbox.Y - d; } else { this.mClusterNode.LearnNodePos( (float)this.mXs[pm], (float)this.mYs[pm], nbox); Vec2F pos = this.mClusterNode.AugmentNodePos( (float)this.mXs[pm], (float)this.mYs[pm]); this.mXs[pm] = pos.X; this.mYs[pm] = pos.Y; } // What if two of the nodes were exchanged? if (this.bExchangeVertices && maxDeltaM < double.Epsilon) { double xenergy, energy = CalcEnergy(); bool noSwaps = true; for (i = 0; i < this.mNodeCount && noSwaps; i++) { if (!this.mHidden[i]) { for (j = 0; j < this.mNodeCount && noSwaps; j++) { if (i == j || this.mHidden[i]) { continue; } xenergy = CalcEnergyIfExchanged(i, j); if (energy > xenergy) { deltaM = this.mXs[i]; this.mXs[i] = this.mXs[j]; this.mXs[j] = deltaM; deltaM = this.mYs[i]; this.mYs[i] = this.mYs[j]; this.mYs[j] = deltaM; noSwaps = false; } } } } } // Use a linear transition to make the animation smooth // pos = pos + 0.1 * (newPos - pos) = 0.9 * pos + 0.1 * newPos for (i = 0; i < this.mNodeCount; i++) { if (!this.mHidden[i]) { node = this.mGraph.NodeAt(i); if (!node.PositionFixed) { //node.SetPosition((float)this.mXPositions[i], // (float)this.mYPositions[i]); nx = 0.9 * node.X + 0.1 * this.mXs[i]; ny = 0.9 * node.Y + 0.1 * this.mYs[i]; node.SetPosition((float)nx, (float)ny); } } } }
protected override void PerformPrecalculations( uint lastNodeVersion, uint lastEdgeVersion) { bool isDirty = lastNodeVersion != this.mGraph.NodeVersion; if (isDirty) { Digraph <Node, Edge> .GNode gNode; this.mNodeCount = this.mGraph.NodeCount; // Cache the nodes for speed-up and in case the graph // is modified during the iteration. if (this.mHidden.Length < this.mNodeCount) { this.mXs = new double[this.mNodeCount]; this.mYs = new double[this.mNodeCount]; this.mHidden = new bool[this.mNodeCount]; } for (int k = 0; k < this.mNodeCount; k++) { gNode = this.mGraph.InternalNodeAt(k); this.mXs[k] = gNode.Data.X; this.mYs[k] = gNode.Data.Y; this.mHidden[k] = gNode.Hidden; } this.bDirty = true; } isDirty = isDirty || lastEdgeVersion != this.mGraph.EdgeVersion; if (isDirty) { // Calculate the distances and diameter of the graph. this.mShortestPathsAlg.Reset(); this.mShortestPathsAlg.Compute(); this.mDistances = this.mShortestPathsAlg.Distances; this.mDiameter = this.mShortestPathsAlg.GetDiameter(); this.bDirty = true; } if (this.bDirty) { Box2F bbox = this.mClusterNode == null ? this.BoundingBox : this.mClusterNode.LayoutBBox; // L0 is the length of a side of the display area float L0 = Math.Min(bbox.W, bbox.H); // ideal length = L0 / max distance this.mIdealEdgeLength = L0 * this.mLengthFactor / this.mDiameter; this.mMaxDistance = this.mDiameter * this.mDisconnectedMultiplier; // Calculate the ideal distances between the nodes /*float dist; * int i, j; * for (i = 0; i < this.mNodeCount; i++) * { * for (j = 0; j < this.mNodeCount; j++) * { * // distance between non-adjacent nodes * dist = (float)Math.Min(this.mDistances[i][j], * this.mMaxDistance); * // calculate the minimal distance between the vertices * this.mDistances[i][j] = dist; * } * }/* */ } this.bDirty = false; }
public LinLogLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.MaxIterations = 100; }
public ATreeLayoutAlgorithm(Digraph <Node, Edge> graph, Box2F boundingBox) : base(graph, boundingBox) { this.mCCAlg = new CCAlgorithm <Node, Edge>(graph, false, false); }
private void ShuffleNodesInternal(float bbx, float bby, float bbw, float bbh, bool immediate) { int i, j, iter; bool intersection; Node node; float x, y, bbxi, bbyi; Vec2F pos; Box2F bboxI, bboxJ; Random rnd = new Random(DateTime.Now.Millisecond); int count = this.mGraph.NodeCount; for (i = 0; i < count; i++) { node = this.mGraph.NodeAt(i); if (!node.PositionFixed) { x = y = 0; bboxI = new Box2F(node.LayoutBBox); bbxi = bboxI.X; bbyi = bboxI.Y; // Set the padding of the bounding box to bboxI bbx -= bbxi; bby -= bbyi; bbw -= bboxI.W; bbh -= bboxI.H; // Iterate until a random position is found that doesn't // intersect with any of the other already placed nodes. intersection = true; for (iter = 0; iter < 50 && intersection; iter++) { intersection = false; x = (float)(rnd.NextDouble() * bbw + bbx); y = (float)(rnd.NextDouble() * bbh + bby); if (this.mClusterNode == null) { bboxI.X = bbxi + x; bboxI.Y = bbyi + y; } else { pos = this.mClusterNode.AugmentNodePos(x, y); bboxI.X = bbxi + pos.X; bboxI.Y = bbyi + pos.Y; } for (j = 0; j < i && !intersection; j++) { node = this.mGraph.NodeAt(j); //if (!(node is IPortNode)) { bboxJ = new Box2F(node.LayoutBBox); bboxJ.X += node.X; //node.NewX; bboxJ.Y += node.Y; //node.NewY; intersection = bboxI.IntersectsWith(bboxJ); //intersection = // bboxJ.X < (bboxI.X + bboxI.Width) && // bboxI.X < (bboxJ.X + bboxJ.Width) && // bboxJ.Y < (bboxI.Y + bboxI.Height) && // bboxI.Y < (bboxJ.Y + bboxJ.Height); } } } // Clear the padding of the bounding box bbx += bbxi; bby += bbyi; bbw += bboxI.W; bbh += bboxI.H; // Set the node's new randomized position node = this.mGraph.NodeAt(i); node.SetPosition(x, y);//SetNewPosition(x, y); } } for (i = this.mGraph.EdgeCount - 1; i >= 0; i--) { this.mGraph.EdgeAt(i).Update(); } /*if (immediate) * { * for (i = 0; i < nodes.Length; i++) * { * node = nodes[i].Data; * if (!node.PositionFixed) * node.SetPosition(node.NewX, node.NewY); * } * Digraph<Node, Edge>.GEdge[] edges * = this.mGraph.InternalEdges; * for (i = 0; i < edges.Length; i++) * { * edges[i].Data.Update(); * } * }/* */ }