/// <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 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 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> /// First validates all bounds and then validates all points recursively on /// all visible cells. /// </summary> public void Validate() { Object cell = graph.Model.Root; if (cell != null && states.Count == 0) { mxRectangle graphBounds = GetBoundingBox(ValidateCellState(ValidateCell(cell))); GraphBounds = (graphBounds != null) ? graphBounds : new mxRectangle(); } }
/// <summary> /// Returns the rectangle that should be used as the perimeter of the cell. /// </summary> /// <param name="border"></param> /// <returns>Returns the rectangle that defines the perimeter.</returns> public mxRectangle GetPerimeterBounds(double border) { mxRectangle bounds = new mxRectangle(this); if (border != 0) { bounds.Grow(border); } return(bounds); }
/* (non-Dotnetdoc) * see com.mxgraph.mxGraphViewReader.CreateCanvas() */ override public mxICanvas CreateCanvas(Dictionary <string, Object> attrs) { int width = 0; int height = 0; int dx = 0; int dy = 0; mxRectangle tmp = Clip; if (tmp != null) { dx -= (int)tmp.X; dy -= (int)tmp.Y; width = (int)tmp.Width; height = (int)tmp.Height; } else { int x = (int)Math.Round(mxUtils.GetDouble(attrs, "x")); int y = (int)Math.Round(mxUtils.GetDouble(attrs, "y")); width = (int)(Math.Round(mxUtils.GetDouble(attrs, "width"))) + border + 3; height = (int)(Math.Round(mxUtils.GetDouble(attrs, "height"))) + border + 3; if (cropping) { dx = -x + 3; dy = -y + 3; } else { width += x; height += y; } } mxImageCanvas canvas = new mxImageCanvas(new mxGdiCanvas(), width, height, Background, AntiAlias); canvas.Translate = new Point(dx, dy); return(canvas); }
/// <summary> /// Updates the label bounds in the given state. /// </summary> /// <param name="state"></param> public void UpdateLabelBounds(mxCellState state) { Object cell = state.Cell; Dictionary <string, Object> style = state.Style; if (mxUtils.GetString(style, mxConstants.STYLE_OVERFLOW, "").Equals("fill")) { state.LabelBounds = new mxRectangle(state); } else { string label = graph.GetLabel(cell); mxRectangle vertexBounds = (!graph.Model.IsEdge(cell)) ? state : null; state.LabelBounds = mxUtils.GetLabelPaintBounds(label, style, false, state.AbsoluteOffset, vertexBounds, scale); } }
/// <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> /// Returns the bounding box of the shape and the label for the given /// cell state and its children if recurse is true. /// </summary> /// <param name="state">Cell state whose bounding box should be returned.</param> /// <param name="recurse">Boolean indicating if the children should be included.</param> public mxRectangle GetBoundingBox(mxCellState state, Boolean recurse) { mxRectangle bbox = null; if (state != null) { if (state.BoundingBox != null) { bbox = (mxRectangle)state.BoundingBox.Clone(); } if (recurse) { mxIGraphModel model = graph.Model; int childCount = model.GetChildCount(state.Cell); for (int i = 0; i < childCount; i++) { mxRectangle bounds = GetBoundingBox( GetState(model.GetChildAt(state.Cell, i)), true); if (bounds != null) { if (bbox == null) { bbox = bounds; } else { bbox.Add(bounds); } } } } } return(bbox); }
/** * 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> /// 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> /// Returns the bounding box for an array of cells or null, if no cells are /// specified. /// </summary> public mxRectangle GetBounds(Object[] cells, bool boundingBox) { mxRectangle result = null; if (cells != null && cells.Length > 0) { mxIGraphModel model = graph.Model; for (int i = 0; i < cells.Length; i++) { if (model.IsVertex(cells[i]) || model.IsEdge(cells[i])) { mxCellState state = GetState(cells[i]); if (state != null) { mxRectangle tmp = (boundingBox) ? state.BoundingBox : state; if (tmp != null) { if (result == null) { result = new mxRectangle(tmp); } else { result.Add(tmp); } } } } } } return(result); }
/// <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; } }
public void execute(Object parent) { mxIGraphModel model = graph.Model; //.GetModel(); // Moves the vertices to build a circle. Makes sure the // radius is large enough for the vertices to not // overlap model.BeginUpdate(); try { // Gets all vertices inside the parent and finds // the maximum dimension of the largest vertex double max = 0; Double top = 0; Double left = 0; List <Object> vertices = new List <Object>(); int childCount = model.GetChildCount(parent); for (int i = 0; i < childCount; i++) { Object cell = model.GetChildAt(parent, i); if (!isVertexIgnored(cell)) { vertices.add(cell); mxRectangle bounds = getVertexBounds(cell); if (top == null) { top = bounds.getY(); } else { top = Math.Min(top, bounds.getY()); } if (left == null) { left = bounds.getX(); } else { left = Math.Min(left, bounds.getX()); } max = Math.Max(max, Math.Max(bounds.getWidth(), bounds .getHeight())); } else if (!isEdgeIgnored(cell)) { if (isResetEdges()) { graph.resetEdge(cell); } if (isDisableEdgeStyle()) { setEdgeStyleEnabled(cell, false); } } } int vertexCount = vertices.size(); double r = Math.Max(vertexCount * max / Math.PI, radius); // Moves the circle to the specified origin if (moveCircle) { left = x0; top = y0; } circle(vertices.ToArray(), r, left, top); } finally { model.EndUpdate(); } }
/// <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); }