mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
247 lines
7.6 KiB
C#
247 lines
7.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
|
|
namespace Radzen.Blazor
|
|
{
|
|
/// <summary>
|
|
/// Base class for RadzenChart scales.
|
|
/// </summary>
|
|
public abstract class ScaleBase
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the input.
|
|
/// </summary>
|
|
/// <value>The input.</value>
|
|
public ScaleRange Input { get; set; } = new ScaleRange();
|
|
|
|
/// <summary>
|
|
/// Gets or sets the output.
|
|
/// </summary>
|
|
/// <value>The output.</value>
|
|
public ScaleRange Output { get; set; } = new ScaleRange();
|
|
|
|
/// <summary>
|
|
/// Gets the size of the output.
|
|
/// </summary>
|
|
/// <value>The size of the output.</value>
|
|
public double OutputSize
|
|
{
|
|
get
|
|
{
|
|
return Math.Abs(Output.End - Output.Start);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the number of ticks with the specified distance.
|
|
/// </summary>
|
|
/// <param name="distance">The distance.</param>
|
|
abstract public (double Start, double End, double Step) Ticks(int distance);
|
|
|
|
/// <summary>
|
|
/// The values at which axis ticks are rendered. The default implementation enumerates
|
|
/// <see cref="Ticks(int)" /> uniformly; scales with non-uniform intervals (e.g. calendar months) override it.
|
|
/// </summary>
|
|
/// <param name="distance">The desired distance between ticks in pixels.</param>
|
|
public virtual IEnumerable<double> TickValues(int distance)
|
|
{
|
|
var (start, end, step) = Ticks(distance);
|
|
|
|
if (step <= 0 || !double.IsFinite(step) || !double.IsFinite(start) || !double.IsFinite(end))
|
|
{
|
|
yield return start;
|
|
yield break;
|
|
}
|
|
|
|
if (IsLogarithmic)
|
|
{
|
|
if (step <= 1 || start <= 0)
|
|
{
|
|
yield return start;
|
|
yield break;
|
|
}
|
|
|
|
for (var value = start; value <= end; value *= step)
|
|
{
|
|
yield return value;
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
for (var value = (decimal)start; value <= (decimal)end; value += (decimal)step)
|
|
{
|
|
yield return (double)value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the specified value to a value from this scale with optional padding.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <param name="padding">Whether to apply padding.</param>
|
|
abstract public double Scale(double value, bool padding = false);
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this scale uses logarithmic tick stepping.
|
|
/// When true, the tick loop uses multiplicative stepping (idx *= step) instead of additive (idx += step).
|
|
/// </summary>
|
|
public virtual bool IsLogarithmic => false;
|
|
|
|
/// <summary>
|
|
/// Composes the specified selector.
|
|
/// </summary>
|
|
/// <typeparam name="TItem">The type of the t item.</typeparam>
|
|
/// <param name="selector">The selector.</param>
|
|
public virtual Func<TItem, double> Compose<TItem>(Func<TItem, double> selector)
|
|
{
|
|
return (value) => Scale(selector(value), true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the step.
|
|
/// </summary>
|
|
/// <value>The step.</value>
|
|
public virtual object? Step { get; set; }
|
|
|
|
/// <summary>
|
|
/// Resizes the scale to the specified values.
|
|
/// </summary>
|
|
/// <param name="min">The minimum.</param>
|
|
/// <param name="max">The maximum.</param>
|
|
public virtual void Resize(object min, object max)
|
|
{
|
|
if (min != null)
|
|
{
|
|
Input.Start = Convert.ToDouble(min, CultureInfo.InvariantCulture);
|
|
Round = false;
|
|
}
|
|
|
|
if (max != null)
|
|
{
|
|
Input.End = Convert.ToDouble(max, CultureInfo.InvariantCulture);
|
|
Round = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a "nice" number (closest power of 10).
|
|
/// </summary>
|
|
/// <param name="range">The range.</param>
|
|
/// <param name="round">Wether to round.</param>
|
|
public double NiceNumber(double range, bool round)
|
|
{
|
|
if (range == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
var sign = Math.Sign(range);
|
|
range = Math.Abs(range);
|
|
var exponent = Math.Floor(Math.Log10(range));
|
|
var fraction = range / Math.Pow(10, exponent);
|
|
|
|
double niceFraction;
|
|
|
|
if (round)
|
|
{
|
|
if (fraction < 1.5)
|
|
{
|
|
niceFraction = 1;
|
|
}
|
|
else if (fraction < 3)
|
|
{
|
|
niceFraction = 2;
|
|
}
|
|
else if (fraction < 7)
|
|
{
|
|
niceFraction = 5;
|
|
}
|
|
else
|
|
{
|
|
niceFraction = 10;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fraction <= 1)
|
|
{
|
|
niceFraction = 1;
|
|
}
|
|
else if (fraction <= 2)
|
|
{
|
|
niceFraction = 2;
|
|
}
|
|
else if (fraction <= 5)
|
|
{
|
|
niceFraction = 5;
|
|
}
|
|
else
|
|
{
|
|
niceFraction = 10;
|
|
}
|
|
}
|
|
|
|
return sign * niceFraction * Math.Pow(10, exponent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the padding.
|
|
/// </summary>
|
|
/// <value>The padding.</value>
|
|
public double Padding { get; set; }
|
|
|
|
/// <summary>
|
|
/// Fits the scale within the distance.
|
|
/// </summary>
|
|
/// <param name="distance">The distance.</param>
|
|
public virtual void Fit(int distance)
|
|
{
|
|
var ticks = Ticks(distance);
|
|
|
|
Input.MergeWidth(new ScaleRange { Start = ticks.Start, End = ticks.End });
|
|
|
|
Round = false;
|
|
Step = ticks.Step;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a value from the scale.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
public abstract object Value(double value);
|
|
|
|
/// <summary>
|
|
/// Formats the tick value.
|
|
/// </summary>
|
|
/// <param name="format">The format.</param>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>System.String.</returns>
|
|
public abstract string FormatTick(string format, object value);
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="ScaleBase"/> is round.
|
|
/// </summary>
|
|
/// <value><c>true</c> if round; otherwise, <c>false</c>.</value>
|
|
public bool Round { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Determines whether the specified scale is equal to the current one.
|
|
/// </summary>
|
|
/// <param name="scale">The scale.</param>
|
|
/// <returns><c>true</c> if the scales are equal; otherwise, <c>false</c>.</returns>
|
|
public bool IsEqualTo(ScaleBase scale)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(scale);
|
|
if (GetType() != scale.GetType())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Input.IsEqualTo(scale.Input) && Output.IsEqualTo(scale.Output);
|
|
}
|
|
}
|
|
} |