/// <summary> /// Finds the nearest point on the specified polyline. /// </summary> /// <param name="point">The point.</param> /// <param name="points">The points.</param> /// <returns>The nearest point.</returns> public static ScreenPoint FindNearestPointOnPolyline(ScreenPoint point, IList<ScreenPoint> points) { double minimumDistance = double.MaxValue; var nearestPoint = default(ScreenPoint); for (int i = 0; i + 1 < points.Count; i++) { var p1 = points[i]; var p2 = points[i + 1]; if (ScreenPoint.IsUndefined(p1) || ScreenPoint.IsUndefined(p2)) { continue; } // Find the nearest point on the line segment. var nearestPointOnSegment = FindPointOnLine(point, p1, p2); if (ScreenPoint.IsUndefined(nearestPointOnSegment)) { continue; } double l2 = (point - nearestPointOnSegment).LengthSquared; if (l2 < minimumDistance) { nearestPoint = nearestPointOnSegment; minimumDistance = l2; } } return nearestPoint; }
/// <summary> /// Gets the polygon outline of the specified rotated and aligned box. /// </summary> /// <param name="size">The size of the box.</param> /// <param name="origin">The origin of the box.</param> /// <param name="angle">The rotation angle of the box.</param> /// <param name="horizontalAlignment">The horizontal alignment of the box.</param> /// <param name="verticalAlignment">The vertical alignment of the box.</param> /// <returns>A sequence of points defining the polygon outline of the box.</returns> public static IEnumerable<ScreenPoint> GetPolygon(this OxySize size, ScreenPoint origin, double angle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { var u = horizontalAlignment == HorizontalAlignment.Left ? 0 : horizontalAlignment == HorizontalAlignment.Center ? 0.5 : 1; var v = verticalAlignment == VerticalAlignment.Top ? 0 : verticalAlignment == VerticalAlignment.Middle ? 0.5 : 1; var offset = new ScreenVector(u * size.Width, v * size.Height); // the corners of the rectangle var p0 = new ScreenVector(0, 0) - offset; var p1 = new ScreenVector(size.Width, 0) - offset; var p2 = new ScreenVector(size.Width, size.Height) - offset; var p3 = new ScreenVector(0, size.Height) - offset; if (angle != 0) { var theta = angle * Math.PI / 180.0; var costh = Math.Cos(theta); var sinth = Math.Sin(theta); Func<ScreenVector, ScreenVector> rotate = p => new ScreenVector((costh * p.X) - (sinth * p.Y), (sinth * p.X) + (costh * p.Y)); p0 = rotate(p0); p1 = rotate(p1); p2 = rotate(p2); p3 = rotate(p3); } yield return origin + p0; yield return origin + p1; yield return origin + p2; yield return origin + p3; }
/// <summary> /// Tracker is calling to determine the nearest point. /// </summary> /// <param name="point">The point clicked</param> /// <param name="interpolate">A value indicating whether interpolation should be used.</param> /// <returns>The return hit result</returns> public override TrackerHitResult GetNearestPoint(OxyPlot.ScreenPoint point, bool interpolate) { TrackerHitResult hitResult = base.GetNearestPoint(point, interpolate); if (hitResult != null && OnHoverOverPoint != null) { HoverPointArgs e = new HoverPointArgs(); if (Title == null) { e.SeriesName = ToolTip; } else { e.SeriesName = Title; } e.X = hitResult.DataPoint.X; e.Y = hitResult.DataPoint.Y; OnHoverOverPoint.Invoke(this, e); if (e.HoverText != null) { hitResult.Series.TrackerFormatString = e.HoverText + "\n{1}: {2}\n{3}: {4}"; } } return(hitResult); }
/// <summary> /// Draws or measures text containing sub- and superscript. /// </summary> /// <param name="rc">The render context.</param> /// <param name="pt">The point.</param> /// <param name="text">The text.</param> /// <param name="textColor">Color of the text.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">The font size.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="angle">The angle.</param> /// <param name="ha">The horizontal alignment.</param> /// <param name="va">The vertical alignment.</param> /// <param name="maxsize">The maximum size of the text.</param> /// <param name="measure">Measure the size of the text if set to <c>true</c>.</param> /// <returns>The size of the text.</returns> /// <example>Subscript: H_{2}O /// Superscript: E=mc^{2} /// Both: A^{2}_{i,j}</example> public static OxySize DrawMathText( this IRenderContext rc, ScreenPoint pt, string text, OxyColor textColor, string fontFamily, double fontSize, double fontWeight, double angle, HorizontalAlignment ha, VerticalAlignment va, OxySize? maxsize, bool measure) { if (string.IsNullOrEmpty(text)) { return OxySize.Empty; } if (text.Contains("^{") || text.Contains("_{")) { double x = pt.X; double y = pt.Y; // Measure var size = InternalDrawMathText(rc, x, y, text, textColor, fontFamily, fontSize, fontWeight, true, angle); switch (ha) { case HorizontalAlignment.Right: x -= size.Width; break; case HorizontalAlignment.Center: x -= size.Width * 0.5; break; } switch (va) { case VerticalAlignment.Bottom: y -= size.Height; break; case VerticalAlignment.Middle: y -= size.Height * 0.5; break; } InternalDrawMathText(rc, x, y, text, textColor, fontFamily, fontSize, fontWeight, false, angle); return measure ? size : OxySize.Empty; } rc.DrawText(pt, text, textColor, fontFamily, fontSize, fontWeight, angle, ha, va, maxsize); if (measure) { return rc.MeasureText(text, fontFamily, fontSize, fontWeight); } return OxySize.Empty; }
public TrackerHitResult(Series.Series series, DataPoint dp, ScreenPoint sp, object item = null, double index = -1, string text = null) { this.DataPoint = dp; this.Position = sp; this.Item = item; this.Index = index; this.Series = series; this.Text = text; }
/// <summary> /// Initializes a new instance of the <see cref="TrackerHitResult"/> class. /// </summary> /// <param name="series">The series.</param> /// <param name="dp">The data point.</param> /// <param name="sp">The screen point.</param> /// <param name="item">The item.</param> /// <param name="index">The index.</param> /// <param name="text">The text.</param> public TrackerHitResult(OxyPlot.Series.Series series, IDataPoint dp, ScreenPoint sp, object item = null, double index = -1, string text = null) { this.DataPoint = dp; this.Position = sp; this.Item = item; this.Index = index; this.Series = series; this.Text = text; var ds = series as DataPointSeries; if (ds != null) { this.XAxis = ds.XAxis; this.YAxis = ds.YAxis; } }
public static PlotModel DrawTextAlignment() { var model = new PlotModel(); model.Annotations.Add(new DelegateAnnotation(rc => { const double FontSize = 20d; for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++) { for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++) { var origin = new ScreenPoint((((int)ha + 1) * 200) + 20, (((int)va + 1) * FontSize * 3) + 20); rc.FillCircle(origin, 3, OxyColors.Blue); rc.DrawText(origin, ha + "-" + va, OxyColors.Black, fontSize: FontSize, horizontalAlignment: ha, verticalAlignment: va); } } })); return model; }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="model">The owner plot model.</param> public override void Render(IRenderContext rc, PlotModel model) { if (Points.Count == 0) { return; } if (Points.Count % 2 != 0) { throw new InvalidOperationException("The number of points should be even."); } if (this.XAxis == null || this.YAxis == null) { throw new InvalidOperationException("Axis has not been defined."); } var clippingRect = GetClippingRect(); var screenPoints = Points.Select(this.Transform).ToList(); var verticalLines = new List<ScreenPoint>(); for (int i = 0; i < screenPoints.Count; i += 2) { if (screenPoints[i].DistanceToSquared(screenPoints[i + 1]) < this.StrokeThickness) { screenPoints[i] = new ScreenPoint(screenPoints[i].X - (this.StrokeThickness * 0.5), screenPoints[i].Y); screenPoints[i + 1] = new ScreenPoint(screenPoints[i].X + (this.StrokeThickness * 0.5), screenPoints[i].Y); } if (this.ShowVerticals && i > 0 && Math.Abs(screenPoints[i - 1].X - screenPoints[i].X) < this.Epsilon) { verticalLines.Add(screenPoints[i - 1]); verticalLines.Add(screenPoints[i]); } } rc.DrawClippedLineSegments(clippingRect, screenPoints, this.ActualColor, this.StrokeThickness, this.LineStyle.GetDashArray(), this.LineJoin, false); rc.DrawClippedLineSegments(clippingRect, verticalLines, this.ActualColor, this.StrokeThickness / 3, LineStyle.Dash.GetDashArray(), this.LineJoin, false); rc.DrawMarkers(screenPoints, clippingRect, this.MarkerType, null, this.MarkerSize, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness); }
public static PlotModel DrawMathTextAlignment() { var text = "A_{2}^{3}B"; var model = new PlotModel(); model.Annotations.Add(new DelegateAnnotation(rc => { const string FontFamily = "Arial"; const double FontSize = 20d; const double FontWeight = FontWeights.Normal; for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++) { for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++) { var origin = new ScreenPoint((((int)ha + 1) * 200) + 20, (((int)va + 1) * FontSize * 3) + 20); rc.FillCircle(origin, 3, OxyColors.Blue); rc.DrawMathText(origin, text, OxyColors.Black, FontFamily, FontSize, FontWeight, 0, ha, va); } } })); return model; }
/// <summary> /// Finds the point on line. /// </summary> /// <param name="p">The point.</param> /// <param name="p1">The first point on the line.</param> /// <param name="p2">The second point on the line.</param> /// <returns>The nearest point on the line.</returns> /// <remarks>See <a href="http://paulbourke.net/geometry/pointlineplane/">Bourke</a>.</remarks> public static ScreenPoint FindPointOnLine(ScreenPoint p, ScreenPoint p1, ScreenPoint p2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; double u = FindPositionOnLine(p, p1, p2); if (double.IsNaN(u)) { u = 0; } if (u < 0) { u = 0; } if (u > 1) { u = 1; } return new ScreenPoint(p1.x + (u * dx), p1.y + (u * dy)); }
/// <summary> /// Gets the distance to the specified point. /// </summary> /// <param name="point"> /// The point. /// </param> /// <returns> /// The distance. /// </returns> public double DistanceTo(ScreenPoint point) { double dx = point.x - this.x; double dy = point.y - this.y; return Math.Sqrt((dx * dx) + (dy * dy)); }
/// <summary> /// Gets the point on the series that is nearest the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param> /// <returns> /// A TrackerHitResult for the current hit. /// </returns> public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) { if (interpolate) { // Cannot interpolate if there is no line if (this.ActualColor == null || this.StrokeThickness.IsZero()) { return null; } if (!this.CanTrackerInterpolatePoints) { return null; } } if (interpolate && this.Smooth && this.SmoothedPoints != null) { return this.GetNearestInterpolatedPointInternal(this.SmoothedPoints, point); } return base.GetNearestPoint(point, interpolate); }
/// <summary> /// Renders the point labels. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRect">The clipping rect.</param> protected void RenderPointLabels(IRenderContext rc, OxyRect clippingRect) { int index = -1; foreach (var point in this.Points) { index++; if (!this.IsValidPoint(point, this.XAxis, this.YAxis)) { continue; } var pt = this.XAxis.Transform(point.X, point.Y, this.YAxis); pt.Y -= this.LabelMargin; if (!clippingRect.Contains(pt)) { continue; } var s = StringHelper.Format( this.ActualCulture, this.LabelFormatString, this.GetItem(index), point.X, point.Y); #if SUPPORTLABELPLACEMENT switch (this.LabelPlacement) { case LabelPlacement.Inside: pt = new ScreenPoint(rect.Right - this.LabelMargin, (rect.Top + rect.Bottom) / 2); ha = HorizontalTextAlign.Right; break; case LabelPlacement.Middle: pt = new ScreenPoint((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2); ha = HorizontalTextAlign.Center; break; case LabelPlacement.Base: pt = new ScreenPoint(rect.Left + this.LabelMargin, (rect.Top + rect.Bottom) / 2); ha = HorizontalTextAlign.Left; break; default: // Outside pt = new ScreenPoint(rect.Right + this.LabelMargin, (rect.Top + rect.Bottom) / 2); ha = HorizontalTextAlign.Left; break; } #endif rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalTextAlign.Center, VerticalTextAlign.Bottom); } }
public DataSeries GetSeriesFromPoint(ScreenPoint pt, double limit) { return Model.GetSeriesFromPoint(pt, limit); }
/// <summary> /// Gets the point on the series that is nearest the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="interpolate"> /// Interpolate the series if this flag is set to <c>true</c>. /// </param> /// <returns>A TrackerHitResult for the current hit.</returns> public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) { foreach (var v in this.Values) { if (double.IsNaN(v) || v < this.XAxis.ActualMinimum || v > this.XAxis.ActualMaximum) { continue; } double x = this.XAxis.Transform(v); var r = new OxyRect( x - this.symbolSize.Width / 2, this.symbolPosition - this.symbolSize.Height, this.symbolSize.Width, this.symbolSize.Height); if (r.Contains(point)) { var text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, this.Title, v); return new TrackerHitResult( this, new DataPoint(v, double.NaN), new ScreenPoint(x, this.symbolPosition - this.symbolSize.Height)) { Text = text }; } } return null; }
/// <summary> /// Gets the first axes that covers the area of the specified point. /// </summary> /// <param name="pt">The point.</param> /// <param name="xaxis">The x-axis.</param> /// <param name="yaxis">The y-axis.</param> public void GetAxesFromPoint(ScreenPoint pt, out Axis xaxis, out Axis yaxis) { xaxis = yaxis = null; // Get the axis position of the given point. Using null if the point is inside the plot area. AxisPosition? position = null; double plotAreaValue = 0; if (pt.X < this.PlotArea.Left) { position = AxisPosition.Left; plotAreaValue = this.PlotArea.Left; } if (pt.X > this.PlotArea.Right) { position = AxisPosition.Right; plotAreaValue = this.PlotArea.Right; } if (pt.Y < this.PlotArea.Top) { position = AxisPosition.Top; plotAreaValue = this.PlotArea.Top; } if (pt.Y > this.PlotArea.Bottom) { position = AxisPosition.Bottom; plotAreaValue = this.PlotArea.Bottom; } foreach (var axis in this.Axes) { if (axis is IColorAxis) { continue; } if (axis is MagnitudeAxis) { xaxis = axis; continue; } if (axis is AngleAxis) { yaxis = axis; continue; } double x = double.NaN; if (axis.IsHorizontal()) { x = axis.InverseTransform(pt.X); } if (axis.IsVertical()) { x = axis.InverseTransform(pt.Y); } if (x >= axis.ActualMinimum && x <= axis.ActualMaximum) { if (position == null) { if (axis.IsHorizontal()) { if (xaxis == null) { xaxis = axis; } } else if (axis.IsVertical()) { if (yaxis == null) { yaxis = axis; } } } else if (position == axis.Position) { // Choose right tier double positionTierMinShift = axis.PositionTierMinShift; double positionTierMaxShift = axis.PositionTierMaxShift; double posValue = axis.IsHorizontal() ? pt.Y : pt.X; bool isLeftOrTop = position == AxisPosition.Top || position == AxisPosition.Left; if ((posValue >= plotAreaValue + positionTierMinShift && posValue < plotAreaValue + positionTierMaxShift && !isLeftOrTop) || (posValue <= plotAreaValue - positionTierMinShift && posValue > plotAreaValue - positionTierMaxShift && isLeftOrTop)) { if (axis.IsHorizontal()) { if (xaxis == null) { xaxis = axis; } } else if (axis.IsVertical()) { if (yaxis == null) { yaxis = axis; } } } } } } }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The p.</param> /// <param name="text">The text.</param> /// <param name="c">The c.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotate.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">Size of the max.</param> public override void DrawText( ScreenPoint p, string text, OxyColor c, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize?maxSize) { if (string.IsNullOrEmpty(text)) { return; } var lines = Regex.Split(text, "\r\n"); var textSize = this.MeasureText(text, fontFamily, fontSize, fontWeight); var lineHeight = textSize.Height / lines.Length; var lineOffset = new ScreenVector(-Math.Sin(rotate / 180.0 * Math.PI) * lineHeight, +Math.Cos(rotate / 180.0 * Math.PI) * lineHeight); if (this.UseVerticalTextAlignmentWorkaround) { // offset the position, and set the valign to neutral value of `Bottom` double offsetRatio = valign == VerticalAlignment.Bottom ? (1.0 - lines.Length) : valign == VerticalAlignment.Top ? 1.0 : (1.0 - (lines.Length / 2.0)); valign = VerticalAlignment.Bottom; p += lineOffset * offsetRatio; foreach (var line in lines) { var size = this.MeasureText(line, fontFamily, fontSize, fontWeight); this.w.WriteText(p, line, c, fontFamily, fontSize, fontWeight, rotate, halign, valign); p += lineOffset; } } else { if (valign == VerticalAlignment.Bottom) { for (var i = lines.Length - 1; i >= 0; i--) { var line = lines[i]; var size = this.MeasureText(line, fontFamily, fontSize, fontWeight); this.w.WriteText(p, line, c, fontFamily, fontSize, fontWeight, rotate, halign, valign); p -= lineOffset; } } else { foreach (var line in lines) { var size = this.MeasureText(line, fontFamily, fontSize, fontWeight); this.w.WriteText(p, line, c, fontFamily, fontSize, fontWeight, rotate, halign, valign); p += lineOffset; } } } }
/// <summary> /// Gets the point on the curve that is nearest the specified point. /// </summary> /// <param name = "point">The point.</param> /// <param name = "dpn">The nearest point (data coordinates).</param> /// <param name = "spn">The nearest point (screen coordinates).</param> /// <returns></returns> public override bool GetNearestInterpolatedPoint(ScreenPoint point, out DataPoint dpn, out ScreenPoint spn) { spn = default(ScreenPoint); dpn = default(DataPoint); // http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ double minimumDistance = double.MaxValue; for (int i = 0; i + 1 < points.Count; i++) { var p1 = points[i]; var p2 = points[i + 1]; var sp1 = AxisBase.Transform(p1, XAxis, YAxis); var sp2 = AxisBase.Transform(p2, XAxis, YAxis); double sp21X = sp2.x - sp1.x; double sp21Y = sp2.y - sp1.y; double u1 = (point.x - sp1.x) * sp21X + (point.y - sp1.y) * sp21Y; double u2 = sp21X * sp21X + sp21Y * sp21Y; double ds = sp21X * sp21X + sp21Y * sp21Y; if (ds < 4) { // if the points are very close, we can get numerical problems, just use the first point... u1 = 0; u2 = 1; } if (u2 == 0) { continue; // P1 && P2 coincident } double u = u1 / u2; if (u < 0 || u > 1) { continue; // outside line } double sx = sp1.x + u * sp21X; double sy = sp1.y + u * sp21Y; double dx = point.x - sx; double dy = point.y - sy; double distance = dx * dx + dy * dy; if (distance < minimumDistance) { double px = p1.x + u * (p2.x - p1.x); double py = p1.y + u * (p2.y - p1.y); dpn = new DataPoint(px, py); spn = new ScreenPoint(sx, sy); minimumDistance = distance; } } return(minimumDistance < double.MaxValue); }
/// <summary> /// Renders the Series on the specified rendering context. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <param name="model"> /// The model. /// </param> public override void Render(IRenderContext rc, PlotModel model) { this.ActualMinimumBarRectangles = new List <OxyRect>(); this.ActualMaximumBarRectangles = new List <OxyRect>(); if (this.ValidItems.Count == 0) { return; } var clippingRect = this.GetClippingRect(); var categoryAxis = this.GetCategoryAxis(); var actualBarWidth = this.GetActualBarWidth(); for (var i = 0; i < this.ValidItems.Count; i++) { var item = this.ValidItems[i]; var categoryIndex = item.GetCategoryIndex(i); var baseValue = double.IsNaN(item.BaseValue) ? this.BaseValue : item.BaseValue; var p0 = this.Transform(item.Minimum, categoryIndex - 0.5 + categoryAxis.BarOffset[categoryIndex]); var p1 = this.Transform( item.Maximum, categoryIndex - 0.5 + categoryAxis.BarOffset[categoryIndex] + actualBarWidth); var p2 = this.Transform(baseValue, categoryIndex - 0.5 + categoryAxis.BarOffset[categoryIndex]); p2.X = (int)p2.X; var minimumRectangle = OxyRect.Create(p0.X, p0.Y, p2.X, p1.Y); var maximumRectangle = OxyRect.Create(p2.X, p0.Y, p1.X, p1.Y); this.ActualMinimumBarRectangles.Add(minimumRectangle); this.ActualMaximumBarRectangles.Add(maximumRectangle); rc.DrawClippedRectangleAsPolygon( minimumRectangle, clippingRect, item.MinimumColor ?? this.ActualMinimumFillColor, this.StrokeColor, this.StrokeThickness); rc.DrawClippedRectangleAsPolygon( maximumRectangle, clippingRect, item.MaximumColor ?? this.ActualMaximumFillColor, this.StrokeColor, this.StrokeThickness); if (this.MinimumLabelFormatString != null) { var s = StringHelper.Format( this.ActualCulture, this.MinimumLabelFormatString, this.GetItem(this.ValidItemsIndexInversion[i]), item.Minimum); var pt = new ScreenPoint( minimumRectangle.Left - this.LabelMargin, (minimumRectangle.Top + minimumRectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalTextAlign.Right, VerticalTextAlign.Middle); } if (this.MaximumLabelFormatString != null) { var s = StringHelper.Format( this.ActualCulture, this.MaximumLabelFormatString, this.GetItem(this.ValidItemsIndexInversion[i]), item.Maximum); var pt = new ScreenPoint( maximumRectangle.Right + this.LabelMargin, (maximumRectangle.Top + maximumRectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalTextAlign.Left, VerticalTextAlign.Middle); } } }
/// <summary> /// Fills a circle at the specified position. /// </summary> /// <param name="rc">The render context.</param> /// <param name="center">The center.</param> /// <param name="r">The radius.</param> /// <param name="fill">The fill color.</param> /// <param name="edgeRenderingMode">The edge rendering mode.</param> public static void FillCircle(this IRenderContext rc, ScreenPoint center, double r, OxyColor fill, EdgeRenderingMode edgeRenderingMode) { DrawCircle(rc, center.X, center.Y, r, fill, OxyColors.Undefined, 0d, edgeRenderingMode); }
/// <summary> /// Adds a marker geometry to the specified collections. /// </summary> /// <param name="p">The position of the marker.</param> /// <param name="type">The marker type.</param> /// <param name="outline">The custom outline, if <paramref name="type" /> is <see cref="MarkerType.Custom" />.</param> /// <param name="size">The size of the marker.</param> /// <param name="ellipses">The output ellipse collection.</param> /// <param name="rects">The output rectangle collection.</param> /// <param name="polygons">The output polygon collection.</param> /// <param name="lines">The output line collection.</param> private static void AddMarkerGeometry( ScreenPoint p, MarkerType type, IEnumerable <ScreenPoint> outline, double size, IList <OxyRect> ellipses, IList <OxyRect> rects, IList <IList <ScreenPoint> > polygons, IList <ScreenPoint> lines) { if (type == MarkerType.Custom) { if (outline == null) { throw new ArgumentNullException("outline", "The outline should be set when MarkerType is 'Custom'."); } var poly = outline.Select(o => new ScreenPoint(p.X + (o.x * size), p.Y + (o.y * size))).ToList(); polygons.Add(poly); return; } switch (type) { case MarkerType.Circle: { ellipses.Add(new OxyRect(p.x - size, p.y - size, size * 2, size * 2)); break; } case MarkerType.Square: { rects.Add(new OxyRect(p.x - size, p.y - size, size * 2, size * 2)); break; } case MarkerType.Diamond: { polygons.Add( new[] { new ScreenPoint(p.x, p.y - (M2 * size)), new ScreenPoint(p.x + (M2 * size), p.y), new ScreenPoint(p.x, p.y + (M2 * size)), new ScreenPoint(p.x - (M2 * size), p.y) }); break; } case MarkerType.Triangle: { polygons.Add( new[] { new ScreenPoint(p.x - size, p.y + (M1 * size)), new ScreenPoint(p.x + size, p.y + (M1 * size)), new ScreenPoint(p.x, p.y - (M2 * size)) }); break; } case MarkerType.Plus: case MarkerType.Star: { lines.Add(new ScreenPoint(p.x - size, p.y)); lines.Add(new ScreenPoint(p.x + size, p.y)); lines.Add(new ScreenPoint(p.x, p.y - size)); lines.Add(new ScreenPoint(p.x, p.y + size)); break; } } switch (type) { case MarkerType.Cross: case MarkerType.Star: { lines.Add(new ScreenPoint(p.x - (size * M3), p.y - (size * M3))); lines.Add(new ScreenPoint(p.x + (size * M3), p.y + (size * M3))); lines.Add(new ScreenPoint(p.x - (size * M3), p.y + (size * M3))); lines.Add(new ScreenPoint(p.x + (size * M3), p.y - (size * M3))); break; } } }
/// <summary> /// Draws a circle at the specified position. /// </summary> /// <param name="rc">The render context.</param> /// <param name="center">The center.</param> /// <param name="r">The radius.</param> /// <param name="fill">The fill color.</param> /// <param name="stroke">The stroke color.</param> /// <param name="thickness">The thickness.</param> /// <param name="edgeRenderingMode">The edge rendering mode.</param> public static void DrawCircle(this IRenderContext rc, ScreenPoint center, double r, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode) { DrawCircle(rc, center.X, center.Y, r, fill, stroke, thickness, edgeRenderingMode); }
/// <summary> /// Draws a list of markers. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="markerPoints">The marker points.</param> /// <param name="markerType">Type of the marker.</param> /// <param name="markerOutline">The marker outline.</param> /// <param name="markerSize">Size of the markers.</param> /// <param name="markerFill">The marker fill.</param> /// <param name="markerStroke">The marker stroke.</param> /// <param name="markerStrokeThickness">The marker stroke thickness.</param> /// <param name="edgeRenderingMode">The edge rendering mode.</param> /// <param name="resolution">The resolution.</param> /// <param name="binOffset">The bin Offset.</param> public static void DrawMarkers( this IRenderContext rc, OxyRect clippingRectangle, IList <ScreenPoint> markerPoints, MarkerType markerType, IList <ScreenPoint> markerOutline, IList <double> markerSize, OxyColor markerFill, OxyColor markerStroke, double markerStrokeThickness, EdgeRenderingMode edgeRenderingMode, int resolution = 0, ScreenPoint binOffset = new ScreenPoint()) { if (markerType == MarkerType.None) { return; } int n = markerPoints.Count; var ellipses = new List <OxyRect>(n); var rects = new List <OxyRect>(n); var polygons = new List <IList <ScreenPoint> >(n); var lines = new List <ScreenPoint>(n); var hashset = new Dictionary <uint, bool>(); int i = 0; double minx = clippingRectangle.Left; double maxx = clippingRectangle.Right; double miny = clippingRectangle.Top; double maxy = clippingRectangle.Bottom; foreach (var p in markerPoints) { if (resolution > 1) { var x = (int)((p.X - binOffset.X) / resolution); var y = (int)((p.Y - binOffset.Y) / resolution); uint hash = (uint)(x << 16) + (uint)y; if (hashset.ContainsKey(hash)) { i++; continue; } hashset.Add(hash, true); } bool outside = p.x <minx || p.x> maxx || p.y <miny || p.y> maxy; if (!outside) { int j = i < markerSize.Count ? i : 0; AddMarkerGeometry(p, markerType, markerOutline, markerSize[j], ellipses, rects, polygons, lines); } i++; } if (edgeRenderingMode == EdgeRenderingMode.Automatic) { edgeRenderingMode = EdgeRenderingMode.PreferGeometricAccuracy; } if (ellipses.Count > 0) { rc.DrawEllipses(ellipses, markerFill, markerStroke, markerStrokeThickness, edgeRenderingMode); } if (rects.Count > 0) { rc.DrawRectangles(rects, markerFill, markerStroke, markerStrokeThickness, edgeRenderingMode); } if (polygons.Count > 0) { rc.DrawPolygons(polygons, markerFill, markerStroke, markerStrokeThickness, edgeRenderingMode); } if (lines.Count > 0) { rc.DrawLineSegments(lines, markerStroke, markerStrokeThickness, edgeRenderingMode); } }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The position of the text.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public override void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize?maxSize) { this.doc.SaveState(); this.doc.SetFont(fontFamily, fontSize / 96 * 72, fontWeight > 500); this.doc.SetFillColor(fill); double width, height; this.doc.MeasureText(text, out width, out height); if (maxSize != null) { if (width > maxSize.Value.Width) { width = Math.Max(maxSize.Value.Width, 0); } if (height > maxSize.Value.Height) { height = Math.Max(maxSize.Value.Height, 0); } } double dx = 0; if (halign == HorizontalAlignment.Center) { dx = -width / 2; } if (halign == HorizontalAlignment.Right) { dx = -width; } double dy = 0; if (valign == VerticalAlignment.Middle) { dy = -height / 2; } if (valign == VerticalAlignment.Top) { dy = -height; } double y = this.doc.PageHeight - p.Y; this.doc.Translate(p.X, y); if (Math.Abs(rotate) > 1e-6) { this.doc.Rotate(-rotate); } this.doc.Translate(dx, dy); // this.doc.DrawRectangle(0, 0, width, height); this.doc.SetClippingRectangle(0, 0, width, height); this.doc.DrawText(0, 0, text); this.doc.RestoreState(); }
/// <summary> /// Draws text. /// </summary> /// <param name="p">The position.</param> /// <param name="text">The text.</param> /// <param name="fill">The text color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font (in device independent units, 1/96 inch).</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text (in device independent units, 1/96 inch).</param> public void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily = null, double fontSize = 10, double fontWeight = 500, double rotate = 0, HorizontalAlignment halign = HorizontalAlignment.Left, VerticalAlignment valign = VerticalAlignment.Top, OxySize? maxSize = null) { }
/// <summary> /// Fines the edge interception. /// </summary> /// <param name="bounds">The bounds.</param> /// <param name="edge">The edge.</param> /// <param name="a">The first point.</param> /// <param name="b">The second point.</param> /// <returns>The interception.</returns> private static ScreenPoint LineIntercept(OxyRect bounds, RectangleEdge edge, ScreenPoint a, ScreenPoint b) { if (a.x.Equals(b.x) && a.y.Equals(b.y)) { return(a); } switch (edge) { case RectangleEdge.Bottom: if (b.Y.Equals(a.Y)) { throw new ArgumentException("no intercept found"); } return(new ScreenPoint(a.X + (((b.X - a.X) * (bounds.Bottom - a.Y)) / (b.Y - a.Y)), bounds.Bottom)); case RectangleEdge.Left: if (b.X.Equals(a.X)) { throw new ArgumentException("no intercept found"); } return(new ScreenPoint(bounds.Left, a.Y + (((b.Y - a.Y) * (bounds.Left - a.X)) / (b.X - a.X)))); case RectangleEdge.Right: if (b.X.Equals(a.X)) { throw new ArgumentException("no intercept found"); } return(new ScreenPoint(bounds.Right, a.Y + (((b.Y - a.Y) * (bounds.Right - a.X)) / (b.X - a.X)))); case RectangleEdge.Top: if (b.Y.Equals(a.Y)) { throw new ArgumentException("no intercept found"); } return(new ScreenPoint(a.X + (((b.X - a.X) * (bounds.Top - a.Y)) / (b.Y - a.Y)), bounds.Top)); } throw new ArgumentException("no intercept found"); }
/// <summary> /// Draws or measures text containing sub- and superscript. /// </summary> /// <param name="rc">The render context.</param> /// <param name="pt">The point.</param> /// <param name="text">The text.</param> /// <param name="textColor">Color of the text.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">The font size.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="angle">The angle.</param> /// <param name="ha">The horizontal alignment.</param> /// <param name="va">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> /// <param name="measure">Measure the size of the text if set to <c>true</c>.</param> /// <returns>The size of the text.</returns> /// <example>Subscript: H_{2}O /// Superscript: E=mc^{2} /// Both: A^{2}_{i,j}</example> public static OxySize DrawMathText( this IRenderContext rc, ScreenPoint pt, string text, OxyColor textColor, string fontFamily, double fontSize, double fontWeight, double angle, HorizontalAlignment ha, VerticalAlignment va, OxySize?maxSize, bool measure) { if (string.IsNullOrEmpty(text)) { return(OxySize.Empty); } if (text.Contains("^{") || text.Contains("_{")) { var x = pt.X; var y = pt.Y; // Measure var size = InternalDrawMathText(rc, x, y, 0, 0, text, textColor, fontFamily, fontSize, fontWeight, true, angle); var dx = 0d; var dy = 0d; switch (ha) { case HorizontalAlignment.Right: dx = -size.Width; break; case HorizontalAlignment.Center: dx = -size.Width * 0.5; break; } switch (va) { case VerticalAlignment.Bottom: dy = -size.Height; break; case VerticalAlignment.Middle: dy = -size.Height * 0.5; break; } InternalDrawMathText(rc, x, y, dx, dy, text, textColor, fontFamily, fontSize, fontWeight, false, angle); return(measure ? size : OxySize.Empty); } rc.DrawText(pt, text, textColor, fontFamily, fontSize, fontWeight, angle, ha, va, maxSize); if (measure) { return(rc.MeasureText(text, fontFamily, fontSize, fontWeight)); } return(OxySize.Empty); }
protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); bool control = Control.ModifierKeys == Keys.Control; bool shift = Control.ModifierKeys == Keys.Shift; var p = new ScreenPoint(e.X, e.Y); foreach (var a in MouseActions) a.OnMouseWheel(p, e.Delta, control, shift); }
/// <summary> /// Gets the point on the series that is nearest the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param> /// <returns> /// A TrackerHitResult for the current hit. /// </returns> public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) { var dp = this.InverseTransform(point); int i = (int)dp.Y; int j = (int)dp.X; if (i >= 0 && i < this.matrix.GetLength(0) && j >= 0 && j < this.matrix.GetLength(1)) { var value = this.matrix[i, j]; var text = StringHelper.Format( this.ActualCulture, this.TrackerFormatString, null, this.Title, i, j, value); return new TrackerHitResult(this, dp, point, null, -1, text); } return null; }
/// <summary> /// Gets the first axes that covers the area of the specified point. /// </summary> /// <param name="pt">The point.</param> /// <param name="xaxis">The x-axis.</param> /// <param name="yaxis">The y-axis.</param> public void GetAxesFromPoint(ScreenPoint pt, out Axis xaxis, out Axis yaxis) { xaxis = yaxis = null; // Get the axis position of the given point. Using null if the point is inside the plot area. AxisPosition?position = null; double plotAreaValue = 0; if (pt.X < this.PlotArea.Left) { position = AxisPosition.Left; plotAreaValue = this.PlotArea.Left; } if (pt.X > this.PlotArea.Right) { position = AxisPosition.Right; plotAreaValue = this.PlotArea.Right; } if (pt.Y < this.PlotArea.Top) { position = AxisPosition.Top; plotAreaValue = this.PlotArea.Top; } if (pt.Y > this.PlotArea.Bottom) { position = AxisPosition.Bottom; plotAreaValue = this.PlotArea.Bottom; } foreach (var axis in this.Axes) { if (!axis.IsAxisVisible) { continue; } if (axis is IColorAxis) { continue; } if (axis is MagnitudeAxis) { xaxis = axis; continue; } if (axis is AngleAxis) { yaxis = axis; continue; } double x = double.NaN; if (axis.IsHorizontal()) { x = axis.InverseTransform(pt.X); } if (axis.IsVertical()) { x = axis.InverseTransform(pt.Y); } if (x >= axis.ActualMinimum && x <= axis.ActualMaximum) { if (position == null) { if (axis.IsHorizontal()) { if (xaxis == null) { xaxis = axis; } } else if (axis.IsVertical()) { if (yaxis == null) { yaxis = axis; } } } else if (position == axis.Position) { // Choose right tier double positionTierMinShift = axis.PositionTierMinShift; double positionTierMaxShift = axis.PositionTierMaxShift; double posValue = axis.IsHorizontal() ? pt.Y : pt.X; bool isLeftOrTop = position == AxisPosition.Top || position == AxisPosition.Left; if ((posValue >= plotAreaValue + positionTierMinShift && posValue < plotAreaValue + positionTierMaxShift && !isLeftOrTop) || (posValue <= plotAreaValue - positionTierMinShift && posValue > plotAreaValue - positionTierMaxShift && isLeftOrTop)) { if (axis.IsHorizontal()) { if (xaxis == null) { xaxis = axis; } } else if (axis.IsVertical()) { if (yaxis == null) { yaxis = axis; } } } } } } }
protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); Focus(); Capture = true; bool control = Control.ModifierKeys == Keys.Control; bool shift = Control.ModifierKeys == Keys.Shift; var button = OxyMouseButton.Left; if (e.Button == MouseButtons.Middle) button = OxyMouseButton.Middle; if (e.Button == MouseButtons.Right) button = OxyMouseButton.Right; if (e.Button == MouseButtons.XButton1) button = OxyMouseButton.XButton1; if (e.Button == MouseButtons.XButton2) button = OxyMouseButton.XButton2; var p = new ScreenPoint(e.X, e.Y); foreach (var a in MouseActions) a.OnMouseDown(p, button, e.Clicks, control, shift); }
/// <summary> /// Initializes a new instance of the <see cref="OxyRect"/> struct by location and size. /// </summary> /// <param name="p0">The location.</param> /// <param name="size">The size.</param> public OxyRect(ScreenPoint p0, OxySize size) : this(p0.X, p0.Y, size.Width, size.Height) { }
public void GetAxesFromPoint(ScreenPoint pt, out AxisBase xaxis, out AxisBase yaxis) { Model.GetAxesFromPoint(pt, out xaxis, out yaxis); }
/// <summary> /// Initializes a new instance of the <see cref="OxyRect" /> struct that is exactly large enough to contain the two specified points. /// </summary> /// <param name="p0">The first point that the new rectangle must contain.</param> /// <param name="p1">The second point that the new rectangle must contain.</param> public OxyRect(ScreenPoint p0, ScreenPoint p1) : this(Math.Min(p0.X, p1.X), Math.Min(p0.Y, p1.Y), Math.Abs(p1.X - p0.X), Math.Abs(p1.Y - p0.Y)) { }
/// <summary> /// Renders the annotation on the specified context. /// </summary> /// <param name="rc">The render context.</param> public override void Render(IRenderContext rc) { base.Render(rc); var lon0 = this.XAxis.ActualMinimum; var lon1 = this.XAxis.ActualMaximum; var lat0 = this.YAxis.ActualMinimum; var lat1 = this.YAxis.ActualMaximum; // the desired number of tiles horizontally double tilesx = this.PlotModel.Width / this.TileSize; // calculate the desired zoom level var n = tilesx / (((lon1 + 180) / 360) - ((lon0 + 180) / 360)); var zoom = (int)Math.Round(Math.Log(n) / Math.Log(2)); if (zoom < this.MinZoomLevel) { zoom = this.MinZoomLevel; } if (zoom > this.MaxZoomLevel) { zoom = this.MaxZoomLevel; } // find tile coordinates for the corners double x0, y0; LatLonToTile(lat0, lon0, zoom, out x0, out y0); double x1, y1; LatLonToTile(lat1, lon1, zoom, out x1, out y1); double xmax = Math.Max(x0, x1); double xmin = Math.Min(x0, x1); double ymax = Math.Max(y0, y1); double ymin = Math.Min(y0, y1); var clippingRectangle = this.GetClippingRect(); // Add the tiles for (var x = (int)xmin; x < xmax; x++) { for (var y = (int)ymin; y < ymax; y++) { string uri = this.GetTileUri(x, y, zoom); var img = this.GetImage(uri, rc.RendersToScreen); if (img == null) { continue; } // transform from tile coordinates to lat/lon double latitude0, latitude1, longitude0, longitude1; TileToLatLon(x, y, zoom, out latitude0, out longitude0); TileToLatLon(x + 1, y + 1, zoom, out latitude1, out longitude1); // transform from lat/lon to screen coordinates var s00 = this.Transform(longitude0, latitude0); var s11 = this.Transform(longitude1, latitude1); var r = OxyRect.Create(s00.X, s00.Y, s11.X, s11.Y); // draw the image rc.DrawClippedImage(clippingRectangle, img, r.Left, r.Top, r.Width, r.Height, this.Opacity, true); } } // draw the copyright notice var p = new ScreenPoint(clippingRectangle.Right - 5, clippingRectangle.Bottom - 5); var textSize = rc.MeasureText(this.CopyrightNotice, this.ActualFont, this.ActualFontSize, this.ActualFontWeight); rc.DrawRectangle(new OxyRect(p.X - textSize.Width - 2, p.Y - textSize.Height - 2, textSize.Width + 4, textSize.Height + 4), OxyColor.FromAColor(200, OxyColors.White), OxyColors.Undefined); rc.DrawText( p, this.CopyrightNotice, OxyColors.Black, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Right, VerticalAlignment.Bottom); }
/// <summary> /// Gets the point on the series that is nearest the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param> /// <returns>A TrackerHitResult for the current hit.</returns> public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) { var points = this.Points; if (points == null) { return null; } var spn = default(ScreenPoint); var dpn = default(DataPoint); double index = -1; double minimumDistance = double.MaxValue; for (int i = 0; i + 1 < points.Count; i += 2) { var p1 = points[i]; var p2 = points[i + 1]; if (!this.IsValidPoint(p1) || !this.IsValidPoint(p2)) { continue; } var sp1 = this.Transform(p1); var sp2 = this.Transform(p2); // Find the nearest point on the line segment. var spl = ScreenPointHelper.FindPointOnLine(point, sp1, sp2); if (ScreenPoint.IsUndefined(spl)) { // P1 && P2 coincident continue; } double l2 = (point - spl).LengthSquared; if (l2 < minimumDistance) { double u = (spl - sp1).Length / (sp2 - sp1).Length; dpn = new DataPoint(p1.X + (u * (p2.X - p1.X)), p1.Y + (u * (p2.Y - p1.Y))); spn = spl; minimumDistance = l2; index = i + u; } } if (minimumDistance < double.MaxValue) { return new TrackerHitResult { Series = this, DataPoint = dpn, Position = spn, Item = this.GetItem((int)index), Index = index }; } return null; }
/// <summary> /// Renders the LineSeries on the specified rendering context. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <param name="model"> /// The owner plot model. /// </param> public override void Render(IRenderContext rc, PlotModel model) { if (this.Points.Count == 0) { return; } if (this.XAxis == null || this.YAxis == null) { Trace("Axis not defined."); return; } double minDistSquared = this.MinimumSegmentLength * this.MinimumSegmentLength; var clippingRect = this.GetClippingRect(); Action <IList <ScreenPoint>, IList <ScreenPoint> > renderPoints = (lpts, mpts) => { var lineStyle = this.ActualLineStyle; // clip the line segments with the clipping rectangle if (this.StrokeThickness > 0 && lineStyle != LineStyle.None) { var verticalStrokeThickness = double.IsNaN(this.VerticalStrokeThickness) ? StrokeThickness : VerticalStrokeThickness; if (!verticalStrokeThickness.Equals(this.StrokeThickness) || this.VerticalLineStyle != lineStyle) { var hlpts = new List <ScreenPoint>(); var vlpts = new List <ScreenPoint>(); for (int i = 0; i + 2 < lpts.Count; i += 2) { hlpts.Add(lpts[i]); hlpts.Add(lpts[i + 1]); vlpts.Add(lpts[i + 1]); vlpts.Add(lpts[i + 2]); } rc.DrawClippedLineSegments( hlpts, clippingRect, this.GetSelectableColor(this.ActualColor), this.StrokeThickness, lineStyle, this.LineJoin, false); rc.DrawClippedLineSegments( vlpts, clippingRect, this.GetSelectableColor(this.ActualColor), verticalStrokeThickness, this.VerticalLineStyle, this.LineJoin, false); } else { rc.DrawClippedLine( lpts, clippingRect, 0, this.GetSelectableColor(this.ActualColor), this.StrokeThickness, lineStyle, this.LineJoin, false); } } if (this.MarkerType != MarkerType.None) { rc.DrawMarkers( mpts, clippingRect, this.MarkerType, this.MarkerOutline, new[] { this.MarkerSize }, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness); } }; // Transform all points to screen coordinates // Render the line when invalid points occur var linePoints = new List <ScreenPoint>(); var markerPoints = new List <ScreenPoint>(); double previousY = double.NaN; foreach (var point in this.Points) { if (!this.IsValidPoint(point, this.XAxis, this.YAxis)) { renderPoints(linePoints, markerPoints); linePoints.Clear(); markerPoints.Clear(); previousY = double.NaN; continue; } ScreenPoint transformedPoint = this.Transform(point); if (!double.IsNaN(previousY)) { // Horizontal line from the previous point to the current x-coordinate linePoints.Add(new ScreenPoint(transformedPoint.X, previousY)); } linePoints.Add(transformedPoint); markerPoints.Add(transformedPoint); previousY = transformedPoint.Y; } renderPoints(linePoints, markerPoints); }
/// <summary> /// Renders the legend symbol for the line series on the /// specified rendering context. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <param name="legendBox"> /// The bounding rectangle of the legend box. /// </param> public override void RenderLegend(IRenderContext rc, OxyRect legendBox) { double xmid = (legendBox.Left + legendBox.Right) / 2; double ymid = (legendBox.Top + legendBox.Bottom) / 2; var pts = new[] { new ScreenPoint(legendBox.Left, ymid), new ScreenPoint(legendBox.Right, ymid) }; rc.DrawLine(pts, this.GetSelectableColor(this.ActualColor), this.StrokeThickness, LineStyleHelper.GetDashArray(this.ActualLineStyle)); var midpt = new ScreenPoint(xmid, ymid); rc.DrawMarker( midpt, legendBox, this.MarkerType, this.MarkerOutline, this.MarkerSize, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness); }
/// <summary> /// Gets the point on the series that is nearest the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param> /// <returns> /// A TrackerHitResult for the current hit. /// </returns> public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) { if (interpolate) { return null; } double minimumDistance = double.MaxValue; var result = new TrackerHitResult(this, DataPoint.Undefined, ScreenPoint.Undefined); Action<DataPoint, HighLowItem, int> check = (p, item, index) => { var sp = this.Transform(p); double dx = sp.x - point.x; double dy = sp.y - point.y; double d2 = (dx * dx) + (dy * dy); if (d2 < minimumDistance) { result.DataPoint = p; result.Position = sp; result.Item = item; result.Index = index; if (this.TrackerFormatString != null) { result.Text = StringHelper.Format( this.ActualCulture, this.TrackerFormatString, item, this.Title, p.X, item.High, item.Low, item.Open, item.Close); } minimumDistance = d2; } }; int i = 0; foreach (var item in this.items) { check(new DataPoint(item.X, item.High), item, i); check(new DataPoint(item.X, item.Low), item, i); check(new DataPoint(item.X, item.Open), item, i); check(new DataPoint(item.X, item.Close), item, i++); } if (minimumDistance < double.MaxValue) { return result; } return null; }
/// <summary> /// Determines whether the specified point is undefined. /// </summary> /// <param name="point"> /// The point. /// </param> /// <returns> /// <c>true</c> if the specified point is undefined; otherwise, <c>false</c> . /// </returns> public static bool IsUndefined(ScreenPoint point) { return double.IsNaN(point.X) && double.IsNaN(point.Y); }
/// <summary> /// Determines whether the specified point is inside the rectangle. /// </summary> /// <param name="p">The point.</param> /// <returns><c>true</c> if the rectangle contains the specified point; otherwise, <c>false</c>.</returns> public bool Contains(ScreenPoint p) { return(this.Contains(p.x, p.y)); }
/// <summary> /// Gets the squared distance to the specified point. /// </summary> /// <param name="point"> /// The point. /// </param> /// <returns> /// The squared distance. /// </returns> public double DistanceToSquared(ScreenPoint point) { double dx = point.x - this.x; double dy = point.y - this.y; return (dx * dx) + (dy * dy); }
/// <summary> /// Gets a series from the specified point. /// </summary> /// <param name="point">The point.</param> /// <param name="limit">The limit.</param> /// <returns>The nearest series.</returns> public Series.Series GetSeriesFromPoint(ScreenPoint point, double limit = 100) { double mindist = double.MaxValue; Series.Series nearestSeries = null; foreach (var series in this.Series.Reverse().Where(s => s.IsVisible)) { var thr = series.GetNearestPoint(point, true) ?? series.GetNearestPoint(point, false); if (thr == null) { continue; } // find distance to this point on the screen double dist = point.DistanceTo(thr.Position); if (dist < mindist) { nearestSeries = series; mindist = dist; } } if (mindist < limit) { return nearestSeries; } return null; }
/// <summary> /// Determines whether the specified point is inside the rectangle. /// </summary> /// <param name="p">The point.</param> /// <returns><c>true</c> if the rectangle contains the specified point; otherwise, <c>false</c>.</returns> public bool Contains(ScreenPoint p) { return this.Contains(p.x, p.y); }
/// <summary> /// Gets the point on the curve that is nearest the specified point. /// </summary> /// <param name = "point">The point.</param> /// <param name = "dpn">The nearest point (data coordinates).</param> /// <param name = "spn">The nearest point (screen coordinates).</param> /// <returns></returns> protected bool GetNearestInterpolatedPointInternal( IList <IDataPoint> points, ScreenPoint point, out DataPoint dpn, out ScreenPoint spn, out int index) { spn = default(ScreenPoint); dpn = default(DataPoint); index = -1; // http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ double minimumDistance = double.MaxValue; for (int i = 0; i + 1 < points.Count; i++) { IDataPoint p1 = points[i]; IDataPoint p2 = points[i + 1]; ScreenPoint sp1 = AxisBase.Transform(p1, this.XAxis, this.YAxis); ScreenPoint sp2 = AxisBase.Transform(p2, this.XAxis, this.YAxis); double sp21X = sp2.x - sp1.x; double sp21Y = sp2.y - sp1.y; double u1 = (point.x - sp1.x) * sp21X + (point.y - sp1.y) * sp21Y; double u2 = sp21X * sp21X + sp21Y * sp21Y; double ds = sp21X * sp21X + sp21Y * sp21Y; if (ds < 4) { // if the points are very close, we can get numerical problems, just use the first point... u1 = 0; u2 = 1; } if (u2 < double.Epsilon && u2 > -double.Epsilon) { continue; // P1 && P2 coincident } double u = u1 / u2; if (u < 0) { u = 0; } if (u > 1) { u = 1; } double sx = sp1.x + u * sp21X; double sy = sp1.y + u * sp21Y; double dx = point.x - sx; double dy = point.y - sy; double distance = dx * dx + dy * dy; if (distance < minimumDistance) { double px = p1.X + u * (p2.X - p1.X); double py = p1.Y + u * (p2.Y - p1.Y); dpn = new DataPoint(px, py); spn = new ScreenPoint(sx, sy); minimumDistance = distance; index = i; } } return(minimumDistance < double.MaxValue); }