/// <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); }
/// <summary> /// Updates the absolute points in the given state using the specified array /// of points as the relative points. /// </summary> /// <param name="edge">Cell state whose absolute points should be updated.</param> /// <param name="points">Array of points that constitute the relative points.</param> /// <param name="source">Cell that represents the source terminal.</param> /// <param name="target">Cell that represents the target terminal.</param> public void UpdatePoints(mxCellState edge, List <mxPoint> points, mxCellState source, mxCellState target) { if (edge != null) { List <mxPoint> pts = new List <mxPoint>(); pts.Add(edge.AbsolutePoints[0]); mxEdgeStyleFunction edgeStyle = GetEdgeStyle(edge, points, source, target); if (edgeStyle != null) { mxCellState src = GetTerminalPort(edge, source, true); mxCellState trg = GetTerminalPort(edge, target, false); ((mxEdgeStyleFunction)edgeStyle)(edge, src, trg, points, pts); } else if (points != null) { for (int i = 0; i < points.Count; i++) { if (points[i] is mxPoint) { mxPoint pt = points[i].Clone(); pts.Add(TransformControlPoint(edge, pt)); } } } List <mxPoint> tmp = edge.AbsolutePoints; pts.Add(tmp[tmp.Count - 1]); edge.AbsolutePoints = pts; } }
/// <summary> /// Returns a point that defines the location of the intersection point between /// the perimeter and the line between the center of the shape and the given point. /// </summary> /// <param name="terminal">State for the source or target terminal.</param> /// <param name="next">Point that lies outside of the given terminal.</param> /// <param name="orthogonal">Specifies if the orthogonal projection onto /// the perimeter should be returned. If this is false then the intersection /// of the perimeter and the line between the next and the center point is /// returned.</param> /// <param name="border">Optional border between the perimeter and the shape.</param> public mxPoint GetPerimeterPoint(mxCellState terminal, mxPoint next, bool orthogonal, double border) { mxPoint point = null; if (terminal != null) { mxPerimeterFunction perimeter = GetPerimeterFunction(terminal); if (perimeter != null && next != null) { mxRectangle bounds = GetPerimeterBounds(terminal, border); if (bounds.Width > 0 || bounds.Height > 0) { point = perimeter(bounds, terminal, next, orthogonal); } } if (point == null) { point = GetPoint(terminal); } } return(point); }
/// <summary> /// Draws the shadow. /// </summary> /// <param name="canvas"></param> /// <param name="state"></param> /// <param name="rotation"></param> /// <param name="flipH"></param> /// <param name="flipV"></param> /// <param name="bounds"></param> /// <param name="alpha"></param> protected void DrawShadow(mxGdiCanvas2D canvas, mxCellState state, double rotation, bool flipH, bool flipV, mxRectangle bounds, double alpha, bool filled) { // Requires background in generic shape for shadow, looks like only one // fillAndStroke is allowed per current path, try working around that // Computes rotated shadow offset double rad = rotation * Math.PI / 180; double cos = Math.Cos(-rad); double sin = Math.Sin(-rad); mxPoint offset = mxUtils.GetRotatedPoint(new mxPoint(mxConstants.SHADOW_OFFSETX, mxConstants.SHADOW_OFFSETY), cos, sin); if (flipH) { offset.X *= -1; } if (flipV) { offset.Y *= -1; } // TODO: Use save/restore instead of negative offset to restore (requires fix for HTML canvas) canvas.Translate(offset.X, offset.Y); // Returns true if a shadow has been painted (path has been created) if (DrawShape(canvas, state, bounds, true)) { canvas.Alpha = mxConstants.STENCIL_SHADOW_OPACITY * alpha; // TODO: Implement new shadow //canvas.Shadow(mxConstants.STENCIL_SHADOWCOLOR, filled); } canvas.Translate(-offset.X, -offset.Y); }
/// <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> /// /// </summary> public void QuadTo(double x1, double y1, double x2, double y2) { if (currentPath != null) { mxPoint nextPoint = new mxPoint((state.dx + x2) * state.scale, (state.dy + y2) * state.scale); if (lastPoint != null) { double cpx0 = lastPoint.X; double cpy0 = lastPoint.Y; double qpx1 = (state.dx + x1) * state.scale; double qpy1 = (state.dy + y1) * state.scale; double cpx1 = cpx0 + 2f / 3f * (qpx1 - cpx0); double cpy1 = cpy0 + 2f / 3f * (qpy1 - cpy0); double cpx2 = nextPoint.X + 2f / 3f * (qpx1 - nextPoint.X); double cpy2 = nextPoint.Y + 2f / 3f * (qpy1 - nextPoint.Y); currentPath.AddBezier((float)cpx0, (float)cpy0, (float)cpx1, (float)cpy1, (float)cpx2, (float)cpy2, (float)nextPoint.X, (float)nextPoint.Y); } lastPoint = nextPoint; } }
/// <summary> /// Transforms the given control point to an absolute point. /// </summary> public mxPoint TransformControlPoint(mxCellState state, mxPoint pt) { mxPoint orig = state.Origin; return(new mxPoint(scale * (pt.X + translate.X + orig.X), scale * (pt.Y + translate.Y + orig.Y))); }
/// <summary> /// Parses the bounds, absolute points and label information from the style /// of the state into its respective fields and returns the label of the /// cell. /// </summary> public string ParseState(mxCellState state, bool edge) { Dictionary <string, object> style = state.Style; // Parses the bounds state.X = mxUtils.GetDouble(style, "x"); state.Y = mxUtils.GetDouble(style, "y"); state.Width = mxUtils.GetDouble(style, "width"); state.Height = mxUtils.GetDouble(style, "height"); // Parses the absolute points list List <mxPoint> pts = ParsePoints(mxUtils.GetString(style, "points")); if (pts.Count > 0) { state.AbsolutePoints = pts; } // Parses the label and label bounds string label = mxUtils.GetString(style, "label"); if (label != null && label.Length > 0) { mxPoint offset = new mxPoint(mxUtils.GetDouble(style, "dx"), mxUtils.GetDouble(style, "dy")); mxRectangle vertexBounds = (!edge) ? state : null; state.LabelBounds = mxUtils.GetLabelPaintBounds(label, style, mxUtils.IsTrue(style, "html", false), offset, vertexBounds, scale); } return(label); }
/// <summary> /// /// </summary> public void MoveTo(double x, double y) { if (currentPath != null) { // StartFigure avoids connection between last figure and new figure currentPath.StartFigure(); lastPoint = new mxPoint((state.dx + x) * state.scale, (state.dy + y) * state.scale); } }
/// <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> /// Sets the sourcePoint or targetPoint to the given point and returns the /// new point. /// </summary> /// <param name="point">Point to be used as the new source or target point.</param> /// <param name="source">Boolean that specifies if the source or target point /// should be set.</param> /// <returns>Returns the new point.</returns> public mxPoint SetTerminalPoint(mxPoint point, bool source) { if (source) { sourcePoint = point; } else { targetPoint = point; } return(point); }
/// <summary> /// Updates the absolute terminal point in the given state for the given /// start and end state, where start is the source if source is true. /// </summary> /// <param name="edge">State whose terminal point should be updated.</param> /// <param name="start">for the terminal on "this" side of the edge.</param> /// <param name="end">for the terminal on the other side of the edge.</param> /// <param name="source">Boolean indicating if start is the source terminal state.</param> public void UpdateFloatingTerminalPoint(mxCellState edge, mxCellState start, mxCellState end, bool source) { start = GetTerminalPort(edge, start, source); mxPoint next = GetNextPoint(edge, end, source); double border = mxUtils.GetDouble(edge.Style, mxConstants.STYLE_PERIMETER_SPACING); border += mxUtils.GetDouble(edge.Style, (source) ? mxConstants.STYLE_SOURCE_PERIMETER_SPACING : mxConstants.STYLE_TARGET_PERIMETER_SPACING); mxPoint pt = GetPerimeterPoint(start, next, graph.IsOrthogonal(edge), border); edge.SetAbsoluteTerminalPoint(pt, source); }
/// <summary> /// /// </summary> public void LineTo(double x, double y) { if (currentPath != null) { mxPoint nextPoint = new mxPoint((state.dx + x) * state.scale, (state.dy + y) * state.scale); if (lastPoint != null) { currentPath.AddLine((float)lastPoint.X, (float)lastPoint.Y, (float)nextPoint.X, (float)nextPoint.Y); } lastPoint = nextPoint; } }
/// <summary> /// Updates the terminal points in the given state after the edge style was /// computed for the edge. /// </summary> /// <param name="state">State whose terminal points should be updated.</param> /// <param name="source">State that represents the source terminal.</param> /// <param name="target">State that represents the target terminal.</param> public void UpdateFloatingTerminalPoints(mxCellState state, mxCellState source, mxCellState target) { mxPoint p0 = state.AbsolutePoints[0]; mxPoint pe = state.AbsolutePoints[state.AbsolutePointCount() - 1]; if (pe == null && target != null) { UpdateFloatingTerminalPoint(state, target, source, false); } if (p0 == null && source != null) { UpdateFloatingTerminalPoint(state, source, target, true); } }
/// <summary> /// Returns the nearest point in the list of absolute points or the center /// of the opposite terminal. /// </summary> /// <param name="edge">State that represents the edge.</param> /// <param name="opposite">State that represents the opposite terminal.</param> /// <param name="source">Boolean indicating if the next point for the source or target /// should be returned.</param> public mxPoint GetNextPoint(mxCellState edge, mxCellState opposite, bool source) { List <mxPoint> pts = edge.AbsolutePoints; mxPoint point = null; if (pts != null && pts.Count >= 2) { int count = pts.Count; int index = (source) ? Math.Min(1, count - 1) : Math.Max(0, count - 2); point = pts[index]; } if (point == null && opposite != null) { point = new mxPoint(opposite.GetCenterX(), opposite.GetCenterY()); } return(point); }
/// <summary> /// /// </summary> public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (currentPath != null) { mxPoint nextPoint = new mxPoint((state.dx + x3) * state.scale, (state.dy + y3) * state.scale); if (lastPoint != null) { currentPath.AddBezier((float)lastPoint.X, (float)lastPoint.Y, (float)((state.dx + x1) * state.scale), (float)((state.dy + y1) * state.scale), (float)((state.dx + x2) * state.scale), (float)((state.dy + y2) * state.scale), (float)nextPoint.X, (float)nextPoint.Y); } lastPoint = nextPoint; } }
/// <summary> /// Translates the geometry by the specified amount. That is, x and y of the /// geometry, the sourcePoint, targetPoint and all elements of points are /// translated by the given amount. X and y are only translated if the /// geometry is not relative. If TRANSLATE_CONTROL_POINTS is false, then /// are not modified by this function. /// </summary> /// <param name="dx">Integer that specifies the x-coordinate of the translation.</param> /// <param name="dy">Integer that specifies the y-coordinate of the translation.</param> public void Translate(double dx, double dy) { // Translates the geometry if (!Relative) { x += dx; y += dy; } // Translates the source point if (sourcePoint != null) { sourcePoint.X += dx; sourcePoint.Y += dy; } // Translates the target point if (targetPoint != null) { targetPoint.X += dx; targetPoint.Y += dy; } // Translate the control points if (TRANSLATE_CONTROL_POINTS && points != null) { int count = points.Count; for (int i = 0; i < count; i++) { mxPoint pt = points[i]; pt.X += dx; pt.Y += dy; } } }
/// <summary> /// Sets the first or last point in the list of points depending on source. /// </summary> /// <param name="point">Point that represents the terminal point.</param> /// <param name="source">Boolean that specifies if the first or last point should /// be assigned.</param> public void SetAbsoluteTerminalPoint(mxPoint point, bool source) { if (source) { if (absolutePoints == null) { absolutePoints = new List <mxPoint>(); } if (absolutePoints == null || absolutePoints.Count == 0) { absolutePoints.Add(point); } else { absolutePoints[0] = point; } } else { if (absolutePoints == null) { absolutePoints = new List <mxPoint>(); absolutePoints.Add(null); absolutePoints.Add(point); } else if (absolutePoints.Count == 1) { absolutePoints.Add(point); } else { absolutePoints[absolutePoints.Count - 1] = point; } } }
/// <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> /// Constructs a new connection constraint for the given point and boolean /// arguments. /// </summary> /// <param name="point">Optional mxPoint that specifies the fixed location of the point /// in relative coordinates. Default is null.</param> /// <param name="perimeter">Optional boolean that specifies if the fixed point should be /// projected onto the perimeter of the terminal. Default is true.</param> public mxConnectionConstraint(mxPoint point, bool perimeter) { Point = point; Perimeter = perimeter; }
/// <summary> /// Constructs a connection constraint for the given point. /// </summary> public mxConnectionConstraint(mxPoint point) : this(point, true) { }
/// <summary> /// Updates the given state using the bounding box of the absolute points. /// Also updates terminal distance, length and segments. /// </summary> /// <param name="state">Cell state whose bounds should be updated.</param> public void UpdateEdgeBounds(mxCellState state) { List <mxPoint> points = state.AbsolutePoints; mxPoint p0 = points[0]; mxPoint pe = points[points.Count - 1]; if (p0 == null || pe == null) { // Note: This is an error that normally occurs // if a connected edge has a null-terminal, ie. // edge.source == null or edge.target == null. states.Remove(state.Cell); } else { if (p0.X != pe.X || p0.Y != pe.Y) { double dx = pe.X - p0.X; double dy = pe.Y - p0.Y; state.TerminalDistance = Math.Sqrt(dx * dx + dy * dy); } else { state.TerminalDistance = 0; } double length = 0; double[] segments = new double[points.Count - 1]; mxPoint pt = p0; if (pt != null) { double minX = pt.X; double minY = pt.Y; double maxX = minX; double maxY = minY; for (int i = 1; i < points.Count; i++) { mxPoint tmp = points[i]; if (tmp != null) { double dx = pt.X - tmp.X; double dy = pt.Y - tmp.Y; double segment = Math.Sqrt(dx * dx + dy * dy); segments[i - 1] = segment; length += segment; pt = tmp; minX = Math.Min(pt.X, minX); minY = Math.Min(pt.Y, minY); maxX = Math.Max(pt.X, maxX); maxY = Math.Max(pt.Y, maxY); } } state.Length = length; state.Segments = segments; double markerSize = 1; // TODO: include marker size state.X = minX; state.Y = minY; state.Width = Math.Max(markerSize, maxX - minX); state.Height = Math.Max(markerSize, maxY - minY); } else { state.Length = 0; } } }
/// <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> /// Returns a point that defines the location of the intersection point between /// the perimeter and the line between the center of the shape and the given point. /// </summary> public mxPoint GetPerimeterPoint(mxCellState terminal, mxPoint next, bool orthogonal) { return(GetPerimeterPoint(terminal, next, orthogonal, 0)); }
/// <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> /// /// </summary> public void Begin() { currentPath = new GraphicsPath(); lastPoint = null; }