Sorry, no, I meant that it is essential to TreeLayout, but if you are implementing ForceDirectedLayout, that is not a problem.
I cannot find an implementation of a virtualized ForceDirectedLayout. I did find one for LayeredDigraphLayout. Perhaps if you see how that is implemented and compared it with how the VirtualizingTreeLayout is implemented, it will help implementing VirtualizingForceDirectedLayout.
Caution: this implementation may have a number of assumptions about the nodes or about the layout needed for the particular app that needed a virtualized LayeredDigraphLayout.
// Virtualizing LayeredDigraphLayout classes
// Here we try to ignore all methods and properties that deal with Nodes or Links.
// Instead we use Vertex and Edge classes that know about the model data.
// This layout implementation assumes the use of a GraphLinksModel (i.e. an ILinksModel).
public class VirtualizingLayeredDigraphLayout : LayeredDigraphLayout {
public ILinksModel Model { get; set; }
public NodeData SubGraph { get; set; } // the containing group's data
public override LayeredDigraphNetwork CreateNetwork() {
return new VirtualizingLayeredDigraphNetwork();
}
// ignore the arguments, because they assume the existence of Nodes and Links
public override LayeredDigraphNetwork MakeNetwork(IEnumerable<Node> nodes, IEnumerable<Link> links) {
var net = (VirtualizingLayeredDigraphNetwork)CreateNetwork();
if (this.Model != null) {
if (this.SubGraph != null) { // just add members of the given group
net.AddSubGraph(this.SubGraph, this.Model as ISubGraphLinksModel);
} else { // add all top-level nodes and links
net.AddTopLevelGraph(this.Model);
}
}
return net;
}
protected override double NodeMinLayerSpace(LayeredDigraphVertex v, bool topleft) {
var vv = v as VirtualizingLayeredDigraphVertex;
if (vv != null && vv.Data == null) return 0;
Rect r = v.Bounds;
Point p = v.Focus;
if (this.Direction == 90 || this.Direction == 270) {
if (topleft)
return p.Y+10;
else
return r.Height-p.Y+10;
} else {
if (topleft)
return p.X+10;
else
return r.Width-p.X+10;
}
}
protected override int NodeMinColumnSpace(LayeredDigraphVertex v, bool topleft) {
var vv = v as VirtualizingLayeredDigraphVertex;
if (vv != null && vv.Data == null) return 0;
Rect r = v.Bounds;
Point p = v.Focus;
if (this.Direction == 90 || this.Direction == 270) {
if (topleft)
return (int)(p.X/this.ColumnSpacing) + 1;
else
return (int)((r.Width-p.X)/this.ColumnSpacing) + 1;
} else {
if (topleft)
return (int)(p.Y/this.ColumnSpacing) + 1;
else
return (int)((r.Height-p.Y)/this.ColumnSpacing) + 1;
}
}
protected override double LinkStraightenWeight(LayeredDigraphEdge edge) {
var fromVertex = edge.FromVertex as VirtualizingLayeredDigraphVertex ;
var toVertex = edge.ToVertex as VirtualizingLayeredDigraphVertex;
if ((fromVertex != null && fromVertex.Data == null) && (toVertex != null && toVertex.Data == null))
return 8;
if ((fromVertex != null && fromVertex.Data == null) || (toVertex != null && toVertex.Data == null))
return 4;
return 1;
}
}
// Use Virtualizing versions of Vertex and Edge.
public class VirtualizingLayeredDigraphNetwork : LayeredDigraphNetwork {
public override LayeredDigraphVertex CreateVertex() {
return new VirtualizingLayeredDigraphVertex();
}
public override LayeredDigraphEdge CreateEdge() {
return new VirtualizingLayeredDigraphEdge();
}
private Dictionary<NodeData, VirtualizingLayeredDigraphVertex> NodeDataMap = new Dictionary<NodeData, VirtualizingLayeredDigraphVertex>();
private Dictionary<LinkData, VirtualizingLayeredDigraphEdge> LinkDataMap = new Dictionary<LinkData, VirtualizingLayeredDigraphEdge>();
// a replacement for LayeredDigraphNetwork.AddNodesAndLinks using top-level model data instead of Nodes or Links
public void AddTopLevelGraph(ILinksModel model) {
if (model == null) return;
NodeDataMap.Clear();
LinkDataMap.Clear();
ISubGraphLinksModel sgmodel = model as ISubGraphLinksModel;
var nodes = model.NodesSource as IEnumerable<NodeData>;
foreach (NodeData d in nodes) {
if (sgmodel == null || sgmodel.GetGroupForNode(d) == null) {
AddNodeData(d, model);
}
}
var links = model.LinksSource as IEnumerable<LinkData>;
foreach (LinkData d in links) {
if (sgmodel == null || sgmodel.GetGroupForLink(d) == null) {
AddLinkData(d, model);
}
}
}
// a replacement for LayeredDigraphNetwork.AddNodesAndLinks using a group's members' model data instead of Nodes or Links
public void AddSubGraph(NodeData sg, ISubGraphLinksModel model) {
if (sg == null || model == null) return;
NodeDataMap.Clear();
LinkDataMap.Clear();
foreach (NodeData d in model.GetMemberNodesForGroup(sg)) {
AddNodeData(d, model);
}
foreach (LinkData d in model.GetMemberLinksForGroup(sg)) {
AddLinkData(d, model);
}
}
public void AddNodeData(NodeData d, ILinksModel model) {
if (NodeDataMap.ContainsKey(d)) return;
// create and add VirtualizingLayeredDigraphVertex
var v = (VirtualizingLayeredDigraphVertex)CreateVertex();
v.Data = d;
NodeDataMap.Add(d, v);
AddVertex(v);
}
public void AddLinkData(LinkData d, ILinksModel model) {
if (LinkDataMap.ContainsKey(d)) return;
// find connected node data
var from = (NodeData)model.FindNodeByKey(d.From);
var to = (NodeData)model.FindNodeByKey(d.To);
if (from == null || to == null || from == to) return; // skip
// now find corresponding vertexes
VirtualizingLayeredDigraphVertex f;
NodeDataMap.TryGetValue(from, out f);
VirtualizingLayeredDigraphVertex t;
NodeDataMap.TryGetValue(to, out t);
if (f == null || t == null) return; // skip
// create and add VirtualizingLayeredDigraphEdge
var e = (VirtualizingLayeredDigraphEdge)CreateEdge();
e.Data = d;
e.FromVertex = f;
e.ToVertex = t;
AddEdge(e);
}
}
// Associate with NodeData rather than with Node.
public class VirtualizingLayeredDigraphVertex : LayeredDigraphVertex {
public NodeData Data {
get { return _Data; }
set {
_Data = value;
if (_Data != null) {
// use bounds information from the NodeData rather than the Node.Bounds!
this.Focus = new Point(_Data.Width/2, _Data.Height/2);
this.Bounds = VirtualizingPartManager.ComputeNodeBounds(_Data);
}
}
}
private NodeData _Data = null;
public override void CommitPosition() {
if (this.Data != null) {
this.Data.Location = this.Center; // set NodeData.Location instead of Node.Location!
} else {
base.CommitPosition();
}
}
}
// Associate with LinkData rather than with Link.
// NOTE: This does not support custom routing of links that is normally done by LayeredDigraphLayout
public class VirtualizingLayeredDigraphEdge : LayeredDigraphEdge {
public LinkData Data { get; set; }
}