From fe9011308211ac029e24d1bda7c7eb0ea8f8784a Mon Sep 17 00:00:00 2001 From: David Hares Date: Mon, 6 Oct 2025 21:27:21 +0200 Subject: [PATCH 1/8] template changes --- SeeSharp.Blazor/FlipViewer.razor | 79 ++++- SeeSharp.Blazor/Scripts.cs | 52 +++- .../Pages/Experiment.razor | 114 ++++++- .../Pages/Experiment.razor.cs | 293 +++++++++++++++++- .../SeeSharp.Blazor.Template/Program.cs | 7 +- .../wwwroot/css/site.css | 8 + 6 files changed, 517 insertions(+), 36 deletions(-) diff --git a/SeeSharp.Blazor/FlipViewer.razor b/SeeSharp.Blazor/FlipViewer.razor index f2f2294e..f2dea8f9 100644 --- a/SeeSharp.Blazor/FlipViewer.razor +++ b/SeeSharp.Blazor/FlipViewer.razor @@ -19,13 +19,32 @@ ( int X, int Y, - bool CtrlKey + @* bool CtrlKey, + bool AltKey,//MOD *@ + + string key, + bool isPressedDown, + + int mouseButtom, + bool isButtomDown, + + int deltaY,//MOD + int selectedIndex,//MOD + string registryKey//MOD ) { } [Parameter] public EventCallback OnClick { get; set; } + [Parameter] + public EventCallback OnWheel { get; set; } //MOD + [Parameter] + public EventCallback OnMouseOver { get; set; } //MOD + [Parameter] + public EventCallback OnKeyDown { get; set; } //MOD + [Parameter] + public EventCallback OnKeyUp { get; set; } //MOD protected override async Task OnParametersSetAsync() { @@ -54,21 +73,71 @@ public struct _OnFlipClickArgs { - [JsonInclude] public bool ctrlKey; + @* [JsonInclude] public bool ctrlKey; + [JsonInclude] public bool altKey;//MOD *@ + [JsonInclude] public string key; + [JsonInclude] public bool isPressedDown; + [JsonInclude] public int mouseButtom; + [JsonInclude] public bool isButtomDown; + [JsonInclude] public int deltaY;//MOD + [JsonInclude] public int selectedIndex;//MOD + [JsonInclude] public string registryKey;//MOD + } + + [JSInvokable] + public void _OnFlipClick(int buttom) + { + OnClick.InvokeAsync(new(X: -1, Y: -1, key: "", isPressedDown: false, mouseButtom: buttom, isButtomDown: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); } + //MOD + // IMPORTANT: There is a bool for altKey, but on the React side a wheel event only is fired when alt is pressed, so altKey is more or less redundant [JSInvokable] - public void _OnFlipClick(int x, int y, _OnFlipClickArgs eventArgs) + public void _OnFlipWheel(int deltaY) { - OnClick.InvokeAsync(new(x, y, eventArgs.ctrlKey)).Wait(); + OnWheel.InvokeAsync(new(X: -1, Y: -1, key: "", isPressedDown: false, mouseButtom: -1, isButtomDown: false, deltaY: deltaY, selectedIndex: -1, registryKey: "")).Wait(); } + //MOD + [JSInvokable] + public void _OnFlipMouseOver(int x, int y) + { + OnMouseOver.InvokeAsync(new(X: x, Y: y, key: "", isPressedDown: false, mouseButtom: -1, isButtomDown: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); + } + + //MOD + [JSInvokable] + public void _OnFlipKey(int selectedIdx, string keyStr, string keyPressed, bool isPressedDown) + { + OnKeyDown.InvokeAsync(new(X: -1, Y: -1, key: keyPressed, isPressedDown: isPressedDown, mouseButtom: -1, isButtomDown: false, deltaY: 0, selectedIndex: selectedIdx, registryKey: keyStr)).Wait(); + } + + //MOD + @* [JSInvokable] + public void _OnFlipKeyUp(_OnFlipClickArgs eventArgs) + { + OnKeyUp.InvokeAsync(new(-1, -1, eventArgs.key, eventArgs.isPressedDown, 0, eventArgs.selectedIndex, eventArgs.registryKey)).Wait(); + } *@ + protected override async Task OnAfterRenderAsync(bool firstRender) { // Need to wait with invoking the JS code until the HTML got added to the DOM on the client side if (flipJson != null) { - await JSRuntime.InvokeVoidAsync("makeFlipBook", flipJson, DotNetObjectReference.Create(this), nameof(_OnFlipClick)); + await JSRuntime.InvokeVoidAsync( + "makeFlipBook", + flipJson, + DotNetObjectReference.Create(this), + nameof(_OnFlipClick), + DotNetObjectReference.Create(this), + nameof(_OnFlipWheel), + DotNetObjectReference.Create(this), + nameof(_OnFlipMouseOver), + DotNetObjectReference.Create(this), + nameof(_OnFlipKey) + @* DotNetObjectReference.Create(this), + nameof(_OnFlipKeyUp) *@ + ); flipJson = null; } } diff --git a/SeeSharp.Blazor/Scripts.cs b/SeeSharp.Blazor/Scripts.cs index 1abe35d9..1f877487 100644 --- a/SeeSharp.Blazor/Scripts.cs +++ b/SeeSharp.Blazor/Scripts.cs @@ -1,12 +1,12 @@ +using Microsoft.AspNetCore.StaticAssets; using Microsoft.JSInterop; +using System.Net.Http.Headers; using System.Reflection; namespace SeeSharp.Blazor; -public static class Scripts -{ - static string ReadResourceText(string filename) - { +public static class Scripts { + static string ReadResourceText(string filename) { var assembly = typeof(Scripts).GetTypeInfo().Assembly; var stream = assembly.GetManifestResourceStream("SeeSharp.Blazor." + filename) ?? throw new FileNotFoundException("resource file not found", filename); @@ -20,14 +20,38 @@ static string ReadResourceText(string filename) $$""" """; @@ -59,7 +83,17 @@ function makeFlipBook(jsonArgs, onClickObj, onClickMethodName) { """; - public static readonly string AllScripts = FlipBookScript + DownloadScript + WidgetScripts; + public static readonly string UpdateImageScript = + $$""" + + """; + + public static readonly string AllScripts = FlipBookScript + DownloadScript + WidgetScripts + UpdateImageScript; /// /// Downloads a stream to the client with the given file name. Requires that diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor index 76543c49..6ae3866c 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor @@ -2,6 +2,105 @@ @using SeeSharp @using SeeSharp.Blazor + + @inject IJSRuntime JS @page "/Experiment" @@ -33,7 +132,7 @@
- @if (selected.HasValue && selected.Value) + @* @if (selected.HasValue && selected.Value) { @@ -41,8 +140,13 @@
Mesh@(selected.Value.Mesh.Name)
Distance@(selected.Value.Distance)
Position@(selected.Value.Position)
- } - + } *@ + @* *@ +
+

Compare Example (SVCM, VCM)

+
+ +
} } @@ -64,11 +168,15 @@ ElementReference runButton; SimpleImageIO.FlipBook flip; + (SimpleImageIO.FlipBook, SimpleImageIO.FlipBook) compare; async Task OnSceneLoaded(SceneFromFile sceneFromFile) { await Task.Run(() => scene = sceneFromFile.MakeScene()); + flip = null; + compare = (null, null); + resultsAvailable = false; readyToRun = true; sceneJustLoaded = true; diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs index 109cc8db..3c61de36 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs @@ -1,60 +1,319 @@ using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; using SeeSharp.Blazor; namespace SeeSharp.Blazor.Template.Pages; -public partial class Experiment : ComponentBase -{ +// Only here for the example to show some possibilities +struct ExampleImageGenerator { + public Image rndImage(float strength, int width, int height, bool colored) { + Image image = new Image(width, height, 3); + RNG rng = new RNG(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (colored) { + image.SetPixelChannel(x, y, 0, rng.NextFloat(0.5f - strength, 0.5f + strength)); + image.SetPixelChannel(x, y, 1, rng.NextFloat(0.5f - strength, 0.5f + strength)); + image.SetPixelChannel(x, y, 2, rng.NextFloat(0.5f - strength, 0.5f + strength)); + } else { + float value = rng.NextFloat(0.5f - strength, 0.5f + strength); + + image.SetPixelChannel(x, y, 0, value); + image.SetPixelChannel(x, y, 1, value); + image.SetPixelChannel(x, y, 2, value); + } + + } + } + + return image; + } +} + +struct ListenerState { + public ListenerState() { } + + /// + /// The index of the selected image of the current selected flipbook (selected by clicking on it) + /// + public int selectedIndex = 0; + + /// + /// Number between 0 and NumSamples. Can be used if data is stored from different iterations + /// + public int currIteration = 0; + + public bool altKeyPressed = false; + public bool ctrlKeyPressed = false; + public int currX = 0; + public int currY = 0; + + /// + /// The key of the current flipbook in string form and concatenated with ',' + /// + public string currFlipKey = ""; +} + +/// +/// Differences between event type so update methods for flipbooks can ignore events +/// +enum FiredType { + Click = 0, + Move = 1, + Wheel = 2, + KeyDown = 4, + KeyUp = 8, +} + +public partial class Experiment : ComponentBase { const int Width = 1280; const int Height = 720; + const int FlipWidth = 660; + const int FlipHeight = 580; const int MaxDepth = 10; int NumSamples = 2; long renderTimePT, renderTimeVCM; - void RunExperiment() - { - flip = new FlipBook(660, 580) + //Methods + PathTracer pathTracer; + VertexConnectionAndMerging vcm; + ExampleImageGenerator imgGen; + + RgbImage ptImage; + RgbImage vcmImage; + + /// + /// Registers all Flipbooks + /// Key will be the stringified key of a Flipbook which is set by Flipbook.SetKey + /// A Flipbook key is an array of ints + /// + Dictionary registry = new Dictionary(); + ListenerState state = new ListenerState(); + public static string Reverse(string s) { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + /// + /// Initializes all flipbooks + /// + void InitFlipbooks() { + // create new Flipbook w/o key + flip = new FlipBook(FlipWidth, FlipHeight) .SetZoom(FlipBook.InitialZoom.FillWidth) .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) .SetToolVisibility(false); + // create Flipbooks with keys + compare = ( + new FlipBook(FlipWidth, FlipHeight) + .SetZoom(FlipBook.InitialZoom.FillWidth) + .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) + .SetGroupName("compare") + .SetToolVisibility(false) + .SetKey([1, 0]), + new FlipBook(FlipWidth, FlipHeight) + .SetZoom(FlipBook.InitialZoom.FillWidth) + .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) + .SetGroupName("compare") + .SetToolVisibility(false) + .SetKey([0, 1]) + ); + registry.Add(compare.Item1.GetKeyAsString(), compare.Item1); + registry.Add(compare.Item2.GetKeyAsString(), compare.Item2); + } + + /// + /// Sets all intial images of flipbooks with extra functions (ex: compare) + /// + void FlipBookSetBaseImages() { + compare.Item1.Add($"PathTracer", ptImage); + compare.Item2.Add($"VCM", vcmImage); + } + + void RunExperiment() { + InitFlipbooks(); + scene.FrameBuffer = new(Width, Height, ""); scene.Prepare(); - PathTracer pathTracer = new() - { + pathTracer = new() { TotalSpp = NumSamples, MaxDepth = MaxDepth, }; pathTracer.Render(scene); flip.Add($"PT", scene.FrameBuffer.Image); renderTimePT = scene.FrameBuffer.RenderTimeMs; + ptImage = scene.FrameBuffer.Image; scene.FrameBuffer = new(Width, Height, ""); - VertexConnectionAndMerging vcm = new() - { + vcm = new() { NumIterations = NumSamples, MaxDepth = MaxDepth, }; vcm.Render(scene); flip.Add($"VCM", scene.FrameBuffer.Image); renderTimeVCM = scene.FrameBuffer.RenderTimeMs; + vcmImage = scene.FrameBuffer.Image; + + FlipBookSetBaseImages(); + } + + // SurfacePoint? selected; + // void OnFlipClick(FlipViewer.OnClickEventArgs args) { + // if (args.CtrlKey) { + // selected = scene.RayCast(new(args.X, args.Y)); + // } + // } + + /// + /// Is fired when clicked on an image in the flipbook + /// + /// ListenerState from HMTL side + void OnFlipClick(FlipViewer.OnClickEventArgs args) { + updateFlipbook(FiredType.Click); } - SurfacePoint? selected; + /// + /// Is fired when the mouse wheel state changes over an image. + /// This gets only called when the alt key is pressed (from HTML side) + /// + /// ListenerState from HMTL side. + void OnFlipWheel(FlipViewer.OnClickEventArgs args) { + if (state.altKeyPressed) { + // scrolled up + if (args.deltaY < 0) { + if (state.currIteration < NumSamples - 1) { + state.currIteration++; + updateFlipbook(FiredType.Wheel); + } + } + // scrolled down + if (args.deltaY >= 0) { + if (state.currIteration > 0) { + state.currIteration--; + updateFlipbook(FiredType.Wheel); + } + } + } + } + + /// + /// Is fired when mouse position changes over the selected flipbook + /// + /// ListenerState from HMTL side. + void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { + if (state.currX == args.X && state.currY == args.Y) + return; + + if (args.X >= 0 && args.X <= Width - 1) + state.currX = args.X; + if (args.Y >= 0 && args.Y <= Height - 1) + state.currY = args.Y; + + updateFlipbook(FiredType.Move); + } + + /// + /// Is fired when key is pressed or released + /// + /// ListenerState from HMTL side. + void OnFlipKey(FlipViewer.OnClickEventArgs args) { + if (args.key == "Alt") + state.altKeyPressed = args.isPressedDown; + + if (args.key == "Control") + state.ctrlKeyPressed = args.isPressedDown; + + state.currFlipKey = args.registryKey; + state.selectedIndex = args.selectedIndex; + + if(args.key == "Alt" && !args.isPressedDown) + state.currIteration = 0; + + if (args.isPressedDown) + updateFlipbook(FiredType.KeyDown); + else + updateFlipbook(FiredType.KeyUp); + } + + /// + /// Catches fired events and forward events to selected flipbooks + /// + /// Fired type + void updateFlipbook(FiredType fired) { + if (String.IsNullOrEmpty(state.currFlipKey)) + return; + + switch (state.currFlipKey) { + case "1,0": + case "0,1": { + updateCompare(fired); + break; + } + default: + break; + } + } + + /// + /// Example method that updates the flipbook pair. + /// When Alt key is pressed, the image change to random noise images + /// When Ctrl key is pressed, the colored and black/white images swap + /// + /// Fired type + /// + async Task updateCompare(FiredType fired) { + // Disable event types that you want to ignore + if (fired == FiredType.Click || fired == FiredType.Move) + return; + + FlipBook flipBook = registry[state.currFlipKey]; + // TODO: some iteration over all pairs with the same number ex: (1,0,0,...) -> (0,1,0,0,..) + // this is a fast solution (and maybe good enough) for now -> flip the string to get the other flipbook + FlipBook flipBookOther = registry[Reverse(state.currFlipKey)]; + + (string Name, Image Image, FlipBook.DataType TargetType, FlipBook.InitialTMO TMOOverride) updateImage = flipBook.GetImage(state.selectedIndex); + (string Name, Image Image, FlipBook.DataType TargetType, FlipBook.InitialTMO TMOOverride) updateImageOther = flipBookOther.GetImage(state.selectedIndex); + + if (state.altKeyPressed) { + bool colored = true; + + if (state.ctrlKeyPressed) + colored = !colored; + + if (state.currIteration == 0) { + updateImage.Image = imgGen.rndImage(0.2f, Width, Height, !colored); + updateImageOther.Image = imgGen.rndImage(0.2f, Width, Height, colored); + } else if (state.currIteration == 1) { + updateImage.Image = imgGen.rndImage(0.4f, Width, Height, !colored); + updateImageOther.Image = imgGen.rndImage(0.4f, Width, Height, colored); + } + + FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + + Console.WriteLine("Cell updated"); + } else { + updateImage.Image = ptImage; + updateImageOther.Image = vcmImage; + + FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); - void OnFlipClick(FlipViewer.OnClickEventArgs args) - { - if (args.CtrlKey) - { - selected = scene.RayCast(new(args.X, args.Y)); + Console.WriteLine("Compare reset"); } } - async Task OnDownloadClick() - { + async Task OnDownloadClick() { HtmlReport report = new(); report.AddMarkdown(""" # Example experiment diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Program.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Program.cs index 01ab1f6f..9245b9cb 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Program.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Program.cs @@ -9,8 +9,11 @@ var app = builder.Build(); -if (!app.Environment.IsDevelopment()) -{ +// Register scene folder +var scenesPath = "Scenes"; +SceneRegistry.AddSource(scenesPath); + +if (!app.Environment.IsDevelopment()) { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css index ddc98cca..3ed98343 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css @@ -68,6 +68,14 @@ button { align-items: flex-start; } +.compare { + display: flex; + gap: 10px; + margin-bottom: 10px; + flex-wrap: wrap; + align-items: flex-start; +} + table { border-collapse: collapse; } From 4fb92d01364148975f9709bfd7ae9c52f2d49e66 Mon Sep 17 00:00:00 2001 From: David Hares Date: Mon, 10 Nov 2025 12:52:21 +0100 Subject: [PATCH 2/8] removed css style out of experiment file as it should be --- .../Pages/Experiment.razor | 99 ------------------- .../wwwroot/css/site.css | 1 + 2 files changed, 1 insertion(+), 99 deletions(-) diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor index 6ae3866c..e341fda6 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor @@ -2,105 +2,6 @@ @using SeeSharp @using SeeSharp.Blazor - - @inject IJSRuntime JS @page "/Experiment" diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css index 3ed98343..edac4442 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/wwwroot/css/site.css @@ -59,6 +59,7 @@ button { flex-direction: column; float: left; margin-right: 1em; + height: 10000px; /*Sets height of parameter window on the left site. Only visual benefits*/ } .experiment-results { From 32d06c267d207432528e0f1f1194da38cff7bd12 Mon Sep 17 00:00:00 2001 From: David Hares Date: Sun, 23 Nov 2025 14:53:44 +0100 Subject: [PATCH 3/8] removed old comments, isPressedDown -> isPressed --- SeeSharp.Blazor/FlipViewer.razor | 56 +++++++++++--------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/SeeSharp.Blazor/FlipViewer.razor b/SeeSharp.Blazor/FlipViewer.razor index f2dea8f9..00df6534 100644 --- a/SeeSharp.Blazor/FlipViewer.razor +++ b/SeeSharp.Blazor/FlipViewer.razor @@ -19,18 +19,16 @@ ( int X, int Y, - @* bool CtrlKey, - bool AltKey,//MOD *@ string key, - bool isPressedDown, + bool isPressed, - int mouseButtom, - bool isButtomDown, + int mouseButton, + bool isButton, - int deltaY,//MOD - int selectedIndex,//MOD - string registryKey//MOD + int deltaY, + int selectedIndex, + string registryKey ) { } @@ -38,13 +36,11 @@ [Parameter] public EventCallback OnClick { get; set; } [Parameter] - public EventCallback OnWheel { get; set; } //MOD + public EventCallback OnWheel { get; set; } [Parameter] - public EventCallback OnMouseOver { get; set; } //MOD + public EventCallback OnMouseOver { get; set; } [Parameter] - public EventCallback OnKeyDown { get; set; } //MOD - [Parameter] - public EventCallback OnKeyUp { get; set; } //MOD + public EventCallback OnKey { get; set; } protected override async Task OnParametersSetAsync() { @@ -73,52 +69,40 @@ public struct _OnFlipClickArgs { - @* [JsonInclude] public bool ctrlKey; - [JsonInclude] public bool altKey;//MOD *@ [JsonInclude] public string key; - [JsonInclude] public bool isPressedDown; + [JsonInclude] public bool isPressed; [JsonInclude] public int mouseButtom; - [JsonInclude] public bool isButtomDown; - [JsonInclude] public int deltaY;//MOD - [JsonInclude] public int selectedIndex;//MOD - [JsonInclude] public string registryKey;//MOD + [JsonInclude] public bool isButtom; + [JsonInclude] public int deltaY; + [JsonInclude] public int selectedIndex; + [JsonInclude] public string registryKey; } [JSInvokable] public void _OnFlipClick(int buttom) { - OnClick.InvokeAsync(new(X: -1, Y: -1, key: "", isPressedDown: false, mouseButtom: buttom, isButtomDown: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); + OnClick.InvokeAsync(new(X: -1, Y: -1, key: "", isPressed: false, mouseButton: buttom, isButton: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); } - //MOD // IMPORTANT: There is a bool for altKey, but on the React side a wheel event only is fired when alt is pressed, so altKey is more or less redundant [JSInvokable] public void _OnFlipWheel(int deltaY) { - OnWheel.InvokeAsync(new(X: -1, Y: -1, key: "", isPressedDown: false, mouseButtom: -1, isButtomDown: false, deltaY: deltaY, selectedIndex: -1, registryKey: "")).Wait(); + OnWheel.InvokeAsync(new(X: -1, Y: -1, key: "", isPressed: false, mouseButton: -1, isButton: false, deltaY: deltaY, selectedIndex: -1, registryKey: "")).Wait(); } - //MOD [JSInvokable] public void _OnFlipMouseOver(int x, int y) { - OnMouseOver.InvokeAsync(new(X: x, Y: y, key: "", isPressedDown: false, mouseButtom: -1, isButtomDown: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); + OnMouseOver.InvokeAsync(new(X: x, Y: y, key: "", isPressed: false, mouseButton: -1, isButton: false, deltaY: 0, selectedIndex: -1, registryKey: "")).Wait(); } - //MOD [JSInvokable] - public void _OnFlipKey(int selectedIdx, string keyStr, string keyPressed, bool isPressedDown) + public void _OnFlipKey(int selectedIdx, string keyStr, string keyPressed, bool isPressed) { - OnKeyDown.InvokeAsync(new(X: -1, Y: -1, key: keyPressed, isPressedDown: isPressedDown, mouseButtom: -1, isButtomDown: false, deltaY: 0, selectedIndex: selectedIdx, registryKey: keyStr)).Wait(); + OnKey.InvokeAsync(new(X: -1, Y: -1, key: keyPressed, isPressed: isPressed, mouseButton: -1, isButton: false, deltaY: 0, selectedIndex: selectedIdx, registryKey: keyStr)).Wait(); } - //MOD - @* [JSInvokable] - public void _OnFlipKeyUp(_OnFlipClickArgs eventArgs) - { - OnKeyUp.InvokeAsync(new(-1, -1, eventArgs.key, eventArgs.isPressedDown, 0, eventArgs.selectedIndex, eventArgs.registryKey)).Wait(); - } *@ - protected override async Task OnAfterRenderAsync(bool firstRender) { // Need to wait with invoking the JS code until the HTML got added to the DOM on the client side @@ -135,8 +119,6 @@ nameof(_OnFlipMouseOver), DotNetObjectReference.Create(this), nameof(_OnFlipKey) - @* DotNetObjectReference.Create(this), - nameof(_OnFlipKeyUp) *@ ); flipJson = null; } From ccab86dce23d46dcc89c9c534cd3fa8b0faf7f2f Mon Sep 17 00:00:00 2001 From: David Hares Date: Sun, 23 Nov 2025 14:54:22 +0100 Subject: [PATCH 4/8] removed old comments, isPressedDown -> isPressed --- SeeSharp.Blazor/Scripts.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/SeeSharp.Blazor/Scripts.cs b/SeeSharp.Blazor/Scripts.cs index 1f877487..c58fda48 100644 --- a/SeeSharp.Blazor/Scripts.cs +++ b/SeeSharp.Blazor/Scripts.cs @@ -41,16 +41,10 @@ function makeFlipBook(jsonArgs, onClickObj, onClickMethodName, onWheelObj, onWhe let onKey = null if (onKeyObj && onKeyMethodName) { - onKey = (selectedIdx, keyStr, keyPressed, isPressedDown) => - onKeyObj.invokeMethodAsync(onKeyMethodName, selectedIdx, keyStr, keyPressed, isPressedDown) + onKey = (selectedIdx, keyStr, keyPressed, isPressed) => + onKeyObj.invokeMethodAsync(onKeyMethodName, selectedIdx, keyStr, keyPressed, isPressed) } - // let onKeyUp = null - // if (onKeyUpObj && onKeyUpMethodName) { - // onKeyUp = (evt, selectedIdx, keyStr) => - // onKeyUpObj.invokeMethodAsync(onKeyUpMethodName, { ctrlKey: evt.ctrlKey, altKey: evt.altKey, deltaY: evt.deltaY, selectedIndex: selectedIdx, registryKey: keyStr }) - // } - window['flipbook']['MakeFlipBook'](jsonArgs, onClick, onWheel, onMouseOver, onKey); } From e750214c5e126dc7b784485d3794b4e235296635 Mon Sep 17 00:00:00 2001 From: David Hares Date: Sun, 23 Nov 2025 14:58:29 +0100 Subject: [PATCH 5/8] seperated example experiment and template experiment, added BaseExperiment where ListenerStates, Flipbook registry lie in -> Experiment now inherits from BaseExperiment --- .../Pages/BaseExperiment.razor.cs | 162 ++++++++++ .../Pages/Experiment.razor | 11 +- .../Pages/Experiment.razor.cs | 276 +----------------- 3 files changed, 175 insertions(+), 274 deletions(-) create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs new file mode 100644 index 00000000..2a6bfe72 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs @@ -0,0 +1,162 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using SeeSharp.Blazor; + +namespace SeeSharp.Blazor.Template.Pages; + +public struct ListenerState { + public ListenerState() { } + + /// + /// The index of the selected image of the current selected flipbook (selected by clicking on it) + /// + public int selectedIndex = 0; + + /// + /// Number between 0 and NumSamples. Can be used if data is stored from different iterations + /// + public int currIteration = 0; + + public bool altKeyPressed = false; + public bool ctrlKeyPressed = false; + public int currX = 0; + public int currY = 0; + + /// + /// The key of the current flipbook in string form and concatenated with ',' + /// + public string currFlipKey = ""; +} + +/// +/// Differences between event type so update methods for flipbooks can ignore events +/// +public enum FiredType { + Click = 0, + Move = 1, + Wheel = 2, + KeyDown = 4, + KeyUp = 8, +} + +public partial class BaseExperiment : ComponentBase { + protected const int Width = 1280; + protected const int Height = 720; + protected const int FlipWidth = 660; + protected const int FlipHeight = 580; + protected const int MaxDepth = 10; + public int NumSamples = 2; + + /// + /// Registers all Flipbooks + /// Key will be the stringified key of a Flipbook which is set by Flipbook.SetKey + /// A Flipbook key is an array of ints + /// + protected Dictionary registry = new Dictionary(); + protected ListenerState state = new ListenerState(); + public static string Reverse(string s) { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + // SurfacePoint? selected; + // void OnFlipClick(FlipViewer.OnClickEventArgs args) { + // if (args.CtrlKey) { + // selected = scene.RayCast(new(args.X, args.Y)); + // } + // } + + /// + /// Is fired when clicked on an image in the flipbook + /// + /// ListenerState from HMTL side + public virtual void OnFlipClick(FlipViewer.OnClickEventArgs args) { + updateFlipbook(FiredType.Click); + } + + /// + /// Is fired when the mouse wheel state changes over an image. + /// This gets only called when the alt key is pressed (from HTML side) + /// + /// ListenerState from HMTL side. + public virtual void OnFlipWheel(FlipViewer.OnClickEventArgs args) { + if (state.altKeyPressed) { + // scrolled up + if (args.deltaY < 0) { + if (state.currIteration < NumSamples - 1) { + state.currIteration++; + updateFlipbook(FiredType.Wheel); + } + } + // scrolled down + if (args.deltaY >= 0) { + if (state.currIteration > 0) { + state.currIteration--; + updateFlipbook(FiredType.Wheel); + } + } + } + } + + /// + /// Is fired when mouse position changes over the selected flipbook + /// + /// ListenerState from HMTL side. + public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { + if (state.currX == args.X && state.currY == args.Y) + return; + + if (args.X >= 0 && args.X <= Width - 1) + state.currX = args.X; + if (args.Y >= 0 && args.Y <= Height - 1) + state.currY = args.Y; + + updateFlipbook(FiredType.Move); + } + + /// + /// Is fired when key is pressed or released + /// + /// ListenerState from HMTL side. + public virtual void OnFlipKey(FlipViewer.OnClickEventArgs args) { + if (args.key == "Alt") + state.altKeyPressed = args.isPressed; + + if (args.key == "Control") + state.ctrlKeyPressed = args.isPressed; + + state.currFlipKey = args.registryKey; + state.selectedIndex = args.selectedIndex; + + if (args.key == "Alt" && !args.isPressed) + state.currIteration = 0; + + if (args.isPressed) + updateFlipbook(FiredType.KeyDown); + else + updateFlipbook(FiredType.KeyUp); + } + + /// + /// Catches fired events and forward events to selected flipbooks + /// + /// Fired type + public virtual void updateFlipbook(FiredType fired) { + if (String.IsNullOrEmpty(state.currFlipKey)) + return; + } + + /// + /// Runs the experiment when "..." is pressed on the HTML + /// + public virtual void RunExperiment() { + } + + /// + /// Is executed when Download button is pressed on the HTML + /// + /// + public virtual async Task OnDownloadClick() { + } +} \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor index e341fda6..3ed66a10 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor @@ -4,6 +4,8 @@ @inject IJSRuntime JS +@inherits SeeSharp.Blazor.Template.Pages.BaseExperiment + @page "/Experiment"

Example experiment

@@ -44,11 +46,6 @@ } *@ @* *@ -

Compare Example (SVCM, VCM)

-
- - -
} } else @@ -68,15 +65,11 @@ bool resultsAvailable = false; ElementReference runButton; - SimpleImageIO.FlipBook flip; - (SimpleImageIO.FlipBook, SimpleImageIO.FlipBook) compare; - async Task OnSceneLoaded(SceneFromFile sceneFromFile) { await Task.Run(() => scene = sceneFromFile.MakeScene()); flip = null; - compare = (null, null); resultsAvailable = false; readyToRun = true; diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs index 3c61de36..e7adb58c 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs @@ -4,138 +4,34 @@ namespace SeeSharp.Blazor.Template.Pages; -// Only here for the example to show some possibilities -struct ExampleImageGenerator { - public Image rndImage(float strength, int width, int height, bool colored) { - Image image = new Image(width, height, 3); - RNG rng = new RNG(); +public partial class Experiment : BaseExperiment { + // protected const int Width = 1280; + // protected const int Height = 720; + // protected const int FlipWidth = 660; + // protected const int FlipHeight = 580; + // protected const int MaxDepth = 10; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - if (colored) { - image.SetPixelChannel(x, y, 0, rng.NextFloat(0.5f - strength, 0.5f + strength)); - image.SetPixelChannel(x, y, 1, rng.NextFloat(0.5f - strength, 0.5f + strength)); - image.SetPixelChannel(x, y, 2, rng.NextFloat(0.5f - strength, 0.5f + strength)); - } else { - float value = rng.NextFloat(0.5f - strength, 0.5f + strength); + // int NumSamples = 2; - image.SetPixelChannel(x, y, 0, value); - image.SetPixelChannel(x, y, 1, value); - image.SetPixelChannel(x, y, 2, value); - } - - } - } - - return image; - } -} - -struct ListenerState { - public ListenerState() { } - - /// - /// The index of the selected image of the current selected flipbook (selected by clicking on it) - /// - public int selectedIndex = 0; - - /// - /// Number between 0 and NumSamples. Can be used if data is stored from different iterations - /// - public int currIteration = 0; - - public bool altKeyPressed = false; - public bool ctrlKeyPressed = false; - public int currX = 0; - public int currY = 0; - - /// - /// The key of the current flipbook in string form and concatenated with ',' - /// - public string currFlipKey = ""; -} - -/// -/// Differences between event type so update methods for flipbooks can ignore events -/// -enum FiredType { - Click = 0, - Move = 1, - Wheel = 2, - KeyDown = 4, - KeyUp = 8, -} - -public partial class Experiment : ComponentBase { - const int Width = 1280; - const int Height = 720; - const int FlipWidth = 660; - const int FlipHeight = 580; - const int MaxDepth = 10; - - int NumSamples = 2; + public SimpleImageIO.FlipBook flip; long renderTimePT, renderTimeVCM; //Methods PathTracer pathTracer; VertexConnectionAndMerging vcm; - ExampleImageGenerator imgGen; - - RgbImage ptImage; - RgbImage vcmImage; - - /// - /// Registers all Flipbooks - /// Key will be the stringified key of a Flipbook which is set by Flipbook.SetKey - /// A Flipbook key is an array of ints - /// - Dictionary registry = new Dictionary(); - ListenerState state = new ListenerState(); - public static string Reverse(string s) { - char[] charArray = s.ToCharArray(); - Array.Reverse(charArray); - return new string(charArray); - } /// /// Initializes all flipbooks /// void InitFlipbooks() { - // create new Flipbook w/o key flip = new FlipBook(FlipWidth, FlipHeight) .SetZoom(FlipBook.InitialZoom.FillWidth) .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) .SetToolVisibility(false); - - // create Flipbooks with keys - compare = ( - new FlipBook(FlipWidth, FlipHeight) - .SetZoom(FlipBook.InitialZoom.FillWidth) - .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) - .SetGroupName("compare") - .SetToolVisibility(false) - .SetKey([1, 0]), - new FlipBook(FlipWidth, FlipHeight) - .SetZoom(FlipBook.InitialZoom.FillWidth) - .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) - .SetGroupName("compare") - .SetToolVisibility(false) - .SetKey([0, 1]) - ); - registry.Add(compare.Item1.GetKeyAsString(), compare.Item1); - registry.Add(compare.Item2.GetKeyAsString(), compare.Item2); } - /// - /// Sets all intial images of flipbooks with extra functions (ex: compare) - /// - void FlipBookSetBaseImages() { - compare.Item1.Add($"PathTracer", ptImage); - compare.Item2.Add($"VCM", vcmImage); - } - - void RunExperiment() { + public override void RunExperiment() { InitFlipbooks(); scene.FrameBuffer = new(Width, Height, ""); @@ -148,7 +44,6 @@ void RunExperiment() { pathTracer.Render(scene); flip.Add($"PT", scene.FrameBuffer.Image); renderTimePT = scene.FrameBuffer.RenderTimeMs; - ptImage = scene.FrameBuffer.Image; scene.FrameBuffer = new(Width, Height, ""); vcm = new() { @@ -158,162 +53,13 @@ void RunExperiment() { vcm.Render(scene); flip.Add($"VCM", scene.FrameBuffer.Image); renderTimeVCM = scene.FrameBuffer.RenderTimeMs; - vcmImage = scene.FrameBuffer.Image; - - FlipBookSetBaseImages(); - } - - // SurfacePoint? selected; - // void OnFlipClick(FlipViewer.OnClickEventArgs args) { - // if (args.CtrlKey) { - // selected = scene.RayCast(new(args.X, args.Y)); - // } - // } - - /// - /// Is fired when clicked on an image in the flipbook - /// - /// ListenerState from HMTL side - void OnFlipClick(FlipViewer.OnClickEventArgs args) { - updateFlipbook(FiredType.Click); } /// - /// Is fired when the mouse wheel state changes over an image. - /// This gets only called when the alt key is pressed (from HTML side) + /// Safes HTML of experiment /// - /// ListenerState from HMTL side. - void OnFlipWheel(FlipViewer.OnClickEventArgs args) { - if (state.altKeyPressed) { - // scrolled up - if (args.deltaY < 0) { - if (state.currIteration < NumSamples - 1) { - state.currIteration++; - updateFlipbook(FiredType.Wheel); - } - } - // scrolled down - if (args.deltaY >= 0) { - if (state.currIteration > 0) { - state.currIteration--; - updateFlipbook(FiredType.Wheel); - } - } - } - } - - /// - /// Is fired when mouse position changes over the selected flipbook - /// - /// ListenerState from HMTL side. - void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { - if (state.currX == args.X && state.currY == args.Y) - return; - - if (args.X >= 0 && args.X <= Width - 1) - state.currX = args.X; - if (args.Y >= 0 && args.Y <= Height - 1) - state.currY = args.Y; - - updateFlipbook(FiredType.Move); - } - - /// - /// Is fired when key is pressed or released - /// - /// ListenerState from HMTL side. - void OnFlipKey(FlipViewer.OnClickEventArgs args) { - if (args.key == "Alt") - state.altKeyPressed = args.isPressedDown; - - if (args.key == "Control") - state.ctrlKeyPressed = args.isPressedDown; - - state.currFlipKey = args.registryKey; - state.selectedIndex = args.selectedIndex; - - if(args.key == "Alt" && !args.isPressedDown) - state.currIteration = 0; - - if (args.isPressedDown) - updateFlipbook(FiredType.KeyDown); - else - updateFlipbook(FiredType.KeyUp); - } - - /// - /// Catches fired events and forward events to selected flipbooks - /// - /// Fired type - void updateFlipbook(FiredType fired) { - if (String.IsNullOrEmpty(state.currFlipKey)) - return; - - switch (state.currFlipKey) { - case "1,0": - case "0,1": { - updateCompare(fired); - break; - } - default: - break; - } - } - - /// - /// Example method that updates the flipbook pair. - /// When Alt key is pressed, the image change to random noise images - /// When Ctrl key is pressed, the colored and black/white images swap - /// - /// Fired type /// - async Task updateCompare(FiredType fired) { - // Disable event types that you want to ignore - if (fired == FiredType.Click || fired == FiredType.Move) - return; - - FlipBook flipBook = registry[state.currFlipKey]; - // TODO: some iteration over all pairs with the same number ex: (1,0,0,...) -> (0,1,0,0,..) - // this is a fast solution (and maybe good enough) for now -> flip the string to get the other flipbook - FlipBook flipBookOther = registry[Reverse(state.currFlipKey)]; - - (string Name, Image Image, FlipBook.DataType TargetType, FlipBook.InitialTMO TMOOverride) updateImage = flipBook.GetImage(state.selectedIndex); - (string Name, Image Image, FlipBook.DataType TargetType, FlipBook.InitialTMO TMOOverride) updateImageOther = flipBookOther.GetImage(state.selectedIndex); - - if (state.altKeyPressed) { - bool colored = true; - - if (state.ctrlKeyPressed) - colored = !colored; - - if (state.currIteration == 0) { - updateImage.Image = imgGen.rndImage(0.2f, Width, Height, !colored); - updateImageOther.Image = imgGen.rndImage(0.2f, Width, Height, colored); - } else if (state.currIteration == 1) { - updateImage.Image = imgGen.rndImage(0.4f, Width, Height, !colored); - updateImageOther.Image = imgGen.rndImage(0.4f, Width, Height, colored); - } - - FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); - JS.InvokeVoidAsync("updateImage", code.Data); - code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); - JS.InvokeVoidAsync("updateImage", code.Data); - - Console.WriteLine("Cell updated"); - } else { - updateImage.Image = ptImage; - updateImageOther.Image = vcmImage; - - FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); - JS.InvokeVoidAsync("updateImage", code.Data); - code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); - JS.InvokeVoidAsync("updateImage", code.Data); - - Console.WriteLine("Compare reset"); - } - } - - async Task OnDownloadClick() { + public override async Task OnDownloadClick() { HtmlReport report = new(); report.AddMarkdown(""" # Example experiment From 2a2a421bfd07c9d5fc964e4923cbbe77c05be142 Mon Sep 17 00:00:00 2001 From: David Hares Date: Sun, 23 Nov 2025 14:59:44 +0100 Subject: [PATCH 6/8] Added example template to see all new features and how to use/implement them --- .../.template.config/template.json | 28 +++ .../SeeSharp.Blazor.TemplateExample/App.razor | 12 ++ .../Imports.cs | 32 +++ .../MainLayout.razor | 3 + .../Pages/BaseExperiment.razor.cs | 162 +++++++++++++++ .../Pages/Experiment.razor | 105 ++++++++++ .../Pages/Experiment.razor.cs | 190 ++++++++++++++++++ .../Pages/Index.razor | 39 ++++ .../Pages/_Host.cshtml | 34 ++++ .../Program.cs | 30 +++ .../SeeSharp.Blazor.Template.csproj | 17 ++ .../_Imports.razor | 6 + .../appsettings.Development.json | 9 + .../appsettings.json | 9 + .../wwwroot/css/site.css | 95 +++++++++ 15 files changed, 771 insertions(+) create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/.template.config/template.json create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/App.razor create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Imports.cs create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/MainLayout.razor create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Index.razor create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/_Host.cshtml create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Program.cs create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/SeeSharp.Blazor.Template.csproj create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/_Imports.razor create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.Development.json create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.json create mode 100644 SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/wwwroot/css/site.css diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/.template.config/template.json b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/.template.config/template.json new file mode 100644 index 00000000..c5ff81fd --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/.template.config/template.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Pascal Grittmann", + "classifications": [ "Web/Blazor/SeeSharp" ], + "identity": "SeeSharp.Blazor.Template", + "name": "SeeSharp Blazor Experiment", + "shortName": "seesharp-blazor", + "sourceName":"SeeSharp.Blazor.Template", + "tags": { + "language": "C#", + "type": "project" + }, + "sources": [ + { + "modifiers": [ + { + "exclude": [ + "Results/**", + "Scenes/*/*.blend1" + ], + "copyOnly": [ + "Scenes/**" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/App.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/App.razor new file mode 100644 index 00000000..6fd3ed1b --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/App.razor @@ -0,0 +1,12 @@ + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Imports.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Imports.cs new file mode 100644 index 00000000..b6c33e5a --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Imports.cs @@ -0,0 +1,32 @@ +global using System; +global using System.Collections.Concurrent; +global using System.Collections.Generic; +global using System.Diagnostics; +global using System.IO; +global using System.Linq; +global using System.Numerics; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using System.Threading; +global using System.Threading.Tasks; + +global using TinyEmbree; +global using SimpleImageIO; + +global using SeeSharp; +global using SeeSharp.Cameras; +global using SeeSharp.Common; +global using SeeSharp.Experiments; +global using SeeSharp.Geometry; +global using SeeSharp.Images; +global using SeeSharp.Integrators; +global using SeeSharp.Integrators.Bidir; +global using SeeSharp.Integrators.Common; +global using SeeSharp.Integrators.Util; +global using SeeSharp.Sampling; +global using SeeSharp.Shading; +global using SeeSharp.Shading.Background; +global using SeeSharp.Shading.Emitters; +global using SeeSharp.Shading.Materials; + +global using SeeSharp.Blazor; diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/MainLayout.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/MainLayout.razor new file mode 100644 index 00000000..a5af3489 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/MainLayout.razor @@ -0,0 +1,3 @@ +@inherits LayoutComponentBase + +
@Body
diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs new file mode 100644 index 00000000..2a6bfe72 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs @@ -0,0 +1,162 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using SeeSharp.Blazor; + +namespace SeeSharp.Blazor.Template.Pages; + +public struct ListenerState { + public ListenerState() { } + + /// + /// The index of the selected image of the current selected flipbook (selected by clicking on it) + /// + public int selectedIndex = 0; + + /// + /// Number between 0 and NumSamples. Can be used if data is stored from different iterations + /// + public int currIteration = 0; + + public bool altKeyPressed = false; + public bool ctrlKeyPressed = false; + public int currX = 0; + public int currY = 0; + + /// + /// The key of the current flipbook in string form and concatenated with ',' + /// + public string currFlipKey = ""; +} + +/// +/// Differences between event type so update methods for flipbooks can ignore events +/// +public enum FiredType { + Click = 0, + Move = 1, + Wheel = 2, + KeyDown = 4, + KeyUp = 8, +} + +public partial class BaseExperiment : ComponentBase { + protected const int Width = 1280; + protected const int Height = 720; + protected const int FlipWidth = 660; + protected const int FlipHeight = 580; + protected const int MaxDepth = 10; + public int NumSamples = 2; + + /// + /// Registers all Flipbooks + /// Key will be the stringified key of a Flipbook which is set by Flipbook.SetKey + /// A Flipbook key is an array of ints + /// + protected Dictionary registry = new Dictionary(); + protected ListenerState state = new ListenerState(); + public static string Reverse(string s) { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + // SurfacePoint? selected; + // void OnFlipClick(FlipViewer.OnClickEventArgs args) { + // if (args.CtrlKey) { + // selected = scene.RayCast(new(args.X, args.Y)); + // } + // } + + /// + /// Is fired when clicked on an image in the flipbook + /// + /// ListenerState from HMTL side + public virtual void OnFlipClick(FlipViewer.OnClickEventArgs args) { + updateFlipbook(FiredType.Click); + } + + /// + /// Is fired when the mouse wheel state changes over an image. + /// This gets only called when the alt key is pressed (from HTML side) + /// + /// ListenerState from HMTL side. + public virtual void OnFlipWheel(FlipViewer.OnClickEventArgs args) { + if (state.altKeyPressed) { + // scrolled up + if (args.deltaY < 0) { + if (state.currIteration < NumSamples - 1) { + state.currIteration++; + updateFlipbook(FiredType.Wheel); + } + } + // scrolled down + if (args.deltaY >= 0) { + if (state.currIteration > 0) { + state.currIteration--; + updateFlipbook(FiredType.Wheel); + } + } + } + } + + /// + /// Is fired when mouse position changes over the selected flipbook + /// + /// ListenerState from HMTL side. + public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { + if (state.currX == args.X && state.currY == args.Y) + return; + + if (args.X >= 0 && args.X <= Width - 1) + state.currX = args.X; + if (args.Y >= 0 && args.Y <= Height - 1) + state.currY = args.Y; + + updateFlipbook(FiredType.Move); + } + + /// + /// Is fired when key is pressed or released + /// + /// ListenerState from HMTL side. + public virtual void OnFlipKey(FlipViewer.OnClickEventArgs args) { + if (args.key == "Alt") + state.altKeyPressed = args.isPressed; + + if (args.key == "Control") + state.ctrlKeyPressed = args.isPressed; + + state.currFlipKey = args.registryKey; + state.selectedIndex = args.selectedIndex; + + if (args.key == "Alt" && !args.isPressed) + state.currIteration = 0; + + if (args.isPressed) + updateFlipbook(FiredType.KeyDown); + else + updateFlipbook(FiredType.KeyUp); + } + + /// + /// Catches fired events and forward events to selected flipbooks + /// + /// Fired type + public virtual void updateFlipbook(FiredType fired) { + if (String.IsNullOrEmpty(state.currFlipKey)) + return; + } + + /// + /// Runs the experiment when "..." is pressed on the HTML + /// + public virtual void RunExperiment() { + } + + /// + /// Is executed when Download button is pressed on the HTML + /// + /// + public virtual async Task OnDownloadClick() { + } +} \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor new file mode 100644 index 00000000..85eedce1 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor @@ -0,0 +1,105 @@ +@using SeeSharp.Experiments +@using SeeSharp +@using SeeSharp.Blazor + +@inject IJSRuntime JS + +@inherits SeeSharp.Blazor.Template.Pages.BaseExperiment + +@page "/Experiment" + +

Example experiment

+ +
+
+ +
+ +
+ +
+
+ @if (readyToRun) + { +

+ } + + + +
+ + @if (!running) + { + @if (resultsAvailable) + { +
+ + + @* @if (selected.HasValue && selected.Value) + { + + + + + +
Mesh@(selected.Value.Mesh.Name)
Material@(selected.Value.Mesh.Material.Name) (roughness: @(selected.Value.Mesh.Material.GetRoughness(selected.Value)), transmissive: @(selected.Value.Mesh.Material.IsTransmissive(selected.Value)))
Distance@(selected.Value.Distance)
Position@(selected.Value.Position)
+ } *@ + @* *@ +
+

Compare Example (PT, VCM)

+
+ + +
+ } + } + else + { +

Rendering...

+ } +
+ + + +@code { + SceneSelector sceneSelector; + Scene scene; + bool readyToRun = false; + bool running = false; + bool sceneJustLoaded = false; + bool resultsAvailable = false; + ElementReference runButton; + + async Task OnSceneLoaded(SceneFromFile sceneFromFile) + { + await Task.Run(() => scene = sceneFromFile.MakeScene()); + + flip = null; + compare = (null, null); + + resultsAvailable = false; + readyToRun = true; + sceneJustLoaded = true; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (readyToRun && sceneJustLoaded) + { + await runButton.FocusAsync(); + } + + sceneJustLoaded = false; + } + + async Task OnRunClick() + { + readyToRun = false; + resultsAvailable = false; + running = true; + await Task.Run(() => RunExperiment()); + readyToRun = true; + running = false; + resultsAvailable = true; + } +} \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs new file mode 100644 index 00000000..9cc27269 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs @@ -0,0 +1,190 @@ +namespace SeeSharp.Blazor.Template.Pages; +using Microsoft.JSInterop; + +// Only here for the example to show some possibilities +struct ExampleImageGenerator { + public Image rndImage(float strength, int width, int height, bool colored) { + Image image = new Image(width, height, 3); + RNG rng = new RNG(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (colored) { + image.SetPixelChannel(x, y, 0, rng.NextFloat(0.5f - strength, 0.5f + strength)); + image.SetPixelChannel(x, y, 1, rng.NextFloat(0.5f - strength, 0.5f + strength)); + image.SetPixelChannel(x, y, 2, rng.NextFloat(0.5f - strength, 0.5f + strength)); + } else { + float value = rng.NextFloat(0.5f - strength, 0.5f + strength); + + image.SetPixelChannel(x, y, 0, value); + image.SetPixelChannel(x, y, 1, value); + image.SetPixelChannel(x, y, 2, value); + } + + } + } + + return image; + } +} + +public partial class Experiment : BaseExperiment { + // const int Width = 1280; + // const int Height = 720; + // const int FlipWidth = 660; + // const int FlipHeight = 580; + // const int MaxDepth = 10; + + // int NumSamples = 2; + + // Define all flip books here + public SimpleImageIO.FlipBook flip; + public (SimpleImageIO.FlipBook, SimpleImageIO.FlipBook) compare; + + long renderTimePT, renderTimeVCM; + + //Methods + PathTracer pathTracer; + VertexConnectionAndMerging vcm; + ExampleImageGenerator imgGen; + + RgbImage ptImage; + RgbImage vcmImage; + + /// + /// Initializes all flipbooks + /// + void InitFlipbooks() { + // create new Flipbook w/o key + flip = new FlipBook(FlipWidth, FlipHeight) + .SetZoom(FlipBook.InitialZoom.FillWidth) + .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) + .SetToolVisibility(false); + + // create Flipbooks with keys + compare = ( + new FlipBook(FlipWidth, FlipHeight) + .SetZoom(FlipBook.InitialZoom.FillWidth) + .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) + .SetGroupName("compare") + .SetToolVisibility(false) + .SetKey("1,0"), + new FlipBook(FlipWidth, FlipHeight) + .SetZoom(FlipBook.InitialZoom.FillWidth) + .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) + .SetGroupName("compare") + .SetToolVisibility(false) + .SetKey("0,1") + ); + registry.Add(compare.Item1.GetKey(), compare.Item1); + registry.Add(compare.Item2.GetKey(), compare.Item2); + } + + /// + /// Sets all intial images of flipbooks with extra functions (ex: compare) + /// + void FlipBookSetBaseImages() { + compare.Item1.Add($"PathTracer", ptImage); + compare.Item2.Add($"VCM", vcmImage); + } + + public override void RunExperiment() { + InitFlipbooks(); + + scene.FrameBuffer = new(Width, Height, ""); + scene.Prepare(); + + pathTracer = new() { + TotalSpp = NumSamples, + MaxDepth = MaxDepth, + }; + pathTracer.Render(scene); + flip.Add($"PT", scene.FrameBuffer.Image); + renderTimePT = scene.FrameBuffer.RenderTimeMs; + ptImage = scene.FrameBuffer.Image; + + scene.FrameBuffer = new(Width, Height, ""); + vcm = new() { + NumIterations = NumSamples, + MaxDepth = MaxDepth, + }; + vcm.Render(scene); + flip.Add($"VCM", scene.FrameBuffer.Image); + renderTimeVCM = scene.FrameBuffer.RenderTimeMs; + vcmImage = scene.FrameBuffer.Image; + + FlipBookSetBaseImages(); + } + + /// + /// Catches fired events and forward events to selected flipbooks + /// + /// Fired type + public override void updateFlipbook(FiredType fired) { + if (String.IsNullOrEmpty(state.currFlipKey)) + return; + + switch (state.currFlipKey) { + case "1,0": + case "0,1": { + updateCompare(fired); + break; + } + default: + break; + } + } + + /// + /// Example method that updates the flipbook pair. + /// When Alt key is pressed, the image change to random noise images + /// When Ctrl key is pressed, the colored and black/white images swap + /// + /// Fired type + /// + async Task updateCompare(FiredType fired) { + // Disable event types that you want to ignore + if (fired == FiredType.Click || fired == FiredType.Move) + return; + + FlipBook flipBook = registry[state.currFlipKey]; + // TODO: some iteration over all pairs with the same number ex: (1,0,0,...) -> (0,1,0,0,..) + // this is a fast solution (and maybe good enough) for now -> flip the string to get the other flipbook + FlipBook flipBookOther = registry[Reverse(state.currFlipKey)]; + + Image updateImage = flipBook.GetImage(state.selectedIndex); + Image updateImageOther = flipBookOther.GetImage(state.selectedIndex); + + if (state.altKeyPressed) { + bool colored = true; + + if (state.ctrlKeyPressed) + colored = !colored; + + if (state.currIteration == 0) { + updateImage = imgGen.rndImage(0.2f, Width, Height, !colored); + updateImageOther = imgGen.rndImage(0.2f, Width, Height, colored); + } else if (state.currIteration == 1) { + updateImage = imgGen.rndImage(0.4f, Width, Height, !colored); + updateImageOther = imgGen.rndImage(0.4f, Width, Height, colored); + } + + FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + + Console.WriteLine("Compare updated"); + } else { + updateImage = ptImage; + updateImageOther = vcmImage; + + FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + JS.InvokeVoidAsync("updateImage", code.Data); + + Console.WriteLine("Compare reset"); + } + } +} \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Index.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Index.razor new file mode 100644 index 00000000..59e43710 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Index.razor @@ -0,0 +1,39 @@ +@page "/" + +@using System.Reflection +@using System.Text.RegularExpressions + + +
+ +
+ + +@code { + /// Enumerates all .razor components in this folder + public IEnumerable<(string Name, string Url)> GetExperimentPages() + { + var routableComponents = Assembly + .GetExecutingAssembly() + .ExportedTypes + .Where(t => t.IsSubclassOf(typeof(ComponentBase))) + .Where(c => c + .GetCustomAttributes(inherit: true) + .OfType() + .Count() > 0); + + foreach (var routableComponent in routableComponents) + { + string name = routableComponent.ToString().Replace("SeeSharp.Blazor.Template.Pages.", string.Empty); + if (name != "Index") + yield return (name, name); + } + } +} diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/_Host.cshtml b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/_Host.cshtml new file mode 100644 index 00000000..b2f07105 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/_Host.cshtml @@ -0,0 +1,34 @@ +@page "/" +@using Microsoft.AspNetCore.Components.Web +@namespace SeeSharp.Blazor.Template.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + + + + + + + + + + @Html.Raw(SeeSharp.Blazor.Scripts.AllScripts) + + + + + +
+ + An error has occurred. This application may no longer respond until reloaded. + + + An unhandled exception has occurred. See browser dev tools for details. + + Reload + 🗙 +
+ + + + diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Program.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Program.cs new file mode 100644 index 00000000..9245b9cb --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Program.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; + +ProgressBar.Silent = true; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); + +var app = builder.Build(); + +// Register scene folder +var scenesPath = "Scenes"; +SceneRegistry.AddSource(scenesPath); + +if (!app.Environment.IsDevelopment()) { + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); + +app.UseRouting(); + +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); + +app.Run(); \ No newline at end of file diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/SeeSharp.Blazor.Template.csproj b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/SeeSharp.Blazor.Template.csproj new file mode 100644 index 00000000..ecf433ba --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/SeeSharp.Blazor.Template.csproj @@ -0,0 +1,17 @@ + + + + Exe + net9.0 + enable + + + + + + + + + + + diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/_Imports.razor b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/_Imports.razor new file mode 100644 index 00000000..05428637 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/_Imports.razor @@ -0,0 +1,6 @@ +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using SeeSharp.Blazor.Template + +@using SeeSharp.Blazor diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.Development.json b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.Development.json new file mode 100644 index 00000000..770d3e93 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.json b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/wwwroot/css/site.css b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/wwwroot/css/site.css new file mode 100644 index 00000000..edac4442 --- /dev/null +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/wwwroot/css/site.css @@ -0,0 +1,95 @@ +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 3.5rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +html { + font-family: system-ui; +} + +button { + background-color: #a4e1f2; + border-style: none; + /* border-width: 2px; + border-color: #245e6f; */ + color: black; + font-size: medium; + padding-left: 8px; + padding-right: 8px; + padding-bottom: 4px; + padding-top: 4px; +} + button:hover { + background-color: #c9eff4; + cursor: pointer; + } + button:disabled { + background-color: #e5f1f5; + color: #96b4bd; + border-color: #96b4bd; + } + +.experiment-settings { + display: flex; + gap: 0.25em; + flex-direction: column; + float: left; + margin-right: 1em; + height: 10000px; /*Sets height of parameter window on the left site. Only visual benefits*/ +} + +.experiment-results { + display: flex; + gap: 10px; + flex-wrap: wrap; + align-items: flex-start; +} + +.compare { + display: flex; + gap: 10px; + margin-bottom: 10px; + flex-wrap: wrap; + align-items: flex-start; +} + +table { + border-collapse: collapse; +} +td, th { + border: none; + padding: 4px; +} +tr:hover { background-color: #e7f2f1; } +th { + padding-top: 6px; + padding-bottom: 6px; + text-align: left; + background-color: #4a96af; + color: white; + font-size: smaller; +} \ No newline at end of file From 485227bd51c80637e627b3aeb4f0e4110c9f7d61 Mon Sep 17 00:00:00 2001 From: David Hares Date: Wed, 26 Nov 2025 13:45:22 +0100 Subject: [PATCH 7/8] clean up in templates --- .../Pages/BaseExperiment.razor.cs | 38 +++++++++++-------- .../Pages/Experiment.razor.cs | 8 ---- .../Pages/BaseExperiment.razor.cs | 26 ++++++++----- .../Pages/Experiment.razor.cs | 12 +----- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs index 2a6bfe72..aaaa4f70 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/BaseExperiment.razor.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using SeeSharp.Blazor; @@ -17,13 +18,18 @@ public ListenerState() { } ///
public int currIteration = 0; - public bool altKeyPressed = false; - public bool ctrlKeyPressed = false; + // Add here all action keys for your functionalities + // Attention: any key press disables the default html scrolling! + public string actionKey1 = "a"; + public string actionKey2 = "d"; + public bool actionKey1Pressed = false; + public bool actionKey2Pressed = false; + public int currX = 0; public int currY = 0; /// - /// The key of the current flipbook in string form and concatenated with ',' + /// The key of the current flipbook /// public string currFlipKey = ""; } @@ -72,7 +78,7 @@ public static string Reverse(string s) { /// /// ListenerState from HMTL side public virtual void OnFlipClick(FlipViewer.OnClickEventArgs args) { - updateFlipbook(FiredType.Click); + updateFlipbook(FiredType.Click); } /// @@ -81,19 +87,19 @@ public virtual void OnFlipClick(FlipViewer.OnClickEventArgs args) { /// /// ListenerState from HMTL side. public virtual void OnFlipWheel(FlipViewer.OnClickEventArgs args) { - if (state.altKeyPressed) { + if (state.actionKey1Pressed) { // scrolled up if (args.deltaY < 0) { if (state.currIteration < NumSamples - 1) { state.currIteration++; - updateFlipbook(FiredType.Wheel); + updateFlipbook(FiredType.Wheel); } } // scrolled down if (args.deltaY >= 0) { if (state.currIteration > 0) { state.currIteration--; - updateFlipbook(FiredType.Wheel); + updateFlipbook(FiredType.Wheel); } } } @@ -108,11 +114,11 @@ public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { return; if (args.X >= 0 && args.X <= Width - 1) - state.currX = args.X; + state.currX = args.X; if (args.Y >= 0 && args.Y <= Height - 1) state.currY = args.Y; - updateFlipbook(FiredType.Move); + updateFlipbook(FiredType.Move); } /// @@ -120,22 +126,22 @@ public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { /// /// ListenerState from HMTL side. public virtual void OnFlipKey(FlipViewer.OnClickEventArgs args) { - if (args.key == "Alt") - state.altKeyPressed = args.isPressed; + if (args.key == state.actionKey1) + state.actionKey1Pressed = args.isPressed; - if (args.key == "Control") - state.ctrlKeyPressed = args.isPressed; + if (args.key == state.actionKey2) + state.actionKey2Pressed = args.isPressed; state.currFlipKey = args.registryKey; state.selectedIndex = args.selectedIndex; - if (args.key == "Alt" && !args.isPressed) + if (args.key == state.actionKey1 && !args.isPressed) state.currIteration = 0; if (args.isPressed) - updateFlipbook(FiredType.KeyDown); + updateFlipbook(FiredType.KeyDown); else - updateFlipbook(FiredType.KeyUp); + updateFlipbook(FiredType.KeyUp); } /// diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs index e7adb58c..802e15c2 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs @@ -5,14 +5,6 @@ namespace SeeSharp.Blazor.Template.Pages; public partial class Experiment : BaseExperiment { - // protected const int Width = 1280; - // protected const int Height = 720; - // protected const int FlipWidth = 660; - // protected const int FlipHeight = 580; - // protected const int MaxDepth = 10; - - // int NumSamples = 2; - public SimpleImageIO.FlipBook flip; long renderTimePT, renderTimeVCM; diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs index 2a6bfe72..750c1c64 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/BaseExperiment.razor.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using SeeSharp.Blazor; @@ -17,13 +18,18 @@ public ListenerState() { } /// public int currIteration = 0; - public bool altKeyPressed = false; - public bool ctrlKeyPressed = false; + // Add here all action keys for your functionalities + // Attention: any key press disables the default html scrolling! + public string actionKey1 = "a"; + public string actionKey2 = "d"; + public bool actionKey1Pressed = false; + public bool actionKey2Pressed = false; + public int currX = 0; public int currY = 0; /// - /// The key of the current flipbook in string form and concatenated with ',' + /// The key of the current flipbook /// public string currFlipKey = ""; } @@ -81,7 +87,7 @@ public virtual void OnFlipClick(FlipViewer.OnClickEventArgs args) { /// /// ListenerState from HMTL side. public virtual void OnFlipWheel(FlipViewer.OnClickEventArgs args) { - if (state.altKeyPressed) { + if (state.actionKey1Pressed) { // scrolled up if (args.deltaY < 0) { if (state.currIteration < NumSamples - 1) { @@ -108,7 +114,7 @@ public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { return; if (args.X >= 0 && args.X <= Width - 1) - state.currX = args.X; + state.currX = args.X; if (args.Y >= 0 && args.Y <= Height - 1) state.currY = args.Y; @@ -120,16 +126,16 @@ public virtual void OnFlipMouseOver(FlipViewer.OnClickEventArgs args) { /// /// ListenerState from HMTL side. public virtual void OnFlipKey(FlipViewer.OnClickEventArgs args) { - if (args.key == "Alt") - state.altKeyPressed = args.isPressed; + if (args.key == state.actionKey1) + state.actionKey1Pressed = args.isPressed; - if (args.key == "Control") - state.ctrlKeyPressed = args.isPressed; + if (args.key == state.actionKey2) + state.actionKey2Pressed = args.isPressed; state.currFlipKey = args.registryKey; state.selectedIndex = args.selectedIndex; - if (args.key == "Alt" && !args.isPressed) + if (args.key == state.actionKey1 && !args.isPressed) state.currIteration = 0; if (args.isPressed) diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs index 9cc27269..7b254441 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs @@ -29,14 +29,6 @@ public Image rndImage(float strength, int width, int height, bool colored) { } public partial class Experiment : BaseExperiment { - // const int Width = 1280; - // const int Height = 720; - // const int FlipWidth = 660; - // const int FlipHeight = 580; - // const int MaxDepth = 10; - - // int NumSamples = 2; - // Define all flip books here public SimpleImageIO.FlipBook flip; public (SimpleImageIO.FlipBook, SimpleImageIO.FlipBook) compare; @@ -155,10 +147,10 @@ async Task updateCompare(FiredType fired) { Image updateImage = flipBook.GetImage(state.selectedIndex); Image updateImageOther = flipBookOther.GetImage(state.selectedIndex); - if (state.altKeyPressed) { + if (state.actionKey1Pressed) { bool colored = true; - if (state.ctrlKeyPressed) + if (state.actionKey2Pressed) colored = !colored; if (state.currIteration == 0) { From a47a47df5bcd909a4c652a4eea5fef7272a0d79b Mon Sep 17 00:00:00 2001 From: David Hares Date: Mon, 19 Jan 2026 17:47:26 +0100 Subject: [PATCH 8/8] adapt template to changes in SSIO --- .../Pages/Experiment.razor.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs index 7b254441..494f9fb9 100644 --- a/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs +++ b/SeeSharp.Templates/content/SeeSharp.Blazor.TemplateExample/Pages/Experiment.razor.cs @@ -60,16 +60,16 @@ void InitFlipbooks() { .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) .SetGroupName("compare") .SetToolVisibility(false) - .SetKey("1,0"), + .SetID("1,0"), new FlipBook(FlipWidth, FlipHeight) .SetZoom(FlipBook.InitialZoom.FillWidth) .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) .SetGroupName("compare") .SetToolVisibility(false) - .SetKey("0,1") + .SetID("0,1") ); - registry.Add(compare.Item1.GetKey(), compare.Item1); - registry.Add(compare.Item2.GetKey(), compare.Item2); + registry.Add(compare.Item1.ID, compare.Item1); + registry.Add(compare.Item2.ID, compare.Item2); } /// @@ -161,9 +161,9 @@ async Task updateCompare(FiredType fired) { updateImageOther = imgGen.rndImage(0.4f, Width, Height, colored); } - FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + FlipBook.GeneratedCode code = flipBook.ReplaceImage(updateImage, state.selectedIndex); JS.InvokeVoidAsync("updateImage", code.Data); - code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + code = flipBookOther.ReplaceImage(updateImageOther, state.selectedIndex); JS.InvokeVoidAsync("updateImage", code.Data); Console.WriteLine("Compare updated"); @@ -171,9 +171,9 @@ async Task updateCompare(FiredType fired) { updateImage = ptImage; updateImageOther = vcmImage; - FlipBook.GeneratedCode code = flipBook.UpdateImage(updateImage, state.selectedIndex); + FlipBook.GeneratedCode code = flipBook.ReplaceImage(updateImage, state.selectedIndex); JS.InvokeVoidAsync("updateImage", code.Data); - code = flipBookOther.UpdateImage(updateImageOther, state.selectedIndex); + code = flipBookOther.ReplaceImage(updateImageOther, state.selectedIndex); JS.InvokeVoidAsync("updateImage", code.Data); Console.WriteLine("Compare reset");