/// <summary> /// Returns a clone of this state where all members are deeply cloned /// except the view and cell references, which are copied with no /// cloning to the new instance. /// </summary> /// <returns></returns> new public mxCellState Clone() { mxCellState clone = new mxCellState(view, cell, style); if (absolutePoints != null) { clone.absolutePoints = new List <mxPoint>(); foreach (mxPoint pt in absolutePoints) { clone.absolutePoints.Add(pt.Clone()); } } if (origin != null) { clone.origin = origin.Clone(); } if (absoluteOffset != null) { clone.absoluteOffset = absoluteOffset.Clone(); } if (labelBounds != null) { clone.labelBounds = labelBounds.Clone(); } if (boundingBox != null) { clone.boundingBox = boundingBox.Clone(); } clone.terminalDistance = terminalDistance; clone.segments = segments; clone.length = length; clone.x = x; clone.y = y; clone.width = width; clone.height = height; return(clone); }
/// <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; canvas.Shadow(mxConstants.STENCIL_SHADOWCOLOR, filled); } canvas.Translate(-offset.X, -offset.Y); }
/// <summary> /// Returns a rectangle that contains the offset in x and y and the horizontal /// and vertical scale in width and height used to draw this shape inside the /// given rectangle. /// </summary> /// <param name="state"></param> /// <param name="bounds"></param> /// <param name="direction"></param> /// <returns></returns> protected mxRectangle ComputeAspect(mxCellState state, mxRectangle bounds, string direction) { double x0 = bounds.X; double y0 = bounds.Y; double sx = bounds.Width / w0; double sy = bounds.Height / h0; bool inverse = (direction != null && (direction.Equals("north") || direction .Equals("south"))); if (inverse) { sy = bounds.Width / h0; sx = bounds.Height / w0; double delta = (bounds.Width - bounds.Height) / 2; x0 += delta; y0 -= delta; } if (aspect.Equals("fixed")) { sy = Math.Min(sx, sy); sx = sy; // Centers the shape inside the available space if (inverse) { x0 += (bounds.Height - this.w0 * sx) / 2; y0 += (bounds.Width - this.h0 * sy) / 2; } else { x0 += (bounds.Width - this.w0 * sx) / 2; y0 += (bounds.Height - this.h0 * sy) / 2; } } return new mxRectangle(x0, y0, sx, sy); }
/// <summary> /// Gets the attribute for the given name from the given node. If the attribute /// does not exist then the text content of the node is evaluated and if it is /// a function it is invoked with <state> as the only argument and the return /// value is used as the attribute value to be returned. /// </summary> /// <param name="elt"></param> /// <param name="attribute"></param> /// <param name="state"></param> /// <returns></returns> public string EvaluateAttribute(XmlElement elt, string attribute, mxCellState state) { string result = elt.GetAttribute(attribute); if (result == null) { // JS functions as text content are currently not supported in .NET } return result; }
/// <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> /// Returns the nearest point in the list of absolute points or the center /// of the opposite terminal. /// </summary> /// <param name="vertex">Cell state that represents the vertex.</param> /// <param name="constraint">Connection constraint that represents the connection /// point constraint as returned by getConnectionConstraint.</param> public mxPoint GetConnectionPoint(mxCellState vertex, mxConnectionConstraint constraint) { mxPoint point = null; if (vertex != null && constraint.Point != null) { point = new mxPoint(vertex.X + constraint.Point.X * vertex.Width, vertex.Y + constraint.Point.Y * vertex.Height); } if (point != null && constraint.Perimeter) { point = View.GetPerimeterPoint(vertex, point, false); } return point; }
/// <summary> /// Draws the given cell and label onto the specified canvas. No /// children or descendants are painted. /// </summary> public void DrawState(mxICanvas canvas, mxCellState state, String label) { Object cell = (state != null) ? state.Cell : null; if (cell != null && cell != model.Root && (model.IsVertex(cell) || model.IsEdge(cell))) { Object obj = canvas.DrawCell(state); Object lab = null; // Holds the current clipping region in case the label will // be clipped Region clip = null; Region newClip = new Region(state.GetRectangle()); // Indirection for image canvas that contains a graphics canvas mxICanvas clippedCanvas = (mxUtils.GetString(state.Style, mxConstants. STYLE_OVERFLOW, "").Equals("hidden")) ? canvas : null; if (clippedCanvas is mxImageCanvas) { clippedCanvas = ((mxImageCanvas) clippedCanvas).GdiCanvas; Point pt = ((mxImageCanvas) canvas).Translate; newClip.Translate(pt.X, pt.Y); } if (clippedCanvas is mxGdiCanvas) { Graphics g = ((mxGdiCanvas) clippedCanvas).Graphics; clip = g.Clip; g.Clip = newClip; } if (label != null && state.LabelBounds != null) { lab = canvas.DrawLabel(label, state, false); } // Restores the previous clipping region if (clippedCanvas is mxGdiCanvas) { ((mxGdiCanvas)clippedCanvas).Graphics.Clip = clip; } // Invokes the cellDrawn callback with the object which was created // by the canvas to represent the cell graphically if (obj != null) { // LATER: Add inner callback for rendering //CellDrawn(cell, obj, lab); } } }
/// <summary> /// Returns the bottom-most cell that intersects the given point (x, y) in /// the cell hierarchy that starts at the given parent. /// </summary> /// <param name="state"></param> /// <param name="rect"></param> /// <returns>Returns true if the given cell state and rectangle intersect.</returns> public bool Intersects(mxCellState state, Rectangle rect) { if (state != null) { // Checks if the label intersects if (state.LabelBounds != null && state.LabelBounds.GetRectangle().IntersectsWith(rect)) { return true; } int pointCount = state.AbsolutePointCount(); // Checks if the segments of the edge intersect if (pointCount > 0) { mxRectangle tmp = new mxRectangle(rect); tmp.Grow(tolerance); rect = tmp.GetRectangle(); mxPoint p0 = state.AbsolutePoints[0]; for (int i = 0; i < pointCount; i++) { mxPoint p1 = state.AbsolutePoints[i]; // FIXME: Implement line intersection check //if (rect.IntersectsLine(p0.X, p0.Y, p1.X, p1 // .Y)) // return true; p0 = p1; } } else { // Checks if the bounds of the shape intersect return state.GetRectangle().IntersectsWith(rect); } } return false; }
/// <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; if (points != null && points.Count > 0) { 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> /// Updates the bounding box in the given cell state. /// </summary> /// <param name="state">Cell state whose bounding box should be /// updated.</param> /// <returns></returns> public mxRectangle UpdateBoundingBox(mxCellState state) { // Gets the cell bounds and adds shadows and markers mxRectangle rect = new mxRectangle(state.GetRectangle()); Dictionary<string, Object> style = state.Style; // Adds extra pixels for the marker and stroke assuming // that the border stroke is centered around the bounds // and the first pixel is drawn inside the bounds double strokeWidth = Math.Max(1, Math.Round(mxUtils.GetInt(style, mxConstants.STYLE_STROKEWIDTH, 1) * scale)); strokeWidth -= Math.Max(1, strokeWidth / 2); if (graph.Model.IsEdge(state.Cell)) { int ms = 0; if (style.ContainsKey(mxConstants.STYLE_ENDARROW) || style.ContainsKey(mxConstants.STYLE_STARTARROW)) { ms = (int) Math.Round(mxConstants.DEFAULT_MARKERSIZE * scale); } // Adds the strokewidth rect.Grow(ms + strokeWidth); // Adds worst case border for an arrow shape if (mxUtils.GetString(style, mxConstants.STYLE_SHAPE, "").Equals( mxConstants.SHAPE_ARROW)) { rect.Grow(mxConstants.ARROW_WIDTH / 2); } } else { rect.Grow(strokeWidth); } // Adds extra pixels for the shadow if (mxUtils.IsTrue(style, mxConstants.STYLE_SHADOW)) { rect.Width += mxConstants.SHADOW_OFFSETX; rect.Height += mxConstants.SHADOW_OFFSETY; } // Adds oversize images in labels if (mxUtils.GetString(style, mxConstants.STYLE_SHAPE, "").Equals( mxConstants.SHAPE_LABEL)) { if (mxUtils.GetString(style, mxConstants.STYLE_IMAGE) != null) { double w = mxUtils.GetInt(style, mxConstants.STYLE_IMAGE_WIDTH, mxConstants.DEFAULT_IMAGESIZE) * scale; double h = mxUtils.GetInt(style, mxConstants.STYLE_IMAGE_HEIGHT, mxConstants.DEFAULT_IMAGESIZE) * scale; double x = state.X; double y = 0; string imgAlign = mxUtils .GetString(style, mxConstants.STYLE_IMAGE_ALIGN, mxConstants.ALIGN_LEFT); string imgValign = mxUtils.GetString(style, mxConstants.STYLE_IMAGE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); if (imgAlign.Equals(mxConstants.ALIGN_RIGHT)) { x += state.Width - w; } else if (imgAlign.Equals(mxConstants.ALIGN_CENTER)) { x += (state.Width - w) / 2; } if (imgValign.Equals(mxConstants.ALIGN_TOP)) { y = state.Y; } else if (imgValign.Equals(mxConstants.ALIGN_BOTTOM)) { y = state.Y + state.Height - h; } else { y = state.Y + (state.Height - h) / 2; } rect.Add(new mxRectangle(x, y, w, h)); } } // Adds the rotated bounds to the bounding box if the // shape is rotated double rotation = mxUtils.GetDouble(style, mxConstants.STYLE_ROTATION); mxRectangle bbox = mxUtils.GetBoundingBox(rect, rotation); // Add the rotated bounding box to the non-rotated so // that all handles are also covered if (bbox != null) { rect.Add(bbox); } // Unifies the cell bounds and the label bounds if (!mxUtils.GetString(style, mxConstants.STYLE_OVERFLOW, "").Equals("hidden")) { rect.Add(state.LabelBounds); } state.BoundingBox = rect; return rect; }
/// <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> /// Returns the given terminal or the port defined in the given edge state if a /// cell state exists for that port. /// </summary> public mxCellState GetTerminalPort(mxCellState state, mxCellState terminal, bool source) { string key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT; string id = mxUtils.GetString(state.Style, key); if (id != null && graph.Model is mxGraphModel) { mxCellState tmp = GetState(((mxGraphModel)graph.Model).GetCell(id)); // Only uses ports where a cell state exists if (tmp != null) { terminal = tmp; } } return terminal; }
/// <summary> /// Returns the y-coordinate of the center point for automatic routing. /// </summary> /// <returns>Returns the y-coordinate of the routing center point.</returns> public double GetRoutingCenterY(mxCellState state) { float f = (state.Style != null) ? mxUtils.GetFloat(state. Style, mxConstants.STYLE_ROUTING_CENTER_Y) : 0; return state.GetCenterY() + f * state.Height; }
/// <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 the absolute center point along the given edge. /// </summary> public mxPoint GetPoint(mxCellState state) { return GetPoint(state, null); }
/// <summary> /// see com.mxgraph.mxICanvas.DrawLabel() /// </summary> public abstract Object DrawLabel(string text, mxCellState state, bool html);
/// <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> /// Returns true if perimeter points should be computed such that the /// resulting edge has only horizontal or vertical segments. /// </summary> /// <param name="edge">Cell state that represents the edge.</param> /// <returns>True if the edge is orthogonal.</returns> public bool IsOrthogonal(mxCellState edge) { if (edge.Style.ContainsKey(mxConstants.STYLE_ORTHOGONAL)) { return mxUtils.IsTrue(edge.Style, mxConstants.STYLE_ORTHOGONAL); } mxEdgeStyleFunction tmp = view.GetEdgeStyle(edge, null, null, null); return tmp == mxEdgeStyle.ElbowConnector || tmp == mxEdgeStyle.SideToSide || tmp == mxEdgeStyle.TopToBottom || tmp == mxEdgeStyle.EntityRelation; }
/// <summary> /// Sets the initial absolute terminal points in the given state before the edge /// style is computed. /// </summary> /// <param name="edge">Cell state whose initial terminal points should be updated.</param> /// <param name="source">Cell state which represents the source terminal.</param> /// <param name="target">Cell state which represents the target terminal.</param> public void UpdateFixedTerminalPoints(mxCellState edge, mxCellState source, mxCellState target) { UpdateFixedTerminalPoint(edge, source, true, graph.GetConnectionConstraint(edge, source, true)); UpdateFixedTerminalPoint(edge, target, false, graph.GetConnectionConstraint(edge, target, false)); }
/// <summary> /// Returns a connection constraint that describes the given connection /// point. This result can then be passed to getConnectionPoint. /// </summary> /// <param name="edge">Cell state that represents the edge.</param> /// <param name="terminal">Cell state that represents the terminal.</param> /// <param name="source">Boolean indicating if the terminal is the source or target.</param> /// <returns></returns> public mxConnectionConstraint GetConnectionConstraint(mxCellState edge, mxCellState terminal, bool source) { mxPoint point = null; string key = (source) ? mxConstants.STYLE_EXIT_X : mxConstants.STYLE_ENTRY_X; if (edge.Style.ContainsKey(key)) { double x = mxUtils.GetDouble(edge.Style, key); key = (source) ? mxConstants.STYLE_EXIT_Y : mxConstants.STYLE_ENTRY_Y; if (edge.Style.ContainsKey(key)) { double y = mxUtils.GetDouble(edge.Style, key); point = new mxPoint(x, y); } } bool perimeter = false; if (point != null) { perimeter = mxUtils.IsTrue(edge.Style, (source) ? mxConstants.STYLE_EXIT_PERIMETER : mxConstants.STYLE_ENTRY_PERIMETER, true); } return new mxConnectionConstraint(point, perimeter); }
/// <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> /// Parses the given element and paints it onto the canvas. /// </summary> /// <param name="tagName">Name of the node to be parsed.</param> /// <param name="attrs">Attributes of the node to be parsed.</param> public void ParseElement(string tagName, Dictionary<string, Object> attrs) { if (canvas == null && tagName.ToLower().Equals("graph")) { scale = mxUtils.GetDouble(attrs, "scale", 1); canvas = CreateCanvas(attrs); if (canvas != null) { canvas.Scale = scale; } } else if (canvas != null) { bool edge = tagName.ToLower().Equals("edge"); bool group = tagName.ToLower().Equals("group"); bool vertex = tagName.ToLower().Equals("vertex"); if ((edge && attrs.ContainsKey("points")) || ((vertex || group) && attrs.ContainsKey("x") && attrs.ContainsKey("y") && attrs.ContainsKey("width") && attrs.ContainsKey("height"))) { mxCellState state = new mxCellState(null, null, attrs); string label = ParseState(state, edge); canvas.DrawCell(state); canvas.DrawLabel(label, state, false); } } }
/// <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); } }
/** * Draws this stencil inside the given bounds. */ public bool DrawShape(mxGdiCanvas2D canvas, mxCellState state, mxRectangle bounds, bool background) { XmlNode elt = (background) ? bgNode : fgNode; if (elt != null) { String direction = mxUtils.GetString(state.Style, mxConstants.STYLE_DIRECTION, null); mxRectangle aspect = ComputeAspect(state, bounds, direction); double minScale = Math.Min(aspect.Width, aspect.Height); double sw = strokewidth.Equals("inherit") ? mxUtils.GetDouble( state.Style, mxConstants.STYLE_STROKEWIDTH, 1) * state.View.Scale : double .Parse(strokewidth) * minScale; lastMoveX = 0; lastMoveY = 0; canvas.StrokeWidth = sw; XmlNode tmp = elt.FirstChild; while (tmp != null) { if (tmp.NodeType == XmlNodeType.Element) { DrawElement(canvas, state, (XmlElement) tmp, aspect); } tmp = tmp.NextSibling; } return true; } return false; }
/// <summary> /// see com.mxgraph.mxICanvas.DrawCell() /// </summary> public override Object DrawCell(mxCellState state) { Dictionary<string, object> style = state.Style; GraphicsState graphicsState = g.Save(); // Applies the local translation g.TranslateTransform(translate.X, translate.Y); // Checks if the cell is an edge if (state.AbsolutePointCount() > 1) { DrawLine(state.AbsolutePoints, style); } else { Rectangle bounds = state.GetRectangle(); int x = bounds.X; int y = bounds.Y; int w = bounds.Width; int h = bounds.Height; // Applies the rotation on the graphics object and stores // the previous transform so that it can be restored float rotation = mxUtils.GetFloat(style, mxConstants.STYLE_ROTATION); if (rotation != 0) { int cx = x + w / 2; int cy = y + h / 2; g.TranslateTransform(cx, cy); g.RotateTransform(rotation); g.TranslateTransform(-cx, -cy); } // Draws a swimlane if start is > 0 string shape = mxUtils.GetString(style, mxConstants.STYLE_SHAPE, ""); if (!shape.Equals(mxConstants.SHAPE_SWIMLANE)) { // NOTE: Should draw built-in shapes first mxStencil stencil = mxStencilRegistry.GetStencil(shape); if (stencil != null) { stencil.PaintShape(this, state); } else { DrawShape(x, y, w, h, style); } } else { int start = (int)Math.Round(mxUtils.GetDouble(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE) * scale); // Removes some styles to draw the content area Dictionary<string, Object> cloned = new Dictionary<string, Object>(style); cloned.Remove(mxConstants.STYLE_FILLCOLOR); cloned.Remove(mxConstants.STYLE_ROUNDED); if (mxUtils.IsTrue(style, mxConstants.STYLE_HORIZONTAL, true)) { DrawShape(x, y, w, Math.Min(h, start), style); DrawShape(x, y + start, w, h - start, cloned); } else { DrawShape(x, y, Math.Min(w, start), h, style); DrawShape(x + start, y, w - start, h, cloned); } } } // Resets all changes to the graphics configuration g.Restore(graphicsState); return null; }
/// <summary> /// Paints the stencil for the given state. /// </summary> public void PaintShape(mxGdiCanvas gc, mxCellState state) { mxGdiCanvas2D canvas = CreateCanvas(gc); Dictionary<string, object> style = state.Style; double rotation = mxUtils.GetDouble(style, mxConstants.STYLE_ROTATION, 0); String direction = mxUtils.GetString(style, mxConstants.STYLE_DIRECTION, null); // Default direction is east (ignored if rotation exists) if (direction != null) { if (direction.Equals("north")) { rotation += 270; } else if (direction.Equals("west")) { rotation += 180; } else if (direction.Equals("south")) { rotation += 90; } } // New styles for shape flipping the stencil bool flipH = mxUtils.IsTrue(style, mxConstants.STYLE_STENCIL_FLIPH, false); bool flipV = mxUtils.IsTrue(style, mxConstants.STYLE_STENCIL_FLIPV, false); if (flipH && flipV) { rotation += 180; flipH = false; flipV = false; } // Saves the global state for each cell canvas.Save(); // Adds rotation and horizontal/vertical flipping rotation = rotation % 360; if (rotation != 0 || flipH || flipV) { canvas.Rotate(rotation, flipH, flipV, state.GetCenterX(), state.GetCenterY()); } // Note: Overwritten in mxStencil.paintShape (can depend on aspect) double scale = state.View.Scale; double sw = mxUtils.GetDouble(style, mxConstants.STYLE_STROKEWIDTH, 1) * scale; canvas.StrokeWidth = sw; double alpha = mxUtils.GetDouble(style, mxConstants.STYLE_OPACITY, 100) / 100; String gradientColor = mxUtils.GetString(style, mxConstants.STYLE_GRADIENTCOLOR, null); // Converts colors with special keyword none to null if (gradientColor != null && gradientColor.Equals(mxConstants.NONE)) { gradientColor = null; } String fillColor = mxUtils.GetString(style, mxConstants.STYLE_FILLCOLOR, null); if (fillColor != null && fillColor.Equals(mxConstants.NONE)) { fillColor = null; } String strokeColor = mxUtils.GetString(style, mxConstants.STYLE_STROKECOLOR, null); if (strokeColor != null && strokeColor.Equals(mxConstants.NONE)) { strokeColor = null; } // Draws the shadow if the fillColor is not transparent if (mxUtils.IsTrue(style, mxConstants.STYLE_SHADOW, false)) { DrawShadow(canvas, state, rotation, flipH, flipV, state, alpha, fillColor != null); } canvas.Alpha = alpha; // Sets the dashed state if (mxUtils.IsTrue(style, mxConstants.STYLE_DASHED, false)) { canvas.Dashed = true; } // Draws background and foreground if (strokeColor != null || fillColor != null) { if (strokeColor != null) { canvas.StrokeColor = strokeColor; } if (fillColor != null) { if (gradientColor != null && !gradientColor.Equals("transparent")) { canvas.SetGradient(fillColor, gradientColor, state.X, state.Y, state.Width, state.Height, direction); } else { canvas.FillColor = fillColor; } } // Draws background and foreground of shape DrawShape(canvas, state, state, true); DrawShape(canvas, state, state, false); } }
/// <summary> /// see com.mxgraph.mxICanvas.DrawLabel() /// </summary> public override Object DrawLabel(string text, mxCellState state, bool html) { Dictionary<string, object> style = state.Style; GraphicsState graphicsState = g.Save(); // Applies the local translation g.TranslateTransform(translate.X, translate.Y); Rectangle bounds = (state.LabelBounds != null) ? state.LabelBounds.GetRectangle() : state.GetRectangle(); DrawText(text, bounds.X, bounds.Y, bounds.Width, bounds.Height, style); // Resets all changes to the graphics configuration g.Restore(graphicsState); return null; }
/// <summary> /// Draws the given element. /// </summary> /// <param name="canvas"></param> /// <param name="state"></param> /// <param name="node"></param> /// <param name="aspect"></param> protected void DrawElement(mxGdiCanvas2D canvas, mxCellState state, XmlElement node, mxRectangle aspect) { string name = node.Name; double x0 = aspect.X; double y0 = aspect.Y; double sx = aspect.Width; double sy = aspect.Height; double minScale = Math.Min(sx, sy); // LATER: Move to lookup table if (name.Equals("save")) { canvas.Save(); } else if (name.Equals("restore")) { canvas.Restore(); } else if (name.Equals("path")) { canvas.Begin(); // Renders the elements inside the given path XmlNode childNode = node.FirstChild; while (childNode != null) { if (childNode.NodeType == XmlNodeType.Element) { DrawElement(canvas, state, (XmlElement) childNode, aspect); } childNode = childNode.NextSibling; } } else if (name.Equals("close")) { canvas.Close(); } else if (name.Equals("move")) { lastMoveX = x0 + GetDouble(node, "x") * sx; lastMoveY = y0 + GetDouble(node, "y") * sy; canvas.MoveTo(lastMoveX, lastMoveY); } else if (name.Equals("line")) { lastMoveX = x0 + GetDouble(node, "x") * sx; lastMoveY = y0 + GetDouble(node, "y") * sy; canvas.LineTo(lastMoveX, lastMoveY); } else if (name.Equals("quad")) { lastMoveX = x0 + GetDouble(node, "x2") * sx; lastMoveY = y0 + GetDouble(node, "y2") * sy; canvas.QuadTo(x0 + GetDouble(node, "x1") * sx, y0 + GetDouble(node, "y1") * sy, lastMoveX, lastMoveY); } else if (name.Equals("curve")) { lastMoveX = x0 + GetDouble(node, "x3") * sx; lastMoveY = y0 + GetDouble(node, "y3") * sy; canvas.CurveTo(x0 + GetDouble(node, "x1") * sx, y0 + GetDouble(node, "y1") * sy, x0 + GetDouble(node, "x2") * sx, y0 + GetDouble(node, "y2") * sy, lastMoveX, lastMoveY); } else if (name.Equals("arc")) { // Arc from stencil is turned into curves in image output double r1 = GetDouble(node, "rx") * sx; double r2 = GetDouble(node, "ry") * sy; double angle = GetDouble(node, "x-axis-rotation"); double largeArcFlag = GetDouble(node, "large-arc-flag"); double sweepFlag = GetDouble(node, "sweep-flag"); double x = x0 + GetDouble(node, "x") * sx; double y = y0 + GetDouble(node, "y") * sy; double[] curves = mxUtils.ArcToCurves(this.lastMoveX, this.lastMoveY, r1, r2, angle, largeArcFlag, sweepFlag, x, y); for (int i = 0; i < curves.Length; i += 6) { canvas.CurveTo(curves[i], curves[i + 1], curves[i + 2], curves[i + 3], curves[i + 4], curves[i + 5]); lastMoveX = curves[i + 4]; lastMoveY = curves[i + 5]; } } else if (name.Equals("rect")) { canvas.Rect(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, GetDouble(node, "w") * sx, GetDouble(node, "h") * sy); } else if (name.Equals("roundrect")) { double arcsize = GetDouble(node, "arcsize"); if (arcsize == 0) { arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100; } double w = GetDouble(node, "w") * sx; double h = GetDouble(node, "h") * sy; double factor = arcsize / 100; double r = Math.Min(w * factor, h * factor); canvas.Roundrect(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, GetDouble(node, "w") * sx, GetDouble(node, "h") * sy, r, r); } else if (name.Equals("ellipse")) { canvas.Ellipse(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, GetDouble(node, "w") * sx, GetDouble(node, "h") * sy); } else if (name.Equals("image")) { string src = EvaluateAttribute(node, "src", state); canvas.Image(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, GetDouble(node, "w") * sx, GetDouble(node, "h") * sy, src, false, GetString(node, "flipH", "0").Equals("1"), GetString(node, "flipV", "0").Equals("1")); } else if (name.Equals("text")) { String str = EvaluateAttribute(node, "str", state); canvas.Text(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, 0, 0, str, node.GetAttribute("align"), node.GetAttribute("valign"), GetString(node, "vertical", "0").Equals("1"), false, ""); } else if (name.Equals("include-shape")) { mxStencil stencil = mxStencilRegistry.GetStencil(node .GetAttribute("name")); if (stencil != null) { double x = x0 + GetDouble(node, "x") * sx; double y = y0 + GetDouble(node, "y") * sy; double w = GetDouble(node, "w") * sx; double h = GetDouble(node, "h") * sy; mxRectangle tmp = new mxRectangle(x, y, w, h); stencil.DrawShape(canvas, state, tmp, true); stencil.DrawShape(canvas, state, tmp, false); } } else if (name.Equals("fillstroke")) { canvas.FillAndStroke(); } else if (name.Equals("fill")) { canvas.Fill(); } else if (name.Equals("stroke")) { canvas.Stroke(); } else if (name.Equals("strokewidth")) { canvas.StrokeWidth = GetDouble(node, "width") * minScale; } else if (name.Equals("dashed")) { canvas.Dashed = node.GetAttribute("dashed") == "1"; } else if (name.Equals("dashpattern")) { string value = node.GetAttribute("pattern"); if (value != null) { string[] tmp = value.Split(' '); StringBuilder pat = new StringBuilder(); for (int i = 0; i < tmp.Length; i++) { if (tmp[i].Length > 0) { pat.Append(double.Parse(tmp[i]) * minScale); pat.Append(" "); } } value = pat.ToString(); } canvas.DashPattern = value; } else if (name.Equals("strokecolor")) { canvas.StrokeColor = node.GetAttribute("color"); } else if (name.Equals("linecap")) { canvas.LineCap = node.GetAttribute("cap"); } else if (name.Equals("linejoin")) { canvas.LineJoin = node.GetAttribute("join"); } else if (name.Equals("miterlimit")) { canvas.MiterLimit = GetDouble(node, "limit"); } else if (name.Equals("fillcolor")) { canvas.FillColor = node.GetAttribute("color"); } else if (name.Equals("fontcolor")) { canvas.FontColor = node.GetAttribute("color"); } else if (name.Equals("fontstyle")) { canvas.FontStyle = GetInt(node, "style", 0); } else if (name.Equals("fontfamily")) { canvas.FontFamily = node.GetAttribute("family"); } else if (name.Equals("fontsize")) { canvas.FontSize = GetDouble(node, "size") * minScale; } }
/// <summary> /// see com.mxgraph.mxICanvas.DrawCell() /// </summary> public abstract Object DrawCell(mxCellState state);
/// <summary> /// Returns a clone of this state where all members are deeply cloned /// except the view and cell references, which are copied with no /// cloning to the new instance. /// </summary> /// <returns></returns> public new mxCellState Clone() { mxCellState clone = new mxCellState(view, cell, style); if (absolutePoints != null) { clone.absolutePoints = new List<mxPoint>(); foreach (mxPoint pt in absolutePoints) { clone.absolutePoints.Add(pt.Clone()); } } if (origin != null) { clone.origin = origin.Clone(); } if (absoluteOffset != null) { clone.absoluteOffset = absoluteOffset.Clone(); } if (labelBounds != null) { clone.labelBounds = labelBounds.Clone(); } if (boundingBox != null) { clone.boundingBox = boundingBox.Clone(); } clone.terminalDistance = terminalDistance; clone.segments = segments; clone.length = length; clone.x = x; clone.y = y; clone.width = width; clone.height = height; return clone; }
/// <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; }