/// <summary> /// Fast index of bar where max(bar[i].X) <= x /// </summary> /// <returns>The index of the bar closest to X, where max(bar[i].X) <= x.</returns> /// <param name="x">The x coordinate.</param> /// <param name="startIndex">starting index</param> public int FindByX(double x, int startIndex = -1) { if (startIndex < 0) { startIndex = this.winIndex; } return(HighLowItem.FindIndex(this.Items, x, startIndex)); }
/// <summary> /// Determines whether the point is valid. /// </summary> /// <param name="pt">The point.</param> /// <param name="xaxis">The x axis.</param> /// <param name="yaxis">The y axis.</param> /// <returns><c>true</c> if the specified point is valid; otherwise, <c>false</c>.</returns> public virtual bool IsValidItem(HighLowItem pt, Axis xaxis, Axis yaxis) { return(!double.IsNaN(pt.X) && !double.IsInfinity(pt.X) && !double.IsNaN(pt.High) && !double.IsInfinity(pt.High) && !double.IsNaN(pt.Low) && !double.IsInfinity(pt.Low)); }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> public override void Render(IRenderContext rc) { var nitems = this.Items.Count; var items = this.Items; if (nitems == 0 || this.StrokeThickness <= 0 || this.LineStyle == LineStyle.None) { return; } this.VerifyAxes(); var clippingRect = this.GetClippingRect(); var dashArray = this.LineStyle.GetDashArray(); var datacandlewidth = (this.CandleWidth > 0) ? this.CandleWidth : this.minDx * 0.80; var candlewidth = this.XAxis.Transform(items[0].X + datacandlewidth) - this.XAxis.Transform(items[0].X); // colors var fillUp = this.GetSelectableFillColor(this.IncreasingColor); var fillDown = this.GetSelectableFillColor(this.DecreasingColor); var lineUp = this.GetSelectableColor(this.IncreasingColor.ChangeIntensity(0.70)); var lineDown = this.GetSelectableColor(this.DecreasingColor.ChangeIntensity(0.70)); // determine render range var xmin = this.XAxis.ActualMinimum; var xmax = this.XAxis.ActualMaximum; this.winIndex = HighLowItem.FindIndex(items, xmin, this.winIndex); for (int i = this.winIndex; i < nitems; i++) { var bar = items[i]; // if item beyond visible range, done if (bar.X > xmax) { return; } // check to see whether is valid if (!this.IsValidItem(bar, this.XAxis, this.YAxis)) { continue; } var fillColor = bar.Close > bar.Open ? fillUp : fillDown; var lineColor = bar.Close > bar.Open ? lineUp : lineDown; var high = this.Transform(bar.X, bar.High); var low = this.Transform(bar.X, bar.Low); var open = this.Transform(bar.X, bar.Open); var close = this.Transform(bar.X, bar.Close); var max = new ScreenPoint(open.X, Math.Max(open.Y, close.Y)); var min = new ScreenPoint(open.X, Math.Min(open.Y, close.Y)); // Upper extent rc.DrawClippedLine( clippingRect, new[] { high, min }, 0, lineColor, this.StrokeThickness, dashArray, this.LineJoin, true); // Lower extent rc.DrawClippedLine( clippingRect, new[] { max, low }, 0, lineColor, this.StrokeThickness, dashArray, this.LineJoin, true); // Body var openLeft = open + new ScreenVector(-candlewidth * 0.5, 0); var rect = new OxyRect(openLeft.X, min.Y, candlewidth, max.Y - min.Y); rc.DrawClippedRectangleAsPolygon(clippingRect, rect, fillColor, lineColor, this.StrokeThickness); } }
/// <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 (this.XAxis == null || this.YAxis == null || interpolate || this.Items.Count == 0) { return(null); } var nbars = this.Items.Count; var xy = this.InverseTransform(point); var targetX = xy.X; // punt if beyond start & end of series if (targetX > (this.Items[nbars - 1].X + this.minDx) || targetX < (this.Items[0].X - this.minDx)) { return(null); } var pidx = HighLowItem.FindIndex(this.Items, targetX, this.winIndex); var nidx = ((pidx + 1) < this.Items.Count) ? pidx + 1 : pidx; Func <HighLowItem, double> distance = bar => { var dx = bar.X - xy.X; var dyo = bar.Open - xy.Y; var dyh = bar.High - xy.Y; var dyl = bar.Low - xy.Y; var dyc = bar.Close - xy.Y; var d2O = (dx * dx) + (dyo * dyo); var d2H = (dx * dx) + (dyh * dyh); var d2L = (dx * dx) + (dyl * dyl); var d2C = (dx * dx) + (dyc * dyc); return(Math.Min(d2O, Math.Min(d2H, Math.Min(d2L, d2C)))); }; // determine closest point var midx = distance(this.Items[pidx]) <= distance(this.Items[nidx]) ? pidx : nidx; var mbar = this.Items[midx]; var hit = new DataPoint(mbar.X, mbar.Close); return(new TrackerHitResult { Series = this, DataPoint = hit, Position = this.Transform(hit), Item = mbar, Index = midx, Text = StringHelper.Format( this.ActualCulture, this.TrackerFormatString, mbar, this.Title, this.XAxis.Title ?? DefaultXAxisTitle, this.XAxis.GetValue(mbar.X), this.YAxis.GetValue(mbar.High), this.YAxis.GetValue(mbar.Low), this.YAxis.GetValue(mbar.Open), this.YAxis.GetValue(mbar.Close)) }); }
/// <summary> /// Determines whether the point is valid. /// </summary> /// <param name="pt">The point.</param> /// <param name="xaxis">The x axis.</param> /// <param name="yaxis">The y axis.</param> /// <returns><c>true</c> if [is valid point] [the specified pt]; otherwise, <c>false</c>.</returns> public virtual bool IsValidItem(HighLowItem pt, Axis xaxis, Axis yaxis) { return !double.IsNaN(pt.X) && !double.IsInfinity(pt.X) && !double.IsNaN(pt.High) && !double.IsInfinity(pt.High) && !double.IsNaN(pt.Low) && !double.IsInfinity(pt.Low); }