internal double FromPreviousAxisState(double value, AxisTags direction, Chart chart) { //y = m * (x - x1) + y1 if (LastAxisMax == null) { return(0); } var p1 = new Point(); var p2 = new Point(); if (direction == AxisTags.Y) { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Y; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.Y + LastPlotArea.Height; } else { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Width + LastPlotArea.X; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.X; } var deltaX = p2.X - p1.X; // ReSharper disable once CompareOfFloatsByEqualityOperator var m = (p2.Y - p1.Y) / (deltaX == 0 ? double.MinValue : deltaX); return(m * (value - p1.X) + p1.Y); }
public double FromPreviousAxisState(double value, AxisTags source, ChartCore chart) { if (LastAxisMax == null) { return(0); } var p1 = new CorePoint(); var p2 = new CorePoint(); if (source == AxisTags.Y) { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Top; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.Top + LastPlotArea.Height; } else { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Width + LastPlotArea.Left; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.Left; } var deltaX = p2.X - p1.X; // ReSharper disable once CompareOfFloatsByEqualityOperator var m = (p2.Y - p1.Y) / (deltaX == 0 ? double.MinValue : deltaX); var d = m * (value - p1.X) + p1.Y; return(d); }
public static double FromDrawMargin(double value, AxisTags axis, Chart chart) { var o = axis == AxisTags.X ? chart.PlotArea.X : chart.PlotArea.Y; var of = axis == AxisTags.X ? chart.XOffset : chart.YOffset; return FromPlotArea(value, axis, chart) - o + of; }
public void Place(Chart chart, AxisTags direction, int axisIndex, Axis axis) { _anSpeed = axis.AnimationsSpeed ?? chart.AnimationsSpeed; switch (State) { case SeparationState.Remove: MoveFromCurrentAx(chart, direction, axisIndex, axis); Remove(chart, axis); break; case SeparationState.Keep: UnanimatedPlace(chart, direction, axisIndex, axis); MoveFromPreviousAx(chart, direction, axisIndex, axis); if (IsNew) { FadeIn(chart, axis); } break; case SeparationState.InitialAdd: UnanimatedPlace(chart, direction, axisIndex, axis); break; default: throw new ArgumentOutOfRangeException(); } }
internal CoreSize PrepareChart(AxisTags source, ChartCore chart) { if (!(Math.Abs(MaxLimit - MinLimit) > S * .01) || !ShowLabels) { return(new CoreSize()); } CalculateSeparator(chart, source); var f = GetFormatter(); var biggest = new CoreSize(0, 0); var tolerance = S / 10; for (var i = MinLimit; i <= MaxLimit - (EvaluatesUnitWidth ? 1 : 0); i += S) { SeparatorElementCore asc; var key = Math.Round(i / tolerance) * tolerance; if (!Cache.TryGetValue(key, out asc)) { asc = new SeparatorElementCore { IsNew = true }; Cache[key] = asc; } else { asc.IsNew = false; } View.RenderSeparator(asc, Chart); asc.Key = key; asc.Value = i; asc.IsActive = true; var labelsSize = asc.View.UpdateLabel(f(i), this); biggest.Width = labelsSize.Width > biggest.Width ? labelsSize.Width : biggest.Width; biggest.Height = labelsSize.Height > biggest.Height ? labelsSize.Height : biggest.Height; if (LastAxisMax == null) { asc.State = SeparationState.InitialAdd; continue; } asc.State = SeparationState.Keep; } #if DEBUG Debug.WriteLine("Axis.Separations: " + Cache.Count); #endif return(biggest); }
public static double FromDrawMargin(double value, AxisTags axis, Chart chart) { var o = axis == AxisTags.X ? chart.PlotArea.X : chart.PlotArea.Y; var of = axis == AxisTags.X ? chart.XOffset : chart.YOffset; return(FromPlotArea(value, axis, chart) - o + of); }
public void Move(ChartCore chart, AxisCore axis, AxisTags direction, int axisIndex, double toLabel, double toLine, double tab) { if (direction == AxisTags.Y) { Line.BeginAnimation(Line.X1Property, new DoubleAnimation(chart.DrawMargin.Left, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(chart.DrawMargin.Left + chart.DrawMargin.Width, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(toLine, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(toLine, chart.View.AnimationsSpeed)); TextBlock.BeginAnimation(Canvas.TopProperty, new DoubleAnimation(toLabel, chart.View.AnimationsSpeed)); TextBlock.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(tab, chart.View.AnimationsSpeed)); } else { Line.BeginAnimation(Line.X1Property, new DoubleAnimation(toLine, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(toLine, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(chart.DrawMargin.Top, chart.View.AnimationsSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(chart.DrawMargin.Top + chart.DrawMargin.Height, chart.View.AnimationsSpeed)); TextBlock.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(toLabel, chart.View.AnimationsSpeed)); TextBlock.BeginAnimation(Canvas.TopProperty, new DoubleAnimation(tab, chart.View.AnimationsSpeed)); } }
public AxisSectionCore AsCoreElement(AxisCore axis, AxisTags source) { var model = new AxisSectionCore(this, axis.Chart); model.View.Model = model; return(model); }
public static double ToPlotArea(double value, AxisTags source, ChartCore chart, AxisCore axis) { var p1 = new CorePoint(); var p2 = new CorePoint(); if (source == AxisTags.Y) { p1.X = axis.MaxLimit; p1.Y = chart.DrawMargin.Top; p2.X = axis.MinLimit; p2.Y = chart.DrawMargin.Top + chart.DrawMargin.Height; } else { p1.X = axis.MaxLimit; p1.Y = chart.DrawMargin.Width + chart.DrawMargin.Left; p2.X = axis.MinLimit; p2.Y = chart.DrawMargin.Left; } var deltaX = p2.X - p1.X; // ReSharper disable once CompareOfFloatsByEqualityOperator var m = (p2.Y - p1.Y) / (deltaX == 0 ? double.MinValue : deltaX); return(m * (value - p1.X) + p1.Y); }
public static double ToDrawMargin(double value, AxisTags source, ChartCore chart, AxisCore axis) { var o = source == AxisTags.X ? chart.DrawMargin.Left : chart.DrawMargin.Top; return(ToPlotArea(value, source, chart, axis) - o); }
public static double FromDrawMargin(double value, AxisTags source, Chart chart, int axis = 0) { //var o = axis == AxisTags.X // ? Canvas.GetLeft(chart.DrawMargin) // : Canvas.GetTop(chart.DrawMargin); //var of = axis == AxisTags.X ? chart.XOffset : chart.YOffset; return(FromPlotArea(value, source, chart, axis));//FromPlotArea(value, axis, chart) - o + of; }
public static double FromDrawMargin(double value, AxisTags axis, Chart chart) { var o = axis == AxisTags.X ? Canvas.GetLeft(chart.DrawMargin) : Canvas.GetTop(chart.DrawMargin); var of = axis == AxisTags.X ? chart.XOffset : chart.YOffset; return(FromPlotArea(value, axis, chart) - o + of); }
public static double FromDrawMargin(double value, AxisTags source, ChartCore chart, int axis = 0) { var o = source == AxisTags.X ? chart.DrawMargin.Left : chart.DrawMargin.Top; //var of = axis == AxisTags.X ? chart.XOffset : chart.YOffset; return(FromPlotArea(value, source, chart, axis)); //- o; //FromPlotArea(value, axis, chart) - o + of; }
/// <summary> /// Axis Y: is Horizontal axis, Axis X: Vertical, confusing but this is how it is! /// </summary> /// <param name="range"></param> /// <param name="axis"></param> /// <returns></returns> protected double CalculateSeparator(double range, AxisTags axis) { //based on: //http://stackoverflow.com/questions/361681/algorithm-for-nice-grid-line-intervals-on-a-graph var m = axis == AxisTags.Y ? Min.Y : Min.X; if (Math.Abs(range) < m * .01) { range = m; } var ft = axis == AxisTags.Y ? new FormattedText( "A label", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(PrimaryAxis.FontFamily, PrimaryAxis.FontStyle, PrimaryAxis.FontWeight, PrimaryAxis.FontStretch), PrimaryAxis.FontSize, Brushes.Black) : new FormattedText( "A label", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(SecondaryAxis.FontFamily, SecondaryAxis.FontStyle, SecondaryAxis.FontWeight, SecondaryAxis.FontStretch), SecondaryAxis.FontSize, Brushes.Black); var separations = axis == AxisTags.Y ? Math.Round(PlotArea.Height / ((ft.Height) * PrimaryAxis.CleanFactor), 0) : Math.Round(PlotArea.Width / ((ft.Width) * SecondaryAxis.CleanFactor), 0); separations = separations < 2 ? 2 : separations; var minimum = range / separations; var magnitude = Math.Pow(10, Math.Floor(Math.Log(minimum) / Math.Log(10))); var residual = minimum / magnitude; double tick; if (residual > 5) { tick = 10 * magnitude; } else if (residual > 2) { tick = 5 * magnitude; } else if (residual > 1) { tick = 2 * magnitude; } else { tick = magnitude; } return(tick); }
internal void CalculateSeparator(Chart chart, AxisTags source) { if (Separator.Step != null) { S = Separator.Step ?? 1; return; } var range = MaxLimit - MinLimit; range = range <= 0 ? 1 : range; var ft = new FormattedText( "A label", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Brushes.Black); var separations = source == AxisTags.Y ? Math.Round(chart.DrawMargin.Height / ((ft.Height) * CleanFactor), 0) : Math.Round(chart.DrawMargin.Width / ((ft.Width) * CleanFactor), 0); separations = separations < 2 ? 2 : separations; var minimum = range / separations; var magnitude = Math.Pow(10, Math.Floor(Math.Log(minimum) / Math.Log(10))); var residual = minimum / magnitude; double tick; if (residual > 5) { tick = 10 * magnitude; } else if (residual > 2) { tick = 5 * magnitude; } else if (residual > 1) { tick = 2 * magnitude; } else { tick = magnitude; } S = tick; if (Labels != null) { S = S < 1 ? 1 : S; } }
public static double FromPlotArea(double value, AxisTags axis, Chart chart) { var p1 = axis == AxisTags.X ? new Point(chart.Max.X, chart.PlotArea.Width + chart.PlotArea.X) : new Point(chart.Max.Y, chart.PlotArea.Y); var p2 = axis == AxisTags.X ? new Point(chart.Min.X, chart.PlotArea.X) : new Point(chart.Min.Y, chart.PlotArea.Y + chart.PlotArea.Height); var m = (p2.Y - p1.Y) / (p2.X - p1.X); return (value + m*p1.X - p1.Y)/m; }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="axis"></param> /// <param name="chart"></param> /// <returns></returns> public static double ToPlotArea(double value, AxisTags axis, Chart chart) { //y = m (x - x1) + y1 var p1 = axis == AxisTags.X ? new Point(chart.Max.X, chart.PlotArea.Width + chart.PlotArea.X) : new Point(chart.Max.Y, chart.PlotArea.Y); var p2 = axis == AxisTags.X ? new Point(chart.Min.X, chart.PlotArea.X) : new Point(chart.Min.Y, chart.PlotArea.Y + chart.PlotArea.Height); var m = (p2.Y - p1.Y) / (p2.X - p1.X); return m * (value - p1.X) + p1.Y; }
public static double FromPlotArea(double value, AxisTags axis, Chart chart) { var p1 = axis == AxisTags.X ? new Point(chart.Max.X, chart.PlotArea.Width + chart.PlotArea.X) : new Point(chart.Max.Y, chart.PlotArea.Y); var p2 = axis == AxisTags.X ? new Point(chart.Min.X, chart.PlotArea.X) : new Point(chart.Min.Y, chart.PlotArea.Y + chart.PlotArea.Height); var m = (p2.Y - p1.Y) / (p2.X - p1.X); return((value + m * p1.X - p1.Y) / m); }
public static double ToDrawMargin(double value, AxisTags source, Chart chart, int axis = 0) { var o = source == AxisTags.X ? Canvas.GetLeft(chart.DrawMargin) : Canvas.GetTop(chart.DrawMargin); var of = source == AxisTags.X ? chart.XOffset : chart.YOffset; return(ToPlotArea(value, source, chart, axis) - o + of); }
private void MoveFromPreviousAx(Chart chart, AxisTags direction, int axisIndex, Axis axis) { if (axis.DisableAnimations || chart.DisableAnimations) { return; } var i = chart.ToPlotArea(Value, direction, axisIndex); if (direction == AxisTags.Y) { var y = IsNew ? axis.FromPreviousAxisState(Value, direction, chart) : Line.Y1; Line.BeginAnimation(Line.X1Property, new DoubleAnimation(Line.X1, Canvas.GetLeft(chart.DrawMargin), _anSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(Line.X2, Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width, _anSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(y, i, _anSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(y, i, _anSpeed)); var hh = axis.IsMerged ? (i + TextBlock.ActualHeight > Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height ? +TextBlock.ActualHeight : 0) : TextBlock.ActualHeight * .5; TextBlock.BeginAnimation(Canvas.TopProperty, new DoubleAnimation(y - hh + axis.UnitWidth * .5, i - hh + axis.UnitWidth * .5, _anSpeed)); } else { var x = IsNew ? axis.FromPreviousAxisState(Value, direction, chart) : Line.X1; Line.BeginAnimation(Line.X1Property, new DoubleAnimation(x, i, _anSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(x, i, _anSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(Line.Y1, Canvas.GetTop(chart.DrawMargin), _anSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(Line.Y2, Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height, _anSpeed)); var hw = axis.IsMerged ? (i + TextBlock.ActualWidth > Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width ? TextBlock.ActualWidth + 2 : -2) : TextBlock.ActualWidth * .5; TextBlock.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(x - hw + axis.UnitWidth * .5, i - hw + axis.UnitWidth * .5, _anSpeed)); } }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="axis"></param> /// <param name="chart"></param> /// <returns></returns> public static double ToPlotArea(double value, AxisTags axis, Chart chart) { //y = m (x - x1) + y1 var p1 = axis == AxisTags.X ? new Point(chart.Max.X, chart.PlotArea.Width + chart.PlotArea.X) : new Point(chart.Max.Y, chart.PlotArea.Y); var p2 = axis == AxisTags.X ? new Point(chart.Min.X, chart.PlotArea.X) : new Point(chart.Min.Y, chart.PlotArea.Y + chart.PlotArea.Height); var m = (p2.Y - p1.Y) / (p2.X - p1.X); return(m * (value - p1.X) + p1.Y); }
public static double GetUnitWidth(AxisTags source, ChartCore chart, int axis = 0) { double min; if (source == AxisTags.Y) { min = chart.AxisY[axis].MinLimit; return(ToDrawMargin(min, AxisTags.Y, chart, axis) - ToDrawMargin(min + 1, AxisTags.Y, chart, axis)); } min = chart.AxisX[axis].MinLimit; return(ToDrawMargin(min + 1, AxisTags.X, chart, axis) - ToDrawMargin(min, AxisTags.X, chart, axis)); }
public static double GetUnitWidth(AxisTags source, ChartCore chart, AxisCore axis) { double min; if (source == AxisTags.Y) { min = axis.MinLimit; return(ToDrawMargin(min, AxisTags.Y, chart, axis) - ToDrawMargin(min + 1, AxisTags.Y, chart, axis)); } min = axis.MinLimit; return(ToDrawMargin(min + 1, AxisTags.X, chart, axis) - ToDrawMargin(min, AxisTags.X, chart, axis)); }
private void UnanimatedPlace(IChartView chart, AxisTags direction, int axisIndex, Axis axis) { var i = ChartFunctions.ToPlotArea(Model.Value, direction, chart.Model, axisIndex); var uw = new CorePoint( axis.Model.EvaluatesUnitWidth ? ChartFunctions.GetUnitWidth(AxisTags.X, chart.Model, axis.Model) / 2 : 0, axis.Model.EvaluatesUnitWidth ? ChartFunctions.GetUnitWidth(AxisTags.Y, chart.Model, axis.Model) / 2 : 0); if (direction == AxisTags.Y) { Line.X1 = chart.Model.DrawMargin.Left; Line.X2 = chart.Model.DrawMargin.Left + chart.Model.DrawMargin.Width; Line.Y1 = i; Line.Y2 = i; var topM = axis.IsMerged ? (i + TextBlock.ActualHeight > chart.Model.DrawMargin.Top + chart.Model.DrawMargin.Height ? +TextBlock.ActualHeight : 0) : TextBlock.ActualHeight * .5; var leftM = axis.IsMerged ? TextBlock.ActualWidth + 10 : -2; Canvas.SetTop(TextBlock, i + uw.Y - topM); Canvas.SetLeft(TextBlock, axis.Position == AxisPosition.LeftBottom ? axis.LabelsReference - TextBlock.ActualWidth + leftM : axis.LabelsReference - leftM); } else { Line.X1 = i; Line.X2 = i; Line.Y1 = chart.Model.DrawMargin.Top; Line.Y2 = chart.Model.DrawMargin.Top + chart.Model.DrawMargin.Height; var left = axis.IsMerged ? (i + TextBlock.ActualWidth > chart.Model.DrawMargin.Left + chart.Model.DrawMargin.Width ? TextBlock.ActualWidth + 2 : -2) : TextBlock.ActualWidth * .5; var top = axis.IsMerged ? TextBlock.ActualHeight : 0; Canvas.SetLeft(TextBlock, i + uw.X - left); Canvas.SetTop(TextBlock, axis.Position == AxisPosition.LeftBottom ? axis.LabelsReference - top : axis.LabelsReference); } }
internal void CalculateSeparator(ChartCore chart, AxisTags source) { var range = MaxLimit - MinLimit; range = range <= 0 ? 1 : range; //ToDO: Improve this according to current labels! var separations = source == AxisTags.Y ? Math.Round(chart.DrawMargin.Height / ((12) * CleanFactor), 0) // at least 3 font 12 labels per separator. : Math.Round(chart.DrawMargin.Width / (50 * CleanFactor), 0); // at least 150 pixels per separator. separations = separations < 2 ? 2 : separations; var minimum = range / separations; Magnitude = Math.Pow(10, Math.Floor(Math.Log(minimum) / Math.Log(10))); if (Separator.Step != null) { S = Separator.Step ?? 1; return; } var residual = minimum / Magnitude; double tick; if (residual > 5) { tick = 10 * Magnitude; } else if (residual > 2) { tick = 5 * Magnitude; } else if (residual > 1) { tick = 2 * Magnitude; } else { tick = Magnitude; } S = tick; if (Labels != null) { S = S < 1 ? 1 : S; } }
public LabelEvaluation UpdateLabel(string text, AxisCore axis, AxisTags source) { TextBlock.Text = text; TextBlock.UpdateLayout(); var transform = new LabelEvaluation(axis.View.LabelsRotation, TextBlock.ActualWidth, TextBlock.ActualHeight, axis, source); TextBlock.RenderTransform = Math.Abs(transform.LabelAngle) > 1 ? new RotateTransform(transform.LabelAngle) : null; LabelModel = transform; return(transform); }
private void UnanimatedPlace(Chart chart, AxisTags direction, int axisIndex, Axis axis) { var i = chart.ToPlotArea(Value, direction, axisIndex); Line.X1 = direction == AxisTags.Y ? Canvas.GetLeft(chart.DrawMargin) : i; Line.X2 = direction == AxisTags.Y ? Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width : i; Line.Y1 = direction == AxisTags.Y ? i : Canvas.GetTop(chart.DrawMargin); Line.Y2 = direction == AxisTags.Y ? i : Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height; if (direction == AxisTags.Y) { var topM = axis.IsMerged ? (i + TextBlock.ActualHeight > Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height ? +TextBlock.ActualHeight : 0) : TextBlock.ActualHeight * .5; var leftM = axis.IsMerged ? TextBlock.ActualWidth + 10 : -2; Canvas.SetTop(TextBlock, i - topM); Canvas.SetLeft(TextBlock, axis.Position == CoreComponents.AxisPosition.LeftBottom ? axis.LabelsReference - TextBlock.ActualWidth + leftM : axis.LabelsReference - leftM); } else { var left = axis.IsMerged ? (i + TextBlock.ActualWidth > Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width ? TextBlock.ActualWidth + 2 : -2) : TextBlock.ActualWidth * .5; var top = axis.IsMerged ? TextBlock.ActualHeight : 0; Canvas.SetLeft(TextBlock, i - left); Canvas.SetTop(TextBlock, axis.Position == CoreComponents.AxisPosition.LeftBottom ? axis.LabelsReference - top : axis.LabelsReference); } }
public ChartCursor(Chart chart, AxisTags tag) { _chart = chart; _tag = tag; Shape = new Rectangle { StrokeThickness = 2, Stroke = new SolidColorBrush(Color.FromRgb(55, 71, 79)), Fill = new SolidColorBrush(Color.FromRgb(55, 71, 79)) { Opacity = .3 } }; Canvas.SetLeft(Shape, 0d); Canvas.SetTop(Shape, 0d); }
internal void UpdateSeparations(AxisTags direction, Chart chart, int axisPosition) { foreach (var separation in Separations.Values.ToArray()) { if (!separation.IsActive) { separation.State = SeparationState.Remove; Separations.Remove(separation.Key); } separation.Place(chart, direction, axisPosition, this); separation.IsActive = false; } LastAxisMax = MaxLimit; LastAxisMin = MinLimit; LastPlotArea = new Rect(Canvas.GetLeft(chart.DrawMargin), Canvas.GetTop(chart.DrawMargin), chart.DrawMargin.Width, chart.DrawMargin.Height); }
internal void UpdateSeparators(AxisTags source, ChartCore chart, int axisPosition) { foreach (var element in Cache.Values.ToArray()) { if (!element.IsActive) { element.State = SeparationState.Remove; Cache.Remove(element.Key); } element.View.UpdateLine(source, chart, axisPosition, this); element.IsActive = false; } LastAxisMax = MaxLimit; LastAxisMin = MinLimit; LastPlotArea = new CoreRectangle(chart.DrawMargin.Left, chart.DrawMargin.Top, chart.DrawMargin.Width, chart.DrawMargin.Height); }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="source"></param> /// <param name="chart"></param> /// <param name="axis"></param> /// <returns></returns> public static double ToPlotArea(double value, AxisTags source, Chart chart, int axis = 0) { //y = m * (x - x1) + y1 var p1 = new Point(); var p2 = new Point(); if (source == AxisTags.Y) { if (axis >= chart.AxisY.Count) { throw new Exception("There is not a valid Y axis at position " + axis); } var ax = chart.AxisY[axis]; p1.X = ax.MaxLimit; p1.Y = Canvas.GetTop(chart.DrawMargin); p2.X = ax.MinLimit; p2.Y = Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height; } else { if (axis >= chart.AxisX.Count) { throw new Exception("There is not a valid X axis at position " + axis); } var ax = chart.AxisX[axis]; p1.X = ax.MaxLimit; p1.Y = chart.DrawMargin.Width + Canvas.GetLeft(chart.DrawMargin); p2.X = ax.MinLimit; p2.Y = Canvas.GetLeft(chart.DrawMargin); } var deltaX = p2.X - p1.X; // ReSharper disable once CompareOfFloatsByEqualityOperator var m = (p2.Y - p1.Y) / (deltaX == 0 ? double.MinValue : deltaX); return(m * (value - p1.X) + p1.Y); }
private void MoveFromCurrentAx(Chart chart, AxisTags direction, int axisIndex, Axis axis) { var i = chart.ToPlotArea(Value, direction, axisIndex); if (direction == AxisTags.Y) { Line.BeginAnimation(Line.X1Property, new DoubleAnimation(Line.X1, Canvas.GetLeft(chart.DrawMargin), _anSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(Line.X2, Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width, _anSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(Line.Y1, i, _anSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(Line.Y2, i, _anSpeed)); var hh = axis.IsMerged ? (i + TextBlock.ActualHeight > Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height ? +TextBlock.ActualHeight : 0) : TextBlock.ActualHeight * .5; TextBlock.BeginAnimation(Canvas.TopProperty, new DoubleAnimation(Line.Y1 - hh, i - hh, _anSpeed)); } else { Line.BeginAnimation(Line.X1Property, new DoubleAnimation(Line.X1, i, _anSpeed)); Line.BeginAnimation(Line.X2Property, new DoubleAnimation(Line.X2, i, _anSpeed)); Line.BeginAnimation(Line.Y1Property, new DoubleAnimation(Line.Y1, Canvas.GetTop(chart.DrawMargin), _anSpeed)); Line.BeginAnimation(Line.Y2Property, new DoubleAnimation(Line.Y2, Canvas.GetTop(chart.DrawMargin) + chart.DrawMargin.Height, _anSpeed)); var hw = axis.IsMerged ? (i + TextBlock.ActualWidth > Canvas.GetLeft(chart.DrawMargin) + chart.DrawMargin.Width ? TextBlock.ActualWidth + 2 : -2) : TextBlock.ActualWidth * .5; TextBlock.BeginAnimation(Canvas.LeftProperty, new DoubleAnimation(Line.X1 - hw, i - hw, _anSpeed)); } }
public void UpdateLine(AxisTags source, ChartCore chart, int axisIndex, AxisCore axis) { var wpfChart = chart.View as Chart; var wpfAxis = axis.View as Axis; if (wpfChart == null || wpfAxis == null) { return; } switch (Model.State) { case SeparationState.Remove: if (!chart.View.DisableAnimations && !axis.DisableAnimations) { MoveFromCurrentAx(wpfChart, source, axisIndex, wpfAxis); } Remove(wpfChart, wpfAxis); break; case SeparationState.Keep: UnanimatedPlace(wpfChart, source, axisIndex, wpfAxis); if (!chart.View.DisableAnimations && !axis.DisableAnimations) { MoveFromPreviousAx(wpfChart, source, axisIndex, wpfAxis); } if (Model.IsNew) { FadeIn(wpfChart, wpfAxis); } break; case SeparationState.InitialAdd: UnanimatedPlace(wpfChart, source, axisIndex, wpfAxis); break; default: throw new ArgumentOutOfRangeException(); } }
public double FromDrawMargin(double value, AxisTags axis) { return Methods.FromDrawMargin(value, axis, this); }
internal CoreMargin PrepareChart(AxisTags source, ChartCore chart) { if (!(Math.Abs(MaxLimit - MinLimit) > S*.01) || !ShowLabels) return new CoreMargin(); CalculateSeparator(chart, source); var f = GetFormatter(); var currentMargin = new CoreMargin(); var tolerance = S/10; InitializeGarbageCollector(); for (var i = MinLimit; i <= MaxLimit - (EvaluatesUnitWidth ? 1 : 0); i += S) { SeparatorElementCore asc; var key = Math.Round(i/tolerance)*tolerance; if (!Cache.TryGetValue(key, out asc)) { asc = new SeparatorElementCore {IsNew = true}; Cache[key] = asc; } else { asc.IsNew = false; } View.RenderSeparator(asc, Chart); asc.Key = key; asc.Value = i; asc.GarbageCollectorIndex = GarbageCollectorIndex; var labelsMargin = asc.View.UpdateLabel(f(i), this, source); currentMargin.Width = labelsMargin.TakenWidth > currentMargin.Width ? labelsMargin.TakenWidth : currentMargin.Width; currentMargin.Height = labelsMargin.TakenHeight > currentMargin.Height ? labelsMargin.TakenHeight : currentMargin.Height; currentMargin.Left = labelsMargin.Left > currentMargin.Left ? labelsMargin.Left : currentMargin.Left; currentMargin.Right = labelsMargin.Right > currentMargin.Right ? labelsMargin.Right : currentMargin.Right; currentMargin.Top = labelsMargin.Top > currentMargin.Top ? labelsMargin.Top : currentMargin.Top; currentMargin.Bottom = labelsMargin.Bottom > currentMargin.Bottom ? labelsMargin.Bottom : currentMargin.Bottom; if (LastAxisMax == null) { asc.State = SeparationState.InitialAdd; continue; } asc.State = SeparationState.Keep; } return currentMargin; }
protected void ConfigureYAsIndexed() { AxisY.ShowLabels = AxisY.Labels != null; var m = (AxisY.Labels ?? new List<string> {""}).OrderByDescending(x => x.Length); var longestYLabel = new FormattedText(m.First(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(AxisY.FontFamily, AxisY.FontStyle, AxisY.FontWeight, AxisY.FontStretch), AxisY.FontSize, Brushes.Black); AxisY.Separator.Step = (longestYLabel.Width*Max.Y)*1.25 > PlotArea.Width ? null : (int?) 1; if (AxisY.Separator.Step != null) S.Y = (int) AxisY.Separator.Step; if (Zooming) ZoomingAxis = AxisTags.Y; }
internal double FromPreviousState(double value, AxisTags source, ChartCore chart) { if (LastAxisMax == null) return 0; var p1 = new CorePoint(); var p2 = new CorePoint(); if (source == AxisTags.Y) { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Top; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.Top + LastPlotArea.Height; } else { p1.X = LastAxisMax ?? 0; p1.Y = LastPlotArea.Width + LastPlotArea.Left; p2.X = LastAxisMin ?? 0; p2.Y = LastPlotArea.Left; } var deltaX = p2.X - p1.X; // ReSharper disable once CompareOfFloatsByEqualityOperator var m = (p2.Y - p1.Y) / (deltaX == 0 ? double.MinValue : deltaX); var d = m * (value - p1.X) + p1.Y; return d; }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="axis"></param> /// <returns></returns> public double ToPlotArea(double value, AxisTags axis) { return EnsureDouble(Methods.ToPlotArea(value, axis, this)); }
internal void UpdateSeparators(AxisTags source, ChartCore chart, int axisIndex) { foreach (var element in Cache.Values.ToArray()) { if (element.GarbageCollectorIndex < GarbageCollectorIndex) { element.State = SeparationState.Remove; Cache.Remove(element.Key); } var toLine = ChartFunctions.ToPlotArea(element.Value, source, chart, axisIndex); var direction = source == AxisTags.X ? 1 : -1; toLine += EvaluatesUnitWidth ? direction*ChartFunctions.GetUnitWidth(source, chart, this)/2 : 0; var toLabel = toLine + element.View.LabelModel.GetOffsetBySource(source); if (IsMerged) { const double padding = 4; if (source == AxisTags.Y) { if (toLabel + element.View.LabelModel.ActualHeight > chart.DrawMargin.Top + chart.DrawMargin.Height) toLabel -= element.View.LabelModel.ActualHeight + padding; } else { if (toLabel + element.View.LabelModel.ActualWidth > chart.DrawMargin.Left + chart.DrawMargin.Width) toLabel -= element.View.LabelModel.ActualWidth + padding; } } var labelTab = Tab; labelTab += element.View.LabelModel.GetOffsetBySource(source.Invert()); switch (element.State) { case SeparationState.Remove: if (!chart.View.DisableAnimations && !View.DisableAnimations) { element.View.Move(chart, this, source, axisIndex, toLabel, toLine, labelTab); element.View.FadeOutAndRemove(chart); } else { element.View.Remove(chart); } break; case SeparationState.Keep: if (!chart.View.DisableAnimations && !View.DisableAnimations) { if (element.IsNew) { var toLinePrevious = FromPreviousState(element.Value, source, chart); toLinePrevious += EvaluatesUnitWidth ? ChartFunctions.GetUnitWidth(source, chart, this) / 2 : 0; var toLabelPrevious = toLinePrevious + element.View.LabelModel.GetOffsetBySource(source); element.View.Place(chart, this, source, axisIndex, toLabelPrevious, toLinePrevious, labelTab); element.View.FadeIn(this, chart); } element.View.Move(chart, this, source, axisIndex, toLabel, toLine, labelTab); } else { element.View.Place(chart, this, source, axisIndex, toLabel, toLine, labelTab); } break; case SeparationState.InitialAdd: element.View.Place(chart, this, source, axisIndex, toLabel, toLine, labelTab); break; default: throw new ArgumentOutOfRangeException(); } } LastAxisMax = MaxLimit; LastAxisMin = MinLimit; LastPlotArea = new CoreRectangle(chart.DrawMargin.Left, chart.DrawMargin.Top, chart.DrawMargin.Width, chart.DrawMargin.Height); #if DEBUG Debug.WriteLine("Axis.Separations: " + Cache.Count); #endif }
public double GetOffsetBySource(AxisTags source) { return source == AxisTags.X ? XOffset : YOffset; }
internal double CalculateSeparator(double range, AxisTags axis) { //based on: //http://stackoverflow.com/questions/361681/algorithm-for-nice-grid-line-intervals-on-a-graph range = range <= 0 ? 1 : range; var ft = axis == AxisTags.Y ? new FormattedText( "A label", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(AxisY.FontFamily, AxisY.FontStyle, AxisY.FontWeight, AxisY.FontStretch), AxisY.FontSize, Brushes.Black) : new FormattedText( "A label", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(AxisX.FontFamily, AxisX.FontStyle, AxisX.FontWeight, AxisX.FontStretch), AxisX.FontSize, Brushes.Black); var separations = axis == AxisTags.Y ? Math.Round(PlotArea.Height / ((ft.Height) * AxisY.CleanFactor), 0) : Math.Round(PlotArea.Width / ((ft.Width) * AxisX.CleanFactor), 0); separations = separations < 2 ? 2 : separations; var minimum = range / separations; var magnitude = Math.Pow(10, Math.Floor(Math.Log(minimum) / Math.Log(10))); var residual = minimum / magnitude; double tick; if (residual > 5) tick = 10 * magnitude; else if (residual > 2) tick = 5 * magnitude; else if (residual > 1) tick = 2 * magnitude; else tick = magnitude; return tick; }
internal void CalculateSeparator(ChartCore chart, AxisTags source) { var range = MaxLimit - MinLimit; range = range <= 0 ? 1 : range; //ToDO: Improve this according to current labels! var separations = source == AxisTags.Y ? Math.Round(chart.DrawMargin.Height/((12)*CleanFactor), 0) // at least 3 font 12 labels per separator. : Math.Round(chart.DrawMargin.Width/(50*CleanFactor), 0); // at least 150 pixels per separator. separations = separations < 2 ? 2 : separations; var minimum = range/separations; Magnitude = Math.Pow(10, Math.Floor(Math.Log(minimum)/Math.Log(10))); if (Separator.Step != null) { S = Separator.Step ?? 1; return; } var residual = minimum/Magnitude; double tick; if (residual > 5) tick = 10*Magnitude; else if (residual > 2) tick = 5*Magnitude; else if (residual > 1) tick = 2*Magnitude; else tick = Magnitude; S = tick; if (Labels != null) S = S < 1 ? 1 : S; }
public LabelEvaluation UpdateLabel(string text, AxisCore axis, AxisTags source) { TextBlock.Text = text; TextBlock.UpdateLayout(); var transform = new LabelEvaluation(axis.View.LabelsRotation, TextBlock.ActualWidth, TextBlock.ActualHeight, axis, source); TextBlock.RenderTransform = Math.Abs(transform.LabelAngle) > 1 ? new RotateTransform(transform.LabelAngle) : null; LabelModel = transform; return transform; }
public void Place(ChartCore chart, AxisCore axis, AxisTags direction, int axisIndex, double toLabel, double toLine, double tab) { if (direction == AxisTags.Y) { Line.X1 = chart.DrawMargin.Left; Line.X2 = chart.DrawMargin.Left + chart.DrawMargin.Width; Line.Y1 = toLine; Line.Y2 = toLine; Canvas.SetLeft(TextBlock, tab); Canvas.SetTop(TextBlock, toLabel); } else { Line.X1 = toLine; Line.X2 = toLine; Line.Y1 = chart.DrawMargin.Top; Line.Y2 = chart.DrawMargin.Top + chart.DrawMargin.Height; Canvas.SetLeft(TextBlock, toLabel); Canvas.SetTop(TextBlock, tab); } }
public double LenghtOf(double value, AxisTags axis) { var isX = axis == AxisTags.X; var m = isX ? Min.X : Min.Y; var o = isX ? PlotArea.X : PlotArea.Y; return Methods.ToPlotArea(m + value, axis, this) - o; }
public double LenghtOf(double value, AxisTags axis) { return Methods.ToPlotArea(value, axis, this) - (axis == AxisTags.X ? PlotArea.X : PlotArea.Y); }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="axis"></param> /// <returns></returns> public double ToPlotArea(double value, AxisTags axis) { return Methods.ToPlotArea(value, axis, this); }
/// <summary> /// Scales a graph value to screen according to an axis. /// </summary> /// <param name="value"></param> /// <param name="axis"></param> /// <returns></returns> protected double ToPlotArea(double value, AxisTags axis) { return Methods.ToPlotArea(value, axis, Chart); }
protected void ConfigureYAsIndexed() { if (AxisY.Labels == null && AxisY.LabelFormatter == null) AxisY.ShowLabels = false; var f = GetFormatter(AxisY); var d = AxisY.Labels == null ? Max.Y : AxisY.Labels.IndexOf(AxisY.Labels.OrderBy(x => x.Length).Reverse().First()); var longestYLabel = new FormattedText(HasValidRange ? f(d) : "", CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(AxisY.FontFamily, AxisY.FontStyle, AxisY.FontWeight, AxisY.FontStretch), AxisY.FontSize, Brushes.Black); AxisY.Separator.Step = (longestYLabel.Width*Max.Y)*1.25 > PlotArea.Width ? null : (int?) 1; if (AxisY.Separator.Step != null) S.Y = (int) AxisY.Separator.Step; if (Zoom != ZoomingOptions.None) ZoomingAxis = AxisTags.Y; }
public LabelEvaluation(double angle, double w, double h, AxisCore axis, AxisTags source) { const double padding = 4; ActualWidth = w; ActualHeight = h; // for now there is no support for rotated and merged labels. // the labels will be rotated but there is no warranty that they are displayed correctly if (axis.View.IsMerged) { Top = 0; Bottom = 0; Left = 0; Right = 0; if (source == AxisTags.Y) { XOffset = padding; YOffset = padding; } else { if (axis.Position == AxisPosition.LeftBottom) { //Bot XOffset = padding; YOffset = -h*2 - padding; } else { //Top XOffset = padding; YOffset = padding + h*2; } } return; } //OK now lets evaluate the rotation angle... // the rotation angle starts from an horizontal line, yes like this text // - 0°, | 90°, - 180°, | 270° // notice normally rotating a label from 90 to 270° will show the label // in a wrong orientation // we need to fix that angle const double toRadians = Math.PI/180; // 1. width components // 2. height components WFromW = Math.Abs(Math.Cos(angle*toRadians)*w); // W generated from the width of the label WFromH = Math.Abs(Math.Sin(angle*toRadians)*h); // W generated from the height of the label HFromW = Math.Abs(Math.Sin(angle*toRadians)*w); // H generated from the width of the label HFromH = Math.Abs(Math.Cos(angle*toRadians)*h); // H generated from the height of the label LabelAngle = angle%360; if (LabelAngle < 0) LabelAngle += 360; if (LabelAngle > 90 && LabelAngle < 270) LabelAngle = (LabelAngle + 180)%360; //at this points angles should only exist in 1st and 4th quadrant //those are the only quadrants that generates readable labels //the other 2 quadrants display inverted labels var quadrant = ((int) (LabelAngle/90))%4 + 1; if (source == AxisTags.Y) { // Y Axis if (quadrant == 1) { if (axis.Position == AxisPosition.LeftBottom) { // 1, L Top = HFromW + (HFromH/2); //space taken from separator to top Bottom = TakenHeight - Top; //space taken from separator to bottom XOffset = -WFromW - padding; //distance from separator to label origin in X YOffset = -Top; //distance from separator to label origin in Y } else { // 1, R Bottom = HFromW + (HFromH/2); Top = TakenHeight - Bottom; XOffset = padding + WFromH; YOffset = -Top; } } else { if (axis.Position == AxisPosition.LeftBottom) { // 4, L Bottom = HFromW + (HFromH/2); Top = TakenHeight - Bottom; XOffset = -TakenWidth - padding; YOffset = HFromW - (HFromH/2); } else { // 4, R Top = HFromW + (HFromH/2); Bottom = TakenHeight - Top; XOffset = padding; YOffset = -Bottom; } } } else { // X Axis //axis x has one exception, if labels rotation equals 0° then the label is centered if (Math.Abs(axis.View.LabelsRotation) < .01) { Left = TakenWidth / 2; Right = Left; XOffset = -Left; YOffset = axis.Position == AxisPosition.LeftBottom ? padding : -padding - TakenHeight; } else { if (quadrant == 1) { if (axis.Position == AxisPosition.LeftBottom) { //1, B Right = WFromW + (WFromH / 2); //space taken from separator to right Left = TakenWidth - Right; //space taken from separator to left XOffset = Left; //distance from separator to label origin in X YOffset = padding; //distance from separator to label origin in Y } else { //1, T Left = WFromW + (WFromH/2); Right = TakenWidth - Left; XOffset = -WFromW; YOffset = -padding - TakenHeight; } } else { if (axis.Position == AxisPosition.LeftBottom) { //4, B Left = WFromW + (WFromH/2); Right = TakenWidth - Left; XOffset = -Left; YOffset = padding + HFromW; } else { //4, T Right = WFromW + (WFromH/2); Left = TakenWidth - Right; XOffset = -Left; YOffset = -HFromH; } } } } }