From 733bae29f35b97e108a8f138bcc4b78ecc0994dd Mon Sep 17 00:00:00 2001 From: Creeper Lv Date: Mon, 5 Jan 2026 03:57:09 +1100 Subject: [PATCH] Rectangle now have a IsStoke property. Added math functions to ExecutionEngine. --- Progrart.Core/Graphics/Rectangle.cs | 6 ++- Progrart.Core/Graphics/VisualRoot.cs | 24 ++++----- Progrart.Core/JSExecution/ExecutionEngine.cs | 49 +++++++++++++++++++ Progrart.Core/JSExecution/ProgrartExecutor.cs | 38 ++++++++++---- Progrart.Core/PrimitiveDrawingCore.cs | 39 +++++++++++++++ Progrart.Core/RenderContext.cs | 45 ++--------------- Progrart/Pages/AboutPage.axaml | 9 ++-- Progrart/Pages/Console.axaml.cs | 5 +- Progrart/Views/MainView.axaml | 2 +- Progrart/Views/MainView.axaml.cs | 30 ++++++++++++ 10 files changed, 180 insertions(+), 67 deletions(-) create mode 100644 Progrart.Core/PrimitiveDrawingCore.cs diff --git a/Progrart.Core/Graphics/Rectangle.cs b/Progrart.Core/Graphics/Rectangle.cs index 34734b0..695dc7c 100644 --- a/Progrart.Core/Graphics/Rectangle.cs +++ b/Progrart.Core/Graphics/Rectangle.cs @@ -13,6 +13,7 @@ namespace Progrart.Core.Graphics SKPoint Start; SKPoint Size; SKColorF Color; + bool IsStroke; SKShader? shader = null; public override void SetupProperties(Engine engine) { @@ -26,6 +27,7 @@ namespace Progrart.Core.Graphics __object.Set("Position", point); } __object.Set("StrokeWidth", 1); + __object.Set("IsStroke", true); __object.Set("Color", ProgrartFunctions.color(engine, 1, 1, 1, 1)); { JsObject point = new JsObject(engine); @@ -42,6 +44,7 @@ namespace Progrart.Core.Graphics if (__object is not null) { StrokeWidth = (float)__object.Get("StrokeWidth").AsNumber(); + IsStroke = (bool)__object.Get("IsStroke").AsBoolean(); { if (__object.Get("Position") is JsObject Start) { @@ -79,7 +82,8 @@ namespace Progrart.Core.Graphics { ColorF = Color, StrokeWidth = context.TranslateSize(StrokeWidth), - Shader = shader + Shader = shader, + IsStroke = true } ); } diff --git a/Progrart.Core/Graphics/VisualRoot.cs b/Progrart.Core/Graphics/VisualRoot.cs index 9c9ee77..5ea9fd4 100644 --- a/Progrart.Core/Graphics/VisualRoot.cs +++ b/Progrart.Core/Graphics/VisualRoot.cs @@ -1,5 +1,6 @@ using Jint; using Jint.Native; +using System.Diagnostics; namespace Progrart.Core.Graphics { @@ -24,6 +25,7 @@ namespace Progrart.Core.Graphics float rotate; void Transform(RenderContext context) { + Trace.WriteLine($"Visual Root: Rotation:{rotate}"); context.canvas.Translate(tx, ty); context.canvas.RotateDegrees(rotate, context.DrawingCore.Width / 2, context.DrawingCore.Height / 2); if (scale != 1) @@ -33,7 +35,7 @@ namespace Progrart.Core.Graphics { if (scale != 1) context.canvas.Scale(1 / scale, 1 / scale, context.DrawingCore.Width / 2, context.DrawingCore.Height / 2); - context.canvas.RotateDegrees(rotate, context.DrawingCore.Width / 2, context.DrawingCore.Height / 2); + context.canvas.RotateDegrees(-rotate, context.DrawingCore.Width / 2, context.DrawingCore.Height / 2); context.canvas.Translate(-tx, -ty); } public override void LoadProperties() @@ -41,20 +43,20 @@ namespace Progrart.Core.Graphics base.LoadProperties(); if (__object is null) return; { - if (__object.Get("translate") is JsObject translate) + if (__object.Get("Translate") is JsObject translate) { tx = (float)translate.Get("x").AsNumber(); ty = (float)translate.Get("y").AsNumber(); } } { - scale = (float)__object.Get("scale").AsNumber(); + scale = (float)__object.Get("Scale").AsNumber(); } { - rotate = (float)__object.Get("rotation").AsNumber(); + rotate = (float)__object.Get("Rotation").AsNumber(); } - w = (float)__object.Get("width").AsNumber(); - h = (float)__object.Get("height").AsNumber(); + w = (float)__object.Get("Width").AsNumber(); + h = (float)__object.Get("Height").AsNumber(); } public override void Render(RenderContext context) { @@ -76,16 +78,16 @@ namespace Progrart.Core.Graphics JsObject point = new JsObject(engine); point.Set("x", 0); point.Set("y", 0); - __object.Set("translate", point); + __object.Set("Translate", point); } { - __object.Set("scale", 1); + __object.Set("Scale", 1); } { - __object.Set("rotation", 0); + __object.Set("Rotation", 0); } - __object.Set("width", 1); - __object.Set("height", 1); + __object.Set("Width", 1); + __object.Set("Height", 1); } } } diff --git a/Progrart.Core/JSExecution/ExecutionEngine.cs b/Progrart.Core/JSExecution/ExecutionEngine.cs index e856fe4..2756ecb 100644 --- a/Progrart.Core/JSExecution/ExecutionEngine.cs +++ b/Progrart.Core/JSExecution/ExecutionEngine.cs @@ -1,4 +1,5 @@ using Jint; +using Jint.Native; using System; using System.Collections.Generic; using System.Diagnostics; @@ -6,6 +7,32 @@ using System.Text; namespace Progrart.Core.JSExecution { + public class MathFunctions + { + public static double abs(JsNumber v) => Math.Abs(v.AsNumber()); + public static double sin(JsNumber v) => Math.Sin(v.AsNumber()); + public static double cos(JsNumber v) => Math.Cos(v.AsNumber()); + public static double tan(JsNumber v) => Math.Tan(v.AsNumber()); + public static double tanh(JsNumber v) => Math.Tanh(v.AsNumber()); + public static double asin(JsNumber v) => Math.Asin(v.AsNumber()); + public static double acos(JsNumber v) => Math.Acos(v.AsNumber()); + public static double atan(JsNumber v) => Math.Atan(v.AsNumber()); + public static double atan2(JsNumber v, JsNumber v2) => Math.Atan2(v.AsNumber(), v2.AsNumber()); + public static double atanh(JsNumber v) => Math.Atanh(v.AsNumber()); + public static double sqrt(JsNumber v) => Math.Sqrt(v.AsNumber()); + public static double log(JsNumber v) => Math.Log(v.AsNumber()); + public static double log2(JsNumber v) => Math.Log2(v.AsNumber()); + public static double log10(JsNumber v) => Math.Log10(v.AsNumber()); + public static double exp(JsNumber v) => Math.Exp(v.AsNumber()); + public static double ceiling(JsNumber v) => Math.Ceiling(v.AsNumber()); + public static double floor(JsNumber v) => Math.Floor(v.AsNumber()); + public static double log_base(JsNumber v, JsNumber v2) => Math.Log(v.AsNumber(), v2.AsNumber()); + public static double pow(JsNumber v, JsNumber v2) => Math.Pow(v.AsNumber(), v2.AsNumber()); + public static double round(JsNumber v) => Math.Round(v.AsNumber()); + public static double sinh(JsNumber v) => Math.Sinh(v.AsNumber()); + public static double cosh(JsNumber v) => Math.Cosh(v.AsNumber()); + public static double cbrt(JsNumber v) => Math.Cbrt(v.AsNumber()); + } public class ExecutionEngine : IDisposable { public Engine Engine; @@ -13,6 +40,28 @@ namespace Progrart.Core.JSExecution public ExecutionEngine() { Engine = new Engine(); + Engine.SetValue("abs", MathFunctions.abs); + Engine.SetValue("sin", MathFunctions.sin); + Engine.SetValue("cos", MathFunctions.cos); + Engine.SetValue("tan", MathFunctions.tan); + Engine.SetValue("tanh", MathFunctions.tanh); + Engine.SetValue("asin", MathFunctions.asin); + Engine.SetValue("acos", MathFunctions.acos); + Engine.SetValue("atan", MathFunctions.atan); + Engine.SetValue("atan2", MathFunctions.atan2); + Engine.SetValue("atanh", MathFunctions.atanh); + Engine.SetValue("sqrt", MathFunctions.sqrt); + Engine.SetValue("cbrt", MathFunctions.cbrt); + Engine.SetValue("pow", MathFunctions.pow); + Engine.SetValue("log", MathFunctions.log); + Engine.SetValue("log_base", MathFunctions.log_base); + Engine.SetValue("log2", MathFunctions.log2); + Engine.SetValue("log10", MathFunctions.log10); + Engine.SetValue("exp", MathFunctions.exp); + Engine.SetValue("ceiling", MathFunctions.ceiling); + Engine.SetValue("floor", MathFunctions.floor); + Engine.SetValue("sinh", MathFunctions.sinh); + Engine.SetValue("cosh", MathFunctions.cosh); } string formSymbol(Dictionary symbols) { diff --git a/Progrart.Core/JSExecution/ProgrartExecutor.cs b/Progrart.Core/JSExecution/ProgrartExecutor.cs index e9aa96a..c63a8f2 100644 --- a/Progrart.Core/JSExecution/ProgrartExecutor.cs +++ b/Progrart.Core/JSExecution/ProgrartExecutor.cs @@ -14,13 +14,13 @@ namespace Progrart.Core.JSExecution public ExecutionEngine engine; public Dictionary ObjectPool = new(); public IStorageProvider StorageProvider; - public ProgrartExecutor(IStorageProvider storageProvider) - { - engine = new ExecutionEngine(); - SetupCalls(); - StorageProvider = storageProvider; - } - public void SetupCalls() + public ProgrartExecutor(IStorageProvider storageProvider) + { + engine = new ExecutionEngine(); + SetupCalls(); + StorageProvider = storageProvider; + } + public void SetupCalls() { Jint.Native.Json.JsonSerializer serializer = new Jint.Native.Json.JsonSerializer(engine.Engine); engine.Engine.SetValue("log", new Action((v) => @@ -37,6 +37,7 @@ namespace Progrart.Core.JSExecution engine.Engine.SetValue("rectangle", rectangle); engine.Engine.SetValue("color4", color4); engine.Engine.SetValue("color3", color3); + engine.Engine.SetValue("color_hex", color_hex); engine.Engine.SetValue("linear_gradient", linear_gradient); engine.Engine.SetValue("radial_gradient", radial_gradient); } @@ -57,6 +58,26 @@ namespace Progrart.Core.JSExecution , b.AsNumber() ); } + float colorFloat(byte b) => ((float)b / (float)byte.MaxValue); + public JsObject color_hex(JsString colorString) + { + byte[] bytes = Convert.FromHexString(colorString.AsString()); + if (bytes.Length == 4) + return ProgrartFunctions.color(engine.Engine + , colorFloat(bytes[0]) + , colorFloat(bytes[1]) + , colorFloat(bytes[2]) + ); + else + if (bytes.Length == 3) + return ProgrartFunctions.color(engine.Engine + , colorFloat(bytes[0]) + , colorFloat(bytes[1]) + , colorFloat(bytes[2]) + , colorFloat(bytes[3]) + ); + else throw new FormatException($"{colorString.AsString()} is not a recognizable color string!"); + } public JsObject visual_root() { return ProgrartFunctions.CreateVisualRoot(this); @@ -85,13 +106,12 @@ namespace Progrart.Core.JSExecution { height = (float)(js_height.AsNumber()); } - RenderContext renderContext = new RenderContext((int)(width * Scale), (int)(width * Scale)) + RenderContext renderContext = new((int)(width * Scale), (int)(width * Scale), StorageProvider) { LogicalW = width, LogicalH = height }; ImageRoot imageRoot = new ImageRoot(); - var img = engine.Engine.Call("main"); if (ObjectPool[$"{img.Get("id")}"] is BaseElement element) imageRoot.Add(element); diff --git a/Progrart.Core/PrimitiveDrawingCore.cs b/Progrart.Core/PrimitiveDrawingCore.cs new file mode 100644 index 0000000..7c916a4 --- /dev/null +++ b/Progrart.Core/PrimitiveDrawingCore.cs @@ -0,0 +1,39 @@ +using Progrart.Core.Storage; +using SkiaSharp; +using System.Diagnostics; + +namespace Progrart.Core +{ + public class PrimitiveDrawingCore : IDisposable + { + internal bool isDisposed = false; + SKSurface surface; + SKImageInfo info; + public readonly int Width; + public readonly int Height; + public SKCanvas canvas { get; } + internal IStorageProvider StorageProvider; + public PrimitiveDrawingCore(int W, int H, IStorageProvider storageProvider) + { + Width = W; + Height = H; + Trace.WriteLine($"Createing Surface as: {W} x {H}"); + info = new SKImageInfo(W, H); + surface = SKSurface.Create(info); + canvas = surface.Canvas; + canvas.Clear(); + StorageProvider = storageProvider; + } + public SKData ToData() + { + return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100); + } + public void Dispose() + { + if (isDisposed) return; + //GC.SuppressFinalize(this); + isDisposed = true; + surface.Dispose(); + } + } +} diff --git a/Progrart.Core/RenderContext.cs b/Progrart.Core/RenderContext.cs index 861f2ff..562667d 100644 --- a/Progrart.Core/RenderContext.cs +++ b/Progrart.Core/RenderContext.cs @@ -1,12 +1,12 @@ -using SkiaSharp; -using System.Diagnostics; +using Progrart.Core.Storage; +using SkiaSharp; namespace Progrart.Core { public class RenderContext { public PrimitiveDrawingCore DrawingCore { get; } - + public IStorageProvider StorageProvider { get => DrawingCore.StorageProvider; } public SKCanvas canvas { get => DrawingCore.canvas; } public float LogicalW; public float LogicalH; @@ -26,39 +26,9 @@ namespace Progrart.Core { return (float)(s * Math.Sqrt(DrawingCore.Width * DrawingCore.Width + DrawingCore.Height * DrawingCore.Height) / Math.Sqrt(LogicalH * LogicalH + LogicalW * LogicalW)); } - public RenderContext(int W, int H) + public RenderContext(int W, int H, IStorageProvider StorageProvider) { - DrawingCore = new PrimitiveDrawingCore(W, H); - } - } - public class PrimitiveDrawingCore : IDisposable - { - internal bool isDisposed = false; - SKSurface surface; - SKImageInfo info; - public readonly int Width; - public readonly int Height; - public SKCanvas canvas { get; } - public PrimitiveDrawingCore(int W, int H) - { - Width = W; - Height = H; - Trace.WriteLine($"Createing Surface as: {W} x {H}"); - info = new SKImageInfo(W, H); - surface = SKSurface.Create(info); - canvas = surface.Canvas; - canvas.Clear(); - } - public SKData ToData() - { - return surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100); - } - public void Dispose() - { - if (isDisposed) return; - //GC.SuppressFinalize(this); - isDisposed = true; - surface.Dispose(); + DrawingCore = new PrimitiveDrawingCore(W, H, StorageProvider); } } [Serializable] @@ -83,9 +53,4 @@ namespace Progrart.Core } } } - [Serializable] - public class RenderConfig - { - public int Scale; - } } diff --git a/Progrart/Pages/AboutPage.axaml b/Progrart/Pages/AboutPage.axaml index 1c3cf62..b432ac9 100644 --- a/Progrart/Pages/AboutPage.axaml +++ b/Progrart/Pages/AboutPage.axaml @@ -5,7 +5,7 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Progrart.Pages.AboutPage"> - + @@ -31,8 +31,9 @@ Version: 0.0.0.0-preview Third-Party Libs Avalonia - github.com/avaloniaui - PanAndZoom - GitHub - wieslawsoltes/PanAndZoom - DialogHost - GitHub - AvaloniaUtils/DialogHost.Avalonia + SkiaSharp - github.com/mono/SkiaSharp + PanAndZoom - github.com/wieslawsoltes/PanAndZoom + DialogHost - github.com/AvaloniaUtils/DialogHost.Avalonia diff --git a/Progrart/Pages/Console.axaml.cs b/Progrart/Pages/Console.axaml.cs index e1d8db6..0ecff94 100644 --- a/Progrart/Pages/Console.axaml.cs +++ b/Progrart/Pages/Console.axaml.cs @@ -6,6 +6,7 @@ using Jint; using Progrart.Commands; using Progrart.Controls.TabSystem; using Progrart.Core; +using Progrart.Core.JSExecution; using System; using System.Threading.Tasks; @@ -13,10 +14,12 @@ namespace Progrart.Pages; public partial class Console : UserControl, ITabPage, IEditorPage { + ExecutionEngine executionEngine; Engine engine; public Console() { - engine = new Engine(); + executionEngine = new ExecutionEngine(); + engine = executionEngine.Engine; InitializeComponent(); engine.SetValue("log", new Action((obj) => { diff --git a/Progrart/Views/MainView.axaml b/Progrart/Views/MainView.axaml index c7b567c..3db4cef 100644 --- a/Progrart/Views/MainView.axaml +++ b/Progrart/Views/MainView.axaml @@ -38,7 +38,7 @@ - + diff --git a/Progrart/Views/MainView.axaml.cs b/Progrart/Views/MainView.axaml.cs index 1cd3fe1..18b50bb 100644 --- a/Progrart/Views/MainView.axaml.cs +++ b/Progrart/Views/MainView.axaml.cs @@ -2,13 +2,17 @@ using Acornima.Ast; using Avalonia.Controls; using Avalonia.Platform.Storage; using Avalonia.Threading; +using Newtonsoft.Json; using Progrart.Commands; using Progrart.Controls; using Progrart.Core; using Progrart.Core.JSExecution; +using Progrart.Core.ProjectSystem; using Progrart.Pages; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; namespace Progrart.Views; @@ -178,4 +182,30 @@ public partial class MainView : UserControl return false; })); } + + private async void CreateProjectMenuItem_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + var topLevel = TopLevel.GetTopLevel(this); + + if (topLevel == null) return; + + var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + { + Title = "Save to ...", + DefaultExtension = ".progrart-project", + FileTypeChoices = new List() { new FilePickerFileType("Progrart Project"){ + Patterns= new List() { ".progrart-project" } + } + } + }); + if ((file is null)) + { + return; + } + using var stream = await file.OpenWriteAsync(); + var txt = JsonConvert.SerializeObject(new Project()); + using var stream_writer = new StreamWriter(stream); + await stream_writer.WriteAsync(txt); + await stream.FlushAsync(); + } } \ No newline at end of file