/// <summary> /// Draws grid lines corresponding to a vertical scale using GDI+</summary> /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param> /// <param name="linePen">Grid line pen</param> /// <param name="g">Graphics GDI+ drawing surface</param> public static void DrawVerticalScaleGrid( Matrix transform, RectangleF graphRect, int majorSpacing, Pen linePen, System.Drawing.Graphics g) { double xScale = transform.Elements[0]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double min = Math.Min(graphRect.Left, graphRect.Right); double max = Math.Max(graphRect.Left, graphRect.Right); double tickAnchor = D2dUtil.CalculateTickAnchor(min, max); double step = D2dUtil.CalculateStep(min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, 0.0); if (step > 0) { double offset = tickAnchor - min; offset = offset - MathUtil.Remainder(offset, step) + step; for (double x = tickAnchor - offset; x <= max; x += step) { double cx = (x - graphRect.Left) * xScale + clientRect.Left; g.DrawLine(linePen, (float)cx, clientRect.Top, (float)cx, clientRect.Bottom); } } }
/// <summary> /// Gets a bounding rectangle for the items, in client coordinates</summary> /// <param name="items">Items to bound</param> /// <returns>Bounding rectangle for the items, in client coordinates</returns> public virtual Rectangle GetBounds(IEnumerable <object> items) { RectangleF bounds = m_renderer.GetBounds(items.AsIEnumerable <TNode>(), m_d2dGraphics); bounds = D2dUtil.Transform(m_d2dGraphics.Transform, bounds); return(Rectangle.Truncate(bounds)); }
/// <summary> /// Draws grid lines corresponding to a horizontal scale using GDI+</summary> /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param> /// <param name="linePen">Grid line pen</param> /// <param name="g">Graphics GDI+ drawing surface</param> public static void DrawHorizontalScaleGrid( Matrix transform, RectangleF graphRect, int majorSpacing, Pen linePen, System.Drawing.Graphics g) { double yScale = transform.Elements[3]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double min = Math.Min(graphRect.Top, graphRect.Bottom); double max = Math.Max(graphRect.Top, graphRect.Bottom); double tickAnchor = D2dUtil.CalculateTickAnchor(min, max); double step = D2dUtil.CalculateStep(min, max, Math.Abs(clientRect.Bottom - clientRect.Top), majorSpacing, 0.0); if (step > 0) { double offset = tickAnchor - min; offset = offset - MathUtil.Remainder(offset, step) + step; for (double y = tickAnchor - offset; y <= max; y += step) { double cy; if (yScale > 0) { cy = (y - min) * yScale + clientRect.Top; } else { cy = (y - max) * yScale + clientRect.Top; } g.DrawLine(linePen, clientRect.Left, (float)cy, clientRect.Right, (float)cy); } } }
/// <summary> /// Performs hit testing for rectangle bounds, in client coordinates</summary> /// <param name="pickRect">Pick rectangle, in client coordinates</param> /// <returns>Items that overlap with the rectangle, in client coordinates</returns> public virtual IEnumerable <object> Pick(Rectangle pickRect) { Matrix3x2F invXform = Matrix3x2F.Invert(m_d2dGraphics.Transform); RectangleF rect = D2dUtil.Transform(invXform, pickRect); return(m_renderer.Pick(m_graph, rect, m_d2dGraphics)); }
/// <summary> /// Performs hit testing for rectangle bounds, in client coordinates</summary> /// <param name="pickRect">Pick rectangle, in client coordinates</param> /// <returns>Items that overlap with the rectangle, in client coordinates</returns> public override IEnumerable <object> Pick(Rectangle pickRect) { #if CS_4 Matrix3x2F invXform = Matrix3x2F.Invert(m_d2dGraphics.Transform); RectangleF rect = D2dUtil.Transform(invXform, pickRect); IEnumerable <object> pickedGraphNodes = base.Pick(pickRect); #else // workaround a C#3 compiler bug( CS1911 warning) Matrix3x2F invXform = Matrix3x2F.Invert(m_d2dGraphics.Transform); RectangleF rect = D2dUtil.Transform(invXform, pickRect); List <object> pickedGraphNodes = new List <object>(); foreach (TNode node in m_graph.Nodes) { RectangleF nodeBounds = m_renderer.GetBounds(node, m_d2dGraphics); // in graph space if (nodeBounds.IntersectsWith(rect)) { pickedGraphNodes.Add(node); } } #endif foreach (var pickedGraphNode in pickedGraphNodes) { yield return(pickedGraphNode); } var pickedFloatingPins = new List <object>(); var circuiElement = m_graph.Cast <ICircuitElementType>(); foreach (var pin in circuiElement.Inputs) { var grpPIn = pin.Cast <ICircuitGroupPin <TNode> >(); RectangleF nodeBounds = m_renderer.GetBounds(grpPIn, true, m_d2dGraphics); if (nodeBounds.IntersectsWith(rect)) { pickedFloatingPins.Add(pin); } } foreach (var pin in circuiElement.Outputs) { var grpPIn = pin.Cast <ICircuitGroupPin <TNode> >(); RectangleF nodeBounds = m_renderer.GetBounds(grpPIn, false, m_d2dGraphics); if (nodeBounds.IntersectsWith(rect)) { pickedFloatingPins.Add(pin); } } foreach (var floatingPin in pickedFloatingPins) { yield return(floatingPin); } }
/// <summary> /// Gets a bounding rectangle for the items, in client coordinates</summary> /// <param name="items">Items to bound</param> /// <returns>Bounding rectangle for the items, in client coordinates</returns> public override Rectangle GetBounds(IEnumerable <object> items) { var bounds = base.GetBounds(items); // include group pins y range var group = m_graph.As <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); if (group != null) { int yMin = int.MaxValue; int yMax = int.MinValue; foreach (var pin in group.Inputs.Concat(group.Info.HiddenInputPins)) { var grpPin = pin.Cast <ICircuitGroupPin <TNode> >(); if (grpPin.Bounds.Location.Y < yMin) { yMin = grpPin.Bounds.Location.Y; } if (grpPin.Bounds.Location.Y > yMax) { yMax = grpPin.Bounds.Location.Y; } } foreach (var pin in group.Outputs.Concat(group.Info.HiddenOutputPins)) { var grpPin = pin.Cast <ICircuitGroupPin <TNode> >(); if (grpPin.Bounds.Location.Y < yMin) { yMin = grpPin.Bounds.Location.Y; } if (grpPin.Bounds.Location.Y > yMax) { yMax = grpPin.Bounds.Location.Y; } } // transform y range to client space if (yMin != int.MaxValue && yMax != int.MinValue) { var yRange = D2dUtil.TransformVector(m_d2dGraphics.Transform, new PointF(yMin, yMax)); yMin = (int)Math.Min(yRange.X, yRange.Y); yMax = (int)Math.Max(yRange.X, yRange.Y); int width = bounds.Width; int height = yMax - yMin + 1; bounds = Rectangle.Union(bounds, new Rectangle(bounds.Location.X, yMin, width, height)); } } return(bounds); }
/// <summary> /// Draws a vertical chart scale using GDI+</summary> /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="left">Scale left</param> /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param> /// <param name="minimumGraphStep">Minimum spacing, in graph (world) space, between ticks. /// For example, 1.0 would limit ticks to being drawn on whole integers.</param> /// <param name="linePen">Scale line pen</param> /// <param name="font">Scale font</param> /// <param name="textBrush">Text brush</param> /// <param name="g">Graphics GDI+ drawing surface</param> public static void DrawVerticalScale( Matrix transform, RectangleF graphRect, bool left, int majorSpacing, float minimumGraphStep, Pen linePen, Font font, Brush textBrush, System.Drawing.Graphics g) { double yScale = transform.Elements[3]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double tickEnd, minorTickStart, textStart; Matrix temp = g.Transform.Clone(); Matrix vertical = g.Transform; vertical.Translate(clientRect.Right, clientRect.Bottom); vertical.Rotate(90); vertical.Translate(-clientRect.Left, -clientRect.Top); g.Transform = vertical; if (left) { tickEnd = clientRect.Right - clientRect.X; minorTickStart = tickEnd - 6; textStart = tickEnd - 19; } else { tickEnd = clientRect.Left + 1; minorTickStart = tickEnd + 6; textStart = tickEnd + 8; } double min = Math.Min(graphRect.Top, graphRect.Bottom); double max = Math.Max(graphRect.Top, graphRect.Bottom); double tickAnchor = D2dUtil.CalculateTickAnchor(min, max); double majorGraphStep = D2dUtil.CalculateStep( min, max, Math.Abs(clientRect.Bottom - clientRect.Top), majorSpacing, minimumGraphStep); int numMinorTicks = D2dUtil.CalculateNumMinorTicks(majorGraphStep, minimumGraphStep, 5); double cMinorStep = (majorGraphStep / numMinorTicks) * yScale; if (majorGraphStep > 0) { double offset = tickAnchor - min; offset = offset - MathUtil.Remainder(offset, majorGraphStep) + majorGraphStep; for (double x = tickAnchor - offset; x <= max; x += majorGraphStep) { double cx = (x - min) * yScale + clientRect.Left; //g.DrawLine(linePen, (float)cx, (float)majorTickStart, (float)cx, (float)tickEnd); // draw minor ticks double cmx = cx; for (int i = 0; i < numMinorTicks; i++) { cmx += cMinorStep; g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd); } string xString = String.Format("{0:G8}", Math.Round(x, 6)); g.DrawString(xString, font, textBrush, (float)cx + 2, (float)textStart); } } g.Transform = temp; }
/// <summary> /// Draws a horizontal chart scale using GDI+</summary> /// <param name="transform">Graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="top">Whether the scale should be aligned along the top of the rectangle</param> /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param> /// <param name="minimumGraphStep">Minimum spacing, in graph (world) space, between ticks. /// For example, 1.0 would limit ticks to being drawn on whole integers.</param> /// <param name="linePen">Scale line pen</param> /// <param name="font">Scale font</param> /// <param name="textBrush">Text brush</param> /// <param name="g">Graphics GDI+ drawing surface</param> public static void DrawHorizontalScale( Matrix transform, RectangleF graphRect, bool top, int majorSpacing, float minimumGraphStep, Pen linePen, Font font, Brush textBrush, System.Drawing.Graphics g) { double xScale = transform.Elements[0]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double tickEnd, majorTickStart, minorTickStart, textStart; if (top) { tickEnd = clientRect.Top + 1; majorTickStart = tickEnd + 12; minorTickStart = tickEnd + 6; textStart = tickEnd + 8; } else { tickEnd = clientRect.Bottom - 1; majorTickStart = tickEnd - 12; minorTickStart = tickEnd - 6; textStart = tickEnd - 19; } double min = Math.Min(graphRect.Left, graphRect.Right); double max = Math.Max(graphRect.Left, graphRect.Right); double tickAnchor = D2dUtil.CalculateTickAnchor(min, max); double majorGraphStep = D2dUtil.CalculateStep( min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, minimumGraphStep); int numMinorTicks = D2dUtil.CalculateNumMinorTicks(majorGraphStep, minimumGraphStep, 5); double cMinorStep = (majorGraphStep / numMinorTicks) * xScale; if (majorGraphStep > 0) { double offset = tickAnchor - min; offset = offset - MathUtil.Remainder(offset, majorGraphStep); // draw leading minor ticks double cmx; cmx = ((tickAnchor - (offset + majorGraphStep)) - min) * xScale + clientRect.Left + cMinorStep; for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++) { // cull minor ticks outside of the view if (cmx > clientRect.Left) { g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd); } cmx += cMinorStep; } for (double x = tickAnchor - offset; x < max; x += majorGraphStep) { double cx = (x - min) * xScale + clientRect.Left; g.DrawLine(linePen, (float)cx, (float)majorTickStart, (float)cx, (float)tickEnd); string xString = String.Format("{0:G8}", Math.Round(x, 6)); g.DrawString(xString, font, textBrush, (float)cx + 1, (float)textStart); // draw minor ticks cmx = cx + cMinorStep; for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++) { g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd); cmx += cMinorStep; } } } }
/// <summary> /// Draws floating group pin</summary> /// <param name="grpPin">Group pin</param> /// <param name="inputSide">True for input pin, false for output pin</param> /// <param name="style">DiagramDrawingStyle</param> /// <param name="g">Graphics object</param> public void DrawFloatingGroupPin(ICircuitGroupPin <TElement> grpPin, bool inputSide, DiagramDrawingStyle style, D2dGraphics g) { SizeF pinNameSize = g.MeasureText(grpPin.Name, Theme.TextFormat); PointF p; if (inputSide) { p = GetGroupPinLocation(grpPin, true); RectangleF pinRect = new RectangleF(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth - Theme.PinSize, grpPin.Bounds.Location.Y + Theme.PinMargin + Theme.PinOffset, Theme.PinSize, Theme.PinSize); // draw output pin for input floating pins g.DrawRectangle(pinRect, m_subGraphPinPen); if (grpPin.Info.Pinned) { D2dUtil.DrawPin((int)(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth), (int)p.Y, true, true, m_pinBrush, g); } else { D2dUtil.DrawPin((int)(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth), (int)p.Y + Theme.PinSize / 2, false, true, m_pinBrush, g); } RectangleF bounds = new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight); RectangleF alignRect = new RectangleF( bounds.Left, bounds.Bottom + Theme.PinMargin, pinNameSize.Width, Theme.RowSpacing); var textAlignment = Theme.TextFormat.TextAlignment; Theme.TextFormat.TextAlignment = D2dTextAlignment.Leading; g.DrawText(grpPin.Name, Theme.TextFormat, alignRect.Location, Theme.TextBrush); Theme.TextFormat.TextAlignment = textAlignment; } else { // assume vertical scroll bar width = 16 p = GetGroupPinLocation(grpPin, false); RectangleF pinRect = new RectangleF(p.X + 1, grpPin.Bounds.Location.Y + Theme.PinMargin + Theme.PinOffset, Theme.PinSize, Theme.PinSize); // draw input pin for output floating pins g.DrawRectangle(pinRect, m_subGraphPinPen); // draw pin icon if (grpPin.Info.Pinned) { D2dUtil.DrawPin((int)p.X, (int)p.Y, true, false, m_pinBrush, g); } else { D2dUtil.DrawPin((int)p.X, (int)p.Y + Theme.PinSize / 2, false, false, m_pinBrush, g); } // draw label RectangleF bounds = new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight); RectangleF alignRectF = new RectangleF(bounds.Right - pinNameSize.Width, bounds.Bottom + Theme.PinMargin, pinNameSize.Width, Theme.RowSpacing); var textAlignment = Theme.TextFormat.TextAlignment; Theme.TextFormat.TextAlignment = D2dTextAlignment.Trailing; g.DrawText(grpPin.Name, Theme.TextFormat, alignRectF, Theme.TextBrush); Theme.TextFormat.TextAlignment = textAlignment; } // draw the fake pin node itself float savedStrokeWidth = Theme.StrokeWidth; Theme.StrokeWidth = 2.0f; if (style == DiagramDrawingStyle.Normal) { g.DrawRectangle(new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight), m_subGraphPinNodePen); } else { g.DrawRectangle(new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight), Theme.HotBrush); } Theme.StrokeWidth = savedStrokeWidth; if (!grpPin.Info.ExternalConnected) { RectangleF eyeRect = GetVisibilityCheckRect(grpPin, inputSide); g.DrawEyeIcon(eyeRect, grpPin.Info.Visible ? m_visiblePinBrush : m_hiddrenPinBrush, 1.0f); } // draw fake edge that connects group pin fake node DrawGroupPinNodeFakeEdge(grpPin, p, inputSide, style, g); }
/// <summary> /// Gets the bounding rectangle of all circuit items in client coordinates</summary> /// <returns>Bounding rectangle of all circuit items in client coordinates</returns> public Rectangle GetBounds() { var items = new List <object>(); if (m_graphContainer != null) { items.AddRange(m_graphContainer.Elements.AsIEnumerable <object>()); if (m_graphContainer.Annotations != null) { items.AddRange(m_graphContainer.Annotations.AsIEnumerable <object>()); } } else { if (m_graph != null) { items.AddRange(m_graph.Nodes.AsIEnumerable <IGraphNode>().AsIEnumerable <object>()); } //TODO: including Annotations } Rectangle bounds = GetBounds(items); if (DomNode.Is <Group>()) // the view is associated with a group editor { // include group pins y range var group = DomNode.Cast <Group>(); int yMin = int.MaxValue; int yMax = int.MinValue; foreach (var pin in group.InputGroupPins) { var grpPin = pin.Cast <GroupPin>(); if (grpPin.Bounds.Location.Y < yMin) { yMin = grpPin.Bounds.Location.Y; } if (grpPin.Bounds.Location.Y > yMax) { yMax = grpPin.Bounds.Location.Y; } } foreach (var pin in group.OutputGroupPins) { var grpPin = pin.Cast <GroupPin>(); if (grpPin.Bounds.Location.Y < yMin) { yMin = grpPin.Bounds.Location.Y; } if (grpPin.Bounds.Location.Y > yMax) { yMax = grpPin.Bounds.Location.Y; } } // transform y range to client space if (yMin != int.MaxValue && yMax != int.MinValue) { var transformAdapter = m_control.Cast <ITransformAdapter>(); var yRange = D2dUtil.TransformVector(transformAdapter.Transform, new PointF(yMin, yMax)); yMin = (int)Math.Min(yRange.X, yRange.Y); yMax = (int)Math.Max(yRange.X, yRange.Y); int width = bounds.Width; int height = yMax - yMin + 1; bounds = Rectangle.Union(bounds, new Rectangle(bounds.Location.X, yMin, width, height)); } } return(bounds); }