diff --git a/.idea/.idea.SomeChartsUi/.idea/indexLayout.xml b/.idea/.idea.SomeChartsUi/.idea/indexLayout.xml index 7b08163..c3fc600 100644 --- a/.idea/.idea.SomeChartsUi/.idea/indexLayout.xml +++ b/.idea/.idea.SomeChartsUi/.idea/indexLayout.xml @@ -1,7 +1,9 @@ - + + ../SomeCharts_ + diff --git a/SomeChartsUi/SomeChartsUi.csproj b/SomeChartsUi/SomeChartsUi.csproj index e616f24..744ebdb 100644 --- a/SomeChartsUi/SomeChartsUi.csproj +++ b/SomeChartsUi/SomeChartsUi.csproj @@ -18,6 +18,8 @@ + + diff --git a/SomeChartsUi/src/backends/ChartsBackendBase.cs b/SomeChartsUi/src/backends/ChartsBackendBase.cs index 7458667..bafb133 100644 --- a/SomeChartsUi/src/backends/ChartsBackendBase.cs +++ b/SomeChartsUi/src/backends/ChartsBackendBase.cs @@ -10,13 +10,8 @@ namespace SomeChartsUi.backends; /// all positions and scales are in screen-space transform /// public abstract class ChartsBackendBase { - public ChartsCanvas owner; - public ChartCanvasRenderer renderer; - - protected ChartsBackendBase(ChartsCanvas owner, ChartCanvasRenderer renderer) { - this.owner = owner; - this.renderer = renderer; - } + public ChartsCanvas owner = null!; + public ChartCanvasRenderer renderer = null!; public abstract unsafe void DrawMesh(float2* points, float2* uvs, color* colors, ushort* indexes, int vertexCount, int indexCount); public abstract void DrawMesh(float2[] points, float2[]? uvs, color[]? colors, ushort[] indexes); @@ -24,4 +19,6 @@ public abstract class ChartsBackendBase { public abstract void DrawText(string text, float2 pos, color col, FontData font, float scale = 12); public abstract void DrawRect(rect rectangle, color color); + + } \ No newline at end of file diff --git a/SomeChartsUi/src/elements/other/TestRenderable.cs b/SomeChartsUi/src/elements/other/TestRenderable.cs new file mode 100644 index 0000000..75caa17 --- /dev/null +++ b/SomeChartsUi/src/elements/other/TestRenderable.cs @@ -0,0 +1,17 @@ +using SomeChartsUi.themes.colors; +using SomeChartsUi.ui.elements; +using SomeChartsUi.utils.rects; +using SomeChartsUi.utils.vectors; + +namespace SomeChartsUi.elements.other; + +public class TestRenderable : RenderableBase { + protected override void Render() { + rect r = new(-100, -100, 200, 200); + float2[] points = {r.leftBottom, r.leftTop, r.rightTop, r.rightBottom}; + color[] colors = {color.red, color.softRed, color.blue, color.softBlue}; + ushort[] indexes = {0, 1, 2, 0, 2, 3}; + DrawVertices(points, null, colors, indexes); + //Console.WriteLine("render"); + } +} \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/ChartCanvasRenderer.cs b/SomeChartsUi/src/ui/canvas/ChartCanvasRenderer.cs index 75b0bc4..6990ef6 100644 --- a/SomeChartsUi/src/ui/canvas/ChartCanvasRenderer.cs +++ b/SomeChartsUi/src/ui/canvas/ChartCanvasRenderer.cs @@ -1,12 +1,14 @@ using SomeChartsUi.backends; +using SomeChartsUi.ui.layers; namespace SomeChartsUi.ui.canvas; public class ChartCanvasRenderer { public ChartsCanvas owner; - public ChartsBackendBase backend; - + public readonly ChartsBackendBase backend; + public readonly List layers = new(); + public readonly Dictionary layerNames = new(); public ChartCanvasRenderer(ChartsCanvas owner, ChartsBackendBase backend) { this.owner = owner; diff --git a/SomeChartsUi/src/ui/canvas/ChartCanvasTransform.cs b/SomeChartsUi/src/ui/canvas/ChartCanvasTransform.cs index d70c22e..cd5fb67 100644 --- a/SomeChartsUi/src/ui/canvas/ChartCanvasTransform.cs +++ b/SomeChartsUi/src/ui/canvas/ChartCanvasTransform.cs @@ -5,8 +5,8 @@ using SomeChartsUi.utils.vectors; namespace SomeChartsUi.ui.canvas; public class ChartCanvasTransform { - public CanvasAnimVariable position = new(float2.zero); - public CanvasAnimVariable zoom = new(float2.one); + public CanvasAnimVariable position = new(float2.zero, animationSpeed: .05f); + public CanvasAnimVariable zoom = new(float2.one, animationSpeed: .05f); public CanvasAnimVariable rotation = new(0); public rect screenBounds; @@ -25,4 +25,10 @@ public class ChartCanvasTransform { worldBounds = screenBounds.ToWorld(position, zoom); } + + public void SetAnimToCurrent() { + position.OnUpdate(10000); + zoom.OnUpdate(10000); + rotation.OnUpdate(10000); + } } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs b/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs index 022b4af..fa11b5a 100644 --- a/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs +++ b/SomeChartsUi/src/ui/canvas/ChartsCanvas.cs @@ -1,4 +1,6 @@ using SomeChartsUi.backends; +using SomeChartsUi.ui.canvas.controls; +using SomeChartsUi.ui.layers; namespace SomeChartsUi.ui.canvas; @@ -6,8 +8,30 @@ public class ChartsCanvas { public ChartCanvasTransform transform; public ChartCanvasRenderer renderer; + public ChartCanvasControllerBase? controller; + private List layers => renderer.layers; + private Dictionary layerNames => renderer.layerNames; + public ChartsCanvas(ChartsBackendBase backend) { transform = new(); renderer = new(this, backend); + backend.owner = this; + backend.renderer = renderer; } + + public CanvasLayer AddLayer(string name) { + CanvasLayer l = new(this, name); + renderer.layerNames.Add(name, layers.Count); + layers.Add(l); + return l; + } + + public void RemoveLayer(string name) { + if (!layerNames.ContainsKey(name)) return; + layers.RemoveAt(layerNames[name]); + layerNames.Remove(name); + } + + public CanvasLayer? GetLayer(string name) => layerNames.TryGetValue(name, out int i) ? layers[i] : null; + public CanvasLayer GetLayer(int i) => layers[i]; } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/ICanvasControls.cs b/SomeChartsUi/src/ui/canvas/ICanvasControls.cs index 290de8e..b39881b 100644 --- a/SomeChartsUi/src/ui/canvas/ICanvasControls.cs +++ b/SomeChartsUi/src/ui/canvas/ICanvasControls.cs @@ -4,8 +4,25 @@ namespace SomeChartsUi.ui.canvas; //TODO: method arguments is a subject of change public interface ICanvasControls { - public void OnMouseMove(float2 pointerPos) {} - public void OnMouseDown(PointerButtons buttons, keymods mods) {} - public void OnMouseUp(PointerButtons buttons, keymods mods) {} + public void OnMouseMove(MouseState state) {} + public void OnMouseDown(MouseState state) {} + public void OnMouseUp(MouseState state) {} + public void OnMouseScroll(MouseState state) {} public void OnKey(keycode key, keymods mods) {} +} + +public class MouseState { + public float2 pos; + public float2 wheel; + public PointerButtons buttons; + public keymods modifiers; + public object? capture; + + public MouseState(float2 pos, float2 wheel, PointerButtons buttons, keymods modifiers, object? capture) { + this.pos = pos; + this.wheel = wheel; + this.buttons = buttons; + this.modifiers = modifiers; + this.capture = capture; + } } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/controls/CanvasCompositeController.cs b/SomeChartsUi/src/ui/canvas/controls/CanvasCompositeController.cs new file mode 100644 index 0000000..aa7b83f --- /dev/null +++ b/SomeChartsUi/src/ui/canvas/controls/CanvasCompositeController.cs @@ -0,0 +1,33 @@ +namespace SomeChartsUi.ui.canvas.controls; + +public class CanvasCompositeController : ChartCanvasControllerBase { + public List controllers = new(); + + public CanvasCompositeController(ChartsCanvas owner) : base(owner) { } + + public override void OnUpdate(float deltatime) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnUpdate(deltatime); + } + + public override void OnMouseMove(MouseState state) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnMouseMove(state); + } + public override void OnMouseDown(MouseState state) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnMouseDown(state); + } + public override void OnMouseUp(MouseState state) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnMouseUp(state); + } + public override void OnMouseScroll(MouseState state) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnMouseScroll(state); + } + public override void OnKey(keycode key, keymods mods) { + foreach (CanvasCompositeController ctrl in controllers) + ctrl.OnKey(key, mods); + } +} \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/controls/CanvasUiController.cs b/SomeChartsUi/src/ui/canvas/controls/CanvasUiController.cs new file mode 100644 index 0000000..a1f4467 --- /dev/null +++ b/SomeChartsUi/src/ui/canvas/controls/CanvasUiController.cs @@ -0,0 +1,76 @@ +using SomeChartsUi.utils.vectors; + +namespace SomeChartsUi.ui.canvas.controls; + +public class CanvasUiController : ChartCanvasControllerBase { + private float2 _start; + private float2 _origin; + + public float zoomSpeed = .2f; + + public CanvasUiController(ChartsCanvas owner) : base(owner) { } + + public override void OnMouseMove(MouseState state) { + float2 pointerPos = state.pos; + pointerPos.FlipY(); + if (state.capture == null) return; + + float speed = 1; + if ((state.modifiers & keymods.alt) != 0) speed = 4; + + float2 mov = (pointerPos - _start) / owner.transform.zoom.currentValue * speed + _origin; + + SetPosition(mov); + + //_start = e.GetPosition(this); + //_start.FlipY(); + _start = pointerPos; + _origin = mov; + } + public override void OnMouseDown(MouseState state) { + float2 pointerPos = state.pos; + pointerPos.FlipY(); + state.capture = this; + + _start = pointerPos; + + _origin = owner.transform.position.currentValue; + //Cursor = Cursor.Parse("Hand"); + } + public override void OnMouseUp(MouseState state) { + state.capture = null; + //Cursor = Cursor.Default; + } + + public override void OnMouseScroll(MouseState state) { + float2 pointerPos = state.pos; + pointerPos.FlipY(); + bool disableAnim = false; + + float2 zoomAdd = zoomSpeed * state.wheel.yy; + + if ((state.modifiers & keymods.shift) != 0) zoomAdd.x = 0; + if ((state.modifiers & keymods.ctrl) != 0) zoomAdd.y = 0; + if ((state.modifiers & keymods.alt) != 0) { + zoomAdd.x *= 2; + zoomAdd.y *= 2; + disableAnim = true; + } + + float2 oldScale = owner.transform.zoom.currentValue; + float2 newScale = float2.Clamp(oldScale * (1 + zoomAdd), .001f, 1.5f); + // float2 newScale = new(Math.Clamp(oldScale.x * (1 + zoomAdd.x), .001f, 1.5f), Math.Clamp(oldScale.y * (1 + zoomAdd.y), .001f, 1.5f)); + zoomAdd = 1 - newScale / oldScale; + + SetZoom(newScale); + float2 posOffset = new(pointerPos.x * zoomAdd.x / newScale.x, + (owner.transform.screenBounds.height + pointerPos.y) * zoomAdd.y / newScale.y); + + Move(posOffset); + + if (disableAnim) owner.transform.SetAnimToCurrent(); + } + + public override void OnUpdate(float deltatime) { } + +} \ No newline at end of file diff --git a/SomeChartsUi/src/ui/canvas/controls/ChartCanvasControllerBase.cs b/SomeChartsUi/src/ui/canvas/controls/ChartCanvasControllerBase.cs index f68e5a4..24520f5 100644 --- a/SomeChartsUi/src/ui/canvas/controls/ChartCanvasControllerBase.cs +++ b/SomeChartsUi/src/ui/canvas/controls/ChartCanvasControllerBase.cs @@ -2,7 +2,7 @@ using SomeChartsUi.utils.vectors; namespace SomeChartsUi.ui.canvas.controls; -public abstract class ChartCanvasControllerBase : ICanvasUpdate { +public abstract class ChartCanvasControllerBase : ICanvasUpdate, ICanvasControls { protected ChartsCanvas owner; protected ChartCanvasControllerBase(ChartsCanvas owner) => this.owner = owner; @@ -30,4 +30,16 @@ public abstract class ChartCanvasControllerBase : ICanvasUpdate { owner.transform.position.OnUpdate(1000); owner.transform.zoom.OnUpdate(1000); } + + public virtual void OnMouseMove(MouseState state) { + + } + public virtual void OnMouseDown(MouseState state) { + } + public virtual void OnMouseUp(MouseState state) { + } + public virtual void OnMouseScroll(MouseState state) { + } + public virtual void OnKey(keycode key, keymods mods) { + } } \ No newline at end of file diff --git a/SomeChartsUi/src/ui/layers/CanvasLayer.cs b/SomeChartsUi/src/ui/layers/CanvasLayer.cs index 60da839..6cfa739 100644 --- a/SomeChartsUi/src/ui/layers/CanvasLayer.cs +++ b/SomeChartsUi/src/ui/layers/CanvasLayer.cs @@ -5,16 +5,23 @@ using SomeChartsUi.ui.elements; namespace SomeChartsUi.ui.layers; public class CanvasLayer { - public ChartsCanvas owner; + public readonly string name; + public readonly ChartsCanvas owner; public color? background; - public List elements = new(); + public readonly List elements = new(); - public CanvasLayer(ChartsCanvas owner) => this.owner = owner; + public CanvasLayer(ChartsCanvas owner, string name) { + this.owner = owner; + this.name = name; + } public void AddElement(RenderableBase r) => elements.Add(r); public void RemoveElement(RenderableBase r) => elements.Remove(r); public void Render() { - + if (background != null) owner.renderer.backend.DrawRect(owner.transform.screenBounds, background.Value); + foreach (RenderableBase element in elements) { + element.Render(owner); + } } } \ No newline at end of file diff --git a/SomeChartsUi/src/utils/rects/rect.cs b/SomeChartsUi/src/utils/rects/rect.cs index 26747d0..5b44577 100644 --- a/SomeChartsUi/src/utils/rects/rect.cs +++ b/SomeChartsUi/src/utils/rects/rect.cs @@ -11,6 +11,11 @@ public struct rect { public float right => left + width; public float top => bottom + height; + public float2 leftBottom => new(left, bottom); + public float2 leftTop => new(left, top); + public float2 rightTop => new(right, top); + public float2 rightBottom => new(right, bottom); + public float midX => left + width * .5f; public float midY => bottom + height * .5f; diff --git a/SomeChartsUi/src/utils/vectors/float2.cs b/SomeChartsUi/src/utils/vectors/float2.cs index 53aeab7..263a127 100644 --- a/SomeChartsUi/src/utils/vectors/float2.cs +++ b/SomeChartsUi/src/utils/vectors/float2.cs @@ -28,6 +28,8 @@ public struct float2 { public void FlipY() => y = -y; public void FlipX() => x = -x; public void Flip() => (x, y) = (-x, -y); + + public static implicit operator float2(float v) => new(v); public static implicit operator float2(int v) => new(v); @@ -62,8 +64,14 @@ public struct float2 { public static float2 SinCos(float rad) => new(MathF.Sin(rad), MathF.Cos(rad)); public static float2 SinCos(float rad, float len) => new(MathF.Sin(rad) * len, MathF.Cos(rad) * len); + + public static float2 Min(float2 a, float2 b) => new(math.min(a.x, b.x), math.min(a.y, b.y)); + public static float2 Max(float2 a, float2 b) => new(math.max(a.x, b.x), math.max(a.y, b.y)); + public static float2 Clamp(float2 v, float2 min, float2 max) => Max(Min(v, max), min); public bool Equals(float2 other) => x == other.x && y == other.y; public override bool Equals(object? obj) => obj is float2 other && Equals(other); public override int GetHashCode() => HashCode.Combine(x, y); + + public override string ToString() => $"({x},{y})"; } \ No newline at end of file diff --git a/SomeChartsUiAvalonia/MainWindow.axaml b/SomeChartsUiAvalonia/MainWindow.axaml index 9a9670f..8cb017d 100644 --- a/SomeChartsUiAvalonia/MainWindow.axaml +++ b/SomeChartsUiAvalonia/MainWindow.axaml @@ -2,8 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:controls="clr-namespace:SomeChartsUiAvalonia.controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SomeChartsUiAvalonia.MainWindow" Title="SomeChartsUiAvalonia"> - Welcome to Avalonia! + + diff --git a/SomeChartsUiAvalonia/SomeChartsUiAvalonia.csproj b/SomeChartsUiAvalonia/SomeChartsUiAvalonia.csproj index 6dc48cb..3abd82f 100644 --- a/SomeChartsUiAvalonia/SomeChartsUiAvalonia.csproj +++ b/SomeChartsUiAvalonia/SomeChartsUiAvalonia.csproj @@ -25,7 +25,6 @@ - diff --git a/SomeChartsUiAvalonia/src/backends/SkiaChartsBackend.cs b/SomeChartsUiAvalonia/src/backends/SkiaChartsBackend.cs index e8d401f..cc28633 100644 --- a/SomeChartsUiAvalonia/src/backends/SkiaChartsBackend.cs +++ b/SomeChartsUiAvalonia/src/backends/SkiaChartsBackend.cs @@ -48,16 +48,21 @@ public class SkiaChartsBackend : ChartsBackendBase, IDisposable { // skia using flipped y axis, so flip back it canvas.Scale(1,-1); canvas.Translate(0, -owner.transform.screenBounds.height); + + canvas.Scale((owner.transform.zoom.animatedValue).sk()); + canvas.Translate(owner.transform.position.animatedValue.sk()); - _canvas?.Dispose(); _canvas = canvas; _paint ??= new(); + + _paint.SubpixelText = true; + _paint.Color = SKColors.White; + _paint.StrokeWidth = 2; + //_paint.Typeface = owner.theme.uiFontName; + _paint.IsAntialias = true; } - - public SkiaChartsBackend(ChartsCanvas owner, ChartCanvasRenderer renderer) : base(owner, renderer) { } public void Dispose() { - _canvas?.Dispose(); _paint?.Dispose(); } } \ No newline at end of file diff --git a/SomeChartsUiAvalonia/src/controls/AvaloniaChartsCanvas.cs b/SomeChartsUiAvalonia/src/controls/AvaloniaChartsCanvas.cs new file mode 100644 index 0000000..a0cdbff --- /dev/null +++ b/SomeChartsUiAvalonia/src/controls/AvaloniaChartsCanvas.cs @@ -0,0 +1,233 @@ +using System; +using System.Threading; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using SkiaSharp; +using SomeChartsUi.elements.other; +using SomeChartsUi.themes.colors; +using SomeChartsUi.ui; +using SomeChartsUi.ui.canvas; +using SomeChartsUi.ui.canvas.controls; +using SomeChartsUi.ui.elements; +using SomeChartsUi.ui.layers; +using SomeChartsUi.utils.vectors; +using SomeChartsUiAvalonia.backends; +using SomeChartsUiAvalonia.utils; + +namespace SomeChartsUiAvalonia.controls; + +public class AvaloniaChartsCanvas : Panel { + public ChartsCanvas canvas = CreateCanvas(); + + public TimeSpan zoomUpdateTime; + public TimeSpan panUpdateTime; + public int updateInterval_hover = 16; + public int updateInterval = 200; + public string chartName = "???"; + public bool stopRender; + private Timer? _updateTimer; + private TimeSpan _prevUpdTime; + + private static ChartsCanvas CreateCanvas() { + ChartsCanvas canvas = new(new SkiaChartsBackend()); + canvas.AddLayer("bg"); + canvas.AddLayer("normal"); + canvas.AddLayer("top"); + + return canvas; + } + + public AvaloniaChartsCanvas() { + _updateTimer = new(_ => Update(), null, 0, 10); + canvas.controller = new CanvasUiController(canvas); + canvas.GetLayer("bg")!.background = color.purple; + AddElement(new TestRenderable()); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) { + PointerPoint currentPoint = e.GetCurrentPoint(this); + + PointerButtons buttons = default; + if (currentPoint.Properties.IsLeftButtonPressed) buttons |= PointerButtons.left; + if (currentPoint.Properties.IsRightButtonPressed) buttons |= PointerButtons.right; + if (currentPoint.Properties.IsMiddleButtonPressed) buttons |= PointerButtons.middle; + if (currentPoint.Properties.IsXButton1Pressed) buttons |= PointerButtons.forward; + if (currentPoint.Properties.IsXButton2Pressed) buttons |= PointerButtons.backward; + + keymods mods = default; + if ((e.KeyModifiers & KeyModifiers.Shift) != 0) mods |= keymods.shift; + if ((e.KeyModifiers & KeyModifiers.Control) != 0) mods |= keymods.ctrl; + if ((e.KeyModifiers & KeyModifiers.Alt) != 0) mods |= keymods.alt; + if ((e.KeyModifiers & KeyModifiers.Meta) != 0) mods |= keymods.super; + + MouseState s = new(currentPoint.Position.ch(), float2.zero, buttons, mods, currentPoint.Pointer.Captured); + + canvas.controller?.OnMouseDown(s); + } + protected override void OnPointerMoved(PointerEventArgs e) { + PointerPoint currentPoint = e.GetCurrentPoint(this); + + PointerButtons buttons = default; + if (currentPoint.Properties.IsLeftButtonPressed) buttons |= PointerButtons.left; + if (currentPoint.Properties.IsRightButtonPressed) buttons |= PointerButtons.right; + if (currentPoint.Properties.IsMiddleButtonPressed) buttons |= PointerButtons.middle; + if (currentPoint.Properties.IsXButton1Pressed) buttons |= PointerButtons.forward; + if (currentPoint.Properties.IsXButton2Pressed) buttons |= PointerButtons.backward; + + keymods mods = default; + if ((e.KeyModifiers & KeyModifiers.Shift) != 0) mods |= keymods.shift; + if ((e.KeyModifiers & KeyModifiers.Control) != 0) mods |= keymods.ctrl; + if ((e.KeyModifiers & KeyModifiers.Alt) != 0) mods |= keymods.alt; + if ((e.KeyModifiers & KeyModifiers.Meta) != 0) mods |= keymods.super; + + MouseState s = new(currentPoint.Position.ch(), float2.zero, buttons, mods, currentPoint.Pointer.Captured); + + canvas.controller?.OnMouseMove(s); + } + protected override void OnPointerReleased(PointerReleasedEventArgs e) { + PointerPoint currentPoint = e.GetCurrentPoint(this); + + PointerButtons buttons = default; + if (currentPoint.Properties.IsLeftButtonPressed) buttons |= PointerButtons.left; + if (currentPoint.Properties.IsRightButtonPressed) buttons |= PointerButtons.right; + if (currentPoint.Properties.IsMiddleButtonPressed) buttons |= PointerButtons.middle; + if (currentPoint.Properties.IsXButton1Pressed) buttons |= PointerButtons.forward; + if (currentPoint.Properties.IsXButton2Pressed) buttons |= PointerButtons.backward; + + keymods mods = default; + if ((e.KeyModifiers & KeyModifiers.Shift) != 0) mods |= keymods.shift; + if ((e.KeyModifiers & KeyModifiers.Control) != 0) mods |= keymods.ctrl; + if ((e.KeyModifiers & KeyModifiers.Alt) != 0) mods |= keymods.alt; + if ((e.KeyModifiers & KeyModifiers.Meta) != 0) mods |= keymods.super; + + MouseState s = new(currentPoint.Position.ch(), float2.zero, buttons, mods, currentPoint.Pointer.Captured); + + canvas.controller?.OnMouseUp(s); + } + protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { + PointerPoint currentPoint = e.GetCurrentPoint(this); + + PointerButtons buttons = default; + if (currentPoint.Properties.IsLeftButtonPressed) buttons |= PointerButtons.left; + if (currentPoint.Properties.IsRightButtonPressed) buttons |= PointerButtons.right; + if (currentPoint.Properties.IsMiddleButtonPressed) buttons |= PointerButtons.middle; + if (currentPoint.Properties.IsXButton1Pressed) buttons |= PointerButtons.forward; + if (currentPoint.Properties.IsXButton2Pressed) buttons |= PointerButtons.backward; + + keymods mods = default; + if ((e.KeyModifiers & KeyModifiers.Shift) != 0) mods |= keymods.shift; + if ((e.KeyModifiers & KeyModifiers.Control) != 0) mods |= keymods.ctrl; + if ((e.KeyModifiers & KeyModifiers.Alt) != 0) mods |= keymods.alt; + if ((e.KeyModifiers & KeyModifiers.Meta) != 0) mods |= keymods.super; + + MouseState s = new(currentPoint.Position.ch(), e.Delta.ch(), buttons, mods, currentPoint.Pointer.Captured); + + canvas.controller?.OnMouseScroll(s); + } + + protected override void OnKeyUp(KeyEventArgs e) { + // if (e.Key == Key.R) { + // if ((e.KeyModifiers & KeyModifiers.Shift) != 0) ResetTransform(); + // else ResetXYZoom(); + // } + // + // if (e.Key == Key.T) ToggleTheme(); + // + // if (e.Key == Key.P) { + // UpdateTransformAnimation(); + // using var img = RenderImage(); + // using var data = img.Encode(SKEncodedImageFormat.Png, 100); + // + // using FileStream fs = new(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + $"/SomeChartsSnapshot_{new Random().Next(100_000, 999_999):X}.png", FileMode.Create); + // data.SaveTo(fs); + // Console.WriteLine("aaaa"); + // } + // + // if (e.Key == Key.W) positionWithoutAnim.Y -= 100 / scale.Y; + // if (e.Key == Key.S) positionWithoutAnim.Y += 100 / scale.Y; + // if (e.Key == Key.A) positionWithoutAnim.X += 100 / scale.X; + // if (e.Key == Key.D) positionWithoutAnim.X -= 100 / scale.X; + } + + public void Rebuild() { + canvas.transform.screenBounds = Bounds.ch(); + canvas.transform.Update(); + InvalidateVisual(); + } + + private void Update() { + try { + if (stopRender || !CheckUpdateDelay()) return; + //UpdateAnim(); + Rebuild(); + _prevUpdTime = DateTime.Now.TimeOfDay; + } + catch (Exception e) { + // ignored + } + } + + private bool CheckUpdateDelay() { + if (Environment.HasShutdownStarted) { + _updateTimer = null; + stopRender = true; + return false; + } + + var now = DateTime.Now.TimeOfDay; + var maxDiff = TimeSpan.FromMilliseconds(IsPointerOver ? updateInterval_hover : updateInterval); + return now - _prevUpdTime >= maxDiff; + } + + public override void Render(DrawingContext context) { + canvas.transform.screenBounds = Bounds.ch(); + //renderer!.bounds = Bounds; + //renderer.position = position; + //renderer.scale = scale; + //renderer.theme = theme; + context.Custom(new CustomRender(canvas, Bounds)); + } + + public SKImage RenderImage() => throw new NotImplementedException(); + // SKImageInfo info = new((int)Bounds.Width, (int)Bounds.Height); + // + // using var surf = SKSurface.Create(info); + // + // var canvas = surf.Canvas; + // canvas.Scale(1, -1); + // canvas.Translate(0, (float)-Bounds.Height); + // renderer!.Render(); + // canvas.DrawBitmap(renderer.snapshot, renderer.bounds); + // return surf.Snapshot(); + public void AddElement(RenderableBase el, string layer = "normal") { + (canvas.GetLayer(layer) ?? canvas.GetLayer(1)).AddElement(el); + } + + public void RemoveElement(RenderableBase el, string layer = "normal") { + (canvas.GetLayer(layer) ?? canvas.GetLayer(1)).RemoveElement(el); + } + + private class CustomRender : ICustomDrawOperation { + private readonly ChartsCanvas _owner; + public CustomRender(ChartsCanvas owner, Rect bounds) { + _owner = owner; + Bounds = bounds; + } + public Rect Bounds { get; } + public void Dispose() { } + public bool HitTest(Point p) => Bounds.Contains(p); + public bool Equals(ICustomDrawOperation? other) => false; + + public void Render(IDrawingContextImpl context) { + ((SkiaChartsBackend)_owner.renderer.backend).SetRenderingVariables(context); + //((ISkiaDrawingContextImpl) context).SkCanvas.Clear(); + foreach (CanvasLayer layer in _owner.renderer!.layers) + layer.Render(); + } + } +} \ No newline at end of file diff --git a/SomeChartsUiAvalonia/src/utils/SkiaChartsUtils.cs b/SomeChartsUiAvalonia/src/utils/SkiaChartsUtils.cs index 8181d63..271cf9d 100644 --- a/SomeChartsUiAvalonia/src/utils/SkiaChartsUtils.cs +++ b/SomeChartsUiAvalonia/src/utils/SkiaChartsUtils.cs @@ -1,3 +1,4 @@ +using Avalonia; using SkiaSharp; using SomeChartsUi.themes.colors; using SomeChartsUi.utils.rects; @@ -6,12 +7,15 @@ using SomeChartsUi.utils.vectors; namespace SomeChartsUiAvalonia.utils; public static class SkiaChartsUtils { - public static SKRect sk(this rect v) => new(v.left, v.bottom, v.right, v.top); // skia using inverted y axis, so swap bottom and top - public static rect ch(this SKRect v) => new(v.Left, v.Bottom, v.Right, v.Top); // skia using inverted y axis, so swap bottom and top + public static SKRect sk(this rect v) => new(v.left, v.bottom, v.right, v.top); // skia using inverted y axis, so swap bottom and top + public static rect ch(this SKRect v) => new(v.Left, v.Bottom, v.Right, v.Top); // skia using inverted y axis, so swap bottom and top + public static rect ch(this Rect v) => new((float)v.Left, (float) v.Bottom, (float)v.Right, (float)v.Top);// skia using inverted y axis, so swap bottom and top public static SKColor sk(this color v) => new(v.raw); public static color ch(this SKColor v) => new(v.Red, v.Green, v.Blue, v.Alpha); public static SKPoint sk(this float2 v) => new(v.x, v.y); public static float2 ch(this SKPoint v) => new(v.X, v.Y); + public static float2 ch(this Point v) => new((float)v.X, (float)v.Y); + public static float2 ch(this Vector v) => new((float)v.X, (float)v.Y); } \ No newline at end of file