Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data labels #1055

Merged
merged 10 commits into from
Jun 12, 2023
2 changes: 1 addition & 1 deletion src/LiveChartsCore/BarSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected class MeasureHelper
uw = scaler.MeasureInPixels(axis.UnitWidth);
actualUw = uw;

var gp = (float)barSeries.GroupPadding;
var gp = (float)barSeries.Padding;

if (uw - gp < 1) gp -= uw - gp;

Expand Down
4 changes: 4 additions & 0 deletions src/LiveChartsCore/ColumnSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ public override void Invalidate(Chart<TDrawingContext> chart)
label.X = labelPosition.X;
label.Y = labelPosition.Y;
}
else
{
// ....
}

OnPointMeasured(point);
}
Expand Down
4 changes: 2 additions & 2 deletions src/LiveChartsCore/Drawing/IDoughnutGeometry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ public interface IDoughnutGeometry<TDrawingContext> : IGeometry<TDrawingContext>
float Height { get; set; }

/// <summary>
/// Gets or sets the start angle.
/// Gets or sets the start angle in degrees.
/// </summary>
/// <value>
/// The start angle.
/// </value>
float StartAngle { get; set; }

/// <summary>
/// Gets or sets the sweep angle.
/// Gets or sets the sweep angle in degrees.
/// </summary>
/// <value>
/// The sweep angle.
Expand Down
9 changes: 5 additions & 4 deletions src/LiveChartsCore/FinancialSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ protected FinancialSeries()
SeriesProperties.Solid | SeriesProperties.PrefersXStrategyTooltips)
{
YToolTipLabelFormatter = p =>
$"H {p.PrimaryValue:N2}{Environment.NewLine}" +
$"O {p.TertiaryValue:N2}{Environment.NewLine}" +
$"C {p.QuaternaryValue:N2}{Environment.NewLine}" +
$"L {p.QuinaryValue:N2}";
$"H {p.PrimaryValue:C2}{Environment.NewLine}" +
$"O {p.TertiaryValue:C2}{Environment.NewLine}" +
$"C {p.QuaternaryValue:C2}{Environment.NewLine}" +
$"L {p.QuinaryValue:C2}";
DataLabelsFormatter = point => $"{point.PrimaryValue:C2} - {point.QuinaryValue:C2}";
}

/// <inheritdoc cref="IFinancialSeries{TDrawingContext}.MaxBarWidth"/>
Expand Down
5 changes: 3 additions & 2 deletions src/LiveChartsCore/HeatSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ protected HeatSeries()

return $"{labeler(point.PrimaryValue)} {point.TertiaryValue}";
};
DataLabelsPosition = DataLabelsPosition.Middle;
}

/// <inheritdoc cref="IHeatSeries{TDrawingContext}.HeatMap"/>
Expand Down Expand Up @@ -231,8 +232,8 @@ public override void Invalidate(Chart<TDrawingContext> chart)
label.TextSize = dls;
label.Padding = DataLabelsPadding;
var labelPosition = GetLabelPosition(
secondary, primary, uws, uws, label.Measure(DataLabelsPaint), DataLabelsPosition,
SeriesProperties, point.PrimaryValue > Pivot, drawLocation, drawMarginSize);
secondary - uws * 0.5f + p.Left, primary - uwp * 0.5f + p.Top, uws - p.Left - p.Right, uwp - p.Top - p.Bottom,
label.Measure(DataLabelsPaint), DataLabelsPosition, SeriesProperties, point.PrimaryValue > Pivot, drawLocation, drawMarginSize);
label.X = labelPosition.X;
label.Y = labelPosition.Y;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LiveChartsCore/Motion/MotionCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ namespace LiveChartsCore.Motion;
public class MotionCanvas<TDrawingContext> : IDisposable
where TDrawingContext : DrawingContext
{
internal HashSet<IPaint<TDrawingContext>> _paintTasks = new();
private readonly Stopwatch _stopwatch = new();
private HashSet<IPaint<TDrawingContext>> _paintTasks = new();
private readonly List<double> _fpsStack = new();
private long _previousFrameTime;
private long _previousLogTime;
Expand Down
2 changes: 2 additions & 0 deletions src/LiveChartsCore/PieSeries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ protected virtual void SoftDeleteOrDisposePoint(ChartPoint point, Scaler primary
break;
case PolarLabelsPosition.Middle:
angle = startAngle + sweepAngle * 0.5f;
radius *= 1.15f;
break;
case PolarLabelsPosition.ChartCenter:
return new LvcPoint(centerX, centerY);
Expand All @@ -621,6 +622,7 @@ protected virtual void SoftDeleteOrDisposePoint(ChartPoint point, Scaler primary
//angle %= 360;
//if (angle < 0) angle += 360;
angle *= toRadians;
radius += 0.5f * (float)Math.Sqrt(Math.Pow(labelSize.Width, 2) + Math.Pow(labelSize.Height, 2));

return new LvcPoint(
(float)(centerX + Math.Cos(angle) * radius),
Expand Down
13 changes: 9 additions & 4 deletions src/LiveChartsCore/Series.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using LiveChartsCore.Drawing;
using LiveChartsCore.Kernel;
Expand All @@ -34,8 +35,6 @@
using LiveChartsCore.Kernel.Providers;
using LiveChartsCore.Kernel.Sketches;
using LiveChartsCore.Measure;
using LiveChartsCore.Motion;

namespace LiveChartsCore;

/// <summary>
Expand Down Expand Up @@ -101,7 +100,7 @@ public abstract class Series<TModel, TVisual, TLabel, TDrawingContext>
private string? _name;
private Action<TModel, ChartPoint>? _mapping;
private int _zIndex;
private Func<ChartPoint<TModel, TVisual, TLabel>, string>? _dataLabelsFormatter = x => x.PrimaryValue.ToString();
private Func<ChartPoint<TModel, TVisual, TLabel>, string> _dataLabelsFormatter = x => x.PrimaryValue.ToString();
private bool _isVisible = true;
private LvcPoint _dataPadding = new(0.5f, 0.5f);
private DataFactory<TModel, TDrawingContext>? _dataFactory;
Expand Down Expand Up @@ -227,7 +226,7 @@ protected Series(SeriesProperties properties)
/// <value>
/// The data label formatter.
/// </value>
public Func<ChartPoint<TModel, TVisual, TLabel>, string>? DataLabelsFormatter
public Func<ChartPoint<TModel, TVisual, TLabel>, string> DataLabelsFormatter
{
get => _dataLabelsFormatter;
set => SetProperty(ref _dataLabelsFormatter, value);
Expand Down Expand Up @@ -273,6 +272,11 @@ public bool IsVisible
}
}

/// <summary>
/// Called when a point is measured.
/// </summary>
public Action<ChartPoint<TModel, TVisual, TLabel>>? WhenPointMeasured { get; set; }

/// <summary>
/// Gets or sets the size of the legend shape.
/// </summary>
Expand Down Expand Up @@ -447,6 +451,7 @@ public override void RemoveFromUI(Chart<TDrawingContext> chart)
/// <param name="chartPoint">The chart point.</param>
protected internal virtual void OnPointMeasured(ChartPoint chartPoint)
{
WhenPointMeasured?.Invoke(new ChartPoint<TModel, TVisual, TLabel>(chartPoint));
PointMeasured?.Invoke(new ChartPoint<TModel, TVisual, TLabel>(chartPoint));
}

Expand Down
89 changes: 74 additions & 15 deletions src/skiasharp/LiveChartsCore.SkiaSharp/LiveChartsSkiaSharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,40 +149,99 @@ public static string MatchChar(char @char)
/// <summary>
/// Converts an IEnumerable to an ObservableCollection of pie series.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <param name="source">The data source.</param>
/// <param name="buider">An optional builder.</param>
/// <param name="builder">An optional builder.</param>
/// <returns></returns>
public static ObservableCollection<PieSeries<T>> AsPieSeries<T>(
this IEnumerable<T> source,
Action<T, PieSeries<T>>? buider = null)
public static ObservableCollection<PieSeries<TModel>> AsPieSeries<TModel>(
this IEnumerable<TModel> source,
Action<TModel, PieSeries<TModel>>? builder = null)
{
return AsLiveChartsPieSeries(source, buider);
builder ??= (instance, series) => { };

return new ObservableCollection<PieSeries<TModel>>(
source.Select(instance =>
{
var series = new PieSeries<TModel> { Values = new ObservableCollection<TModel> { instance } };
builder(instance, series);
return series;
})
.ToArray());
}

/// <summary>
/// Converts an IEnumerable to an ObservableCollection of pie series.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TVisual">The type of the visual.</typeparam>
/// <param name="source">The data source.</param>
/// <param name="buider">An optional builder.</param>
/// <param name="builder">An optional builder.</param>
/// <returns></returns>
public static ObservableCollection<PieSeries<T>> AsLiveChartsPieSeries<T>(
this IEnumerable<T> source,
Action<T, PieSeries<T>>? buider = null)
public static ObservableCollection<PieSeries<TModel, TVisual>> AsPieSeries<TModel, TVisual>(
this IEnumerable<TModel> source,
Action<TModel, PieSeries<TModel, TVisual>>? builder = null)
where TVisual : class, IDoughnutGeometry<SkiaSharpDrawingContext>, new()
{
builder ??= (instance, series) => { };

return new ObservableCollection<PieSeries<TModel, TVisual>>(
source.Select(instance =>
{
var series = new PieSeries<TModel, TVisual>
{
Values = new ObservableCollection<TModel> { instance }
};
builder(instance, series);
return series;
})
.ToArray());
}

/// <summary>
/// Converts an IEnumerable to an ObservableCollection of pie series.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TVisual">The type of the visual.</typeparam>
/// <typeparam name="TLabel">The type of the label.</typeparam>
/// <param name="source">The data source.</param>
/// <param name="builder">An optional builder.</param>
/// <returns></returns>
public static ObservableCollection<PieSeries<TModel, TVisual, TLabel>> AsPieSeries<TModel, TVisual, TLabel>(
this IEnumerable<TModel> source,
Action<TModel, PieSeries<TModel, TVisual, TLabel>>? builder = null)
where TVisual : class, IDoughnutGeometry<SkiaSharpDrawingContext>, new()
where TLabel : class, ILabelGeometry<SkiaSharpDrawingContext>, new()
{
buider ??= (instance, series) => { };
builder ??= (instance, series) => { };

return new ObservableCollection<PieSeries<T>>(
return new ObservableCollection<PieSeries<TModel, TVisual, TLabel>>(
source.Select(instance =>
{
var series = new PieSeries<T> { Values = new ObservableCollection<T> { instance } };
buider(instance, series);
var series = new PieSeries<TModel, TVisual, TLabel>
{
Values = new ObservableCollection<TModel> { instance }
};
builder(instance, series);
return series;
})
.ToArray());
}

/// <summary>
/// Converts an IEnumerable to an ObservableCollection of pie series.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="source">The data source.</param>
/// <param name="buider">An optional builder.</param>
/// <returns></returns>
[Obsolete($"Renamed to {nameof(AsPieSeries)}.")]
public static ObservableCollection<PieSeries<T>> AsLiveChartsPieSeries<T>(
this IEnumerable<T> source,
Action<T, PieSeries<T>>? buider = null)
{
return AsPieSeries(source, buider);
}

/// <summary>
/// Calculates the distance in pixels from the target <see cref="ChartPoint"/> to the given location in the UI.
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions tests/LiveChartsCore.UnitTesting/MockedObjects/TestLabel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// The MIT License(MIT)
//
// Copyright(c) 2021 Alberto Rodriguez Orozco & LiveCharts Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using LiveChartsCore.SkiaSharpView.Drawing.Geometries;

namespace LiveChartsCore.UnitTesting.MockedObjects;

public class TestLabel : LabelGeometry
{
public TestLabel()
{
Background = new Drawing.LvcColor(200, 200, 200, 50);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System;
using System.Collections.Generic;
using LiveChartsCore.Kernel;
using LiveChartsCore.SkiaSharpView.Drawing.Geometries;

namespace LiveChartsCore.UnitTesting.MockedObjects;

Expand Down