mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
417 lines
12 KiB
C#
417 lines
12 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : LineChart.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : GUI custom controls.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Draws a line chart.
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2020 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using Eto.Drawing;
|
|
using Eto.Forms;
|
|
|
|
namespace Aaru.Gui.Controls
|
|
{
|
|
/// <summary>Draws a line chart</summary>
|
|
public class LineChart : Drawable
|
|
{
|
|
bool absoluteMargins;
|
|
Color axesColor;
|
|
Color backgroundColor;
|
|
Color colorX;
|
|
Color colorY;
|
|
bool drawAxes;
|
|
Color lineColor;
|
|
float marginX;
|
|
float marginXrated;
|
|
float marginY;
|
|
float marginYrated;
|
|
float maxX;
|
|
float maxY;
|
|
float minX;
|
|
float minY;
|
|
PointF previousPoint;
|
|
float ratioX;
|
|
float ratioY;
|
|
RectangleF rect;
|
|
bool showStepsX;
|
|
bool showStepsY;
|
|
float stepsX;
|
|
float stepsY;
|
|
|
|
public LineChart()
|
|
{
|
|
Values = new ObservableCollection<PointF>();
|
|
Values.CollectionChanged += OnValuesChanged;
|
|
showStepsX = true;
|
|
stepsX = 5;
|
|
showStepsY = true;
|
|
stepsY = 5;
|
|
minX = 0;
|
|
maxX = 100;
|
|
minY = 0;
|
|
maxY = 100;
|
|
backgroundColor = Colors.Transparent;
|
|
colorX = Colors.DarkGray;
|
|
colorY = Colors.DarkGray;
|
|
lineColor = Colors.Red;
|
|
axesColor = Colors.Black;
|
|
drawAxes = true;
|
|
marginX = 5;
|
|
marginY = 5;
|
|
absoluteMargins = true;
|
|
previousPoint = new PointF(0, 0);
|
|
}
|
|
|
|
/// <summary>If set the margins would be in absolute pixels, otherwise in relative points</summary>
|
|
public bool AbsoluteMargins
|
|
{
|
|
get => absoluteMargins;
|
|
set
|
|
{
|
|
if(absoluteMargins == value)
|
|
return;
|
|
|
|
absoluteMargins = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Margin between the leftmost border and the Y axis</summary>
|
|
public float MarginX
|
|
{
|
|
get => marginX;
|
|
set
|
|
{
|
|
marginX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Margin between the bottommost border and the X axis</summary>
|
|
public float MarginY
|
|
{
|
|
get => marginY;
|
|
set
|
|
{
|
|
marginY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Contains the relative poitns to be drawn</summary>
|
|
public ObservableCollection<PointF> Values { get; }
|
|
|
|
/// <summary>If axes borders should be drawn</summary>
|
|
public bool DrawAxes
|
|
{
|
|
get => drawAxes;
|
|
set
|
|
{
|
|
if(drawAxes == value)
|
|
return;
|
|
|
|
drawAxes = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>If a grid should be drawn every X step</summary>
|
|
public bool ShowStepsX
|
|
{
|
|
get => showStepsX;
|
|
set
|
|
{
|
|
if(showStepsX == value)
|
|
return;
|
|
|
|
showStepsX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Separation between X grid lines</summary>
|
|
public float StepsX
|
|
{
|
|
get => stepsX;
|
|
set
|
|
{
|
|
stepsX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>If a grid should be drawn every Y step</summary>
|
|
public bool ShowStepsY
|
|
{
|
|
get => showStepsY;
|
|
set
|
|
{
|
|
if(showStepsY == value)
|
|
return;
|
|
|
|
showStepsY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Separation between X grid lines</summary>
|
|
public float StepsY
|
|
{
|
|
get => stepsY;
|
|
set
|
|
{
|
|
stepsY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Relative point that is equal to start of X</summary>
|
|
public float MinX
|
|
{
|
|
get => minX;
|
|
set
|
|
{
|
|
minX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Relative point that is equal to start of Y</summary>
|
|
public float MinY
|
|
{
|
|
get => minY;
|
|
set
|
|
{
|
|
minY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Relative point that is equal to end of X</summary>
|
|
public float MaxX
|
|
{
|
|
get => maxX;
|
|
set
|
|
{
|
|
maxX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Relative point that is equal to end of Y</summary>
|
|
public float MaxY
|
|
{
|
|
get => maxY;
|
|
set
|
|
{
|
|
maxY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Color for background</summary>
|
|
public new Color BackgroundColor
|
|
{
|
|
get => backgroundColor;
|
|
set
|
|
{
|
|
if(backgroundColor == value)
|
|
return;
|
|
|
|
backgroundColor = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Color to draw the axes borders</summary>
|
|
public Color AxesColor
|
|
{
|
|
get => axesColor;
|
|
set
|
|
{
|
|
if(axesColor == value)
|
|
return;
|
|
|
|
axesColor = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Color to draw the X grid</summary>
|
|
public Color ColorX
|
|
{
|
|
get => colorX;
|
|
set
|
|
{
|
|
if(colorX == value)
|
|
return;
|
|
|
|
colorX = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Color to draw the Y grid</summary>
|
|
public Color ColorY
|
|
{
|
|
get => colorY;
|
|
set
|
|
{
|
|
if(colorY == value)
|
|
return;
|
|
|
|
colorY = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>Color to draw the line between points</summary>
|
|
public Color LineColor
|
|
{
|
|
get => lineColor;
|
|
set
|
|
{
|
|
if(lineColor == value)
|
|
return;
|
|
|
|
lineColor = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void OnValuesChanged(object sender, NotifyCollectionChangedEventArgs args)
|
|
{
|
|
// If we do not support to drawn on the graphics we will need to redraw it, slowly
|
|
if(!SupportsCreateGraphics)
|
|
Invalidate();
|
|
|
|
Graphics g;
|
|
|
|
// If the control is not visible (hidden in another tab) this raises an exception
|
|
try
|
|
{
|
|
g = CreateGraphics();
|
|
}
|
|
catch
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(args.Action)
|
|
{
|
|
// Draw only next point
|
|
case NotifyCollectionChangedAction.Add:
|
|
foreach(object item in args.NewItems)
|
|
{
|
|
if(!(item is PointF nextPoint))
|
|
continue;
|
|
|
|
float prevXrated = previousPoint.X * ratioX;
|
|
float prevYrated = previousPoint.Y * ratioY;
|
|
|
|
float nextXrated = nextPoint.X * ratioX;
|
|
float nextYrated = nextPoint.Y * ratioY;
|
|
|
|
if(prevYrated <= 0)
|
|
prevYrated = nextYrated;
|
|
|
|
g.DrawLine(lineColor, marginXrated + prevXrated, rect.Height - marginYrated - prevYrated,
|
|
marginXrated + nextXrated, rect.Height - marginYrated - nextYrated);
|
|
|
|
previousPoint = nextPoint;
|
|
}
|
|
|
|
break;
|
|
|
|
// Need to redraw all points
|
|
default:
|
|
Invalidate();
|
|
|
|
break;
|
|
}
|
|
|
|
g.Dispose();
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
base.OnPaint(e);
|
|
|
|
Graphics g = e.Graphics;
|
|
rect = e.ClipRectangle;
|
|
|
|
g.FillRectangle(backgroundColor, rect);
|
|
|
|
ratioX = rect.Width / (maxX - minX);
|
|
ratioY = rect.Height / (maxY - minY);
|
|
|
|
marginXrated = marginX * (absoluteMargins ? 1 : ratioX);
|
|
marginYrated = marginY * (absoluteMargins ? 1 : ratioY);
|
|
|
|
if(drawAxes)
|
|
{
|
|
g.DrawLine(axesColor, marginXrated, 0, marginXrated, rect.Height);
|
|
g.DrawLine(axesColor, 0, rect.Height - marginYrated, rect.Width, rect.Height - marginYrated);
|
|
}
|
|
|
|
if(showStepsX)
|
|
{
|
|
float stepsXraged = stepsX * ratioX;
|
|
|
|
for(float x = marginXrated + stepsXraged; x < rect.Width;
|
|
x += stepsXraged)
|
|
g.DrawLine(colorX, x, 0, x, rect.Height - marginYrated - 1);
|
|
}
|
|
|
|
if(showStepsY)
|
|
{
|
|
float stepsYraged = stepsY * ratioY;
|
|
|
|
for(float y = rect.Height - marginYrated - stepsYraged; y > 0; y -= stepsYraged)
|
|
g.DrawLine(colorY, marginXrated + 1, y, rect.Width, y);
|
|
}
|
|
|
|
previousPoint = new PointF(0, 0);
|
|
|
|
foreach(Point nextPoint in Values)
|
|
{
|
|
float prevXrated = previousPoint.X * ratioX;
|
|
float prevYrated = previousPoint.Y * ratioY;
|
|
|
|
float nextXrated = nextPoint.X * ratioX;
|
|
float nextYrated = nextPoint.Y * ratioY;
|
|
|
|
g.DrawLine(lineColor, marginXrated + prevXrated, rect.Height - marginYrated - prevYrated,
|
|
marginXrated + nextXrated, rect.Height - marginYrated - nextYrated);
|
|
|
|
previousPoint = nextPoint;
|
|
}
|
|
}
|
|
}
|
|
} |