/// <summary> /// Applies a coordinate offset to a subgraph (useful for dragging entire clusters) /// </summary> /// <param name="parent"> The origin vertex for the translation </param> /// <param name="offsetX"> Initial X-coord offset </param> /// <param name="offsetY"> Initial Y-coord offset </param> /// <param name="childStepDec"> </param> /// <param name="walk"> Current subgraph depth </param> public void TranslateSubtree(Vertex parent, float offsetX, float offsetY, float childStepDec = 0, int walk = 0) { if (offsetX == 0 && offsetY == 0) { // Restore original coords parent.transCoords.X = parent.transCoords.Y = 0; } else { // Transform coords parent.transCoords.X = parent.coordinates.X + offsetX - (offsetX * childStepDec * walk); parent.transCoords.Y = parent.coordinates.Y + offsetY - (offsetY * childStepDec * walk); if (childStepDec != 0) { // Adjust opacity per level (for simulated perspective) double adjOpacity = 1.0 / ((walk + 0.0001) * 0.5); parent.box.Opacity = adjOpacity; foreach (Edge e in parent.incomingEdges.Values.Union(parent.outgoingEdges.Values)) { e.line.Opacity = 1.0 / (walk + 0.0001); e.arrowLine0.Opacity = adjOpacity; e.arrowLine1.Opacity = adjOpacity; } Canvas.SetZIndex(parent.box, int.MaxValue - walk); Canvas.SetZIndex(parent.labelBlock, int.MaxValue - walk); foreach (Edge e in parent.incomingEdges.Values.Union(parent.outgoingEdges.Values)) { Canvas.SetZIndex(e.line, int.MaxValue - walk); Canvas.SetZIndex(e.arrowLine0, int.MaxValue - walk); Canvas.SetZIndex(e.arrowLine1, int.MaxValue - walk); } } } foreach (Vertex child in parent.children) { if (!child.bIsolated) TranslateSubtree(child, offsetX, offsetY, childStepDec, walk + 1); } }
/// <summary> /// Adds a vertex to the graph /// </summary> public void Add(Vertex v) { if (v.style == null) v.style = DefaultVertexStyleTemplate; if (!vertices.ContainsKey(v.label)) { vertices.TryAdd(v.label, v); // Handle Exception??? } elements.Add(v.box); elements.Add(v.labelBlock); }
public void GenerateGraphData(double ticker) { if (Math.Round(ticker, 0) % 9 == 0) { //int toDelete = rand.Next(0, gr.vertices.Count - 1); //gr.Remove(gr.vertices.Values.ToList()[toDelete]); Vertex newVertex = new Vertex(); newVertex.style = Styles.Green_VertexStyle; newVertex.type = 5; newVertex.label = "" + Math.Round(ticker, 0); newVertex.coordinates = Insilico.Engine.ToWPFCoords(new Point(rand.Next(-100, 100), rand.Next(-100, 100)), canvasWidth, canvasHeight); //graph.forceConstant += rand.Next(-3, 3); graph.Add(newVertex); //graph.CreateUnidirectionalEdge(newVertex, graph.GetRandom()); //graph.CreateUnidirectionalEdge(graph.GetRandom(), newVertex); } }
/// <summary> /// Applies a coordinate transform to a subgraph (cheap zoom) /// </summary> /// <param name="current"> The origin vertex of the transform </param> /// <param name="factor"> By how much we wish to transform the graph </param> /// <param name="parentX"> Final X-coord for parent vertex </param> /// <param name="parentY"> Final Y-coord for parent vertex </param> /// <param name="chOffX"> X-coord offset for parent vertex </param> /// <param name="chOffY"> Y-coord offset for parent vertex </param> /// <param name="walk"> How deep we are in the subgraph </param> public void TransformSubtree(Vertex current, float factor, float parentX, float parentY, float chOffX, float chOffY, int walk = 1) { float dx = (float)(current.coordinates.X - parentX); float dy = (float)(current.coordinates.Y - parentY); // Adjust zoom factor so children don't interfere with parents float f = factor; // Compute component contributions float xc = (dx * f) + (-dx); float yc = (dy * f) + (-dy); // Add inherited offset and contributions from each component current.transCoords.X = current.coordinates.X + (xc) + chOffX; current.transCoords.Y = current.coordinates.Y + (yc) + chOffY; foreach (Vertex child in current.children) { Point c = current.transCoords.X == 0.0 && current.transCoords.Y == 0.0 ? current.coordinates : current.transCoords; TransformSubtree(child, factor, (float)current.coordinates.X, (float)current.coordinates.Y, xc + chOffX, yc + chOffY, walk + 1); } }
public void RequestSnapBack(Vertex parent, Point origin) { bRequestSnapBack = true; snapBackVertex = parent; snapBackOrigin = origin; }
/// <summary> /// Removes a vertex from the graph /// </summary> public void Remove(Vertex v) { Edge outEdge; foreach (Edge e in v.incomingEdges.Values) { e.origin.outgoingEdges.TryRemove(e, out outEdge); } foreach (Edge e in v.outgoingEdges.Values) { e.origin.incomingEdges.TryRemove(e, out outEdge); } vertices.TryRemove(v.label, out v); }
public bool CreateUnidirectionalEdge(Vertex vA, Vertex vB, EdgeStyleTemplate style = null, string tooltip = "") { Edge newEdge = new Edge(vA, vB, tooltip); newEdge.style = newEdge.style == null ? style : DefaultEdgeStyleTemplate; if (vA != null && vB != null) { vA.children.Add(vB); vA.outgoingEdges.TryAdd(newEdge, newEdge); vB.incomingEdges.TryAdd(newEdge, newEdge); Line newLine = new Line(); newLine.Stroke = Cached.BrushLimeGreen; newLine.StrokeThickness = 4; newLine.Opacity = 0.5; elements.Add(newLine); newEdge.line = newLine; return true; } return false; }
public Vertex CreateNewVertex(int x, int y, int radius, string label) { Point p = new Point(x, y); Vertex newVertex = new Vertex(); newVertex.style = Styles.Green_VertexStyle; newVertex.label = label; newVertex.labelBlock = Primitives.CreateTextBlock(label, Cached.typeface, 12, newVertex.style.vertexTextColor, Cached.BrushTransparent, newVertex.coordinates.X, newVertex.coordinates.Y); newVertex.labelBlockSize = Primitives.MeasureString(newVertex.labelBlock, Cached.typeface); newVertex.coordinates = p; double vrad = Math.Max(newVertex.labelBlockSize.Width, newVertex.labelBlockSize.Height) * 1.25; newVertex.box = Primitives.CreateEllipse(newVertex.coordinates.X, newVertex.coordinates.Y, vrad, vrad, newVertex.style.vertexColor); newVertex.box.Opacity = newVertex.style.vertexOpacity; Add(newVertex); return newVertex; }
public bool CreateBidirectionalEdge(Vertex vA, Vertex vB, EdgeStyleTemplate style = null, string tooltip = "") { Edge newEdge = new Edge(vA, vB, tooltip); newEdge.style = newEdge.style == null ? style : DefaultEdgeStyleTemplate; if (vA != null && vB != null) { vA.children.Add(vB); vA.outgoingEdges.TryAdd(newEdge, newEdge); vB.incomingEdges.TryAdd(newEdge, newEdge); vB.outgoingEdges.TryAdd(newEdge, newEdge); vA.incomingEdges.TryAdd(newEdge, newEdge); return true; } return false; }
/// <summary> /// Compute the force a pair of vertices exhibit on each other, and their resultant accelerations /// </summary> public void ComputePairInteraction(Vertex a, Vertex b) { float ax; float ay; float bx; float by; float d; if (a.transCoords.X == 0.0 && a.transCoords.Y == 0.0) { ax = (float)a.coordinates.X; ay = (float)a.coordinates.Y; } else { ax = (float)a.transCoords.X; ay = (float)a.transCoords.Y; } if (b.transCoords.X == 0.0 && b.transCoords.Y == 0.0) { bx = (float)b.coordinates.X; by = (float)b.coordinates.Y; } else { bx = (float)b.transCoords.X; by = (float)b.transCoords.Y; } float dx = (float)(bx - ax); float dy = (float)(by - ay); if (dx < maxDist || dy < maxDist) { // Indicates that it's *possible* the distance could be within our bounds float preDist = (float)((dx * dx) + (dy * dy)); if (!float.IsNaN(preDist)) { d = (float)Math.Sqrt(preDist); if (d > minDist && d < maxDist) { float f = (float)((forceConstant * charge * charge) / (d * d)); // F_e = k*q_1*q_2 / r^2 float dxd = dx / d; float fxcomp = (float)(f * Math.Abs(Math.Acos(dxd))); float aix = -(float)(fxcomp / mass); float ajx = aix; float dyd = dy / d; float fycomp = (float)(f * Math.Abs(Math.Acos(dyd))); float aiy = -(float)(fycomp / mass); float ajy = aiy; a.transCoords.X = ax - aix; a.transCoords.Y = ay - aiy; b.transCoords.X = bx + ajx; b.transCoords.Y = by + ajy; } } } }
public BoundingBox(UIElement e, Vertex v) { element = e; vertex = v; }
public Edge(Vertex origin, Vertex destination, string tooltip) { this.origin = origin; this.destination = destination; this.tooltip = tooltip; }