add line chart, fix renderable culling, (probably) fix run loop

This commit is contained in:
firedef 2022-03-12 18:34:40 +03:00
Родитель 5019baea9f
Коммит 5cbf35eefa
9 изменённых файлов: 131 добавлений и 53 удалений

Просмотреть файл

@ -6,6 +6,6 @@ namespace SomeChartsAvaloniaExamples;
internal class Program {
[STAThread]
public static void Main(string[] args) {
ElementsExamples.RunLabel();
ElementsExamples.RunLineChart();
}
}

Просмотреть файл

@ -149,28 +149,29 @@ public static class ElementsExamples {
public static void RunLineChart() {
AvaloniaRunUtils.RunAfterStart(() => {
AvaloniaChartsCanvas canvas = AvaloniaRunUtils.AddCanvas();
AvaloniaGlChartsCanvas canvas = AvaloniaRunUtils.AddGlCanvas();
const int rulerOffset = 1_000_000;
canvas.AddElement(new Ruler(canvas.canvas) {
drawLabels = true,
orientation = Orientation.horizontal,
length = rulerOffset,
names = new FuncChartManagedData<string>(i => i.ToString(), 1),
stickRange = new(0, 0, 0, rulerOffset)
});
// canvas.AddElement(new Ruler(canvas.canvas) {
// drawLabels = true,
// orientation = Orientation.horizontal,
// length = rulerOffset,
// names = new FuncChartManagedData<string>(i => i.ToString(), 1),
// stickRange = new(0, 0, 0, rulerOffset)
// });
//
// canvas.AddElement(new Ruler(canvas.canvas) {
// drawLabels = true,
// orientation = Orientation.vertical,
// length = rulerOffset,
// names = new FuncChartManagedData<string>(i => (i * 100).ToString(), 1),
// stickRange = new(0, 0, rulerOffset, 0)
// });
canvas.AddElement(new Ruler(canvas.canvas) {
drawLabels = true,
orientation = Orientation.vertical,
length = rulerOffset,
names = new FuncChartManagedData<string>(i => (i * 100).ToString(), 1),
stickRange = new(0, 0, rulerOffset, 0)
});
IChartData<float> data = new FuncChartData<float>(i => MathF.Sin(i * .1f) * 1000, 20480);
IChartData<indexedColor> colors = new ConstChartData<indexedColor>(new(theme.bad_ind));
IChartData<float> data = new FuncChartData<float>(i => MathF.Sin(i * .1f) * 1000, 2048);
IChartData<indexedColor> colors = new ConstChartData<indexedColor>(new(theme.good_ind));
canvas.AddElement(new LineChart(data, colors, canvas.canvas));
canvas.AddElement(new LineChart(data, colors, canvas.canvas) {isDynamic = true});
});
AvaloniaRunUtils.RunAvalonia();

Просмотреть файл

@ -1,3 +1,5 @@
using MathStuff;
using MathStuff.vectors;
using SomeChartsUi.data;
using SomeChartsUi.themes.colors;
using SomeChartsUi.ui.canvas;
@ -24,33 +26,33 @@ public class LineChart : RenderableBase, IDownsample {
public float downsampleMultiplier { get; set; } = .5f;
public float elementScale { get; set; } = 100;
protected override void GenerateMesh() { }
// protected override unsafe void Render() {
// int length = values.GetLength();
// if (length < 1) return;
//
// int downsample = GetDownsample(orientation, downsampleMultiplier);
// (float startPos, float endPos) culledPositions = GetStartEndPos(float2.zero, length * elementScale, orientation);
// (float start, int count) = GetStartCountIndexes(culledPositions, elementScale * (1 << downsample));
// int startIndex = (int)(start / elementScale);
//
// if (count <= 1) return;
//
// float2 vec = GetOrientationVector(orientation);
//
// // get line points
// float2* linePoints = stackalloc float2[count];
// float* pointHeightsStart = (float*)linePoints + (int) vec.x;
// float* pointWidthStart = (float*)linePoints + (int) vec.y;
// values.GetValuesWithStride(startIndex, count, downsample, pointHeightsStart, 2);
// for (int i = 0; i < count; i++)
// pointWidthStart[i << 1] = (startIndex + (i << downsample)) * elementScale;
//
// // get line colors
// color* lineColors = stackalloc color[count];
// colors.GetColors(startIndex, count, downsample, lineColors);
//
// DrawConnectedLines(linePoints, lineColors, lineThickness.Get(this), count - 1, lineAlphaMul.Get(this));
// DrawPoints(linePoints, lineColors, pointThickness.Get(this), count - 1);
// }
protected override unsafe void GenerateMesh() {
mesh!.Clear();
int length = values.GetLength();
if (length < 1) return;
int downsample = GetDownsample(orientation, downsampleMultiplier);
(float startPos, float endPos) culledPositions = GetStartEndPos(float2.zero, length * elementScale, orientation);
(float start, int count) = GetStartCountIndexes(culledPositions, elementScale * (1 << downsample));
int startIndex = (int)(start / elementScale);
if (count <= 1) return;
float2 vec = GetOrientationVector(orientation);
// get line points
float2* linePoints = stackalloc float2[count];
float* pointHeightsStart = (float*)linePoints + (int) vec.x;
float* pointWidthStart = (float*)linePoints + (int) vec.y;
values.GetValuesWithStride(startIndex, count, downsample, pointHeightsStart, 2);
for (int i = 0; i < count; i++)
pointWidthStart[i << 1] = (startIndex + (i << downsample)) * elementScale;
// get line colors
color* lineColors = stackalloc color[count];
colors.GetColors(startIndex, count, downsample, lineColors);
AddPoints(mesh, linePoints, lineColors, pointThickness.Get(this), count - 1);
AddConnectedLines(mesh!, linePoints, lineColors, lineThickness.Get(this), count - 1, lineAlphaMul.Get(this));
mesh.OnModified();
}
}

Просмотреть файл

@ -27,8 +27,8 @@ public class ChartCanvasTransform {
scale.OnUpdate(deltaTime);
rotation.OnUpdate(deltaTime);
worldBounds = screenBounds.ToWorld(position, scale);
//worldBounds = screenBounds.ToWorld(-position.animatedValue, scale);
worldBounds = new(screenBounds.left + position.animatedValue.x - screenBounds.width / 2 / scale.animatedValue.x, screenBounds.bottom + position.animatedValue.y, screenBounds.width / scale.animatedValue.x, screenBounds.height / scale.animatedValue.y);
RecalculateMatrix();
}

Просмотреть файл

@ -1,9 +1,76 @@
using MathStuff;
using MathStuff.vectors;
using SomeChartsUi.elements;
using SomeChartsUi.utils.mesh;
namespace SomeChartsUi.ui.elements;
public abstract partial class RenderableBase {
protected unsafe void AddConnectedLines(Mesh m, float2* linePoints, color* lineColors, float thickness, int len, float alphaMul = 1) {
int vCount = len * 4;
int iCount = len * 6;
m.vertices.EnsureFreeSpace(vCount);
m.indexes.EnsureFreeSpace(iCount);
int curVIndex = m.vertices.count;
for (int i = 0; i < len; i++) {
float2 p0 = linePoints[i];
float2 p1 = linePoints[i + 1];
color c0 = lineColors[i];
c0.aF *= alphaMul;
float2 offset = Rot90DegFastWithLen(p0 - p1, thickness);
m.AddVertex(new(new(p1.x - offset.x, p1.y - offset.y), float3.front, float2.zero, c0));
m.AddVertex(new(new(p1.x + offset.x, p1.y + offset.y), float3.front, float2.zero, c0));
m.AddVertex(new(new(p0.x + offset.x, p0.y + offset.y), float3.front, float2.zero, c0));
m.AddVertex(new(new(p0.x - offset.x, p0.y - offset.y), float3.front, float2.zero, c0));
m.AddIndex(curVIndex + 0);
m.AddIndex(curVIndex + 1);
m.AddIndex(curVIndex + 2);
m.AddIndex(curVIndex + 0);
m.AddIndex(curVIndex + 2);
m.AddIndex(curVIndex + 3);
curVIndex += 4;
}
//m.vertices.Add();
//
//DrawVertices(points, null, colors, indexes, vCount, iCount);
}
protected unsafe void AddPoints(Mesh m, float2* elementPoints, color* elementColors, float size, int len) {
int vCount = len * 4;
int iCount = len * 6;
m.vertices.EnsureFreeSpace(vCount);
m.indexes.EnsureFreeSpace(iCount);
int curVIndex = m.vertices.count;
for (int i = 0; i < len; i++) {
float2 p0 = elementPoints[i];
color c0 = elementColors[i];
m.AddVertex(new(new(p0.x - size, p0.y - size), float3.front, float2.zero, c0));
m.AddVertex(new(new(p0.x - size, p0.y + size), float3.front, float2.zero, c0));
m.AddVertex(new(new(p0.x + size, p0.y + size), float3.front, float2.zero, c0));
m.AddVertex(new(new(p0.x + size, p0.y - size), float3.front, float2.zero, c0));
m.AddIndex(curVIndex + 0);
m.AddIndex(curVIndex + 1);
m.AddIndex(curVIndex + 2);
m.AddIndex(curVIndex + 0);
m.AddIndex(curVIndex + 2);
m.AddIndex(curVIndex + 3);
curVIndex += 4;
}
}
/* /// <summary>draws non-connected lines</summary>
/// <param name="linePoints">first and second points of lines <br/>the length is 2 * lineCount</param>
/// <param name="lineColors">first and second point colors of lines <br/>the length is 2 * lineCount</param>

Просмотреть файл

@ -61,6 +61,8 @@ public abstract partial class RenderableBase {
protected (float start, float end) GetStartEndPos(float startLim, float endLim, Orientation orientation) {
float2 s = 1 / canvasScale;
Transform tr = transform;
//Console.WriteLine(startLim + " " + endLim + " " + math.min(endLim, canvas.transform.worldBounds.right*10 - tr.position.x));
//return (math.max(startLim, canvas.transform.worldBounds.left - tr.position.x), math.min(endLim, canvas.transform.worldBounds.right - tr.position.x));
if ((orientation & Orientation.vertical) != 0)
return (orientation & Orientation.reversed) != 0
? (math.max(startLim, canvas.transform.worldBounds.top - tr.position.y), math.min(endLim, canvas.transform.worldBounds.bottom - tr.position.y))

Просмотреть файл

@ -53,6 +53,9 @@ public class Mesh : IDisposable {
}
}
public void AddVertex(Vertex v) => vertices.Add(v);
public void AddIndex(int v) => indexes.Add((ushort) v);
public unsafe void RecalculateNormals() {
int c = indexes.count;

Просмотреть файл

@ -33,7 +33,10 @@ public class AvaloniaGlChartsCanvas : CustomGlControlBase {
public AvaloniaGlChartsCanvas() {
_updateTimer = new(_ => {
try { Dispatcher.UIThread?.RunJobs(); }
try {
if (Dispatcher.UIThread.CheckAccess())
Dispatcher.UIThread?.RunJobs();
}
catch (Exception e) {// ignored
}
}, null, 0, 1000 / 50);

Просмотреть файл

@ -68,7 +68,7 @@ public class GlMesh : Mesh {
else {
List<Range> changes = vertices.GetChanges();
foreach (Range r in changes)
GlInfo.glExt!.BufferSubData(GL_ARRAY_BUFFER, r.Start.Value * vSize, (r.End.Value - r.Start.Value) * vSize, vertices.dataPtr);
GlInfo.glExt!.BufferSubData(GL_ARRAY_BUFFER, r.Start.Value * vSize, (r.End.Value - r.Start.Value) * vSize, vertices.dataPtr + r.Start.Value);
}
// indexes
@ -82,7 +82,7 @@ public class GlMesh : Mesh {
else {
List<Range> changes = indexes.GetChanges();
foreach (Range r in changes)
GlInfo.glExt!.BufferSubData(GL_ELEMENT_ARRAY_BUFFER, r.Start.Value * iSize, (r.End.Value - r.Start.Value) * iSize, indexes.dataPtr);
GlInfo.glExt!.BufferSubData(GL_ELEMENT_ARRAY_BUFFER, r.Start.Value * iSize, (r.End.Value - r.Start.Value) * iSize, indexes.dataPtr + r.Start.Value);
}
updateRequired = false;