/// <summary> /// Validates the given cell state. /// </summary> public void UpdateEdgeState(mxCellState state, mxGeometry geo, mxCellState source, mxCellState target) { // This will remove edges with no terminals and no terminal points // as such edges are invalid and produce NPEs in the edge styles. // Also removes connected edges that have no visible terminals. if ((graph.Model.GetTerminal(state.Cell, true) != null && source == null) || (source == null && geo.GetTerminalPoint(true) == null) || (graph.Model.GetTerminal(state.Cell, false) != null && target == null) || (target == null && geo.GetTerminalPoint(false) == null)) { RemoveState(state.Cell, true); } else { UpdateFixedTerminalPoints(state, source, target); UpdatePoints(state, geo.Points, source, target); UpdateFloatingTerminalPoints(state, source, target); if (state.AbsolutePointCount() < 2 || state.AbsolutePoints[0] == null || state .AbsolutePoints[state.AbsolutePointCount() - 1] == null) { // This will remove edges with invalid points from the list of states in the view. // Happens if the one of the terminals and the corresponding terminal point is null. RemoveState(state.Cell, true); } else { UpdateEdgeBounds(state); state.AbsoluteOffset = GetPoint(state, geo); } } }
/// <summary> /// Sets the fixed source or target terminal point on the given edge. /// </summary> /// <param name="edge">State whose terminal point should be updated.</param> /// <param name="terminal">State which represents the actual terminal.</param> /// <param name="source">Boolean that specifies if the terminal is the source.</param> /// <param name="constraint">Constraint that specifies the connection.</param> public void UpdateFixedTerminalPoint(mxCellState edge, mxCellState terminal, bool source, mxConnectionConstraint constraint) { mxPoint pt = null; if (constraint != null) { pt = graph.GetConnectionPoint(terminal, constraint); } if (pt == null && terminal == null) { mxPoint orig = edge.Origin; mxGeometry geo = graph.GetCellGeometry(edge.Cell); pt = geo.GetTerminalPoint(source); if (pt != null) { pt = new mxPoint(scale * (translate.X + pt.X + orig.X), scale * (translate.Y + pt.Y + orig.Y)); } } edge.SetAbsoluteTerminalPoint(pt, source); }
/* (non-Dotnetdoc) * see com.mxgraph.mxICell.Clone() */ public Object Clone() { mxCell cell = new mxCell(); cell.Collapsed = Collapsed; cell.Connectable = Connectable; cell.Edge = Edge; cell.Style = Style; cell.Vertex = Vertex; cell.Visible = Visible; mxGeometry geometry = Geometry; if (geometry != null) { cell.Geometry = geometry.Clone(); } Object value = Value; if (value is XmlNode) { cell.Value = ((XmlNode)value).CloneNode(true); } else { cell.Value = Value; } return(cell); }
/// <summary> /// Returns the absolute, cummulated origin for the children inside the /// given parent. /// </summary> public mxPoint GetOrigin(Object cell) { mxPoint result = null; if (cell != null) { result = GetOrigin(GetParent(cell)); if (!IsEdge(cell)) { mxGeometry geo = GetGeometry(cell); if (geo != null) { result.X += geo.X; result.Y += geo.Y; } } } else { result = new mxPoint(); } return(result); }
/// <summary> /// Inner helper method to update the parent of the specified edge to the /// nearest-common-ancestor of its two terminals. /// </summary> /// <param name="edge">Specifies the edge to be updated.</param> /// <param name="root">Current root of the model.</param> public void UpdateEdgeParent(Object edge, Object root) { Object source = GetTerminal(edge, true); Object target = GetTerminal(edge, false); Object cell = null; // Uses the first non-relative descendants of the source terminal while (source != null && !IsEdge(source) && GetGeometry(source) != null && GetGeometry(source).Relative) { source = GetParent(source); } // Uses the first non-relative descendants of the target terminal while (target != null && !IsEdge(target) && GetGeometry(target) != null && GetGeometry(target).Relative) { target = GetParent(target); } if (IsAncestor(root, source) && IsAncestor(root, target)) { if (source == target) { cell = GetParent(source); } else { cell = GetNearestCommonAncestor(source, target); } if (cell != null && GetParent(cell) != root && GetParent(edge) != cell) { mxGeometry geo = GetGeometry(edge); if (geo != null) { mxPoint origin1 = GetOrigin(GetParent(edge)); mxPoint origin2 = GetOrigin(cell); double dx = origin2.X - origin1.X; double dy = origin2.Y - origin1.Y; geo = (mxGeometry)geo.Clone(); geo.Translate(-dx, -dy); SetGeometry(edge, geo); } Add(cell, edge, GetChildCount(cell)); } } }
/// <summary> /// see com.mxgraph.mxIGraphModel.SetGeometry(Object, mxGeometry) /// </summary> public mxGeometry SetGeometry(Object cell, mxGeometry geometry) { BeginUpdate(); try { ((mxICell)cell).Geometry = geometry; } finally { EndUpdate(); } return(geometry); }
/// <summary> /// Constructs a copy of the given geometry. /// </summary> /// <param name="geometry">Geometry to construct a copy of.</param> public mxGeometry(mxGeometry geometry) : base(geometry.X, geometry.Y, geometry.Width, geometry .Height) { if (geometry.points != null) { points = new List <mxPoint>(geometry.points.Count); foreach (mxPoint pt in geometry.points) { points.Add(pt.Clone()); } } if (geometry.sourcePoint != null) { sourcePoint = geometry.sourcePoint.Clone(); } if (geometry.targetPoint != null) { targetPoint = geometry.targetPoint.Clone(); } if (geometry.offset != null) { offset = geometry.offset.Clone(); } if (geometry.alternateBounds != null) { alternateBounds = geometry.alternateBounds.Clone(); } relative = geometry.relative; }
/// <summary> /// Creates a new vertex to be used in insertVertex. /// </summary> public Object CreateVertex(Object parent, string id, Object value, double x, double y, double width, double height, string style, bool relative) { mxGeometry geometry = new mxGeometry(x, y, width, height); geometry.Relative = relative; mxCell vertex = new mxCell(value, geometry, style); vertex.Id = id; vertex.Vertex = true; vertex.Connectable = true; return vertex; }
/// <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(); } }
/// <summary> /// Validates the given cell state. /// </summary> public void UpdateVertexState(mxCellState state, mxGeometry geo) { // LATER: Add support for rotation UpdateVertexLabelOffset(state); }
/// <summary> /// Returns the absolute point on the edge for the given relative /// geometry as a point. The edge is represented by the given cell state. /// </summary> /// <param name="state">Represents the state of the parent edge.</param> /// <param name="geometry">Represents the relative location.</param> public mxPoint GetPoint(mxCellState state, mxGeometry geometry) { double x = state.GetCenterX(); double y = state.GetCenterY(); if (state.Segments != null && (geometry == null || geometry.Relative)) { double gx = (geometry != null) ? geometry.X / 2 : 0; int pointCount = state.AbsolutePoints.Count; double dist = (gx + 0.5) * state.Length; double[] segments = state.Segments; double segment = segments[0]; double length = 0; int index = 1; while (dist > length + segment && index < pointCount - 1) { length += segment; segment = segments[index++]; } double factor = (segment == 0) ? 0 : (dist - length) / segment; mxPoint p0 = state.AbsolutePoints[index - 1]; mxPoint pe = state.AbsolutePoints[index]; if (p0 != null && pe != null) { double gy = 0; double offsetX = 0; double offsetY = 0; if (geometry != null) { gy = geometry.Y; mxPoint offset = geometry.Offset; if (offset != null) { offsetX = offset.X; offsetY = offset.Y; } } double dx = pe.X - p0.X; double dy = pe.Y - p0.Y; double nx = (segment == 0) ? 0 : dy / segment; double ny = (segment == 0) ? 0 : dx / segment; x = p0.X + dx * factor + (nx * gy + offsetX) * scale; y = p0.Y + dy * factor - (ny * gy - offsetY) * scale; } } else if (geometry != null) { mxPoint offset = geometry.Offset; if (offset != null) { x += offset.X; y += offset.Y; } } return new mxPoint(x, y); }
/// <summary> /// see com.mxgraph.mxIGraphModel.SetGeometry(Object, mxGeometry) /// </summary> public mxGeometry SetGeometry(Object cell, mxGeometry geometry) { BeginUpdate(); try { ((mxICell)cell).Geometry = geometry; } finally { EndUpdate(); } return geometry; }
/// <summary> /// Constructs a new cell for the given value, geometry and style. /// </summary> /// <param name="value">Value that represents the user object.</param> /// <param name="geometry">Geometry of the cell to be created.</param> /// <param name="style">Style of the cell to be created.</param> public mxCell(Object value, mxGeometry geometry, string style) { Value = value; Geometry = geometry; Style = style; }
/// <summary> /// Validates the points for the state of the given cell recursively if the /// cell is not collapsed and returns the bounding box of all visited states /// as a rectangle. /// </summary> public mxRectangle ValidatePoints(mxCellState parentState, Object cell) { mxIGraphModel model = graph.Model; mxCellState state = GetState(cell); mxRectangle bbox = null; if (state != null) { mxGeometry geo = graph.GetCellGeometry(cell); if (geo != null && model.IsEdge(cell)) { // Updates the points on the source terminal if its an edge mxCellState source = GetState(GetVisibleTerminal(cell, true)); if (source != null && model.IsEdge(source.Cell) && !model.IsAncestor(source, cell)) { mxCellState tmp = GetState(model.GetParent(source.Cell)); ValidatePoints(tmp, source.Cell); } // Updates the points on the target terminal if its an edge mxCellState target = GetState(GetVisibleTerminal(cell, false)); if (target != null && model.IsEdge(target.Cell) && !model.IsAncestor(target.Cell, cell)) { mxCellState tmp = GetState(model.GetParent(target.Cell)); ValidatePoints(tmp, target.Cell); } UpdateFixedTerminalPoints(state, source, target); UpdatePoints(state, geo.Points, source, target); UpdateFloatingTerminalPoints(state, source, target); UpdateEdgeBounds(state); state.AbsoluteOffset = GetPoint(state, geo); } else if (geo != null && geo.Relative && parentState != null && model.IsEdge(parentState.Cell)) { mxPoint origin = GetPoint(parentState, geo); if (origin != null) { state.X = origin.X; state.Y = origin.Y; origin.X = (origin.X / scale) - translate.X; origin.Y = (origin.Y / scale) - translate.Y; state.Origin = origin; childMoved(parentState, state); } } if (model.IsEdge(cell) || model.IsVertex(cell)) { UpdateLabelBounds(state); bbox = new mxRectangle(UpdateBoundingBox(state)); } } if (state != null && !graph.IsCellCollapsed(cell)) { int childCount = model.GetChildCount(cell); for (int i = 0; i < childCount; i++) { Object child = model.GetChildAt(cell, i); mxRectangle bounds = ValidatePoints(state, child); if (bounds != null) { if (bbox == null) { bbox = bounds; } else { bbox.Add(bounds); } } } } return(bbox); }
/// <summary> /// Validates the bounds of the given parent's child using the given parent /// state as the origin for the child. The validation is carried out /// recursively for all non-collapsed descendants. /// </summary> /// <param name="parentState">Cell state for the given parent.</param> /// <param name="cell">Cell for which the bounds in the state should be updated.</param> public void ValidateBounds(mxCellState parentState, Object cell) { mxIGraphModel model = graph.Model; mxCellState state = GetState(cell, true); if (state != null) { if (!graph.IsCellVisible(cell)) { RemoveState(cell); } else if (parentState != null) { state.AbsoluteOffset.X = 0; state.AbsoluteOffset.Y = 0; state.Origin = new mxPoint(parentState.Origin.X, parentState.Origin.Y); mxGeometry geo = graph.GetCellGeometry(cell); if (geo != null) { if (!model.IsEdge(cell)) { mxPoint origin = state.Origin; mxPoint offset = geo.Offset; if (offset == null) { offset = EMPTY_POINT; } if (geo.Relative) { origin.X += geo.X * parentState.Width / Scale + offset.X; origin.Y += geo.Y * parentState.Height / Scale + offset.Y; } else { state.AbsoluteOffset = new mxPoint( scale * offset.X, scale * offset.Y); origin.X += geo.X; origin.Y += geo.Y; } } // Updates the cell state's bounds state.X = scale * (translate.X + state.Origin.X); state.Y = scale * (translate.Y + state.Origin.Y); state.Width = scale * geo.Width; state.Height = scale * geo.Height; if (model.IsVertex(cell)) { UpdateVertexLabelOffset(state); } } } // Applies child offset to origin mxPoint childOffset = graph.GetChildOffsetForCell(cell); if (childOffset != null) { state.Origin.X += childOffset.X; state.Origin.Y += childOffset.Y; } } // Recursively validates the child bounds if (state != null && !graph.IsCellCollapsed(cell)) { int childCount = model.GetChildCount(cell); for (int i = 0; i < childCount; i++) { ValidateBounds(state, model.GetChildAt(cell, i)); } } }
/// <summary> /// Returns the absolute point on the edge for the given relative /// geometry as a point. The edge is represented by the given cell state. /// </summary> /// <param name="state">Represents the state of the parent edge.</param> /// <param name="geometry">Represents the relative location.</param> public mxPoint GetPoint(mxCellState state, mxGeometry geometry) { double x = state.GetCenterX(); double y = state.GetCenterY(); if (state.Segments != null && (geometry == null || geometry.Relative)) { double gx = (geometry != null) ? geometry.X / 2 : 0; int pointCount = state.AbsolutePoints.Count; double dist = (gx + 0.5) * state.Length; double[] segments = state.Segments; double segment = segments[0]; double length = 0; int index = 1; while (dist > length + segment && index < pointCount - 1) { length += segment; segment = segments[index++]; } double factor = (segment == 0) ? 0 : (dist - length) / segment; mxPoint p0 = state.AbsolutePoints[index - 1]; mxPoint pe = state.AbsolutePoints[index]; if (p0 != null && pe != null) { double gy = 0; double offsetX = 0; double offsetY = 0; if (geometry != null) { gy = geometry.Y; mxPoint offset = geometry.Offset; if (offset != null) { offsetX = offset.X; offsetY = offset.Y; } } double dx = pe.X - p0.X; double dy = pe.Y - p0.Y; double nx = (segment == 0) ? 0 : dy / segment; double ny = (segment == 0) ? 0 : dx / segment; x = p0.X + dx * factor + (nx * gy + offsetX) * scale; y = p0.Y + dy * factor - (ny * gy - offsetY) * scale; } } else if (geometry != null) { mxPoint offset = geometry.Offset; if (offset != null) { x += offset.X; y += offset.Y; } } return(new mxPoint(x, y)); }
/// <summary> /// Updates the given cell state. /// </summary> /// <param name="state"></param> public void UpdateCellState(mxCellState state, mxCellState source, mxCellState target) { state.AbsoluteOffset.X = 0; state.AbsoluteOffset.Y = 0; state.Origin.X = 0; state.Origin.Y = 0; state.Length = 0; mxIGraphModel model = graph.Model; mxCellState pState = GetState(model.GetParent(state.Cell)); if (pState != null) { state.Origin.X += pState.Origin.X; state.Origin.Y += pState.Origin.Y; } mxPoint offset = graph.GetChildOffsetForCell(state.Cell); if (offset != null) { state.Origin.X += offset.X; state.Origin.Y += offset.Y; } mxGeometry geo = graph.GetCellGeometry(state.Cell); if (geo != null) { if (!model.IsEdge(state.Cell)) { mxPoint origin = state.Origin; offset = geo.Offset; if (offset == null) { offset = EMPTY_POINT; } if (geo.Relative && pState != null) { if (model.IsEdge(pState.Cell)) { mxPoint orig = GetPoint(pState, geo); if (orig != null) { origin.X += (orig.X / scale) - pState.Origin.X - translate.X; origin.Y += (orig.Y / scale) - pState.Origin.Y - translate.Y; } } else { origin.X += geo.X * pState.Width / scale + offset.X; origin.Y += geo.Y * pState.Height / scale + offset.Y; } } else { state.AbsoluteOffset = new mxPoint(scale * offset.X, scale * offset.Y); origin.X += geo.X; origin.Y += geo.Y; } } state.X = scale * (translate.X + state.Origin.X); state.Y = scale * (translate.Y + state.Origin.Y); state.Width = scale * geo.Width; state.Height = scale * geo.Height; if (model.IsVertex(state.Cell)) { UpdateVertexState(state, geo); } if (model.IsEdge(state.Cell)) { UpdateEdgeState(state, geo, source, target); } } }
/// <summary> /// Constructs a copy of the given geometry. /// </summary> /// <param name="geometry">Geometry to construct a copy of.</param> public mxGeometry(mxGeometry geometry) : base(geometry.X, geometry.Y, geometry.Width, geometry .Height) { if (geometry.points != null) { points = new List<mxPoint>(geometry.points.Count); foreach (mxPoint pt in geometry.points) { points.Add(pt.Clone()); } } if (geometry.sourcePoint != null) { sourcePoint = geometry.sourcePoint.Clone(); } if (geometry.targetPoint != null) { targetPoint = geometry.targetPoint.Clone(); } if (geometry.offset != null) { offset = geometry.offset.Clone(); } if (geometry.alternateBounds != null) { alternateBounds = geometry.alternateBounds.Clone(); } relative = geometry.relative; }