internal override void Draw(Graphics g, Pen pen) { if (pointedBezier) { pen = new Pen(Color.Yellow, 1); g.DrawBeziers(pen, points); pen.Dispose(); } else g.DrawBeziers(pen, points); }
public void DrawBeziers(Pen pen, PointD[] points) { if (pen == null) { throw new ArgumentNullException("pen"); } if (points == null) { throw new ArgumentNullException("points"); } lock (bitmapLock) { double maxX = 0.0d; double maxY = 0.0d; for (long i = 0L; i < points.LongLength; i++) { if (points[i].X > maxX) { maxX = points[i].X; } if (points[i].Y > maxY) { maxY = points[i].Y; } } TryExpand(0.0d, 0.0d, maxX, maxY, pen.Width); using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap)) { g.SmoothingMode = (Antialiasing) ? SmoothingMode.AntiAlias : SmoothingMode.None; g.DrawBeziers(pen, ToPointFArray(points)); } Texturize(); } }
/// <summary> /// Paints the polyline. /// </summary> /// <param name="g">Graphic context</param> /// <param name="p">Pen to use</param> protected override void PaintPolyline(Graphics g, Pen p) { if (From == null) return; if (To==null || ((this.Points.Count-4)%3!=0)) { base.PaintPolyline(g,p); return; } // Get end points PointF s = From.Shape.ConnectionPoint(From); PointF e = To.Shape.ConnectionPoint(To); // Iterate the list of polyline points and paint the lines between them SmoothingMode m = g.SmoothingMode; g.SmoothingMode = SmoothingMode.HighQuality; PointF[] points = new PointF[this.Points.Count]; this.Points.CopyTo(points,0); points[0]=s; points[points.Length-1]=e; g.DrawBeziers(p,points); g.SmoothingMode = m; // draw label if (label!=null && this.label.Length!=0) { PointF middle = (PointF)this.Points[this.Points.Count/2]; SolidBrush labelBrush = new SolidBrush(this.LabelColor); g.DrawString(this.label,this.labelFont,labelBrush,middle); } }
public override void Draw(Graphics g) { using(Pen p = new Pen(penColor,penWidth)) { g.DrawBeziers(p,pointlist); } }
/// <summary> /// Draw the disc flight. /// /// Currently simply a straight line from A to B. Could enhance this so /// that it draws a cardinal spline instead at some point. The UI to draw /// that is the more difficult area. /// </summary> /// <param name="display"></param> /// <param name="startLocation"></param> /// <param name="controlPoint"></param> /// <param name="endLocation"></param> /// <param name="converter"></param> private void DrawDiscFlight(Graphics display, PointF startLocation, PointF controlPoint, PointF endLocation, PitchScreenCoordConverter converter) { Point screenCoordsStart = converter.pitchToScreenCoords(startLocation); Point screenCoordsEnd = converter.pitchToScreenCoords(endLocation); Point screenControlPoint = converter.pitchToScreenCoords(controlPoint); QuadraticBezierCurve curve = new QuadraticBezierCurve(screenCoordsStart, screenControlPoint, screenCoordsEnd); display.DrawBeziers(mDiscFlightPen, curve.ToCubic()); display.DrawRectangle(mDiscFlightPen, screenControlPoint.X - 1, screenControlPoint.Y - 1, 2, 2); }
/// <summary> /// Repaints the form with cool background and stuff /// </summary> /// <param name="graph">The graphics object to paint to, the element will be drawn to 0, 0</param> public override void Paint(Graphics graph) { //Draws Rectangular Shapes if (Shape == ModelShape.Arrow) { _arrowPath = new GraphicsPath(); //Draws the basic shape Pen arrowPen; if (Highlight < 1) arrowPen = new Pen(Color.Cyan, 3F); else arrowPen = new Pen(Color.Black, 3F); //Draws the curved arrow Point[] lineArray = new Point[4]; lineArray[0] = new Point(_startPoint.X, _startPoint.Y); lineArray[1] = new Point(_startPoint.X - ((_startPoint.X - _stopPoint.X) / 3), _startPoint.Y); lineArray[2] = new Point(_stopPoint.X - ((_stopPoint.X - _startPoint.X) / 3), _stopPoint.Y); lineArray[3] = new Point(_stopPoint.X, _stopPoint.Y); graph.DrawBeziers(arrowPen, lineArray); _arrowPath.AddBeziers(lineArray); _arrowPath.Flatten(); //Draws the arrow head Point[] arrowArray = new Point[3]; arrowArray[0] = _stopPoint; arrowArray[1] = new Point(_stopPoint.X - (5 * Math.Sign(_stopPoint.X - _startPoint.X)), _stopPoint.Y - 2); arrowArray[2] = new Point(_stopPoint.X - (5 * Math.Sign(_stopPoint.X - _startPoint.X)), _stopPoint.Y + 2); graph.DrawPolygon(arrowPen, arrowArray); //Garbage collection arrowPen.Dispose(); } }
public void DrawEllipse(Graphics g, RectangleD worldRect, Rectangle canvasRect, BrushesStorage brushStorage, PenStorage penStorage, Font font) { const int markerSize = 3; var pen = penStorage.GetPen(Color, Selected ? 3f : 1f); var screenPoints = new List<PointD>(); foreach (var pt in points) { screenPoints.Add(Conversion.WorldToScreen(new PointD(pt.X, pt.Y), worldRect, canvasRect)); } foreach (var pt in screenPoints) { g.DrawRectangle(pen, (float)pt.X - markerSize, (float)pt.Y - markerSize, markerSize * 2F, markerSize * 2F); } if (screenPoints.Count == 2) { g.DrawLine(pen, screenPoints[0].ToPointF(), screenPoints[1].ToPointF()); } if (screenPoints.Count == 3) { // нарисовать собственно эллипс double newAngle; float newCx, newCy, newA, newB; correctEllipse = Geometry.GetEllipseParams(screenPoints[0].ToPointF(), screenPoints[1].ToPointF(), screenPoints[2].ToPointF(), out newAngle, out newA, out newB, out newCx, out newCy); if (correctEllipse) // можно построить эллипс - рисуем его { a = newA; b = newB; angle = newAngle; cx = newCx; cy = newCy; var ellipseBezierPoints = Geometry.GetEllipseBezierPoints(newAngle, newA, newB, newCx, newCy); g.DrawBeziers(pen, ellipseBezierPoints); if (BrushAlpha > 0) { var brush = brushStorage.GetBrush(Color.FromArgb(BrushAlpha, BrushColor)); g.FillClosedCurve(brush, ellipseBezierPoints); } // строить касательную if (BuildTangent) DrawTangent(screenPoints, canvasRect, g, penStorage, brushStorage, font); } else // построить эллипс по указанным координатам невозможно { g.DrawLine(pen, screenPoints[1].ToPointF(), screenPoints[2].ToPointF()); g.DrawLine(pen, screenPoints[0].ToPointF(), screenPoints[2].ToPointF()); } } // маркеры if (Selected) DrawComments(g, worldRect, canvasRect, penStorage, brushStorage); }
public List<double> Spokes { get; set; } // the angles of the spokes, in radians //public double R { get { return TheShape.R; } set { TheShape.R = value; } } //public Point Center { get { return TheShape.Center; } set { TheShape.Center = value; } } //public List<double> Spokes { get { return TheShape.Spokes; } set { TheShape.Spokes = value; } } // the angles of the spokes, in radians /// <summary> /// Draw an arc /// </summary> /// <param name="dc"></param> public override void Draw(Graphics dc) { if (R == 0 || Spokes == null || Spokes.Count < 2 || ThePen == null) return; dc.DrawLine(ThePen, Center.ToPointF(), spokep(0)); for (int i = 1; i < Spokes.Count; i++) { if (Math.Abs(Spokes[i] - Spokes[i - 1]) > 2 * Math.PI - .001) { // Display a circle double ControlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3; var x0 = (float)(Center.X - R); var x1 = (float)(Center.X - R * ControlPointRatio); var x2 = (float)(Center.X); var x3 = (float)(Center.X + R * ControlPointRatio); var x4 = (float)(Center.X + R); var y0 = (float)( Center.Y - R); var y1 = (float)(Center.Y - R * ControlPointRatio); var y2 = (float)(Center.Y); var y3 = (float)(Center.Y + R * ControlPointRatio); var y4 = (float)(Center.Y + R); PointF[] pts = new PointF[] { new PointF(x2, y0), new PointF(x3, y0), new PointF(x4, y1), new PointF(x4, y2), new PointF(x1, y4), new PointF(x0, y3), new PointF(x0, y2), new PointF(x0, y1), new PointF(x1, y0), new PointF(x2, y0) }; dc.DrawBeziers(ThePen, pts); /* context.BeginFigure(new Point(x2, y0), true, true); context.BezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2), true, true); context.BezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4), true, true); context.BezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2), true, true); context.BezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0), true, true); */ } else { bool largearc = Math.Abs(Spokes[i] - Spokes[i - 1]) > Math.PI; /* SweepDirection sd = SweepDirection.Counterclockwise; if (Spokes[i] < Spokes[i - 1]) sd = SweepDirection.Clockwise; dc.DrawArc(ThePen, (float)Center.X, (float(Center.Y), (float)R, (float)R, ) context.ArcTo(spokep(i), new Size(R, R), 0, largearc, sd, true, false);*/ //TODO:support } dc.DrawLine(ThePen, Center.ToPointF(), spokep(i)); } }
public void Draw(Graphics g, System.Drawing.Color color) { switch (kind) { case SymbolStrokes.Disc: using (Brush b = new SolidBrush(color)) g.FillEllipse(b, new RectangleF(points[0].X - radius, points[0].Y - radius, radius * 2, radius * 2)); break; case SymbolStrokes.Circle: try { using (Pen p = new Pen(color, thickness)) g.DrawEllipse(p, new RectangleF(points[0].X - radius, points[0].Y - radius, radius * 2, radius * 2)); } catch (ExternalException) { // Ignore this exeption. Not sure what causes it. } break; case SymbolStrokes.Polyline: using (Pen p = new Pen(color, thickness)) { p.LineJoin = corners; p.StartCap = ends; p.EndCap = ends; g.DrawLines(p, points); } break; case SymbolStrokes.Polygon: using (Pen p = new Pen(color, thickness)) { p.LineJoin = corners; g.DrawPolygon(p, points); } break; case SymbolStrokes.FilledPolygon: using (Brush b = new SolidBrush(color)) g.FillPolygon(b, points); break; case SymbolStrokes.PolyBezier: using (Pen p = new Pen(color, thickness)) { p.StartCap = ends; p.EndCap = ends; g.DrawBeziers(p, points); } break; case SymbolStrokes.FilledPolyBezier: using (Brush b = new SolidBrush(color)) using (GraphicsPath path = new GraphicsPath()) { path.AddBeziers(points); g.FillPath(b, path); } break; default: Debug.Fail("Bad SymbolStroke kind"); break; } }
private void DrawSegmentConnector(Graphics g, int segmentStartIndex, int segmentEndIndex, float weight) { PointF[] endB = GetBezierPoints(segmentEndIndex, Offset.Bottom); PointF[] startB = GetBezierPoints(segmentStartIndex, Offset.Top); GraphicsPath gp = new GraphicsPath(); gp.StartFigure(); gp.AddLine(startB[0], endB[0]); gp.AddBezier(endB[0], endB[1], endB[2], endB[3]); gp.AddLine(endB[3], startB[3]); gp.AddBezier(startB[3], startB[2], startB[1], startB[0]); gp.CloseFigure(); gp.StartFigure(); gp.AddRectangle(new RectangleF(startB[3], new SizeF(_overviewRectangle.Width, endB[3].Y - startB[3].Y))); gp.CloseFigure(); Color background = ColorUtilities.Interpolate(Settings.Default.OverviewBackgroundColor, Settings.Default.ConnectorsColor, 0.2f); Color color = ColorUtilities.Interpolate(background, Settings.Default.ConnectorsColor, weight); g.FillPath( new SolidBrush(color), gp); if (Model.Default.SelectedLine != -1 && Model.Default.SelectedLine >= segmentStartIndex && Model.Default.SelectedLine <= segmentEndIndex) { g.DrawBeziers(_selectedPen, GetBezierPoints(Model.Default.SelectedLine, Offset.Middle)); int y = TransformToScaled(Model.Default.SelectedLine); g.DrawLine(_selectedPen, _overviewRectangle.X, y, this.Width, y); } }
public void DrawSwitches(Graphics g, Pen switchPen, Pen switchOutline, Font f, StringFormat sf, string label, bool drawHandles, PointF start, PointF control1, PointF control2, PointF end, PointF labelLoc) { PointF[] bezier = new [] {start, control1, control2, end}; g.DrawBeziers(switchOutline, bezier); g.DrawBeziers(switchPen, bezier); using (GraphicsPath gp = new GraphicsPath()) { gp.AddString(label, f.FontFamily, (int) f.Style, f.GetHeight(g), labelLoc, sf); g.DrawPath(switchOutline, gp); g.FillPath(Brushes.Black, gp); } if (!drawHandles) return; DrawBezierHandle(g, start, control1); DrawBezierHandle(g, end, control2); }
// ************ drawing ************ internal void drawArrowSegments(Graphics g, System.Drawing.Pen p, System.Drawing.Brush b, System.Drawing.Pen pHeads, bool shadow, bool custom) { PointCollection pts = points; float xoff = shadow ? ShadowOffsetX : 0; float yoff = shadow ? ShadowOffsetY : 0; // offset if drawing shadow if (shadow) { pts = new PointCollection(points.Count); for (int i = 0; i < pts.Count; ++i) { pts[i] = new PointF( points[i].X + xoff, points[i].Y + yoff); } } if (custom && (customDraw == CustomDraw.Full || (customDraw == CustomDraw.ShadowOnly && shadow))) { // call the custom draw function flowChart.drawArrow(g, this, shadow, pts); } else { // draw the arrow's line if (style == ArrowStyle.Bezier) { g.DrawBeziers(p, pts.getArray()); } else { float mm = Constants.getMillimeter(flowChart.MeasureUnit); float pathThresh = mm / 3; if (!drawCrossings || flowChart.ArrowCrossings == MindFusion.FlowChartX.ArrowCrossings.Straight) { if (flowChart.RoundedArrows) { DrawRoundedPolyline(g, p, pts.getArray(), flowChart.RoundedArrowsRadius, true, pathThresh, null); } else { g.DrawLines(p, pts.getArray()); } } else { float crad = flowChart.CrossingRadius; ArrowCrossings crossings = getCrossings(); object startPoint = null; GraphicsPath gpath = pen.Width > pathThresh ? new GraphicsPath() : null; for (int i = 0; i < segmentCount; ++i) { PointCollection pc = crossings.segmentCrossings[i] as PointCollection; if (pc.Count > 0) { for (int j = 0; j < pc.Count - 1; ++j) { PointF pt1 = pc[j]; PointF pt2 = pc[j + 1]; pt1.X += xoff; pt1.Y += yoff; pt2.X += xoff; pt2.Y += yoff; if (startPoint != null) pt1 = (PointF)startPoint; startPoint = null; if (j % 2 == 0) { // The subsegment between two crossings or // between a crossing and a segment end-point if (flowChart.RoundedArrows) { // Check if this is the last subsegment // in this segment. If that is the case, // and this is not the last segment of the // arrow, draw rounded arrow if (j == pc.Count - 2 && i != segmentCount - 1) { // The third point in the poly is // the second point of the next segment // if it does not have crossings, or // the second point of the crossings array int ni = i + 2; PointF next = pts[ni]; while (Math.Abs(next.X - pt2.X) + Math.Abs(next.Y - pt2.Y) < 0.00001f) { ni++; if (ni == pts.Count) break; next = pts[ni]; } if (ni == pts.Count) { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } else { PointCollection nextPc = crossings.segmentCrossings[ni - 1] as PointCollection; if (nextPc.Count > 2) { next = nextPc[1]; next.X += xoff; next.Y += yoff; } PointF[] triPoints = new PointF[] { pt1, pt2, next }; startPoint = DrawRoundedPolyline(g, p, triPoints, flowChart.RoundedArrowsRadius, false, pathThresh, gpath); } } else { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } } else { if (gpath != null) gpath.AddLine(pt1, pt2); else g.DrawLine(p, pt1, pt2); } } else { if (flowChart.ArrowCrossings == MindFusion.FlowChartX.ArrowCrossings.Arcs) { float rad = Utilities.Distance(pt1, pt2) / 2; float aa = 0; float rr = 0; Geometry.Geometry2D.Convert.DekartToPolar( pt1, pt2, ref aa, ref rr); PointF[] centers = new PointF[] { PointF.Empty, PointF.Empty }; Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, crad, ref centers[0]); Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * rad - crad, ref centers[1]); PointF[] startPts = new PointF[] { pt1, PointF.Empty }; PointF[] endPts = new PointF[] { PointF.Empty, pt2 }; Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * crad, ref endPts[0]); Geometry.Geometry2D.Convert.PolarToDekart( pt1, aa, 2 * rad - 2 * crad, ref startPts[1]); float angle = aa; if (angle < 90) angle += 180; RectangleF rc = RectangleF.FromLTRB( centers[0].X - crad, centers[0].Y - crad, centers[0].X + crad, centers[0].Y + crad); float ded = 0 * 90; if (aa < 90) ded = 90 - ded; float start = 180 - angle - ded; float sweep = -90; if (aa < 90) { start += sweep; sweep = -sweep; } if (gpath != null) gpath.AddArc(rc, start, sweep); else g.DrawArc(p, rc, start, sweep); PointF p1 = PointF.Empty; PointF p2 = PointF.Empty; Geometry.Geometry2D.Convert.PolarToDekart( centers[0], angle - 90, crad, ref p1); Geometry.Geometry2D.Convert.PolarToDekart( centers[1], angle - 90, crad, ref p2); if (gpath != null) gpath.AddLine(p1, p2); else g.DrawLine(p, p1, p2); rc = RectangleF.FromLTRB( centers[1].X - crad, centers[1].Y - crad, centers[1].X + crad, centers[1].Y + crad); ded = 1 * 90; if (aa < 90) ded = 90 - ded; start = 180 - angle - ded; sweep = -90; if (aa < 90) { start += sweep; sweep = -sweep; } if (gpath != null) gpath.AddArc(rc, start, sweep); else g.DrawArc(p, rc, start, sweep); } else { // Start new figure in the graph, // thus preventing the graph // to automatically connect broken // lines and losing break-offs if (gpath != null) gpath.StartFigure(); } } } } else { if (gpath != null) gpath.AddLine(pts[i], pts[i+1]); else g.DrawLine(p, pts[i], pts[i+1]); } } if (gpath != null) { gpath.Flatten(new Matrix(), 0.05f); g.DrawPath(p, gpath); gpath.Dispose(); } } } // draw arrowheads, intermediate are skipped for asBezier arrows ahBase.draw(g, pHeads, b, xoff, yoff); if (style != ArrowStyle.Bezier && arrowInterm != ArrowHead.None) { for (int i = 0; i < points.Count - 1; i++) { PointF pt1 = pts[i]; PointF pt2 = new PointF((pt1.X + pts[i+1].X)/2, (pt1.Y + pts[i+1].Y)/2); headTemplates[(int)arrowInterm].recalcArrowHead(ahInterm, pt1, pt2); ahInterm.draw(g, pHeads, b, 0, 0); } } ahHead.draw(g, pHeads, b, xoff, yoff); // additional custom draw type if (custom && customDraw == CustomDraw.Additional) flowChart.drawArrow(g, this, false, pts); } }
/// <summary> /// Template to make a line draw. /// </summary> /// <param name="g">Graphics context.</param> /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param> /// <param name="range">The plot range to use.</param> /// <param name="layer">Graphics layer.</param> /// <param name="linePen">The pen to draw the line.</param> /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index. /// This function is null if no symbol gap is required.</param> /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param> /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param> /// <param name="linePlotStyle">The line plot style.</param> public override void PaintOneRange( Graphics g, PointF[] allLinePoints, IPlotRange range, IPlotArea layer, PenX linePen, Func<int, double> symbolGap, int skipFrequency, bool connectCircular, LinePlotStyle linePlotStyle) { PointF[] subLinePoints; if (range.LowerBound == 0 && range.UpperBound == allLinePoints.Length) { // under optimal conditions we can use allLinePoints directly subLinePoints = allLinePoints; } else { // otherwise, make a new array subLinePoints = new PointF[range.Length]; Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, range.Length); // Extract } int lastIdx = range.Length - 1; var layerSize = layer.Size; if (connectCircular) { if (symbolGap != null) { // convert points to bezier segments var bezierSegments = GdiExtensionMethods.ClosedCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length); var subBezierSegments = new PointF[0]; int subPointLengthM1, subBezierLength; for (int i = 0; i < (range.Length); i += skipFrequency) { subPointLengthM1 = Math.Min(skipFrequency, range.Length - i); int originalIndexAtStart = range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound); double gapAtStart = symbolGap(originalIndexAtStart); int originalIndexAtEnd = ((i + skipFrequency) < range.Length) ? range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound + skipFrequency) : range.OriginalFirstPoint; double gapAtEnd = symbolGap(originalIndexAtEnd); subBezierLength = 3 * subPointLengthM1 + 1; if (subBezierSegments.Length != subBezierLength) subBezierSegments = new PointF[subBezierLength]; Array.Copy(bezierSegments, i * 3, subBezierSegments, 0, subBezierLength); var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, gapAtStart / 2, gapAtEnd / 2); if (null != shortenedBezierSegments) { g.DrawBeziers(linePen, shortenedBezierSegments); } } } else { g.DrawClosedCurve(linePen, subLinePoints); } } else { if (symbolGap != null) { // convert points to bezier segments var bezierSegments = GdiExtensionMethods.OpenCardinalSplineToBezierSegments(subLinePoints, subLinePoints.Length); var subBezierSegments = new PointF[0]; int subPointLengthM1, subBezierLength; for (int i = 0; i < (range.Length - 1); i += skipFrequency) { subPointLengthM1 = Math.Min(skipFrequency, range.Length - 1 - i); int originalIndex = range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound); double gapAtStart = symbolGap(originalIndex); double gapAtEnd = subPointLengthM1 == skipFrequency ? symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(i + range.LowerBound + skipFrequency)) : 0; subBezierLength = 3 * subPointLengthM1 + 1; if (subBezierSegments.Length != subBezierLength) subBezierSegments = new PointF[subBezierLength]; Array.Copy(bezierSegments, i * 3, subBezierSegments, 0, subBezierLength); var shortenedBezierSegments = GdiExtensionMethods.ShortenBezierCurve(subBezierSegments, gapAtStart / 2, gapAtEnd / 2); if (null != shortenedBezierSegments) { g.DrawBeziers(linePen, shortenedBezierSegments); } } } else { g.DrawCurve(linePen, subLinePoints); } } }
/// <summary> /// Template to make a line draw. /// </summary> /// <param name="g">Graphics context.</param> /// <param name="allLinePoints">The plot data. Don't use the Range property of the pdata, since it is overriden by the next argument.</param> /// <param name="range">The plot range to use.</param> /// <param name="layer">Graphics layer.</param> /// <param name="linePen">The pen to draw the line.</param> /// <param name="symbolGap">The size of the symbol gap. Argument is the original index of the data. The return value is the absolute symbol gap at this index. /// This function is null if no symbol gap is required.</param> /// <param name="skipFrequency">Skip frequency. Normally 1, thus all gaps are taken into account. If 2, only every 2nd gap is taken into account, and so on.</param> /// <param name="connectCircular">If true, there is a line connecting the start and the end of the range.</param> /// <param name="linePlotStyle">The line plot style.</param> public override void PaintOneRange( Graphics g, PointF[] allLinePoints, IPlotRange range, IPlotArea layer, PenX linePen, Func<int, double> symbolGap, int skipFrequency, bool connectCircular, LinePlotStyle linePlotStyle) { // Bezier is only supported with point numbers n=4+3*k // so trim the range appropriately if (range.Length < 4) return; // then too less points are in this range if (connectCircular) { var circularLinePointsLengthM1 = 2 + TrimToValidBezierLength(range.Length); var circularLinePoints = new PointF[circularLinePointsLengthM1 + 1]; Array.Copy(allLinePoints, range.LowerBound, circularLinePoints, 0, range.Length); // Extract circularLinePoints[circularLinePointsLengthM1] = circularLinePoints[0]; // amend missing control points if (circularLinePointsLengthM1 - range.Length >= 1) circularLinePoints[circularLinePointsLengthM1 - 1] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Last Control point should be halfway between if (circularLinePointsLengthM1 - range.Length >= 2) circularLinePoints[circularLinePointsLengthM1 - 2] = GdiExtensionMethods.Interpolate(circularLinePoints[circularLinePointsLengthM1 - 3], circularLinePoints[circularLinePointsLengthM1], 0.5); // Middle Control point should be halfway between previous fixed point and last(=first) fixed point if (null != symbolGap) // circular with symbol gap { var realSkipFrequency = skipFrequency % 3 == 0 ? skipFrequency : skipFrequency * 3; // least common multiple of skipFrequency and 3 for (int i = 0; i < range.Length; i += realSkipFrequency) { var skipLinePointsLength = Math.Min(realSkipFrequency + 1, TrimToValidBezierLength(circularLinePoints.Length - i)); if (skipLinePointsLength >= 4) { var skipLinePoints = new PointF[skipLinePointsLength]; Array.Copy(circularLinePoints, i, skipLinePoints, 0, skipLinePointsLength); // Extract var gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i)); double gapAtEnd; if (connectCircular && realSkipFrequency >= (range.Length - 1 - i)) gapAtEnd = symbolGap(range.OriginalFirstPoint); else if (realSkipFrequency <= (range.Length - 1 - i)) gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + realSkipFrequency)); else gapAtEnd = 0; if (gapAtStart != 0 || gapAtEnd != 0) { skipLinePoints = GdiExtensionMethods.ShortenBezierCurve(skipLinePoints, gapAtStart / 2, gapAtEnd / 2); } if (null != skipLinePoints) { g.DrawBeziers(linePen, skipLinePoints); } } } } else // circular without symbol gap { g.DrawBeziers(linePen, circularLinePoints); } } else // not circular { if (null != symbolGap) // not circular with symbol gap { var realSkipFrequency = skipFrequency % 3 == 0 ? skipFrequency : skipFrequency * 3; // least common multiple of skipFrequency and 3 for (int i = 0; i < range.Length; i += realSkipFrequency) { var skipLinePointsLength = Math.Min(realSkipFrequency + 1, TrimToValidBezierLength(range.Length - i)); if (skipLinePointsLength >= 4) { var skipLinePoints = new PointF[skipLinePointsLength]; Array.Copy(allLinePoints, range.LowerBound + i, skipLinePoints, 0, skipLinePointsLength); // Extract var gapAtStart = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i)); var gapAtEnd = symbolGap(range.GetOriginalRowIndexFromPlotPointIndex(range.LowerBound + i + skipLinePointsLength - 1)); if (gapAtStart != 0 || gapAtEnd != 0) { skipLinePoints = GdiExtensionMethods.ShortenBezierCurve(skipLinePoints, gapAtStart / 2, gapAtEnd / 2); } if (null != skipLinePoints) { g.DrawBeziers(linePen, skipLinePoints); } } } } else // not circular without symbol gap { var trimmedLength = TrimToValidBezierLength(range.Length); var subLinePoints = new PointF[trimmedLength]; Array.Copy(allLinePoints, range.LowerBound, subLinePoints, 0, trimmedLength); // Extract g.DrawBeziers(linePen, subLinePoints); } } }