From 9d786ab096ed36ea384e03ee5cea498790e31eec Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Sun, 17 Jul 2022 20:26:28 +0200 Subject: [PATCH] wip --- Backends/CairoBackend/src/Context.cs | 5 +- Backends/CairoBackend/src/EglBackend.cs | 21 +- Backends/CairoBackend/src/ImageBackend.cs | 9 + Backends/CairoBackend/src/ImageSurface.cs | 4 + Backends/CairoBackend/src/Surface.cs | 2 + Backends/VkvgBackend/Crow.VkvgBackend.csproj | 2 +- Backends/VkvgBackend/src/DefaultBackend.cs | 19 +- Crow.sln | 14 + Crow/Default.style | 2 +- Crow/Icons/expandable.svg | 2 +- Crow/Icons/radiobutton.svg | 18 - Crow/Templates/ColorPicker2.template | 44 +- Crow/src/Fill/BmpPicture.cs | 2 +- Crow/src/Fill/SvgPicture.cs | 1 - Crow/src/Interface.cs | 63 ++- Crow/src/LayoutingQueueItem.cs | 1 + Crow/src/Widgets/HueSelector.cs | 26 +- Crow/src/Widgets/Image.cs | 10 +- Crow/src/Widgets/Widget.cs | 3 +- Directory.Build.props | 2 +- Drawing2D/src/ISurface.cs | 1 + Samples/AnimTest/AnimTest.csproj | 11 + Samples/AnimTest/Animation/AngleAnimation.cs | 19 + Samples/AnimTest/Animation/Animation.cs | 155 ++++++ .../AnimTest/Animation/AnimationTemplate.cs | 110 +++++ Samples/AnimTest/Animation/BezierPath.cs | 34 ++ Samples/AnimTest/Animation/FloatAnimation.cs | 83 ++++ Samples/AnimTest/Animation/FloatAnimation2.cs | 60 +++ Samples/AnimTest/Animation/MathHelper.cs | 16 + Samples/AnimTest/Animation/Path.cs | 51 ++ Samples/AnimTest/Animation/PathAnimation.cs | 53 +++ Samples/AnimTest/Animation/ShakeAnimation.cs | 60 +++ Samples/AnimTest/MiscUtil/ExpressionUtil.cs | 99 ++++ Samples/AnimTest/MiscUtil/NullOp.cs | 59 +++ Samples/AnimTest/MiscUtil/Operator.cs | 449 ++++++++++++++++++ Samples/AnimTest/main.cs | 28 ++ Samples/AnimTest/ui/anim.crow | 10 + Samples/AnimTest/ui/background.crow | 4 + Samples/AnimTest/ui/background.jpg | Bin 0 -> 89291 bytes Samples/BasicTests/BasicTests.cs | 14 +- Samples/HelloWorld/main.cs | 1 + Samples/HelloWorld/ui/helloworld.crow | 9 +- Samples/ShowCase/ShowCase.cs | 37 ++ Samples/ShowCase/ui/showcase.crow | 10 +- .../ui/Interfaces/Experimental/svg.crow | 2 + .../TemplatedContainer/buttons.crow | 62 +++ 46 files changed, 1550 insertions(+), 137 deletions(-) create mode 100644 Samples/AnimTest/AnimTest.csproj create mode 100644 Samples/AnimTest/Animation/AngleAnimation.cs create mode 100644 Samples/AnimTest/Animation/Animation.cs create mode 100644 Samples/AnimTest/Animation/AnimationTemplate.cs create mode 100644 Samples/AnimTest/Animation/BezierPath.cs create mode 100644 Samples/AnimTest/Animation/FloatAnimation.cs create mode 100644 Samples/AnimTest/Animation/FloatAnimation2.cs create mode 100644 Samples/AnimTest/Animation/MathHelper.cs create mode 100644 Samples/AnimTest/Animation/Path.cs create mode 100644 Samples/AnimTest/Animation/PathAnimation.cs create mode 100644 Samples/AnimTest/Animation/ShakeAnimation.cs create mode 100644 Samples/AnimTest/MiscUtil/ExpressionUtil.cs create mode 100644 Samples/AnimTest/MiscUtil/NullOp.cs create mode 100644 Samples/AnimTest/MiscUtil/Operator.cs create mode 100644 Samples/AnimTest/main.cs create mode 100644 Samples/AnimTest/ui/anim.crow create mode 100644 Samples/AnimTest/ui/background.crow create mode 100644 Samples/AnimTest/ui/background.jpg create mode 100644 Samples/common/ui/Interfaces/Experimental/svg.crow create mode 100644 Samples/common/ui/Interfaces/TemplatedContainer/buttons.crow diff --git a/Backends/CairoBackend/src/Context.cs b/Backends/CairoBackend/src/Context.cs index c15ecaa3..f9af1bf1 100644 --- a/Backends/CairoBackend/src/Context.cs +++ b/Backends/CairoBackend/src/Context.cs @@ -661,7 +661,8 @@ namespace Crow.CairoBackend public void RestoreTransformations() => NativeMethods.cairo_set_matrix (handle, ref savedMat); public void SetSource(IPattern pat) => NativeMethods.cairo_set_source (handle, (pat as Pattern).handle); - public void SetSource(ISurface surf, double x = 0, double y = 0) - => NativeMethods.cairo_set_source_surface (handle, (surf as Surface).handle, x, y); + public async void SetSource(ISurface surf, double x = 0, double y = 0) { + NativeMethods.cairo_set_source_surface (handle, surf.Handle, x, y); + } } } diff --git a/Backends/CairoBackend/src/EglBackend.cs b/Backends/CairoBackend/src/EglBackend.cs index 909c3cd9..073793e7 100644 --- a/Backends/CairoBackend/src/EglBackend.cs +++ b/Backends/CairoBackend/src/EglBackend.cs @@ -7,11 +7,13 @@ namespace Crow.CairoBackend { public class EglBackend : CairoBackendBase { EGLDevice device; + GLSurface winSurf; /// /// Create a new generic backend bound to the application surface /// /// backend surface width /// backend surface height + /// public EglBackend (int width, int height, IntPtr nativeWindoPointer) : base (width, height, nativeWindoPointer) { if (nativeWindoPointer == IntPtr.Zero) { @@ -32,7 +34,9 @@ namespace Crow.CairoBackend Glfw3.SwapInterval (0); device = new EGLDevice (Glfw3.GetEGLDisplay (), Glfw3.GetEGLContext (hWin)); - surf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height); + //surf = new GLTextureSurface (device, width, height); + surf = new ImageSurface (Format.ARGB32, width, height); + winSurf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height); } /// /// Create a new offscreen backend, used in perfTests @@ -61,9 +65,20 @@ namespace Crow.CairoBackend } public override void FlushUIFrame(IContext ctx) { - base.FlushUIFrame (ctx); - (surf as GLSurface).SwapBuffers (); + base.FlushUIFrame (ctx); + surf.Flush(); + using (Context gr = new Context (winSurf)) { + gr.SetSource (surf); + gr.Paint(); + } + winSurf.SwapBuffers (); + } + public override void ResizeMainSurface(int width, int height) + { + base.ResizeMainSurface(width, height); + winSurf?.Dispose(); + winSurf = new GLSurface (device, Glfw3.GetEGLSurface (hWin), width, height); } } } diff --git a/Backends/CairoBackend/src/ImageBackend.cs b/Backends/CairoBackend/src/ImageBackend.cs index 96899dbd..a90d275a 100644 --- a/Backends/CairoBackend/src/ImageBackend.cs +++ b/Backends/CairoBackend/src/ImageBackend.cs @@ -79,6 +79,15 @@ namespace Crow.CairoBackend base.FlushUIFrame (ctx); } + public override void ResizeMainSurface(int width, int height) + { + if (surf is ImageSurface) { + surf?.Dispose(); + surf = new ImageSurface (Format.ARGB32, width, height); + } else { + base.ResizeMainSurface(width, height); + } + } } } diff --git a/Backends/CairoBackend/src/ImageSurface.cs b/Backends/CairoBackend/src/ImageSurface.cs index 2cfec61c..8870366a 100644 --- a/Backends/CairoBackend/src/ImageSurface.cs +++ b/Backends/CairoBackend/src/ImageSurface.cs @@ -96,5 +96,9 @@ namespace Crow.CairoBackend { public int Stride { get { return NativeMethods.cairo_image_surface_get_stride (handle); } } + public override void Resize(int width, int height) + { + + } } } diff --git a/Backends/CairoBackend/src/Surface.cs b/Backends/CairoBackend/src/Surface.cs index 80435b5e..1f44d3ab 100644 --- a/Backends/CairoBackend/src/Surface.cs +++ b/Backends/CairoBackend/src/Surface.cs @@ -41,6 +41,8 @@ namespace Crow.CairoBackend { public class Surface : ISurface { internal IntPtr handle = IntPtr.Zero; + public IntPtr Handle => handle; + protected Surface() { diff --git a/Backends/VkvgBackend/Crow.VkvgBackend.csproj b/Backends/VkvgBackend/Crow.VkvgBackend.csproj index 13386d36..b6fbb004 100644 --- a/Backends/VkvgBackend/Crow.VkvgBackend.csproj +++ b/Backends/VkvgBackend/Crow.VkvgBackend.csproj @@ -15,7 +15,7 @@ - + diff --git a/Backends/VkvgBackend/src/DefaultBackend.cs b/Backends/VkvgBackend/src/DefaultBackend.cs index 8521d503..eeed4d0a 100644 --- a/Backends/VkvgBackend/src/DefaultBackend.cs +++ b/Backends/VkvgBackend/src/DefaultBackend.cs @@ -32,7 +32,7 @@ namespace Crow.VkvgBackend protected Fence drawFence; Surface surf; Device vkvgDev; - SampleCount samples = SampleCount.Sample_1; + SampleCount samples = SampleCount.Sample_8; bool vsync = false; bool tryGetPhy (vke.PhysicalDeviceCollection physicalDevices, VkPhysicalDeviceType phyType, out PhysicalDevice phy, bool swapchainSupport) { @@ -189,6 +189,7 @@ namespace Crow.VkvgBackend new SvgHandle (vkvgDev, System.Text.Encoding.Unicode.GetBytes (svgFragment)); bool disposeContextOnFlush; IRegion clipping; + volatile bool resizeMainSurface = false; protected void clear(IContext ctx) { for (int i = 0; i < clipping.NumRectangles; i++) ctx.Rectangle (clipping.GetRectangle (i)); @@ -200,6 +201,11 @@ namespace Crow.VkvgBackend } public override IContext PrepareUIFrame(IContext existingContext, IRegion clipping) { + if (resizeMainSurface) { + createMainSurface (swapChain.Width, swapChain.Height); + clipping.UnionRectangle (new Rectangle(new Size((int)swapChain.Width, (int)swapChain.Height))); + resizeMainSurface = false; + } this.clipping = clipping; IContext ctx = existingContext; if (ctx == null) { @@ -216,23 +222,24 @@ namespace Crow.VkvgBackend { if (disposeContextOnFlush) ctx.Dispose (); - clipping = null; - - dev.WaitIdle(); int idx = swapChain.GetNextImage (); if (idx < 0) { - createMainSurface (swapChain.Width, swapChain.Height); + resizeMainSurface = true; return; } + clipping = null; + + //dev.WaitIdle(); + drawFence.Wait (); drawFence.Reset (); graphicQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence); (graphicQueue as PresentQueue).Present (swapChain, drawComplete[idx]); - dev.WaitIdle(); + //dev.WaitIdle(); } public override void ResizeMainSurface (int width, int height) { //resize is done on swapchain image aquisition failure diff --git a/Crow.sln b/Crow.sln index 3f256efd..df9d9f8c 100644 --- a/Crow.sln +++ b/Crow.sln @@ -31,6 +31,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.VkvgBackend", "Backend EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.SkiaBackend", "Backends\SkiaBackend\Crow.SkiaBackend.csproj", "{ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicTests", "Samples\BasicTests\BasicTests.csproj", "{1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnimTest", "Samples\AnimTest\AnimTest.csproj", "{17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -65,6 +69,14 @@ Global {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175}.Release|Any CPU.Build.0 = Release|Any CPU + {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA}.Release|Any CPU.Build.0 = Release|Any CPU + {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -76,6 +88,8 @@ Global {E06441A9-0CFD-45BB-9478-99D28CEB327F} = {451F5727-2A2E-4361-A41B-089429ADE8F9} {34976828-80CF-4AC5-8C81-F66F635DC5FC} = {451F5727-2A2E-4361-A41B-089429ADE8F9} {ADAABC24-0152-41E2-BBEC-3AC9ACEE1175} = {451F5727-2A2E-4361-A41B-089429ADE8F9} + {1A0FF3B6-E97B-4F1D-9CDE-9C54742317BA} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} + {17DEFFB4-AA0C-4C92-999B-1F3DD4BB9AA1} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {00D4E149-7131-49F4-BAAD-559AA961A78E} diff --git a/Crow/Default.style b/Crow/Default.style index 066eb21b..6e14f802 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -346,6 +346,6 @@ MessageBox, Popper, Slider, Spinner, TextInheritBox, NumericControl { Margin="0"; BubbleEvents="MouseWheel|Keyboard"; } -TemplatedControl, GenericStack { +TemplatedControl, GenericStack, Image { CacheEnabled="true"; } diff --git a/Crow/Icons/expandable.svg b/Crow/Icons/expandable.svg index e2bffbc5..a689ee98 100644 --- a/Crow/Icons/expandable.svg +++ b/Crow/Icons/expandable.svg @@ -1,4 +1,4 @@ - + --> diff --git a/Crow/Icons/radiobutton.svg b/Crow/Icons/radiobutton.svg index b102b3e1..49a0c328 100644 --- a/Crow/Icons/radiobutton.svg +++ b/Crow/Icons/radiobutton.svg @@ -6,37 +6,19 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" id="svg2" - width="64" - height="64" viewBox="0 0 64 64" version="1.1"> - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs index 813e8dec..7cf8983c 100644 --- a/Crow/src/Fill/BmpPicture.cs +++ b/Crow/src/Fill/BmpPicture.cs @@ -95,7 +95,7 @@ namespace Crow gr.Scale (widthRatio, heightRatio); gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - using (ISurface imgSurf = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs index e6e54aff..e2c73bbd 100644 --- a/Crow/src/Fill/SvgPicture.cs +++ b/Crow/src/Fill/SvgPicture.cs @@ -69,7 +69,6 @@ namespace Crow else widthRatio = heightRatio; } - using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { using (IContext gr = iFace.Backend.CreateContext (tmp)) { gr.Translate (bounds.Left, bounds.Top); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 1505ed12..507bed69 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -160,39 +160,42 @@ namespace Crow /// backends are search where the main crow assembly is. /// public static string BackendsDirectory = null; + protected static Type getBackendType (IEnumerable backendTypes) { + if (backendTypes == null) + return null; + switch (PreferedBackendType) { + case BackendType.Default: + return backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend"); + case BackendType.Egl: + return backendTypes.FirstOrDefault(be => be.Name == "EglBackend"); + case BackendType.Vulkan: + return backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend"); + case BackendType.Gl: + return backendTypes.FirstOrDefault(be => be.Name == "GlBackend"); + default: + return backendTypes.FirstOrDefault(); + } + } protected static bool tryFindBackendType (out Type backendType) { backendType = default; - //IEnumerable backendAssemblies = Assembly.GetEntryAssembly ().GetReferencedAssemblies().Where (ra=>backends.Contains (ra.Name)); + //search loaded assemblies System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); foreach (Assembly a in ldCtx.Assemblies.Where (asb => backends.Contains (asb.GetName ().Name))) { - IEnumerable backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract); - if (backendTypes != null) { - backendType = backendTypes.FirstOrDefault(); + backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); + if (backendType != null) return true; - } } + string bp = (!string.IsNullOrEmpty(BackendsDirectory) && Directory.Exists(BackendsDirectory)) ? BackendsDirectory : Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location); + //search backend directory foreach (string b in backends) { string bPath = Path.Combine (bp,$"Crow.{b}.dll"); if (File.Exists (bPath)) { Assembly a = ldCtx.LoadFromAssemblyPath (bPath); - IEnumerable backendTypes = a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract); - if (backendTypes != null) { - if (PreferedBackendType == BackendType.Default) - backendType = backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend"); - else if (PreferedBackendType == BackendType.Egl) - backendType = backendTypes.FirstOrDefault(be => be.Name == "EglBackend"); - else if (PreferedBackendType == BackendType.Vulkan) - backendType = backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend"); - else if (PreferedBackendType == BackendType.Gl) - backendType = backendTypes.FirstOrDefault(be => be.Name == "GlBackend"); - - if (backendType == null) - backendType = backendTypes.FirstOrDefault(); - return backendType != null; - } + backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); + return backendType != null; } } @@ -433,9 +436,7 @@ namespace Crow Glfw3.SetCursorPosCallback (hWin, HandleCursorPosDelegate); Glfw3.SetScrollCallback (hWin, HandleScrollDelegate); Glfw3.SetCharCallback (hWin, HandleCharDelegate); -#if !VKVG//resize is processed on context.Render failing Glfw3.SetWindowSizeCallback (hWin, HandleWindowSizeDelegate); -#endif Glfw3.SetWindowRefreshCallback (hWin, HandleWindowRefreshDelegate); } @@ -451,7 +452,7 @@ namespace Crow /// /// search for graphic object type in crow assembly, if not found, /// search for type independently of namespace in all the loaded assemblies - /// + /// /// /// /// the corresponding type object @@ -675,7 +676,13 @@ namespace Crow Terminate (); } public virtual void Terminate () {} - public virtual void UpdateFrame () {} + int cpt; + public virtual void UpdateFrame () { + if (cpt++ > 1000) { + PerformanceMeasure.Notify (); + cpt=0; + } + } public virtual void Quit () => Glfw3.SetWindowShouldClose (hWin, 1); public bool Shift => Glfw3.GetKey(hWin, Key.LeftShift) == InputAction.Press || @@ -1176,8 +1183,6 @@ namespace Crow Monitor.Exit (UpdateMutex); } - - PerformanceMeasure.Notify (); } /// Layouting loop, this is the first step of the udpate and process registered /// Layouting queue items. Failing LQI's are requeued in this cycle until MaxTry is reached which @@ -1610,6 +1615,7 @@ namespace Crow DbgLogger.StartEvent (DbgEvtType.MouseMove); try { + Monitor.Enter (UpdateMutex); int deltaX = x - MousePosition.X; int deltaY = y - MousePosition.Y; @@ -1723,6 +1729,7 @@ namespace Crow HoverOrDropTarget = null; return false; } finally { + Monitor.Exit (UpdateMutex); DbgLogger.EndEvent (DbgEvtType.MouseMove); } } @@ -1736,6 +1743,7 @@ namespace Crow DbgLogger.StartEvent (DbgEvtType.MouseDown); try { + Monitor.Enter (UpdateMutex); doubleClickTriggered = (lastMouseDown.ElapsedMilliseconds < DOUBLECLICK_TRESHOLD); lastMouseDown.Restart (); @@ -1750,6 +1758,7 @@ namespace Crow return true; } finally { + Monitor.Exit (UpdateMutex); DbgLogger.EndEvent (DbgEvtType.MouseDown); } } @@ -1763,6 +1772,7 @@ namespace Crow DbgLogger.StartEvent (DbgEvtType.MouseUp); try { + Monitor.Enter (UpdateMutex); mouseRepeatTimer.Reset (); lastMouseDownEvent = null; @@ -1803,6 +1813,7 @@ namespace Crow return true; } finally { + Monitor.Exit (UpdateMutex); DbgLogger.EndEvent (DbgEvtType.MouseUp); } } diff --git a/Crow/src/LayoutingQueueItem.cs b/Crow/src/LayoutingQueueItem.cs index 64549c08..9ce5895b 100644 --- a/Crow/src/LayoutingQueueItem.cs +++ b/Crow/src/LayoutingQueueItem.cs @@ -3,6 +3,7 @@ // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; +using Drawing2D; namespace Crow { diff --git a/Crow/src/Widgets/HueSelector.cs b/Crow/src/Widgets/HueSelector.cs index d4d23f36..ae93626d 100644 --- a/Crow/src/Widgets/HueSelector.cs +++ b/Crow/src/Widgets/HueSelector.cs @@ -37,7 +37,7 @@ namespace Crow RegisterForGraphicUpdate (); } } - + public virtual double Hue { get { return hue; } set { @@ -51,7 +51,7 @@ namespace Crow } protected override void onDraw (IContext gr) { - base.onDraw (gr); + //base.onDraw (gr); RectangleD r = ClientRectangle; r.Height -= 4; @@ -60,13 +60,13 @@ namespace Crow Gradient grad = new Gradient ( Orientation == Orientation.Vertical ? GradientType.Vertical : GradientType.Horizontal); - grad.Stops.Add (new Gradient.ColorStop (0, new Color (1, 0, 0, 1))); - grad.Stops.Add (new Gradient.ColorStop (0.167, new Color (1, 1, 0, 1))); - grad.Stops.Add (new Gradient.ColorStop (0.333, new Color (0, 1, 0, 1))); - grad.Stops.Add (new Gradient.ColorStop (0.5, new Color (0, 1, 1, 1))); - grad.Stops.Add (new Gradient.ColorStop (0.667, new Color (0, 0, 1, 1))); - grad.Stops.Add (new Gradient.ColorStop (0.833, new Color (1, 0, 1, 1))); - grad.Stops.Add (new Gradient.ColorStop (1, new Color (1, 0, 0, 1))); + grad.Stops.Add (new Gradient.ColorStop (0, new Color (1.0, 0.0, 0.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (0.167, new Color (1.0, 1.0, 0.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (0.333, new Color (0.0, 1.0, 0.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (0.5, new Color (0.0, 1.0, 1.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (0.667, new Color (0.0, 0.0, 1.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (0.833, new Color (1.0, 0.0, 1.0, 1.0))); + grad.Stops.Add (new Gradient.ColorStop (1, new Color (1.0, 0.0, 0.0, 1.0))); grad.SetAsSource (IFace, gr, r); CairoHelpers.CairoRectangle (gr, r, CornerRadius); @@ -74,7 +74,7 @@ namespace Crow r = ClientRectangle; - switch (cursor) { + /*switch (cursor) { case CursorType.Rectangle: if (Orientation == Orientation.Horizontal) { r.Width = 5; @@ -103,9 +103,9 @@ namespace Crow gr.LineTo (r.Left, r.Bottom-0.5); gr.LineTo (r.Left, y); gr.ClosePath (); - } else { + } else { } - break; + break; } gr.SetSource (Colors.Black); @@ -113,7 +113,7 @@ namespace Crow gr.StrokePreserve (); gr.SetSource (Colors.White); gr.LineWidth = 1.0; - gr.Stroke (); + gr.Stroke ();*/ } public override void OnLayoutChanges (LayoutingType layoutType) diff --git a/Crow/src/Widgets/Image.cs b/Crow/src/Widgets/Image.cs index 30908847..2c102c22 100644 --- a/Crow/src/Widgets/Image.cs +++ b/Crow/src/Widgets/Image.cs @@ -60,7 +60,7 @@ namespace Crow } /// /// Image file path, may be on disk or embedded. Accepts bitmaps or SVG drawings. - /// + /// public string Path { get { return _pic == null ? "" : _pic.Path; } set { @@ -84,9 +84,9 @@ namespace Crow /// /// Used only for svg images, repaint only node named referenced in SvgSub. /// If null, all the svg is rendered - /// + /// public string SvgSub { - get { return _svgSub; } + get => _svgSub; set { if (_svgSub == value) return; @@ -95,9 +95,9 @@ namespace Crow } } /// - /// Object holding the image data once loaded, may be used directely to pupulate this control without + /// Object holding the image data once loaded, may be used directely to pupulate this control without /// specifying a path. - /// + /// public Picture Picture { get { return _pic; } set { diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 39ce20e5..c3f08fd4 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -1685,9 +1685,9 @@ namespace Crow /// The children that is calling the constraints /// The currently registering layouting types public virtual void ChildrenLayoutingConstraints(ILayoutable layoutable, ref LayoutingType layoutType){ } - /// Query a layouting for the type pass as parameter, redraw only if layout changed. internal ReaderWriterLockSlim layoutMutex = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion); + /// Query a layouting for the type pass as parameter, redraw only if layout changed. public virtual void RegisterForLayouting(LayoutingType layoutType){ if (disposed) { DbgLogger.AddEvent (DbgEvtType.AlreadyDisposed, this); @@ -1778,6 +1778,7 @@ namespace Crow if (LayoutChanged != null) LayoutChanged.Invoke (this, new LayoutingEventArgs (layoutType)); } + //Todo: is it necessary?? Raise method already check for null handler. internal protected void raiseLayoutChanged(LayoutingType layoutingType){ if (LayoutChanged != null) LayoutChanged.Raise (this, new LayoutingEventArgs(layoutingType)); diff --git a/Directory.Build.props b/Directory.Build.props index d674fd6e..37361807 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,7 +15,7 @@ - false + true false diff --git a/Drawing2D/src/ISurface.cs b/Drawing2D/src/ISurface.cs index 198f7539..292334c4 100644 --- a/Drawing2D/src/ISurface.cs +++ b/Drawing2D/src/ISurface.cs @@ -18,6 +18,7 @@ namespace Drawing2D void Clear (); ISurface CreateSimilar (int width, int height);*/ void Resize (int width, int height); + IntPtr Handle { get; } } } diff --git a/Samples/AnimTest/AnimTest.csproj b/Samples/AnimTest/AnimTest.csproj new file mode 100644 index 00000000..178d4537 --- /dev/null +++ b/Samples/AnimTest/AnimTest.csproj @@ -0,0 +1,11 @@ + + + + + netcoreapp3.1 + Exe + + + + + \ No newline at end of file diff --git a/Samples/AnimTest/Animation/AngleAnimation.cs b/Samples/AnimTest/Animation/AngleAnimation.cs new file mode 100644 index 00000000..02faef76 --- /dev/null +++ b/Samples/AnimTest/Animation/AngleAnimation.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace vke +{ + public class AngleAnimation : FloatAnimation2 + { + #region CTOR + public AngleAnimation(Object instance, string PropertyName, float Target, int stepCount = 20) : + base (instance,PropertyName,MathHelper.NormalizeAngle(Target),stepCount) { + initialValue = MathHelper.NormalizeAngle (initialValue); + } + #endregion + } +} + diff --git a/Samples/AnimTest/Animation/Animation.cs b/Samples/AnimTest/Animation/Animation.cs new file mode 100644 index 00000000..6ca7af8c --- /dev/null +++ b/Samples/AnimTest/Animation/Animation.cs @@ -0,0 +1,155 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Diagnostics; + +namespace vke +{ + public delegate void AnimationEventHandler(Animation a); + + public delegate float GetterDelegate(); + public delegate void SetterDelegate(float value); + + public class Animation + { + public event AnimationEventHandler AnimationFinished; + + public static Random random = new Random (); + public static int DelayMs = 0; + + protected GetterDelegate getValue; + protected SetterDelegate setValue; + + public string propertyName; + + protected Stopwatch timer = new Stopwatch(); + protected int delayStartMs = 0; + /// + /// Delay before firing ZnimationFinished event. + /// + protected int delayFinishMs = 0; + public static List AnimationList = new List(); + public static bool HasAnimations => AnimationList.Count > 0; + //public FieldInfo member; + public Object AnimatedInstance; + + #region CTOR + public Animation (){} + public Animation(Object instance, string _propertyName) + { + propertyName = _propertyName; + AnimatedInstance = instance; + PropertyInfo pi = instance.GetType().GetProperty(propertyName); + try { + getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod()); + setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod()); + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + } + } + #endregion + + public static void StartAnimation(Animation a, int delayMs = 0, AnimationEventHandler OnEnd = null) + { + lock (AnimationList) { + Animation aa = null; + if (Animation.GetAnimation (a.AnimatedInstance, a.propertyName, ref aa)) { + aa.CancelAnimation (); + } + + //a.AnimationFinished += onAnimationFinished; + + a.AnimationFinished += OnEnd; + a.delayStartMs = delayMs + DelayMs; + + + if (a.delayStartMs > 0) + a.timer.Start (); + + AnimationList.Add (a); + } + + } + + static Stack anims = new Stack(); + static int frame = 0; + public static void ProcessAnimations() + { + frame++; + +// #region FLYING anim +// if (frame % 20 == 0){ +// foreach (Player p in MagicEngine.CurrentEngine.Players) { +// foreach (CardInstance c in p.InPlay.Cards.Where(ci => ci.HasAbility(AbilityEnum.Flying) && ci.z < 0.4f)) { +// +// } +// } +// } +// #endregion + //Stopwatch animationTime = new Stopwatch(); + //animationTime.Start(); + + const int maxAnim = 200000; + int count = 0; + + + lock (AnimationList) { + if (anims.Count == 0) + anims = new Stack (AnimationList); + } + + while (anims.Count > 0 && count < maxAnim) { + Animation a = anims.Pop (); + if (a == null) + continue; + if (a.timer.IsRunning) { + if (a.timer.ElapsedMilliseconds > a.delayStartMs) + a.timer.Stop (); + else + continue; + } + + a.Process (); + count++; + } + + //animationTime.Stop(); + //Debug.WriteLine("animation: {0} ticks \t {1} ms ", animationTime.ElapsedTicks,animationTime.ElapsedMilliseconds); + } + public static bool GetAnimation(object instance, string PropertyName, ref Animation a) + { + for (int i = 0; i < AnimationList.Count; i++) { + Animation anim = AnimationList [i]; + if (anim == null) { + continue; + } + if (anim.AnimatedInstance == instance && anim.propertyName == PropertyName) { + a = anim; + return true; + } + } + + return false; + } + public virtual void Process () {} + public void CancelAnimation() + { + //Debug.WriteLine("Cancel anim: " + this.ToString()); + AnimationList.Remove(this); + } + public void RaiseAnimationFinishedEvent() + { + if (AnimationFinished != null) + AnimationFinished (this); + } + + public static void onAnimationFinished(Animation a) + { + Debug.WriteLine ("\t\tAnimation finished: " + a.ToString ()); + } + } +} diff --git a/Samples/AnimTest/Animation/AnimationTemplate.cs b/Samples/AnimTest/Animation/AnimationTemplate.cs new file mode 100644 index 00000000..d5c784c3 --- /dev/null +++ b/Samples/AnimTest/Animation/AnimationTemplate.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using MiscUtil; +using System.Reflection; + +namespace vke +{ + public class Animation : Animation + { + public delegate T GetterDelegate(); + public delegate void SetterDelegate(T value); + + public T TargetValue; + T initialValue; + T zero; + public T Step; + public bool Cycle; + + protected GetterDelegate getValue; + protected SetterDelegate setValue; + + #region CTOR + public Animation(Object instance, string _propertyName) + { + propertyName = _propertyName; + AnimatedInstance = instance; + PropertyInfo pi = instance.GetType().GetProperty(propertyName); + getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod()); + setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod()); + } + public Animation(Object instance, string _propertyName, T Target, T step) + { + propertyName = _propertyName; + AnimatedInstance = instance; + PropertyInfo pi = instance.GetType().GetProperty(propertyName); + getValue = (GetterDelegate)Delegate.CreateDelegate(typeof(GetterDelegate), instance, pi.GetGetMethod()); + setValue = (SetterDelegate)Delegate.CreateDelegate(typeof(SetterDelegate), instance, pi.GetSetMethod()); + + TargetValue = Target; + + T value = getValue(); + initialValue = value; + Type t = typeof(T); + + if (t.IsPrimitive) { + Step = (T)Convert.ChangeType (step, t); + zero = (T)Convert.ChangeType (0, t); + }else { + Step = (T)Activator.CreateInstance (typeof(T), new Object[] { step }); + zero = (T)Activator.CreateInstance (typeof(T), 0f); + } + T test = (T)Operator.SubtractAlternative (value, TargetValue); + + if (Operator.LessThan(test, zero)) + { + if (Operator.LessThan (Step, zero)) + Step = Operator.Negate (Step); + } + else if (Operator.GreaterThan(Step, zero)) + Step = Operator.Negate (Step); + } + #endregion + + public override void Process() + { + T value = getValue(); + + //Debug.WriteLine ("Anim: {0} <= {1}", value, this.ToString ()); + + if (Operator.GreaterThan(Step, zero)) + { + value = Operator.Add (value, Step); + setValue(value); + //Debug.WriteLine(value); + if (Operator.GreaterThan(Operator.Subtract(TargetValue, value), zero)) + return; + } + else + { + value = Operator.Add (value, Step); + setValue(value); + + if (Operator.LessThan(Operator.Subtract(TargetValue, value), zero)) + return; + } + + if (Cycle) { + Step = Operator.Negate (Step); + TargetValue = initialValue; + Cycle = false; + return; + } + + setValue(TargetValue); + AnimationList.Remove(this); + + RaiseAnimationFinishedEvent (); + } + + public override string ToString () + { + return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step); + } + } + +} + diff --git a/Samples/AnimTest/Animation/BezierPath.cs b/Samples/AnimTest/Animation/BezierPath.cs new file mode 100644 index 00000000..a3126e3e --- /dev/null +++ b/Samples/AnimTest/Animation/BezierPath.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Numerics; + +namespace vke +{ + public class BezierPath : Path + { + public Vector3 ControlPointStart; + public Vector3 ControlPointEnd; + + public BezierPath (Vector3 startPos, Vector3 controlPointStart, + Vector3 controlPointEnd, Vector3 endPos) + :base(startPos, endPos) + { + ControlPointStart = controlPointStart; + ControlPointEnd = controlPointEnd; + } + public BezierPath (Vector3 startPos, Vector3 endPos, Vector3 vUp) + :base(startPos, endPos) + { + ControlPointStart = startPos + vUp; + ControlPointEnd = endPos + vUp; + } + public override Vector3 GetStep (float pos) + { + return Path.CalculateBezierPoint (pos, Start, ControlPointStart, ControlPointEnd, End); + } + } +} + diff --git a/Samples/AnimTest/Animation/FloatAnimation.cs b/Samples/AnimTest/Animation/FloatAnimation.cs new file mode 100644 index 00000000..a7e4bf7c --- /dev/null +++ b/Samples/AnimTest/Animation/FloatAnimation.cs @@ -0,0 +1,83 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace vke +{ + public class FloatAnimation : Animation + { + + public float TargetValue; + float initialValue; + public float Step; + public bool Cycle; + + #region CTOR + public FloatAnimation(Object instance, string _propertyName, float Target, float step = 0.2f) + : base(instance, _propertyName) + { + + TargetValue = Target; + + float value = getValue(); + initialValue = value; + + Step = step; + + if (value < TargetValue) + { + if (Step < 0) + Step = -Step; + } + else if (Step > 0) + Step = -Step; + } + #endregion + + public override void Process() + { + float value = getValue(); + + //Debug.WriteLine ("Anim: {0} <= {1}", value, this.ToString ()); + + if (Step > 0f) + { + value += Step; + setValue(value); + //Debug.WriteLine(value); + if (TargetValue > value) + return; + } + else + { + value += Step; + setValue(value); + + if (TargetValue < value) + return; + } + + if (Cycle) { + Step = -Step; + TargetValue = initialValue; + Cycle = false; + return; + } + + setValue(TargetValue); + lock(AnimationList) + AnimationList.Remove(this); + + RaiseAnimationFinishedEvent (); + } + + public override string ToString () + { + return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step); + } + } + +} + diff --git a/Samples/AnimTest/Animation/FloatAnimation2.cs b/Samples/AnimTest/Animation/FloatAnimation2.cs new file mode 100644 index 00000000..1d5c9193 --- /dev/null +++ b/Samples/AnimTest/Animation/FloatAnimation2.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace vke +{ + public class FloatAnimation2 : Animation + { + + public float TargetValue; + protected float initialValue; + int stepCount, currentStep; + + #region CTOR + public FloatAnimation2(Object instance, string _propertyName, float Target, int _stepCount = 20) + : base(instance, _propertyName) + { + + TargetValue = Target; + + float value = getValue(); + initialValue = value; + + stepCount = _stepCount; + } + #endregion + + bool smooth = true; + + float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f; + + public override void Process() + { + + currentStep++; + + float t = (float)currentStep / (float)stepCount; + if (smooth) + t = smoothedStep (t); + + setValue (initialValue + t * (TargetValue - initialValue)); + + if (currentStep < stepCount) + return; + + setValue(TargetValue); + AnimationList.Remove(this); + RaiseAnimationFinishedEvent (); + } + + public override string ToString () + { + return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,stepCount); + } + } + +} + diff --git a/Samples/AnimTest/Animation/MathHelper.cs b/Samples/AnimTest/Animation/MathHelper.cs new file mode 100644 index 00000000..35a3009f --- /dev/null +++ b/Samples/AnimTest/Animation/MathHelper.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2019-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +namespace vke { + public static class MathHelper { + public const float Pi = (float)Math.PI; + public const float TwoPi = (float)Math.PI * 2; + public static float NormalizeAngle (float a) { + float tmp = a % MathHelper.TwoPi; + if (tmp < 0) + tmp += MathHelper.TwoPi; + return tmp; + } + } +} diff --git a/Samples/AnimTest/Animation/Path.cs b/Samples/AnimTest/Animation/Path.cs new file mode 100644 index 00000000..1f04e090 --- /dev/null +++ b/Samples/AnimTest/Animation/Path.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System.Numerics; + +namespace vke { + public class Path + { + public Vector3 Start; + public Vector3 End; + + float length; + Vector3 vDir; + + public Path(Vector3 startPos, Vector3 endPos){ + Start = startPos; + End = endPos; + initialComputations (); + } + protected void initialComputations(){ + Vector3 vPath = End - Start; + vDir = Vector3.Normalize (vPath); + length = vPath.Length(); + } + /// + /// Get single step on the path + /// + /// return position on the path + /// Position expressed as percentage of total length + public virtual Vector3 GetStep(float pos){ + return Start + vDir * length * pos; + } + + public static Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) + { + float u = 1 - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; + + Vector3 p = uuu * p0; + p += 3 * uu * t * p1; + p += 3 * u * tt * p2; + p += ttt * p3; + + return p; + } + } + +} diff --git a/Samples/AnimTest/Animation/PathAnimation.cs b/Samples/AnimTest/Animation/PathAnimation.cs new file mode 100644 index 00000000..54c30e9a --- /dev/null +++ b/Samples/AnimTest/Animation/PathAnimation.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Numerics; + +namespace vke +{ + public class PathAnimation : Animation + { + Path path; + int stepCount, currentStep; + + #region CTOR + public PathAnimation(Object instance, string _propertyName, Path _path, int _stepCount = 20) + : base(instance, _propertyName) + { + path = _path; + stepCount = _stepCount; + } + + #endregion + + bool smooth = true; + + float smoothedStep (float step) => (-MathF.Cos (step * MathF.PI) + 1)/2.0f; + + public override void Process() + { + currentStep++; + + float t = (float)currentStep / (float)stepCount; + if (smooth) + t = smoothedStep (t); + + Vector3 pos = path.GetStep (t); + setValue(pos); + + if (currentStep < stepCount) + return; + + AnimationList.Remove(this); + RaiseAnimationFinishedEvent (); + } + + public override string ToString () + { + return string.Format ("{0}:->{1}:{2}",base.ToString(),TargetValue,Step); + } + } +} + diff --git a/Samples/AnimTest/Animation/ShakeAnimation.cs b/Samples/AnimTest/Animation/ShakeAnimation.cs new file mode 100644 index 00000000..37a089bd --- /dev/null +++ b/Samples/AnimTest/Animation/ShakeAnimation.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2015-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace vke +{ + public class ShakeAnimation : Animation + { + const float stepMin = 0.001f, stepMax = 0.005f; + bool rising = true; + + public float LowBound; + public float HighBound; + + #region CTOR + public ShakeAnimation( + Object instance, + string _propertyName, + float lowBound, float highBound) + : base(instance, _propertyName) + { + + LowBound = Math.Min (lowBound, highBound); + HighBound = Math.Max (lowBound, highBound); + + float value = getValue (); + + if (value > HighBound) + rising = false; + } + #endregion + + public override void Process () + { + float value = getValue (); + float step = stepMin + (float)random.NextDouble () * stepMax; + + if (rising) { + value += step; + if (value > HighBound) { + value = HighBound; + rising = false; + } + } else { + value -= step; + if (value < LowBound) { + value = LowBound; + rising = true; + } else if (value > HighBound) + value -= step * 10f; + } + setValue (value); + } + + } + +} + diff --git a/Samples/AnimTest/MiscUtil/ExpressionUtil.cs b/Samples/AnimTest/MiscUtil/ExpressionUtil.cs new file mode 100644 index 00000000..8f9ca4e6 --- /dev/null +++ b/Samples/AnimTest/MiscUtil/ExpressionUtil.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq.Expressions; + +namespace MiscUtil.Linq +{ + /// + /// General purpose Expression utilities + /// + public static class ExpressionUtil + { + /// + /// Create a function delegate representing a unary operation + /// + /// The parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp"); + try + { + return Expression.Lambda>(body(inp), inp).Compile(); + } + catch (Exception ex) + { + string msg = ex.Message; // avoid capture of ex itself + return delegate { throw new InvalidOperationException(msg); }; + } + } + + /// + /// Create a function delegate representing a binary operation + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body) + { + return CreateExpression(body, false); + } + + /// + /// Create a function delegate representing a binary operation + /// + /// + /// If no matching operation is possible, attempt to convert + /// TArg1 and TArg2 to TResult for a match? For example, there is no + /// "decimal operator /(decimal, int)", but by converting TArg2 (int) to + /// TResult (decimal) a match is found. + /// + /// The first parameter type + /// The second parameter type + /// The return type + /// Body factory + /// Compiled function delegate + public static Func CreateExpression( + Func body, bool castArgsToResultOnFailure) + { + ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs"); + ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs"); + try + { + try + { + return Expression.Lambda>(body(lhs, rhs), lhs, rhs).Compile(); + } + catch (InvalidOperationException) + { + if (castArgsToResultOnFailure && !( // if we show retry + typeof(TArg1) == typeof(TResult) && // and the args aren't + typeof(TArg2) == typeof(TResult))) + { // already "TValue, TValue, TValue"... + // convert both lhs and rhs to TResult (as appropriate) + Expression castLhs = typeof(TArg1) == typeof(TResult) ? + (Expression)lhs : + (Expression)Expression.Convert(lhs, typeof(TResult)); + Expression castRhs = typeof(TArg2) == typeof(TResult) ? + (Expression)rhs : + (Expression)Expression.Convert(rhs, typeof(TResult)); + + return Expression.Lambda>( + body(castLhs, castRhs), lhs, rhs).Compile(); + } + else throw; + } + } + catch (Exception ex) + { + string msg = ex.Message; // avoid capture of ex itself + return delegate { throw new InvalidOperationException(msg); }; + } + } + } +} \ No newline at end of file diff --git a/Samples/AnimTest/MiscUtil/NullOp.cs b/Samples/AnimTest/MiscUtil/NullOp.cs new file mode 100644 index 00000000..7bb8790a --- /dev/null +++ b/Samples/AnimTest/MiscUtil/NullOp.cs @@ -0,0 +1,59 @@ + +namespace MiscUtil +{ + interface INullOp + { + bool HasValue(T value); + bool AddIfNotNull(ref T accumulator, T value); + } + sealed class StructNullOp + : INullOp, INullOp + where T : struct + { + public bool HasValue(T value) + { + return true; + } + public bool AddIfNotNull(ref T accumulator, T value) + { + accumulator = Operator.Add(accumulator, value); + return true; + } + public bool HasValue(T? value) + { + return value.HasValue; + } + public bool AddIfNotNull(ref T? accumulator, T? value) + { + if (value.HasValue) + { + accumulator = accumulator.HasValue ? + Operator.Add( + accumulator.GetValueOrDefault(), + value.GetValueOrDefault()) + : value; + return true; + } + return false; + } + } + sealed class ClassNullOp + : INullOp + where T : class + { + public bool HasValue(T value) + { + return value != null; + } + public bool AddIfNotNull(ref T accumulator, T value) + { + if (value != null) + { + accumulator = accumulator == null ? + value : Operator.Add(accumulator, value); + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Samples/AnimTest/MiscUtil/Operator.cs b/Samples/AnimTest/MiscUtil/Operator.cs new file mode 100644 index 00000000..ee1168cf --- /dev/null +++ b/Samples/AnimTest/MiscUtil/Operator.cs @@ -0,0 +1,449 @@ +using System; +using System.Linq.Expressions; +using MiscUtil.Linq; + +namespace MiscUtil +{ + /// + /// The Operator class provides easy access to the standard operators + /// (addition, etc) for generic types, using type inference to simplify + /// usage. + /// + public static class Operator + { + + /// + /// Indicates if the supplied value is non-null, + /// for reference-types or Nullable<T> + /// + /// True for non-null values, else false + public static bool HasValue(T value) + { + return Operator.NullOp.HasValue(value); + } + /// + /// Increments the accumulator only + /// if the value is non-null. If the accumulator + /// is null, then the accumulator is given the new + /// value; otherwise the accumulator and value + /// are added. + /// + /// The current total to be incremented (can be null) + /// The value to be tested and added to the accumulator + /// True if the value is non-null, else false - i.e. + /// "has the accumulator been updated?" + public static bool AddIfNotNull(ref T accumulator, T value) + { + return Operator.NullOp.AddIfNotNull(ref accumulator, value); + } + + /// + /// Evaluates unary negation (-) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Negate(T value) + { + return Operator.Negate(value); + } + /// + /// Evaluates bitwise not (~) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Not(T value) + { + return Operator.Not(value); + } + /// + /// Evaluates bitwise or (|) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Or(T value1, T value2) + { + return Operator.Or(value1, value2); + } + /// + /// Evaluates bitwise and (&) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T And(T value1, T value2) + { + return Operator.And(value1, value2); + } + /// + /// Evaluates bitwise xor (^) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Xor(T value1, T value2) + { + return Operator.Xor(value1, value2); + } + /// + /// Performs a conversion between the given types; this will throw + /// an InvalidOperationException if the type T does not provide a suitable cast, or for + /// Nullable<TInner> if TInner does not provide this cast. + /// + public static TTo Convert(TFrom value) + { + return Operator.Convert(value); + } + /// + /// Evaluates binary addition (+) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Add(T value1, T value2) + { + return Operator.Add(value1, value2); + } + /// + /// Evaluates binary addition (+) for the given type(s); this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static TArg1 AddAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Add(value1, value2); + } + /// + /// Evaluates binary subtraction (-) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Subtract(T value1, T value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// Evaluates binary subtraction(-) for the given type(s); this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static TArg1 SubtractAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Subtract(value1, value2); + } + /// + /// Evaluates binary multiplication (*) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Multiply(T value1, T value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// Evaluates binary multiplication (*) for the given type(s); this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static TArg1 MultiplyAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Multiply(value1, value2); + } + /// + /// Evaluates binary division (/) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static T Divide(T value1, T value2) + { + return Operator.Divide(value1, value2); + } + /// + /// Evaluates binary division (/) for the given type(s); this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static TArg1 DivideAlternative(TArg1 value1, TArg2 value2) + { + return Operator.Divide(value1, value2); + } + /// + /// Evaluates binary equality (==) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool Equal(T value1, T value2) + { + return Operator.Equal(value1, value2); + } + /// + /// Evaluates binary inequality (!=) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool NotEqual(T value1, T value2) + { + return Operator.NotEqual(value1, value2); + } + /// + /// Evaluates binary greater-than (>) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool GreaterThan(T value1, T value2) + { + return Operator.GreaterThan(value1, value2); + } + /// + /// Evaluates binary less-than (<) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool LessThan(T value1, T value2) + { + return Operator.LessThan(value1, value2); + } + /// + /// Evaluates binary greater-than-on-eqauls (>=) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool GreaterThanOrEqual(T value1, T value2) + { + return Operator.GreaterThanOrEqual(value1, value2); + } + /// + /// Evaluates binary less-than-or-equal (<=) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static bool LessThanOrEqual(T value1, T value2) + { + return Operator.LessThanOrEqual(value1, value2); + } + /// + /// Evaluates integer division (/) for the given type; this will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + /// This operation is particularly useful for computing averages and + /// similar aggregates. + /// + public static T DivideInt32(T value, int divisor) + { + return Operator.Divide(value, divisor); + } + public static T Max(T v1, T v2) + { + return Operator.GreaterThanOrEqual (v1, v2) ? v1 : v2; + } + public static T Min(T v1, T v2) + { + return Operator.LessThanOrEqual (v1, v2) ? v1 : v2; + } + } + /// + /// Provides standard operators (such as addition) that operate over operands of + /// different types. For operators, the return type is assumed to match the first + /// operand. + /// + /// + /// + public static class Operator + { + private static readonly Func convert; + /// + /// Returns a delegate to convert a value between two types; this delegate will throw + /// an InvalidOperationException if the type T does not provide a suitable cast, or for + /// Nullable<TInner> if TInner does not provide this cast. + /// + public static Func Convert { get { return convert; } } + static Operator() + { + convert = ExpressionUtil.CreateExpression(body => Expression.Convert(body, typeof(TResult))); + add = ExpressionUtil.CreateExpression(Expression.Add, true); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract, true); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply, true); + divide = ExpressionUtil.CreateExpression(Expression.Divide, true); + } + + private static readonly Func add, subtract, multiply, divide; + /// + /// Returns a delegate to evaluate binary addition (+) for the given types; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Add { get { return add; } } + /// + /// Returns a delegate to evaluate binary subtraction (-) for the given types; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Subtract { get { return subtract; } } + /// + /// Returns a delegate to evaluate binary multiplication (*) for the given types; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Multiply { get { return multiply; } } + /// + /// Returns a delegate to evaluate binary division (/) for the given types; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Divide { get { return divide; } } + } + + /// + /// Provides standard operators (such as addition) over a single type + /// + /// + /// + public static class Operator + { + static readonly INullOp nullOp; + internal static INullOp NullOp { get { return nullOp; } } + + static readonly T zero; + /// + /// Returns the zero value for value-types (even full Nullable<TInner>) - or null for reference types + /// + public static T Zero { get { return zero;} } + + static readonly Func negate, not; + static readonly Func or, and, xor; + /// + /// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Negate { get { return negate; } } + /// + /// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Not { get { return not; } } + /// + /// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Or { get { return or; } } + /// + /// Returns a delegate to evaluate bitwise and (&) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func And { get { return and; } } + /// + /// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Xor { get { return xor; } } + + static readonly Func add, subtract, multiply, divide; + /// + /// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Add { get { return add; } } + /// + /// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Subtract { get { return subtract; } } + /// + /// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Multiply { get { return multiply; } } + /// + /// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Divide { get { return divide; } } + + + static readonly Func equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual; + /// + /// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func Equal { get { return equal; } } + /// + /// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func NotEqual { get { return notEqual; } } + /// + /// Returns a delegate to evaluate binary greater-then (>) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func GreaterThan { get { return greaterThan; } } + /// + /// Returns a delegate to evaluate binary less-than (<) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func LessThan { get { return lessThan; } } + /// + /// Returns a delegate to evaluate binary greater-than-or-equal (>=) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func GreaterThanOrEqual { get { return greaterThanOrEqual; } } + /// + /// Returns a delegate to evaluate binary less-than-or-equal (<=) for the given type; this delegate will throw + /// an InvalidOperationException if the type T does not provide this operator, or for + /// Nullable<TInner> if TInner does not provide this operator. + /// + public static Func LessThanOrEqual { get { return lessThanOrEqual; } } + + static Operator() + { + add = ExpressionUtil.CreateExpression(Expression.Add); + subtract = ExpressionUtil.CreateExpression(Expression.Subtract); + divide = ExpressionUtil.CreateExpression(Expression.Divide); + multiply = ExpressionUtil.CreateExpression(Expression.Multiply); + + greaterThan = ExpressionUtil.CreateExpression(Expression.GreaterThan); + greaterThanOrEqual = ExpressionUtil.CreateExpression(Expression.GreaterThanOrEqual); + lessThan = ExpressionUtil.CreateExpression(Expression.LessThan); + lessThanOrEqual = ExpressionUtil.CreateExpression(Expression.LessThanOrEqual); + equal = ExpressionUtil.CreateExpression(Expression.Equal); + notEqual = ExpressionUtil.CreateExpression(Expression.NotEqual); + + negate = ExpressionUtil.CreateExpression(Expression.Negate); + and = ExpressionUtil.CreateExpression(Expression.And); + or = ExpressionUtil.CreateExpression(Expression.Or); + not = ExpressionUtil.CreateExpression(Expression.Not); + xor = ExpressionUtil.CreateExpression(Expression.ExclusiveOr); + + Type typeT = typeof(T); + if(typeT.IsValueType && typeT.IsGenericType && (typeT.GetGenericTypeDefinition() == typeof(Nullable<>))) { + // get the *inner* zero (not a null Nullable, but default(TValue)) + Type nullType = typeT.GetGenericArguments()[0]; + zero = (T)Activator.CreateInstance(nullType); + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(nullType)); + } else { + zero = default(T); + if (typeT.IsValueType) + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(StructNullOp<>).MakeGenericType(typeT)); + } + else + { + nullOp = (INullOp)Activator.CreateInstance( + typeof(ClassNullOp<>).MakeGenericType(typeT)); + } + } + } + } +} diff --git a/Samples/AnimTest/main.cs b/Samples/AnimTest/main.cs new file mode 100644 index 00000000..6dd0b525 --- /dev/null +++ b/Samples/AnimTest/main.cs @@ -0,0 +1,28 @@ +using System; +using Crow; +using Glfw; +using Samples; + +namespace HelloWorld +{ + class Program : Interface { + Program() : base (800, 600, true) {} + static void Main (string[] args) { + //Interface.PreferedBackendType = Drawing2D.BackendType.Egl; + using (Program app = new Program ()) { + //app.Initialized += (sender, e) => app.LoadIMLFragment (@"