/// <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> /// 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> /// 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); }
#pragma warning disable CS1570 // XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'state'.' #pragma warning disable CS1570 // XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.' /// <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, #pragma warning restore CS1570 // XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.' #pragma warning restore CS1570 // XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'state'.' mxCellState state) { string result = elt.GetAttribute(attribute); if (result == null) { // JS functions as text content are currently not supported in .NET } return(result); }
/// <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)); }
/** * 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 Object DrawCell(mxCellState state) { return(canvas.DrawCell(state)); }
/// <summary> /// see com.mxgraph.mxICanvas.DrawLabel() /// </summary> public Object DrawLabel(string text, mxCellState state, bool html) { return(canvas.DrawLabel(text, state, html)); }
/// <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, 1, 1); } else { canvas.FillColor = fillColor; } } // Draws background and foreground of shape DrawShape(canvas, state, state, true); DrawShape(canvas, state, state, false); } }
/// <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); double rotation = GetString(node, "vertical", "0").Equals("1") ? -90 : 0; canvas.Text(x0 + GetDouble(node, "x") * sx, y0 + GetDouble(node, "y") * sy, 0, 0, str, node.GetAttribute("align"), node.GetAttribute("valign"), false, "", null, false, rotation, null); } 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.DrawLabel() /// </summary> public abstract Object DrawLabel(string text, mxCellState state, bool html);
/// <summary> /// see com.mxgraph.mxICanvas.DrawCell() /// </summary> public abstract Object DrawCell(mxCellState state);