/// <summary> /// Executes the fast organic layout. /// </summary> /// <param name="parent"></param> public void execute(Object parent) { mxIGraphModel model = graph.Model; // Finds the relevant vertices for the layout int childCount = model.GetChildCount(parent); List <Object> tmp = new List <Object>(childCount); for (int i = 0; i < childCount; i++) { Object child = model.GetChildAt(parent, i); if (!IsCellIgnored(child)) { tmp.Add(child); } } vertexArray = tmp.ToArray(); int n = vertexArray.Length; dispX = new double[n]; dispY = new double[n]; cellLocation = new double[n][]; isMoveable = new bool[n]; neighbours = new int[n][]; radius = new double[n]; radiusSquared = new double[n]; minDistanceLimitSquared = minDistanceLimit * minDistanceLimit; if (forceConstant < 0.001) { forceConstant = 0.001; } forceConstantSquared = forceConstant * forceConstant; // Create a map of vertices first. This is required for the array of // arrays called neighbours which holds, for each vertex, a list of // ints which represents the neighbours cells to that vertex as // the indices into vertexArray for (int i = 0; i < vertexArray.Length; i++) { Object vertex = vertexArray[i]; cellLocation[i] = new double[2]; // Set up the mapping from array indices to cells indices[vertex] = i; mxGeometry bounds = model.GetGeometry(vertex); // Set the X,Y value of the internal version of the cell to // the center point of the vertex for better positioning double width = bounds.Width; double height = bounds.Height; // Randomize (0, 0) locations double x = bounds.X; double y = bounds.Y; cellLocation[i][0] = x + width / 2.0; cellLocation[i][1] = y + height / 2.0; radius[i] = Math.Min(width, height); radiusSquared[i] = radius[i] * radius[i]; } for (int i = 0; i < n; i++) { dispX[i] = 0; dispY[i] = 0; isMoveable[i] = graph.IsCellMovable(vertexArray[i]); // Get lists of neighbours to all vertices, translate the cells // obtained in indices into vertexArray and store as an array // against the orginial cell index Object[] edges = mxGraphModel.GetEdges(model, vertexArray[i]); Object[] cells = mxGraphModel.GetOpposites(model, edges, vertexArray[i], true, true); neighbours[i] = new int[cells.Length]; for (int j = 0; j < cells.Length; j++) { int?index = indices[cells[j]]; // Check the connected cell in part of the vertex list to be // acted on by this layout if (index != null) { neighbours[i][j] = (int)index; } // Else if index of the other cell doesn't correspond to // any cell listed to be acted upon in this layout. Set // the index to the value of this vertex (a dummy self-loop) // so the attraction force of the edge is not calculated else { neighbours[i][j] = i; } } } temperature = initialTemp; // If max number of iterations has not been set, guess it if (maxIterations == 0) { maxIterations = (int)(20 * Math.Sqrt(n)); } // Main iteration loop for (iteration = 0; iteration < maxIterations; iteration++) { if (!allowedToRun) { return; } // Calculate repulsive forces on all vertices calcRepulsion(); // Calculate attractive forces through edges calcAttraction(); calcPositions(); reduceTemperature(); } // Moved cell location back to top-left from center locations used in // algorithm model.BeginUpdate(); try { double?minx = null; double?miny = null; for (int i = 0; i < vertexArray.Length; i++) { Object vertex = vertexArray[i]; mxGeometry geo = model.GetGeometry(vertex); if (geo != null) { cellLocation[i][0] -= geo.Width / 2.0; cellLocation[i][1] -= geo.Height / 2.0; geo = geo.Clone(); geo.X = graph.Snap(cellLocation[i][0]); geo.Y = graph.Snap(cellLocation[i][1]); model.SetGeometry(vertex, geo); if (minx == null) { minx = geo.X; } else { minx = Math.Min((double)minx, geo.X); } if (miny == null) { miny = geo.Y; } else { miny = Math.Min((double)miny, geo.Y); } } } // Modifies the cloned geometries in-place. Not needed // to clone the geometries again as we're in the same // undoable change. if (minx != null || miny != null) { for (int i = 0; i < vertexArray.Length; i++) { Object vertex = vertexArray[i]; mxGeometry geo = model.GetGeometry(vertex); if (geo != null) { if (minx != null) { geo.X -= ((double)minx) - 1; } if (miny != null) { geo.Y -= ((double)miny) - 1; } } } } } finally { model.EndUpdate(); } }