/// <summary> /// Renders the specified axis. /// </summary> /// <param name="axis">The axis.</param> /// <param name="pass">The pass.</param> public override void Render(Axis axis, int pass) { base.Render(axis, pass); double totalShift = axis.AxisDistance + axis.PositionTierMinShift; double tierSize = axis.PositionTierSize - this.Plot.AxisTierDistance; // store properties locally for performance double plotAreaLeft = this.Plot.PlotArea.Left; double plotAreaRight = this.Plot.PlotArea.Right; double plotAreaTop = this.Plot.PlotArea.Top; double plotAreaBottom = this.Plot.PlotArea.Bottom; // Axis position (x or y screen coordinate) double axisPosition = 0; double titlePosition = 0; switch (axis.Position) { case AxisPosition.Left: axisPosition = plotAreaLeft - totalShift; titlePosition = axisPosition - tierSize; break; case AxisPosition.Right: axisPosition = plotAreaRight + totalShift; titlePosition = axisPosition + tierSize; break; case AxisPosition.Top: axisPosition = plotAreaTop - totalShift; titlePosition = axisPosition - tierSize; break; case AxisPosition.Bottom: axisPosition = plotAreaBottom + totalShift; titlePosition = axisPosition + tierSize; break; } if (axis.PositionAtZeroCrossing) { var perpendicularAxis = axis.IsHorizontal() ? this.Plot.DefaultYAxis : this.Plot.DefaultXAxis; axisPosition = perpendicularAxis.Transform(0); } if (pass == 0) { this.RenderMinorItems(axis, axisPosition); } if (pass == 1) { this.RenderMajorItems(axis, axisPosition, titlePosition); this.RenderAxisTitle(axis, titlePosition); } }
/// <summary> /// Gets the maximum scale of the specified axis. /// </summary> public double AxisMaximum(Models.Graph.Axis.AxisType axisType) { OxyPlot.Axes.Axis axis = GetAxis(axisType); if (axis != null) { return(axis.ActualMaximum); } else { return(double.NaN); } }
/// <summary> /// Gets the interval (major step) of the specified axis. /// </summary> public double AxisMajorStep(Models.Graph.Axis.AxisType axisType) { OxyPlot.Axes.Axis axis = GetAxis(axisType); if (axis != null) { return(axis.IntervalLength); } else { return(double.NaN); } }
/// <summary> /// Gets the minimum scale of the specified axis. /// </summary> public double AxisMinimum(Models.Graph.Axis.AxisType axisType) { plot1.Refresh(); OxyPlot.Axes.Axis axis = GetAxis(axisType); if (axis != null) { return(axis.ActualMinimum); } else { return(double.NaN); } }
/// <summary> /// Gets the minimum scale of the specified axis. /// </summary> public double AxisMinimum(Models.Graph.Axis.AxisType axisType) { /// TBI foreach (PlotView p in plots) /// TBI p.Refresh(); OxyPlot.Axes.Axis axis = GetAxis(axisType); if (axis != null) { return(axis.ActualMinimum); } else { return(double.NaN); } }
/// <summary> /// Format axis tick labels so that there is a leading zero on the tick /// labels when necessary. /// </summary> /// <param name="axis">The axis to format</param> private void FormatAxisTickLabels(OxyPlot.Axes.Axis axis) { // axis.IntervalLength = 100; if (axis is DateTimeAxis) { DateTimeAxis dateAxis = axis as DateTimeAxis; int numDays = (largestDate - smallestDate).Days; if (numDays < 100) { dateAxis.IntervalType = DateTimeIntervalType.Days; } else if (numDays <= 366) { dateAxis.IntervalType = DateTimeIntervalType.Months; dateAxis.StringFormat = "dd-MMM"; } else if (numDays <= 720) { dateAxis.IntervalType = DateTimeIntervalType.Months; dateAxis.StringFormat = "MMM-yyyy"; } else { dateAxis.IntervalType = DateTimeIntervalType.Years; dateAxis.StringFormat = "yyyy"; } } if (axis is LinearAxis && (axis.ActualStringFormat == null || !axis.ActualStringFormat.Contains("yyyy"))) { // We want the axis labels to always have a leading 0 when displaying decimal places. // e.g. we want 0.5 rather than .5 // Use the current culture to format the string. string st = axis.ActualMajorStep.ToString(System.Globalization.CultureInfo.InvariantCulture); // count the number of decimal places in the above string. int pos = st.IndexOfAny(".,".ToCharArray()); if (pos != -1) { int numDecimalPlaces = st.Length - pos - 1; axis.StringFormat = "F" + numDecimalPlaces.ToString(); } } }
/// <summary> /// Gets the minimum scale of the specified axis. /// </summary> public double AxisMinimum(Models.Graph.Axis.AxisType axisType) { foreach (PlotView p in plots) { p.InvalidatePlot(true); } OxyPlot.Axes.Axis axis = GetAxis(axisType); if (axis != null) { return(axis.ActualMinimum); } else { return(double.NaN); } }
/// <summary> /// Inverse transform the specified screen point. /// </summary> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="yaxis">The y-axis.</param> /// <returns>The data point.</returns> public override DataPoint InverseTransform(double x, double y, Axis yaxis) { var angleAxis = yaxis as AngleAxis; if (angleAxis == null) { throw new InvalidOperationException("Polar angle axis not defined!"); } x -= this.MidPoint.x; y -= this.MidPoint.y; y *= -1; double th = Math.Atan2(y, x); double r = Math.Sqrt((x * x) + (y * y)); x = (r / this.Scale) + this.Offset; y = (th / angleAxis.Scale) + angleAxis.Offset; return new DataPoint(x, y); }
/// <summary> /// Renders the specified axis. /// </summary> /// <param name="axis">The axis.</param> /// <param name="pass">The pass.</param> /// <exception cref="System.NullReferenceException">Angle axis should not be <c>null</c>.</exception> public override void Render(Axis axis, int pass) { base.Render(axis, pass); var angleAxis = this.Plot.DefaultAngleAxis; if (angleAxis == null) { throw new NullReferenceException("Angle axis should not be null."); } angleAxis.UpdateActualMaxMin(); var majorTicks = MajorTickValues.Where(x => x > axis.ActualMinimum && x <= axis.ActualMaximum).ToArray(); if (pass == 0 && this.MinorPen != null) { var minorTicks = MinorTickValues.Where(x => x >= axis.ActualMinimum && x <= axis.ActualMaximum && !majorTicks.Contains(x)).ToArray(); foreach (var tickValue in minorTicks) { this.RenderTick(axis, angleAxis, tickValue, this.MinorPen); } } if (pass == 0 && this.MajorPen != null) { foreach (var tickValue in majorTicks) { this.RenderTick(axis, angleAxis, tickValue, this.MajorPen); } } if (pass == 1) { foreach (double tickValue in majorTicks) { this.RenderTickText(axis, tickValue, angleAxis); } } }
/// <summary> /// Format the specified axis. /// </summary> /// <param name="axisType">The axis type to format</param> /// <param name="title">The axis title. If null then a default axis title will be shown</param> /// <param name="inverted">Invert the axis?</param> /// <param name="minimum">Minimum axis scale</param> /// <param name="maximum">Maximum axis scale</param> /// <param name="interval">Axis scale interval</param> public void FormatAxis( Models.Graph.Axis.AxisType axisType, string title, bool inverted, double minimum, double maximum, double interval) { OxyPlot.Axes.Axis oxyAxis = this.GetAxis(axisType); if (oxyAxis != null) { oxyAxis.Title = title; oxyAxis.MinorTickSize = 0; oxyAxis.AxislineStyle = LineStyle.Solid; oxyAxis.AxisTitleDistance = 10; if (inverted) { oxyAxis.StartPosition = 1; oxyAxis.EndPosition = 0; } else { oxyAxis.StartPosition = 0; oxyAxis.EndPosition = 1; } if (!double.IsNaN(minimum)) { oxyAxis.Minimum = minimum; } if (!double.IsNaN(maximum)) { oxyAxis.Maximum = maximum; } if (!double.IsNaN(interval) && interval > 0) { oxyAxis.MajorStep = interval; } } }
/// <summary> /// Determines whether the specified 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 point is valid; otherwise, <c>false</c> . /// </returns> public virtual bool IsValidPoint(ScatterPoint pt, Axis xaxis, Axis yaxis) { return !double.IsNaN(pt.X) && !double.IsInfinity(pt.X) && !double.IsNaN(pt.Y) && !double.IsInfinity(pt.Y) && (xaxis != null && xaxis.IsValidValue(pt.X)) && (yaxis != null && yaxis.IsValidValue(pt.Y)); }
/// <summary> /// Gets the axis title position, rotation and alignment. /// </summary> /// <param name="axis">The axis.</param> /// <param name="titlePosition">The title position.</param> /// <param name="angle">The angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <returns>The <see cref="ScreenPoint" />.</returns> protected virtual ScreenPoint GetAxisTitlePositionAndAlignment( Axis axis, double titlePosition, ref double angle, ref HorizontalAlignment halign, ref VerticalAlignment valign) { double middle = axis.IsHorizontal() ? Lerp(axis.ScreenMin.X, axis.ScreenMax.X, axis.TitlePosition) : Lerp(axis.ScreenMax.Y, axis.ScreenMin.Y, axis.TitlePosition); if (axis.PositionAtZeroCrossing) { middle = Lerp(axis.Transform(axis.ActualMaximum), axis.Transform(axis.ActualMinimum), axis.TitlePosition); } switch (axis.Position) { case AxisPosition.Left: return new ScreenPoint(titlePosition, middle); case AxisPosition.Right: valign = VerticalAlignment.Bottom; return new ScreenPoint(titlePosition, middle); case AxisPosition.Top: halign = HorizontalAlignment.Center; valign = VerticalAlignment.Top; angle = 0; return new ScreenPoint(middle, titlePosition); case AxisPosition.Bottom: halign = HorizontalAlignment.Center; valign = VerticalAlignment.Bottom; angle = 0; return new ScreenPoint(middle, titlePosition); default: throw new ArgumentOutOfRangeException("axis"); } }
/// <summary> /// Convert the given apsim axis to an oxyplot <see cref="Axis"/>. /// </summary> /// <param name="graph">The graph to be converted.</param> public static Axis ToOxyPlotAxis(this APSIM.Shared.Graphing.Axis axis, AxisRequirements requirements, IEnumerable <string> labels) { if (requirements.AxisKind == null) { throw new InvalidOperationException("Unable to create series - axis requirements unknown, possibly because no series have any data"); } Axis result = CreateAxis((AxisType)requirements.AxisKind); result.Position = axis.Position.ToOxyAxisPosition(); result.Title = axis.Title; result.PositionAtZeroCrossing = axis.CrossesAtZero; if (axis.Minimum is double min) { if (double.IsNaN(min)) { Debug.WriteLine("Axis minimum is NaN"); } else { result.Minimum = min; } } if (axis.Maximum is double max) { if (double.IsNaN(max)) { Debug.WriteLine("Axis maximum is NaN"); } else { result.Maximum = max; } } if (axis.Interval is double interval) { if (double.IsNaN(interval)) { Debug.WriteLine("Axis interval is NaN"); } else { if (requirements.AxisKind == AxisType.DateTime) { Debug.WriteLine("WARNING: Axis interval is set manually on a date axis - need to double check the implementation."); } result.MajorStep = interval; } } if (axis.Inverted) { result.StartPosition = 1; result.EndPosition = 0; } // There are many other options which could be exposed to the user. result.MinorTickSize = 0; result.AxisTitleDistance = 10; result.AxislineStyle = OxyPlot.LineStyle.Solid; if (requirements.AxisKind == AxisType.Category && result is CategoryAxis categoryAxis) { categoryAxis.LabelField = "Label"; categoryAxis.Labels.AddRange(labels); } return(result); }
/// <summary> /// Transforms the specified point to screen coordinates. /// </summary> /// <param name="p"> /// The point. /// </param> /// <param name="xaxis"> /// The x axis. /// </param> /// <param name="yaxis"> /// The y axis. /// </param> /// <returns> /// The transformed point. /// </returns> public static ScreenPoint Transform(IDataPoint p, Axis xaxis, Axis yaxis) { return xaxis.Transform(p.X, p.Y, yaxis); }
/// <summary> /// Transforms the specified point to screen coordinates. /// </summary> /// <param name="x">The x value (for the current axis).</param> /// <param name="y">The y value.</param> /// <param name="yaxis">The y axis.</param> /// <returns>The transformed point.</returns> public override ScreenPoint Transform(double x, double y, Axis yaxis) { var angleAxis = yaxis as AngleAxis; if (angleAxis == null) { throw new InvalidOperationException("Polar angle axis not defined!"); } var r = (x - this.Offset) * this.Scale; var theta = (y - angleAxis.Offset) * angleAxis.Scale; return new ScreenPoint(this.MidPoint.x + (r * Math.Cos(theta / 180 * Math.PI)), this.MidPoint.y - (r * Math.Sin(theta / 180 * Math.PI))); }
/// <summary> /// Renders a tick by drawing an lot of segments /// </summary> /// <param name="axis">The axis.</param> /// <param name="angleAxis">The angle axis.</param> /// <param name="x">The x-value.</param> /// <param name="pen">The pen.</param> private void RenderTickArc(Axis axis, AngleAxis angleAxis, double x, OxyPen pen) { // caution: make sure angleAxis.UpdateActualMaxMin(); has been called var minAngle = angleAxis.ActualMinimum; var maxAngle = angleAxis.ActualMaximum; // number of segment to draw a full circle // - decrease if you want get more speed // - increase if you want more detail // (making a public property of it would be a great idea) const double MaxSegments = 90.0; // compute the actual number of segments var segmentCount = (int)(MaxSegments * Math.Abs(angleAxis.EndAngle - angleAxis.StartAngle) / 360.0); var angleStep = (maxAngle - minAngle) / (segmentCount - 1); var points = new List<ScreenPoint>(); for (var i = 0; i < segmentCount; i++) { var angle = minAngle + (i * angleStep); points.Add(axis.Transform(x, angle, angleAxis)); } this.RenderContext.DrawLine(points, pen.Color, pen.Thickness, pen.ActualDashArray); }
/// <summary> /// Transforms the specified point to screen coordinates. /// </summary> /// <param name="x">The x value (for the current axis).</param> /// <param name="y">The y value.</param> /// <param name="yaxis">The y axis.</param> /// <returns>The transformed point.</returns> public virtual ScreenPoint Transform(double x, double y, Axis yaxis) { if (yaxis == null) { throw new NullReferenceException("Y axis should not be null when transforming."); } return new ScreenPoint(this.Transform(x), yaxis.Transform(y)); }
/// <summary> /// Renders the specified axis. /// </summary> /// <param name="axis">The axis.</param> /// <param name="pass">The pass.</param> public override void Render(Axis axis, int pass) { base.Render(axis, pass); bool drawAxisLine = true; double totalShift = axis.AxisDistance + axis.PositionTierMinShift; double tierSize = axis.PositionTierSize - this.Plot.AxisTierDistance; // store properties locally for performance double plotAreaLeft = this.Plot.PlotArea.Left; double plotAreaRight = this.Plot.PlotArea.Right; double plotAreaTop = this.Plot.PlotArea.Top; double plotAreaBottom = this.Plot.PlotArea.Bottom; // Axis position (x or y screen coordinate) double axisPosition = 0; double titlePosition = 0; switch (axis.Position) { case AxisPosition.Left: axisPosition = plotAreaLeft - totalShift; break; case AxisPosition.Right: axisPosition = plotAreaRight + totalShift; break; case AxisPosition.Top: axisPosition = plotAreaTop - totalShift; break; case AxisPosition.Bottom: axisPosition = plotAreaBottom + totalShift; break; } if (axis.PositionAtZeroCrossing) { var perpendicularAxis = axis.IsHorizontal() ? this.Plot.DefaultYAxis : this.Plot.DefaultXAxis; // the axis should be positioned at the origin of the perpendicular axis axisPosition = perpendicularAxis.Transform(0); var p0 = perpendicularAxis.Transform(perpendicularAxis.ActualMinimum); var p1 = perpendicularAxis.Transform(perpendicularAxis.ActualMaximum); // find the min/max positions var min = Math.Min(p0, p1); var max = Math.Max(p0, p1); // also consider the plot area var areaMin = axis.IsHorizontal() ? plotAreaTop : plotAreaLeft; var areaMax = axis.IsHorizontal() ? plotAreaBottom : plotAreaRight; min = Math.Max(min, areaMin); max = Math.Min(max, areaMax); if (axisPosition < min) { axisPosition = min; var borderThickness = axis.IsHorizontal() ? this.Plot.PlotAreaBorderThickness.Top : this.Plot.PlotAreaBorderThickness.Left; if (borderThickness > 0 && this.Plot.PlotAreaBorderColor.IsVisible()) { // there is already a line here... drawAxisLine = false; } } if (axisPosition > max) { axisPosition = max; var borderThickness = axis.IsHorizontal() ? this.Plot.PlotAreaBorderThickness.Bottom : this.Plot.PlotAreaBorderThickness.Right; if (borderThickness > 0 && this.Plot.PlotAreaBorderColor.IsVisible()) { // there is already a line here... drawAxisLine = false; } } } switch (axis.Position) { case AxisPosition.Left: titlePosition = axisPosition - tierSize; break; case AxisPosition.Right: titlePosition = axisPosition + tierSize; break; case AxisPosition.Top: titlePosition = axisPosition - tierSize; break; case AxisPosition.Bottom: titlePosition = axisPosition + tierSize; break; } if (pass == 0) { this.RenderMinorItems(axis, axisPosition); } if (pass == 1) { this.RenderMajorItems(axis, axisPosition, titlePosition, drawAxisLine); this.RenderAxisTitle(axis, titlePosition); } }
/// <summary> /// Transforms the specified point from screen space to data space. /// </summary> /// <param name="p">The point.</param> /// <param name="xaxis">The x axis.</param> /// <param name="yaxis">The y axis.</param> /// <returns>The data point.</returns> public static DataPoint InverseTransform(ScreenPoint p, Axis xaxis, Axis yaxis) { return xaxis.InverseTransform(p.x, p.y, yaxis); }
/// <summary> /// Inverse transform the specified screen point. /// </summary> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="yaxis">The y-axis.</param> /// <returns>The data point.</returns> public virtual DataPoint InverseTransform(double x, double y, Axis yaxis) { return new DataPoint(this.InverseTransform(x), yaxis != null ? yaxis.InverseTransform(y) : 0); }
/// <summary> /// Returns the angle (in radian) of the axis line in screen coordinate /// </summary> /// <param name="axis">The axis.</param> /// <param name="angleAxis">The angle axis.</param> /// <returns>The angle (in radians).</returns> private static double GetActualAngle(Axis axis, Axis angleAxis) { var a = axis.Transform(0, angleAxis.Angle, angleAxis); var b = axis.Transform(1, angleAxis.Angle, angleAxis); return Math.Atan2(b.y - a.y, b.x - a.x); }
/// <summary> /// Renders a tick, chooses the best implementation /// </summary> /// <param name="axis">The axis.</param> /// <param name="angleAxis">The angle axis.</param> /// <param name="x">The x-value.</param> /// <param name="pen">The pen.</param> private void RenderTick(Axis axis, AngleAxis angleAxis, double x, OxyPen pen) { var isFullCircle = Math.Abs(Math.Abs(angleAxis.EndAngle - angleAxis.StartAngle) - 360) < 1e-6; if (isFullCircle && pen.ActualDashArray == null) { this.RenderTickCircle(axis, angleAxis, x, pen); } else { this.RenderTickArc(axis, angleAxis, x, pen); } }
/// <summary> /// Renders major tick text /// </summary> /// <param name="axis">The axis.</param> /// <param name="x">The x-value.</param> /// <param name="angleAxis">The angle axis.</param> private void RenderTickText(Axis axis, double x, Axis angleAxis) { var actualAngle = GetActualAngle(axis, angleAxis); var dx = axis.AxisTickToLabelDistance * Math.Sin(actualAngle); var dy = -axis.AxisTickToLabelDistance * Math.Cos(actualAngle); HorizontalAlignment ha; VerticalAlignment va; GetTickTextAligment(actualAngle, out ha, out va); var pt = axis.Transform(x, angleAxis.Angle, angleAxis); pt = new ScreenPoint(pt.X + dx, pt.Y + dy); string text = axis.FormatValue(x); this.RenderContext.DrawMathText( pt, text, axis.ActualTextColor, axis.ActualFont, axis.ActualFontSize, axis.ActualFontWeight, axis.Angle, ha, va); }
/// <summary> /// Renders the axis title. /// </summary> /// <param name="axis">The axis.</param> /// <param name="titlePosition">The title position.</param> protected virtual void RenderAxisTitle(Axis axis, double titlePosition) { if (string.IsNullOrEmpty(axis.ActualTitle)) { return; } bool isHorizontal = axis.IsHorizontal(); OxySize? maxSize = null; if (axis.ClipTitle) { // Calculate the title clipping dimensions double screenLength = isHorizontal ? Math.Abs(axis.ScreenMax.X - axis.ScreenMin.X) : Math.Abs(axis.ScreenMax.Y - axis.ScreenMin.Y); maxSize = new OxySize(screenLength * axis.TitleClippingLength, double.MaxValue); } double angle = -90; var halign = HorizontalAlignment.Center; var valign = VerticalAlignment.Top; var lpt = this.GetAxisTitlePositionAndAlignment(axis, titlePosition, ref angle, ref halign, ref valign); this.RenderContext.DrawMathText( lpt, axis.ActualTitle, axis.ActualTitleColor, axis.ActualTitleFont, axis.ActualTitleFontSize, axis.ActualTitleFontWeight, angle, halign, valign, maxSize); }
/// <summary> /// Checks if the specified value is valid. /// </summary> /// <param name="v">The value.</param> /// <param name="yaxis">The y axis.</param> /// <returns>True if the value is valid.</returns> public virtual bool IsValidPoint(double v, Axis yaxis) { return !double.IsNaN(v) && !double.IsInfinity(v); }
/// <summary> /// Renders the major items. /// </summary> /// <param name="axis">The axis.</param> /// <param name="axisPosition">The axis position.</param> /// <param name="titlePosition">The title position.</param> /// <param name="drawAxisLine">Draw the axis line if set to <c>true</c>.</param> protected virtual void RenderMajorItems(Axis axis, double axisPosition, double titlePosition, bool drawAxisLine) { double eps = axis.ActualMinorStep * 1e-3; double actualMinimum = axis.ActualMinimum; double actualMaximum = axis.ActualMaximum; double plotAreaLeft = this.Plot.PlotArea.Left; double plotAreaRight = this.Plot.PlotArea.Right; double plotAreaTop = this.Plot.PlotArea.Top; double plotAreaBottom = this.Plot.PlotArea.Bottom; bool isHorizontal = axis.IsHorizontal(); double a0; double a1; var majorSegments = new List<ScreenPoint>(); var majorTickSegments = new List<ScreenPoint>(); this.GetTickPositions(axis, axis.TickStyle, axis.MajorTickSize, axis.Position, out a0, out a1); foreach (double value in this.MajorTickValues) { if (value < actualMinimum - eps || value > actualMaximum + eps) { continue; } if (axis.PositionAtZeroCrossing && Math.Abs(value) < eps) { continue; } double transformedValue = axis.Transform(value); if (isHorizontal) { SnapTo(plotAreaLeft, ref transformedValue); SnapTo(plotAreaRight, ref transformedValue); } else { SnapTo(plotAreaTop, ref transformedValue); SnapTo(plotAreaBottom, ref transformedValue); } if (this.MajorPen != null) { if (isHorizontal) { majorSegments.Add(new ScreenPoint(transformedValue, plotAreaTop)); majorSegments.Add(new ScreenPoint(transformedValue, plotAreaBottom)); } else { majorSegments.Add(new ScreenPoint(plotAreaLeft, transformedValue)); majorSegments.Add(new ScreenPoint(plotAreaRight, transformedValue)); } } if (axis.TickStyle != TickStyle.None && axis.MajorTickSize > 0) { if (isHorizontal) { majorTickSegments.Add(new ScreenPoint(transformedValue, axisPosition + a0)); majorTickSegments.Add(new ScreenPoint(transformedValue, axisPosition + a1)); } else { majorTickSegments.Add(new ScreenPoint(axisPosition + a0, transformedValue)); majorTickSegments.Add(new ScreenPoint(axisPosition + a1, transformedValue)); } } } // Render the axis labels (numbers or category names) foreach (double value in this.MajorLabelValues) { if (value < actualMinimum - eps || value > actualMaximum + eps) { continue; } if (axis.PositionAtZeroCrossing && Math.Abs(value) < eps) { continue; } double transformedValue = axis.Transform(value); if (isHorizontal) { SnapTo(plotAreaLeft, ref transformedValue); SnapTo(plotAreaRight, ref transformedValue); } else { SnapTo(plotAreaTop, ref transformedValue); SnapTo(plotAreaBottom, ref transformedValue); } var pt = new ScreenPoint(); var ha = HorizontalAlignment.Right; var va = VerticalAlignment.Middle; switch (axis.Position) { case AxisPosition.Left: pt = new ScreenPoint(axisPosition + a1 - axis.AxisTickToLabelDistance, transformedValue); this.GetRotatedAlignments( axis.Angle, HorizontalAlignment.Right, VerticalAlignment.Middle, out ha, out va); break; case AxisPosition.Right: pt = new ScreenPoint(axisPosition + a1 + axis.AxisTickToLabelDistance, transformedValue); this.GetRotatedAlignments( axis.Angle, HorizontalAlignment.Left, VerticalAlignment.Middle, out ha, out va); break; case AxisPosition.Top: pt = new ScreenPoint(transformedValue, axisPosition + a1 - axis.AxisTickToLabelDistance); this.GetRotatedAlignments( axis.Angle, HorizontalAlignment.Center, VerticalAlignment.Bottom, out ha, out va); break; case AxisPosition.Bottom: pt = new ScreenPoint(transformedValue, axisPosition + a1 + axis.AxisTickToLabelDistance); this.GetRotatedAlignments( axis.Angle, HorizontalAlignment.Center, VerticalAlignment.Top, out ha, out va); break; } string text = axis.FormatValue(value); this.RenderContext.DrawMathText( pt, text, axis.ActualTextColor, axis.ActualFont, axis.ActualFontSize, axis.ActualFontWeight, axis.Angle, ha, va); } // Draw the zero crossing line if (axis.PositionAtZeroCrossing && this.ZeroPen != null && this.IsWithin(0, actualMinimum, actualMaximum)) { double t0 = axis.Transform(0); if (isHorizontal) { this.RenderContext.DrawLine(t0, plotAreaTop, t0, plotAreaBottom, this.ZeroPen); } else { this.RenderContext.DrawLine(plotAreaLeft, t0, plotAreaRight, t0, this.ZeroPen); } } // Draw extra grid lines if (axis.ExtraGridlines != null && this.ExtraPen != null) { foreach (double value in axis.ExtraGridlines) { if (!this.IsWithin(value, actualMinimum, actualMaximum)) { continue; } double transformedValue = axis.Transform(value); if (isHorizontal) { this.RenderContext.DrawLine( transformedValue, plotAreaTop, transformedValue, plotAreaBottom, this.ExtraPen); } else { this.RenderContext.DrawLine( plotAreaLeft, transformedValue, plotAreaRight, transformedValue, this.ExtraPen); } } } if (drawAxisLine) { // Draw the axis line (across the tick marks) if (isHorizontal) { this.RenderContext.DrawLine( axis.Transform(actualMinimum), axisPosition, axis.Transform(actualMaximum), axisPosition, this.AxislinePen); } else { this.RenderContext.DrawLine( axisPosition, axis.Transform(actualMinimum), axisPosition, axis.Transform(actualMaximum), this.AxislinePen); } } if (this.MajorPen != null) { this.RenderContext.DrawLineSegments(majorSegments, this.MajorPen); } if (this.MajorTickPen != null) { this.RenderContext.DrawLineSegments(majorTickSegments, this.MajorTickPen); } }
/// <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); }
/// <summary> /// Renders the minor items. /// </summary> /// <param name="axis">The axis.</param> /// <param name="axisPosition">The axis position.</param> protected virtual void RenderMinorItems(Axis axis, double axisPosition) { double eps = axis.ActualMinorStep * 1e-3; double actualMinimum = axis.ActualMinimum; double actualMaximum = axis.ActualMaximum; double plotAreaLeft = this.Plot.PlotArea.Left; double plotAreaRight = this.Plot.PlotArea.Right; double plotAreaTop = this.Plot.PlotArea.Top; double plotAreaBottom = this.Plot.PlotArea.Bottom; bool isHorizontal = axis.IsHorizontal(); double a0; double a1; var minorSegments = new List<ScreenPoint>(); var minorTickSegments = new List<ScreenPoint>(); this.GetTickPositions(axis, axis.TickStyle, axis.MinorTickSize, axis.Position, out a0, out a1); foreach (double value in this.MinorTickValues) { if (value < actualMinimum - eps || value > actualMaximum + eps) { continue; } if (this.MajorTickValues.Contains(value)) { continue; } if (axis.PositionAtZeroCrossing && Math.Abs(value) < eps) { continue; } double transformedValue = axis.Transform(value); if (isHorizontal) { SnapTo(plotAreaLeft, ref transformedValue); SnapTo(plotAreaRight, ref transformedValue); } else { SnapTo(plotAreaTop, ref transformedValue); SnapTo(plotAreaBottom, ref transformedValue); } // Draw the minor grid line if (this.MinorPen != null) { if (isHorizontal) { minorSegments.Add(new ScreenPoint(transformedValue, plotAreaTop)); minorSegments.Add(new ScreenPoint(transformedValue, plotAreaBottom)); } else { if (transformedValue < plotAreaTop || transformedValue > plotAreaBottom) { } minorSegments.Add(new ScreenPoint(plotAreaLeft, transformedValue)); minorSegments.Add(new ScreenPoint(plotAreaRight, transformedValue)); } } // Draw the minor tick if (axis.TickStyle != TickStyle.None && axis.MinorTickSize > 0) { if (isHorizontal) { minorTickSegments.Add(new ScreenPoint(transformedValue, axisPosition + a0)); minorTickSegments.Add(new ScreenPoint(transformedValue, axisPosition + a1)); } else { minorTickSegments.Add(new ScreenPoint(axisPosition + a0, transformedValue)); minorTickSegments.Add(new ScreenPoint(axisPosition + a1, transformedValue)); } } } // Draw all the line segments); if (this.MinorPen != null) { this.RenderContext.DrawLineSegments(minorSegments, this.MinorPen); } if (this.MinorTickPen != null) { this.RenderContext.DrawLineSegments(minorTickSegments, this.MinorTickPen); } }
/// <summary> /// Inverse transforms the specified screen point. /// </summary> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="yaxis">The y-axis.</param> /// <returns>The data point.</returns> /// <exception cref="System.InvalidOperationException">Angle axis should always be the y-axis.</exception> public override DataPoint InverseTransform(double x, double y, Axis yaxis) { throw new InvalidOperationException("Angle axis should always be the y-axis."); }
/// <summary> /// Check if the data series is using the specified axis. /// </summary> /// <param name="axis">An axis.</param> /// <returns>True if the axis is in use.</returns> protected internal override bool IsUsing(Axis axis) { return false; }
/// <summary> /// Renders a tick by drawing an ellipse /// </summary> /// <param name="axis">The axis.</param> /// <param name="angleAxis">The angle axis.</param> /// <param name="x">The x-value.</param> /// <param name="pen">The pen.</param> private void RenderTickCircle(Axis axis, Axis angleAxis, double x, OxyPen pen) { var zero = angleAxis.Offset; var center = axis.Transform(axis.ActualMinimum, zero, angleAxis); var right = axis.Transform(x, zero, angleAxis).X; var radius = right - center.X; var width = radius * 2; var left = right - width; var top = center.Y - radius; var height = width; this.RenderContext.DrawEllipse(new OxyRect(left, top, width, height), OxyColors.Undefined, pen.Color, pen.Thickness); }
private Axis ConvertAxis(ChartAxisViewModel axis) { Axis newAxis = null; switch (axis.AxisType) { case (ChartAxisType.CategoryX): { newAxis = new CategoryAxis { LabelField = axis.ValueMemberPath, ItemsSource = axis.DataSource.Data }; break; } case (ChartAxisType.NumericX): case (ChartAxisType.NumericY): { newAxis = new LinearAxis { }; break; } case (ChartAxisType.CategoryDateTimeX): { var dtaxis = new DateTimeAxis { IntervalType = DateTimeIntervalType.Auto, }; var scale = axis.GetScaleForProperty(axis.ValueMemberPath); if (null != scale) { scale.PropertyChanged += (s, a) => { if (a.PropertyName != "Minimum" && a.PropertyName != "Maximum") { return; } var min = new DateTime((long)scale.Minimum); var max = new DateTime((long)scale.Maximum); var laxis = _plotModel.Axes.FirstOrDefault(x => x.Title == scale.Name) as DateTimeAxis; if (null == laxis) { return; } var delta = max - min; var lmax = 0.0d; var lmin = 0.0d; if (TimeSpan.FromSeconds(1) > delta) { laxis.IntervalType = DateTimeIntervalType.Seconds; } else if (TimeSpan.FromMinutes(1) > delta) { laxis.IntervalType = DateTimeIntervalType.Minutes; } else if (TimeSpan.FromHours(1) > delta) { laxis.IntervalType = DateTimeIntervalType.Hours; } else if (TimeSpan.FromDays(1) > delta) { laxis.IntervalType = DateTimeIntervalType.Days; } else if (TimeSpan.FromDays(30) > delta) { laxis.IntervalType = DateTimeIntervalType.Months; } else { laxis.IntervalType = DateTimeIntervalType.Auto; } //TODO: remove laxis.IntervalType = DateTimeIntervalType.Seconds; //laxis.Minimum = scale.Minimum; //laxis.Maximum = scale.Maximum; OnPlotModelChanged(); }; } newAxis = dtaxis; break; } } if (null == newAxis) { return(null); } switch (axis.AxisLocation) { case (AxisLocation.InsideBottom): case (AxisLocation.OutsideBottom): { newAxis.Position = AxisPosition.Bottom; break; } case (AxisLocation.InsideTop): case (AxisLocation.OutsideTop): { newAxis.Position = AxisPosition.Top; break; } case (AxisLocation.InsideLeft): case (AxisLocation.OutsideLeft): { newAxis.Position = AxisPosition.Left; break; } case (AxisLocation.InsideRight): case (AxisLocation.OutsideRight): { newAxis.Position = AxisPosition.Right; break; } default: { newAxis.Position = AxisPosition.None; break; } } newAxis.Title = axis.Name; var series = axis.AxisScaleDescriptors.FirstOrDefault(); if (null != series) { newAxis.Title = series.Scale.Name; } newAxis.Key = newAxis.Title; return(newAxis); }
/// <summary> /// Resets the specified axis. /// </summary> /// <param name="axis"> /// The axis. /// </param> public void Reset(Axis axis) { axis.Reset(); }
/// <summary> /// Zooms the specified axis to the specified values. /// </summary> /// <param name="axis"> /// The axis. /// </param> /// <param name="p1"> /// The new minimum value. /// </param> /// <param name="p2"> /// The new maximum value. /// </param> public void Zoom(Axis axis, double p1, double p2) { axis.Zoom(p1, p2); this.RefreshPlot(false); }
/// <summary> /// Zooms at the specified position. /// </summary> /// <param name="axis"> /// The axis. /// </param> /// <param name="factor"> /// The zoom factor. /// </param> /// <param name="x"> /// The position to zoom at. /// </param> public void ZoomAt(Axis axis, double factor, double x = double.NaN) { if (double.IsNaN(x)) { double sx = (axis.Transform(axis.ActualMaximum) + axis.Transform(axis.ActualMinimum)) * 0.5; x = axis.InverseTransform(sx); } axis.ZoomAt(factor, x); }
/// <summary> /// Gets the axes from a 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) { if (this.ActualModel != null) { this.ActualModel.GetAxesFromPoint(pt, out xaxis, out yaxis); } else { xaxis = null; yaxis = null; } }
/// <summary> /// Determines whether the specified item contains a valid point. /// </summary> /// <param name="item">The item.</param> /// <param name="xaxis">The x axis.</param> /// <param name="yaxis">The y axis.</param> /// <returns><c>true</c> if the point is valid; otherwise, <c>false</c> .</returns> public virtual bool IsValidPoint(BoxPlotItem item, Axis xaxis, Axis yaxis) { return !double.IsNaN(item.X) && !double.IsInfinity(item.X) && !item.Values.Any(double.IsNaN) && !item.Values.Any(double.IsInfinity) && (xaxis != null && xaxis.IsValidValue(item.X)) && (yaxis != null && item.Values.All(yaxis.IsValidValue)); }
/// <summary> /// Check if the data series is using the specified axis. /// </summary> /// <param name="axis">An axis which should be checked if used</param> /// <returns>True if the axis is in use.</returns> protected internal override bool IsUsing(Axis axis) { return this.XAxis == axis || this.YAxis == axis; }
/// <summary> /// Pans the specified axis. /// </summary> /// <param name="axis"> /// The axis. /// </param> /// <param name="ppt"> /// The previous point (screen coordinates). /// </param> /// <param name="cpt"> /// The current point (screen coordinates). /// </param> public void Pan(Axis axis, ScreenPoint ppt, ScreenPoint cpt) { axis.Pan(ppt, cpt); this.InvalidatePlot(false); }