/// <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 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> /// 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> /// 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> /// 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; }