private void DrawArrow(D2dGraphics g, D2dBrush brush, PointF p, float dx, float dy) { const float cos = 0.866f; const float sin = 0.500f; PointF end1 = new PointF( (float)(p.X + (dx * cos + dy * -sin)), (float)(p.Y + (dx * sin + dy * cos))); PointF end2 = new PointF( (float)(p.X + (dx * cos + dy * sin)), (float)(p.Y + (dx * -sin + dy * cos))); g.DrawLine(p, end1, brush, m_theme.StrokeWidth); g.DrawLine(p, end2, brush, m_theme.StrokeWidth); }
/// <summary> /// Draws grid lines corresponding to a vertical scale</summary> /// <param name="g">The Direct2D graphics object</param> /// <param name="transform">Graph (world) to window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="majorSpacing">Scale's major spacing</param> /// <param name="lineBrush">Grid line brush</param> public static void DrawVerticalScaleGrid( this D2dGraphics g, Matrix transform, RectangleF graphRect, int majorSpacing, D2dBrush lineBrush) { double xScale = transform.Elements[0]; RectangleF clientRect = Transform(transform, graphRect); double min = Math.Min(graphRect.Left, graphRect.Right); double max = Math.Max(graphRect.Left, graphRect.Right); double tickAnchor = CalculateTickAnchor(min, max); double step = 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((float)cx, clientRect.Top, (float)cx, clientRect.Bottom, lineBrush); } } }
/// <summary> /// Draws the snap-to indicator to give the user a visual cue that snapping is occurring</summary> /// <param name="sender">The TimelineControl whose Paint event is being raised</param> /// <param name="e">The paint event args</param> /// <remarks>Draws a vertical line at the snapping location</remarks> private void owner_DrawingD2d(object sender, EventArgs e) { if (m_snapInfo.Count == 0) { return; } D2dGraphics g = m_owner.D2dGraphics; Rectangle clipRectangle = m_owner.VisibleClientRectangle; try { g.PushAxisAlignedClip(clipRectangle); Matrix worldToView = m_owner.Transform; foreach (SnapOffsetInfo info in m_snapInfo) { float viewXCoord = GdiUtil.Transform(worldToView, info.SnapToPoint); g.DrawLine(viewXCoord, clipRectangle.Top, viewXCoord, clipRectangle.Bottom, s_color, 3.0f, null); } } finally { g.PopAxisAlignedClip(); } }
private void DrawArrow(Vec2F p, Vec2F d, D2dBrush brush, D2dGraphics g) { d.Normalize(); // draw arrowhead const double cos = -0.866; const double sin = -0.500; PointF head = new PointF(p.X, p.Y); PointF end1 = new PointF( (float)(p.X + (d.X * cos + d.Y * -sin) * m_arrowSize), (float)(p.Y + (d.X * sin + d.Y * cos) * m_arrowSize)); PointF end2 = new PointF( (float)(p.X + (d.X * cos + d.Y * sin) * m_arrowSize), (float)(p.Y + (d.X * -sin + d.Y * cos) * m_arrowSize)); g.DrawLine(head, end1, brush, m_theme.StrokeWidth); g.DrawLine(head, end2, brush, m_theme.StrokeWidth); }
/// <summary> /// Draws a tree-control style expander, which looks like a square with /// a dash (expanded) or a cross (unexpanded).</summary> /// <param name="x">X coordinate of expander top right corner</param> /// <param name="y">Y coordinate of expander top right corner</param> /// <param name="pen">Pen, should be 1 pixel wide</param> /// <param name="size">Size of pin, in pixels</param> /// <param name="pinned">Whether or not expander should appear "expanded"</param> /// <param name="g">Graphics object</param> public static void DrawRightPin(int x, int y, int size, D2dBrush pen, bool pinned, D2dGraphics g) { //g.DrawRectangle(pen, x - size, y, size, size); int rectWidth = size / 4; int rectHeight = 2 * size / 3; int center = size / 2; if (pinned) { g.DrawLine(x + center - size, y + rectHeight, x + center - size, y + size, pen); //lower center-vertical line g.DrawLine(x - size, y + rectHeight, x, y + rectHeight, pen); // middle-horizontal line g.DrawRectangle(new RectangleF(x + rectWidth - size, y, 2 * rectWidth, rectHeight), pen); //g.DrawLine(pen, x + 3 * rectWidth - 1, y, x + 3 * rectWidth - 1, y + rectHeight); // a vertial line next to the right side of the rect g.DrawLine(x + 3 * rectWidth - 1 - size, y, x + 3 * rectWidth - 1 - size, y + rectHeight, pen); // a vertial line next to the right side of the rect } else { g.DrawLine(x, y + center, x - size + rectHeight, y + center, pen); //left center-horizontal line g.DrawLine(x - size + rectHeight, y, x - size + rectHeight, y + size, pen); // middle-vertical line g.DrawRectangle(new RectangleF(x - size, y + (size - rectWidth) / 2 - 1, rectHeight, 2 * rectWidth), pen); g.DrawLine(x - size + rectHeight, y + (size - rectWidth) / 2 + 2 * rectWidth - 2, x - size, y + (size - rectWidth) / 2 + 2 * rectWidth - 2, pen); // a horizontal line next to the bottom side of the rect } }
/// <summary> /// Draws the scale manipulator and calculates the bounding rectangles on the left and right /// handles</summary> /// <param name="g">The graphics object to draw with</param> /// <param name="leftHandle">The left handle's bounding rectangle for pick tests, in view /// coordinates</param> /// <param name="rightHandle">The right handle's bounding rectangle for pick tests, in view /// coordinates</param> protected virtual void DrawManipulator(D2dGraphics g, out RectangleF leftHandle, out RectangleF rightHandle) { const float penWidth = 3; Matrix worldToView = Owner.Transform; float viewMin = GdiUtil.Transform(worldToView, WorldMin); float viewMax = GdiUtil.Transform(worldToView, WorldMax); leftHandle = new RectangleF(viewMin - penWidth * 0.5f, 0.0f, penWidth, HandleHeight); rightHandle = new RectangleF(viewMax - penWidth * 0.5f, 0.0f, penWidth, HandleHeight); if (IsScaling && ScaleHelper.Mode == ScaleMode.TimePeriod) { // Draw lines in red at current ghost position. viewMin = GdiUtil.Transform(worldToView, ScaleHelper.WorldGhostMin); viewMax = GdiUtil.Transform(worldToView, ScaleHelper.WorldGhostMax); g.DrawLine(viewMin, 0.0f, viewMax, 0.0f, Color.Red, penWidth, null); g.DrawLine(viewMin, 0.0f, viewMin, HandleHeight, Color.Red, penWidth, null); g.DrawLine(viewMax, 0.0f, viewMax, HandleHeight, Color.Red, penWidth, null); } else { // Draw using original positions and the usual color. g.DrawLine(viewMin, 0.0f, viewMax, 0.0f, Color, penWidth, null); g.DrawLine(viewMin, 0.0f, viewMin, HandleHeight, Color, penWidth, null); g.DrawLine(viewMax, 0.0f, viewMax, HandleHeight, Color, penWidth, null); } }
/// <summary> /// Draws a partially defined graph edge</summary> /// <param name="fromNode">Source node, or null</param> /// <param name="fromRoute">Source route, or null</param> /// <param name="toNode">Destination node, or null</param> /// <param name="toRoute">Destination route, or null</param> /// <param name="label">Edge label</param> /// <param name="endPoint">Endpoint to substitute for source or destination (in client coords), if either is null</param> /// <param name="g">Graphics object</param> public override void Draw( TNode fromNode, NumberedRoute fromRoute, TNode toNode, NumberedRoute toRoute, string label, Point endPoint, D2dGraphics g) { var inverse = g.Transform; inverse.Invert(); PointF end = Matrix3x2F.TransformPoint(inverse, endPoint); TNode node = (fromNode != null) ? fromNode : toNode; CircleF boundary = GetBoundary(node); Vec2F proj = new Vec2F(); if (CircleF.Project(new Vec2F(end), boundary, ref proj)) { PointF start = new PointF(proj.X, proj.Y); g.DrawLine(start, end, m_theme.OutlineBrush); if (fromNode == null) { PointF temp = end; end = start; start = temp; } Vec2F endTangent = new Vec2F(end.X - start.X, end.Y - start.Y); Vec2F arrowPosition = new Vec2F(end); DrawArrow(arrowPosition, endTangent, m_theme.OutlineBrush, g); if (!string.IsNullOrEmpty(label)) { PointF textPoint = new PointF((end.X + start.X) * 0.5f, (end.Y + start.Y) * 0.5f); RectangleF textBox = new RectangleF(textPoint.X - 512, textPoint.Y, 1024, m_theme.TextFormat.FontHeight); //g.DrawString(label, m_theme.Font, m_theme.TextBrush, textBox, m_theme.CenterStringFormat); g.DrawText(label, m_theme.TextFormat, textBox, m_theme.TextBrush); } } }
/// <summary> /// Draws the scrubber manipulator and calculates the bounding rectangle on the handle</summary> /// <param name="g">The graphics object to draw with</param> /// <param name="handleRect">The handle's bounding rectangle for pick tests, in view /// coordinates</param> protected virtual void DrawManipulator(D2dGraphics g, out RectangleF handleRect) { Matrix worldToView = Owner.Transform; float viewX = Sce.Atf.GdiUtil.Transform(worldToView, Position); Rectangle clipRectangle = Owner.VisibleClientRectangle; // allow only the arrow portion to be selected handleRect = new RectangleF( viewX - 5, clipRectangle.Top, 10, 7); g.DrawLine(viewX, clipRectangle.Top, viewX, clipRectangle.Bottom, s_color, 1.0f, null); Color handle_color = m_isMoving ? Color.Tomato : s_color; float pos_x = viewX; float pos_y = clipRectangle.Top + 5; s_arrow[0] = new PointF(pos_x - 4, pos_y - 5); s_arrow[1] = new PointF(pos_x - 4, pos_y); s_arrow[2] = new PointF(pos_x - 5, pos_y + 1); s_arrow[3] = new PointF(pos_x - 5, pos_y + 2); s_arrow[4] = new PointF(pos_x, pos_y + 7); s_arrow[5] = new PointF(pos_x + 5, pos_y + 2); s_arrow[6] = new PointF(pos_x + 5, pos_y + 1); s_arrow[7] = new PointF(pos_x + 4, pos_y); s_arrow[8] = new PointF(pos_x + 4, pos_y - 5); //g.FillPolygon(s_arrow, handle_color); // Fill arrow // Draw arrow border with same gray Photoshop uses //g.DrawLines(s_arrow, Color.FromArgb(116, 114, 106), 3.0f); g.DrawLines(s_arrow, handle_color, 2.0f); string label = Position.ToString(CultureInfo.CurrentCulture); g.DrawText(label, Owner.Renderer.TextFormat, new PointF(pos_x + 6, clipRectangle.Top), SystemColors.WindowText); }
/// <summary> /// Draws horizontal grid using Direct2D</summary> /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="step">Grid step in canvas coordinates</param> /// <param name="color">Grid line color</param> /// <param name="g">Graphics Direct2D drawing surface</param> public static void DrawHorizontalGrid( Matrix transform, RectangleF graphRect, double step, Color color, D2dGraphics g) { double yScale = transform.Elements[3]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double screenStep = Math.Abs(yScale * step); int a = ComputeOpacity(screenStep); color = Color.FromArgb(a, color); double start = graphRect.Top - MathUtil.Remainder(graphRect.Top, step) + step; for (double y = start; y < graphRect.Bottom; y += step) { double cy = (y - graphRect.Top) * yScale + clientRect.Top; g.DrawLine(clientRect.Left, (float)cy, clientRect.Right, (float)cy, color); } }
/// <summary> /// Draws vertical grid lines using Direct2D</summary> /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="step">Grid step in canvas coordinates</param> /// <param name="color">Grid line color</param> /// <param name="g">Graphics Direct2D drawing surface</param> public static void DrawVerticalGrid( Matrix transform, RectangleF graphRect, double step, Color color, D2dGraphics g) { double xScale = transform.Elements[0]; RectangleF clientRect = GdiUtil.Transform(transform, graphRect); double screenStep = Math.Abs(xScale * step); int a = ComputeOpacity(screenStep); color = Color.FromArgb(a, color); double start = graphRect.Left - MathUtil.Remainder(graphRect.Left, step) + step; for (double x = start; x < graphRect.Right; x += step) { double cx = (x - graphRect.Left) * xScale + clientRect.Left; g.DrawLine((float)cx, clientRect.Top, (float)cx, clientRect.Bottom, color); } }
private void Draw(TEdge edge, D2dBrush brush, D2dGraphics g) { Vec2F startPoint = new Vec2F(); Vec2F endPoint = new Vec2F(); CircleF c = new CircleF(); bool moreThan180 = false; Vec2F endTangent; Vec2F textPoint; int route = edge.FromRoute.Index; if (GetEdgeGeometry(edge, route, ref startPoint, ref endPoint, ref c, ref moreThan180)) { g.DrawLine(new PointF(startPoint.X, startPoint.Y), new PointF(endPoint.X, endPoint.Y), brush, m_theme.StrokeWidth); endTangent = endPoint - startPoint; textPoint = (endPoint + startPoint) * 0.5f; } else { // prepare to draw arc RectangleF rect = new RectangleF(c.Center.X - c.Radius, c.Center.Y - c.Radius, 2 * c.Radius, 2 * c.Radius); double angle1 = Math.Atan2(startPoint.Y - c.Center.Y, startPoint.X - c.Center.X); double angle2 = Math.Atan2(endPoint.Y - c.Center.Y, endPoint.X - c.Center.X); const double twoPi = 2 * Math.PI; // swap so we always go clockwise if (angle1 > angle2) { double temp = angle1; angle1 = angle2; angle2 = temp; } double startAngle = angle1; double sweepAngle = angle2 - angle1; if (moreThan180) { if (sweepAngle < Math.PI) { sweepAngle = -(twoPi - sweepAngle); } } else { if (sweepAngle > Math.PI) { sweepAngle = -(twoPi - sweepAngle); } } const double RadiansToDegrees = 360 / twoPi; startAngle *= RadiansToDegrees; sweepAngle *= RadiansToDegrees; g.DrawArc((D2dEllipse)rect, brush, (float)startAngle, (float)sweepAngle, m_theme.StrokeWidth); endTangent = endPoint - c.Center; endTangent = endTangent.Perp; textPoint = (endPoint + startPoint) * 0.5f; CircleF.Project(textPoint, c, ref textPoint); if (moreThan180) { textPoint -= 2 * (textPoint - c.Center); } } DrawArrow(endPoint, endTangent, brush, g); string label = edge.Label; if (!string.IsNullOrEmpty(label)) { RectangleF textBox = new RectangleF(textPoint.X - 512, textPoint.Y, 1024, m_theme.TextFormat.FontHeight); g.DrawText(label, m_theme.TextFormat, textBox, m_theme.TextBrush); } }
/// <summary> /// Draws a horizontal chart scale</summary> /// <param name="g">The Direct2D graphics object</param> /// <param name="transform">Graph (world) to window's client (screen) transform</param> /// <param name="graphRect">Graph rectangle</param> /// <param name="top">Whether or not 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="lineBrush">Scale line pen</param> /// <param name="textFormat">Text format</param> /// <param name="textBrush">Text brush</param> public static void DrawHorizontalScale( this D2dGraphics g, Matrix transform, RectangleF graphRect, bool top, int majorSpacing, float minimumGraphStep, D2dBrush lineBrush, D2dTextFormat textFormat, D2dBrush textBrush) { double xScale = transform.Elements[0]; RectangleF clientRect = 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 = CalculateTickAnchor(min, max); double majorGraphStep = CalculateStep( min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, minimumGraphStep); int numMinorTicks = 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((float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd, lineBrush); } cmx += cMinorStep; } for (double x = tickAnchor - offset; x < max; x += majorGraphStep) { double cx = (x - min) * xScale + clientRect.Left; g.DrawLine((float)cx, (float)majorTickStart, (float)cx, (float)tickEnd, lineBrush); string xString = String.Format("{0:G8}", Math.Round(x, 6)); SizeF textSize = g.MeasureText(xString, textFormat); var textRect = new RectangleF(new PointF((float)cx + 1, (float)textStart), textSize); g.DrawText(xString, textFormat, textRect, textBrush); // draw minor ticks cmx = cx + cMinorStep; for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++) { g.DrawLine((float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd, lineBrush); cmx += cMinorStep; } } } }
private void Draw(TNode state, D2dGraphics g, bool outline) { RectangleF bounds = state.Bounds; if (state.Type != StateType.Normal) { DrawPseudostate(state, g, outline); } else { float scaleX = g.Transform.M11; // assume no rotation. float radInPixel = scaleX * CornerRadius; IComplexState <TNode, TEdge> complexState = state as IComplexState <TNode, TEdge>; StateIndicators indicators = state.Indicators; if ((indicators & StateIndicators.Active) != 0) { if (radInPixel > MinRadiusInPixel) { D2dEllipse ellipse = new D2dEllipse(); ellipse.RadiusX = CornerRadius; ellipse.RadiusY = CornerRadius; ellipse.Center = bounds.Location; g.FillEllipse(ellipse, Color.SpringGreen); } } if (radInPixel > MinRadiusInPixel) { m_stateRect.Rect = bounds; D2dLinearGradientBrush gradbrush = m_theme.FillGradientBrush; gradbrush.StartPoint = bounds.Location; gradbrush.EndPoint = new PointF(bounds.Right, bounds.Bottom); g.FillRoundedRectangle(m_stateRect, gradbrush); if (outline) { g.DrawRoundedRectangle(m_stateRect, m_theme.OutlineBrush); } } else { g.FillRectangle(bounds, m_theme.FillBrush); if (outline) { g.DrawRectangle(bounds, m_theme.OutlineBrush); } } g.DrawLine(bounds.Left, bounds.Top + m_fontHeight + Margin, bounds.Right, bounds.Top + m_fontHeight + Margin, m_theme.OutlineBrush); if ((scaleX * m_fontHeight) > MinFontHeightInPixel) { g.DrawText(complexState.TitleText, m_theme.TextFormat, new PointF(bounds.X + CornerRadius, bounds.Y + Margin), m_theme.TextBrush); } //RectangleF textBounds = new RectangleF( // (float)(bounds.Left + 4), // (float)(bounds.Top + m_fontHeight + 2), // (float)(bounds.Width - 5), // (float)(bounds.Height - m_fontHeight - 4)); //g.DrawString(complexState.Text, m_theme.Font, m_theme.TextBrush, textBounds, s_stateTextFormat); //IList<int> partitionWidths = complexState.PartitionSizes; //if (partitionWidths.Count > 0) //{ // // draw AND-state dividers // int lastDivider = bounds.Left; // foreach (int width in partitionWidths) // { // g.DrawLine( // m_dividerPen, // lastDivider, bounds.Y + m_fontHeight + Margin, // lastDivider, bounds.Y + bounds.Height); // lastDivider += width; // } //} } }