/// <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> /// 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> /// Creates an image for the given arguments. /// </summary> public static Image CreateImage(mxGraph graph, Object[] cells, double scale, Color? background, bool antiAlias, mxRectangle clip, mxGdiCanvas graphicsCanvas) { mxImageCanvas canvas = (mxImageCanvas) DrawCells(graph, cells, scale, clip, new ImageCanvasFactory(graphicsCanvas, background, antiAlias)); return canvas.Destroy(); }
/// <summary> /// Creates an image for the given arguments. /// </summary> public static Image CreateImage(mxGraph graph, Object[] cells, double scale, Color?background, bool antiAlias, mxRectangle clip, mxGdiCanvas graphicsCanvas) { mxImageCanvas canvas = (mxImageCanvas)DrawCells(graph, cells, scale, clip, new ImageCanvasFactory(graphicsCanvas, background, antiAlias)); return(canvas.Destroy()); }
/// <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); }
/// <summary> /// Returns true if the given object equals this rectangle. /// </summary> /// <returns>Returns true if obj is equal.</returns> new public Boolean Equals(Object obj) { if (obj is mxRectangle) { mxRectangle rect = (mxRectangle)obj; return(rect.X == X && rect.Y == Y && rect.Width == Width && rect.Height == height); } return(false); }
/// <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) { ValidateBounds(null, cell); GraphBounds = ValidatePoints(null, cell); if (GraphBounds == null) { GraphBounds = new mxRectangle(); } } }
/// <summary> /// Adds the given rectangle to this rectangle. /// </summary> public void Add(mxRectangle rect) { if (rect != null) { double minX = Math.Min(x, rect.x); double minY = Math.Min(y, rect.y); double maxX = Math.Max(x + width, rect.x + rect.width); double maxY = Math.Max(y + height, rect.y + rect.height); x = minX; y = minY; width = maxX - minX; height = maxY - minY; } }
/* (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); }
/// <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); }
/** * 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 rectangle. /// </summary> /// <param name="rect">Rectangle to construct a copy of.</param> public mxRectangle(mxRectangle rect) : this(rect.X, rect.Y, rect.Width, rect.Height) { }
/// <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 bounds for the given cells. /// </summary> public mxRectangle GetBoundsForCells(Object[] cells, bool includeEdges, bool includeDescendants, bool boundingBox) { mxRectangle result = null; if (cells != null && cells.Length > 0) { for (int i = 0; i < cells.Length; i++) { mxRectangle tmp = GetCellBounds(cells[i], includeEdges, includeDescendants, boundingBox); if (tmp != null) { if (result == null) { result = new mxRectangle(tmp); } else { result.Add(tmp); } } } } return result; }
/// <summary> /// Returns the bounds for a label for the given location and size, taking /// into account the alignment and spacing in the specified style, as well /// as the width and height of the rectangle that contains the label. /// (For edge labels this width and height is 0.) The scale is used to scale /// the given size and the spacings in the specified style. /// </summary> public static mxRectangle GetScaledLabelBounds(double x, double y, mxRectangle size, double outerWidth, double outerHeight, Dictionary<string, Object> style, double scale) { // Adds an inset of 3 pixels double inset = mxConstants.LABEL_INSET* scale; // Scales the size of the label double width = size.Width * scale + 2 * inset; double height = size.Height * scale; // Gets the global spacing and orientation bool horizontal = IsTrue(style, mxConstants.STYLE_HORIZONTAL, true); int spacing = (int)(GetInt(style, mxConstants.STYLE_SPACING) * scale); // Gets the alignment settings Object align = GetString(style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); Object valign = GetString(style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); // Gets the vertical spacing int top = (int)(GetInt(style, mxConstants.STYLE_SPACING_TOP) * scale); int bottom = (int)(GetInt(style, mxConstants.STYLE_SPACING_BOTTOM) * scale); // Gets the horizontal spacing int left = (int)(GetInt(style, mxConstants.STYLE_SPACING_LEFT) * scale); int right = (int)(GetInt(style, mxConstants.STYLE_SPACING_RIGHT) * scale); // Applies the orientation to the spacings if (!horizontal) { int tmp = top; top = right; right = bottom; bottom = left; left = tmp; double tmp2 = width; width = height; height = tmp2; } // Computes the position of the label for the horizontal alignment if ((horizontal && align.Equals(mxConstants.ALIGN_CENTER)) || (!horizontal && valign.Equals(mxConstants.ALIGN_MIDDLE))) { x += (outerWidth - width) / 2 + left - right; } else if ((horizontal && align.Equals(mxConstants.ALIGN_RIGHT)) || (!horizontal && valign.Equals(mxConstants.ALIGN_BOTTOM))) { x += outerWidth - width - spacing - right; } else { x += spacing + left; } // Computes the position of the label for the vertical alignment if ((!horizontal && align.Equals(mxConstants.ALIGN_CENTER)) || (horizontal && valign.Equals(mxConstants.ALIGN_MIDDLE))) { y += (outerHeight - height) / 2 + top - bottom; } else if ((!horizontal && align.Equals(mxConstants.ALIGN_LEFT)) || (horizontal && valign.Equals(mxConstants.ALIGN_BOTTOM))) { y += outerHeight - height - spacing - bottom; } else { y += spacing + top; } return new mxRectangle(x, y, width, height); }
/// <summary> /// Returns the bounding box of the rotated rectangle. /// </summary> public static mxRectangle GetBoundingBox(mxRectangle rect, double rotation) { // TODO: Check use of GraphicsPath (see mxGdiCanvas.DrawText) mxRectangle result = null; if (rect != null && rotation != 0) { double rad = ToRadians(rotation); double cos = Math.Cos(rad); double sin = Math.Sin(rad); mxPoint cx = new mxPoint(rect.X + rect.Width / 2, rect.Y + rect.Height / 2); mxPoint p1 = new mxPoint(rect.X, rect.Y); mxPoint p2 = new mxPoint(rect.X + rect.Width, rect.Y); mxPoint p3 = new mxPoint(p2.X, rect.Y + rect.Height); mxPoint p4 = new mxPoint(rect.X, p3.Y); p1 = GetRotatedPoint(p1, cos, sin, cx); p2 = GetRotatedPoint(p2, cos, sin, cx); p3 = GetRotatedPoint(p3, cos, sin, cx); p4 = GetRotatedPoint(p4, cos, sin, cx); result = new mxRectangle((int)p1.X, (int)p1.Y, 0, 0); result.Add(new mxRectangle(p2.X, p2.Y, 0, 0)); result.Add(new mxRectangle(p3.X, p3.Y, 0, 0)); result.Add(new mxRectangle(p4.X, p4.Y, 0, 0)); } return result; }
/// <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> /// Draws the given cells using a Graphics2D canvas and returns the buffered image /// that represents the cells. /// </summary> public static mxICanvas DrawCells(mxGraph graph, Object[] cells, double scale, mxRectangle clip, CanvasFactory factory) { mxICanvas canvas = null; if (cells == null) { cells = new Object[] { graph.Model.Root }; } if (cells != null) { // Gets the current state of the view mxGraphView view = graph.View; Dictionary<Object, mxCellState> states = view.States; double oldScale = view.Scale; // Keeps the existing translation as the cells might // be aligned to the grid in a different way in a graph // that has a translation other than zero bool eventsEnabled = view.IsEventsEnabled; // Disables firing of scale events so that there is no // repaint or update of the original graph view.IsEventsEnabled = false; try { // TODO: Factor-out into mxTemporaryCellStates class view.States = new Dictionary<Object, mxCellState>(); view.Scale = scale; // Creates virtual parent state for validation mxCellState state = view.CreateState(new mxCell()); // Validates the vertices and edges without adding them to // the model so that the original cells are not modified for (int i = 0; i < cells.Length; i++) { view.ValidateBounds(state, cells[i]); } for (int i = 0; i < cells.Length; i++) { view.ValidatePoints(state, cells[i]); } if (clip == null) { clip = graph.GetPaintBounds(cells); } if (clip != null && clip.Width > 0 && clip.Height > 0) { Rectangle rect = clip.GetRectangle(); canvas = factory.CreateCanvas(rect.Width + 1, rect.Height + 1); if (canvas != null) { double previousScale = canvas.Scale; Point previousTranslate = canvas.Translate; try { canvas.Translate = new Point(-rect.X, -rect.Y); canvas.Scale = view.Scale; for (int i = 0; i < cells.Length; i++) { graph.DrawCell(canvas, cells[i]); } } finally { canvas.Scale = previousScale; canvas.Translate = previousTranslate; } } } } finally { view.Scale = oldScale; view.States = states; view.IsEventsEnabled = eventsEnabled; } } return canvas; }
/// <summary> /// Draws the given cells using a Graphics2D canvas and returns the buffered image /// that represents the cells. /// </summary> public static mxICanvas DrawCells(mxGraph graph, Object[] cells, double scale, mxRectangle clip, CanvasFactory factory) { mxICanvas canvas = null; if (cells == null) { cells = new Object[] { graph.Model.Root }; } if (cells != null) { // Gets the current state of the view mxGraphView view = graph.View; Dictionary <Object, mxCellState> states = view.States; double oldScale = view.Scale; // Keeps the existing translation as the cells might // be aligned to the grid in a different way in a graph // that has a translation other than zero bool eventsEnabled = view.IsEventsEnabled; // Disables firing of scale events so that there is no // repaint or update of the original graph view.IsEventsEnabled = false; try { // TODO: Factor-out into mxTemporaryCellStates class view.States = new Dictionary <Object, mxCellState>(); view.Scale = scale; // Creates virtual parent state for validation mxCellState state = view.CreateState(new mxCell()); // Validates the vertices and edges without adding them to // the model so that the original cells are not modified for (int i = 0; i < cells.Length; i++) { view.ValidateBounds(state, cells[i]); } for (int i = 0; i < cells.Length; i++) { view.ValidatePoints(state, cells[i]); } if (clip == null) { clip = graph.GetPaintBounds(cells); } if (clip != null && clip.Width > 0 && clip.Height > 0) { Rectangle rect = clip.GetRectangle(); canvas = factory.CreateCanvas(rect.Width + 1, rect.Height + 1); if (canvas != null) { double previousScale = canvas.Scale; Point previousTranslate = canvas.Translate; try { canvas.Translate = new Point(-rect.X, -rect.Y); canvas.Scale = view.Scale; for (int i = 0; i < cells.Length; i++) { graph.DrawCell(canvas, cells[i]); } } finally { canvas.Scale = previousScale; canvas.Translate = previousTranslate; } } } } finally { view.Scale = oldScale; view.States = states; view.IsEventsEnabled = eventsEnabled; } } return(canvas); }
/// <summary> /// Creates an image for the given arguments. /// </summary> public static Image CreateImage(mxGraph graph, Object[] cells, double scale, Color? background, bool antiAlias, mxRectangle clip) { return CreateImage(graph, cells, scale, background, antiAlias, clip, new mxGdiCanvas()); }
/// <summary> /// Constructs a copy of the given geometry. /// </summary> /// <param name="geometry">Geometry to construct a copy of.</param> public mxGeometry(mxGeometry geometry) : base(geometry.X, geometry.Y, geometry.Width, geometry .Height) { if (geometry.points != null) { points = new List<mxPoint>(geometry.points.Count); foreach (mxPoint pt in geometry.points) { points.Add(pt.Clone()); } } if (geometry.sourcePoint != null) { sourcePoint = geometry.sourcePoint.Clone(); } if (geometry.targetPoint != null) { targetPoint = geometry.targetPoint.Clone(); } if (geometry.offset != null) { offset = geometry.offset.Clone(); } if (geometry.alternateBounds != null) { alternateBounds = geometry.alternateBounds.Clone(); } relative = geometry.relative; }
/// <summary> /// Creates an image for the given arguments. /// </summary> public static Image CreateImage(mxGraph graph, Object[] cells, double scale, Color?background, bool antiAlias, mxRectangle clip) { return(CreateImage(graph, cells, scale, background, antiAlias, clip, new mxGdiCanvas())); }
/// <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> /// 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> /// Returns the paint bounds for the given label. /// </summary> /// <returns></returns> public static mxRectangle GetLabelPaintBounds(String label, Dictionary<string, Object> style, bool isHtml, mxPoint offset, mxRectangle vertexBounds, double scale) { bool horizontal = mxUtils.IsTrue(style, mxConstants.STYLE_HORIZONTAL, true); int w = 0; if (vertexBounds != null && GetString(style, mxConstants.STYLE_WHITE_SPACE, "nowrap").Equals("wrap")) { if (horizontal) { w = (int)(vertexBounds.Width / scale); } else { w = (int)(vertexBounds.Height / scale); } } mxRectangle size = mxUtils.GetLabelSize(label, style, w); double x = offset.X; double y = offset.Y; double width = 0; double height = 0; if (vertexBounds != null) { x += vertexBounds.X; y += vertexBounds.Y; // Limits the label to the swimlane title if (mxUtils.GetString(style, mxConstants.STYLE_SHAPE, "").Equals( mxConstants.SHAPE_SWIMLANE)) { double start = mxUtils.GetDouble(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE) * scale; if (horizontal) { width += vertexBounds.Width; height += start; } else { width += start; height += vertexBounds.Height; } } else { width += vertexBounds.Width; height += vertexBounds.Height; } } return mxUtils.GetScaledLabelBounds(x, y, size, width, height, style, scale); }
/// <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> /// 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 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); }
/** * 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> /// Validates the points for the state of the given cell recursively if the /// cell is not collapsed and returns the bounding box of all visited states /// as a rectangle. /// </summary> public mxRectangle ValidatePoints(mxCellState parentState, Object cell) { mxIGraphModel model = graph.Model; mxCellState state = GetState(cell); mxRectangle bbox = null; if (state != null) { mxGeometry geo = graph.GetCellGeometry(cell); if (geo != null && model.IsEdge(cell)) { // Updates the points on the source terminal if its an edge mxCellState source = GetState(GetVisibleTerminal(cell, true)); if (source != null && model.IsEdge(source.Cell) && !model.IsAncestor(source, cell)) { mxCellState tmp = GetState(model.GetParent(source.Cell)); ValidatePoints(tmp, source.Cell); } // Updates the points on the target terminal if its an edge mxCellState target = GetState(GetVisibleTerminal(cell, false)); if (target != null && model.IsEdge(target.Cell) && !model.IsAncestor(target.Cell, cell)) { mxCellState tmp = GetState(model.GetParent(target.Cell)); ValidatePoints(tmp, target.Cell); } UpdateFixedTerminalPoints(state, source, target); UpdatePoints(state, geo.Points, source, target); UpdateFloatingTerminalPoints(state, source, target); UpdateEdgeBounds(state); state.AbsoluteOffset = GetPoint(state, geo); } else if (geo != null && geo.Relative && parentState != null && model.IsEdge(parentState.Cell)) { mxPoint origin = GetPoint(parentState, geo); if (origin != null) { state.X = origin.X; state.Y = origin.Y; origin.X = (origin.X / scale) - translate.X; origin.Y = (origin.Y / scale) - translate.Y; state.Origin = origin; childMoved(parentState, state); } } if (model.IsEdge(cell) || model.IsVertex(cell)) { UpdateLabelBounds(state); bbox = new mxRectangle(UpdateBoundingBox(state)); } } if (state != null && !graph.IsCellCollapsed(cell)) { int childCount = model.GetChildCount(cell); for (int i = 0; i < childCount; i++) { Object child = model.GetChildAt(cell, i); mxRectangle bounds = ValidatePoints(state, child); if (bounds != null) { if (bbox == null) { bbox = bounds; } else { bbox.Add(bounds); } } } } return bbox; }
/// <summary> /// 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> /// Validates the points for the state of the given cell recursively if the /// cell is not collapsed and returns the bounding box of all visited states /// as a rectangle. /// </summary> public mxRectangle ValidatePoints(mxCellState parentState, Object cell) { mxIGraphModel model = graph.Model; mxCellState state = GetState(cell); mxRectangle bbox = null; if (state != null) { mxGeometry geo = graph.GetCellGeometry(cell); if (geo != null && model.IsEdge(cell)) { // Updates the points on the source terminal if its an edge mxCellState source = GetState(GetVisibleTerminal(cell, true)); if (source != null && model.IsEdge(source.Cell) && !model.IsAncestor(source, cell)) { mxCellState tmp = GetState(model.GetParent(source.Cell)); ValidatePoints(tmp, source.Cell); } // Updates the points on the target terminal if its an edge mxCellState target = GetState(GetVisibleTerminal(cell, false)); if (target != null && model.IsEdge(target.Cell) && !model.IsAncestor(target.Cell, cell)) { mxCellState tmp = GetState(model.GetParent(target.Cell)); ValidatePoints(tmp, target.Cell); } UpdateFixedTerminalPoints(state, source, target); UpdatePoints(state, geo.Points, source, target); UpdateFloatingTerminalPoints(state, source, target); UpdateEdgeBounds(state); state.AbsoluteOffset = GetPoint(state, geo); } else if (geo != null && geo.Relative && parentState != null && model.IsEdge(parentState.Cell)) { mxPoint origin = GetPoint(parentState, geo); if (origin != null) { state.X = origin.X; state.Y = origin.Y; origin.X = (origin.X / scale) - translate.X; origin.Y = (origin.Y / scale) - translate.Y; state.Origin = origin; childMoved(parentState, state); } } if (model.IsEdge(cell) || model.IsVertex(cell)) { UpdateLabelBounds(state); bbox = new mxRectangle(UpdateBoundingBox(state)); } } if (state != null && !graph.IsCellCollapsed(cell)) { int childCount = model.GetChildCount(cell); for (int i = 0; i < childCount; i++) { Object child = model.GetChildAt(cell, i); mxRectangle bounds = ValidatePoints(state, child); if (bounds != null) { if (bbox == null) { bbox = bounds; } else { bbox.Add(bounds); } } } } return(bbox); }
/// <summary> /// 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; }