Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions SeeSharp.Blazor/LongSetting.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Mvc

@namespace SeeSharp.Blazor

@inherits SettingBase<long>

@{
base.BuildRenderTree(__builder);
}

@code {
protected override string Type => "number";

protected override long ParseValue(ChangeEventArgs e)
{
if (long.TryParse((string)e.Value, out long result))
return result;
return Value;
}

protected override Dictionary<string, object> CustomAttributes => new() {
{ "step", 1 }
};
}

12 changes: 12 additions & 0 deletions SeeSharp.ReferenceManager/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
32 changes: 32 additions & 0 deletions SeeSharp.ReferenceManager/Imports.cs
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 3 additions & 0 deletions SeeSharp.ReferenceManager/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@inherits LayoutComponentBase

<main> @Body </main>
39 changes: 39 additions & 0 deletions SeeSharp.ReferenceManager/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@page "/"

@using System.Reflection
@using System.Text.RegularExpressions


<div>
<nav>
<ul>
@foreach (var (name, url) in GetExperimentPages())
{
<li><a href=@(url)>@(name)</a></li>
}
</ul>
</nav>
</div>


@code {
/// <summary>Enumerates all .razor components in this folder</summary>
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<RouteAttribute>()
.Count() > 0);

foreach (var routableComponent in routableComponents)
{
string name = routableComponent.ToString().Replace("SeeSharp.ReferenceManager.Pages.", string.Empty);
if (name != "Index")
yield return (name, name);
}
}
}
52 changes: 52 additions & 0 deletions SeeSharp.ReferenceManager/Pages/IntegratorSelector.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
@using SeeSharp.Experiments
@using SeeSharp
@using SeeSharp.Blazor
@using SeeSharp.Integrators
@using System.Reflection
@using System.Collections.Generic
@using System.ComponentModel

<div class="integrator-selector">
<div style="display: flex; align-items: center; gap: 10px;">
<label style="margin: 0; white-space: nowrap;">Integrator:</label>
<select value="@selectedIntegrator" @onchange="OnSelectionChanged" style="flex-grow: 1;">
@foreach (var type in integratorTypes)
{
<option value="@type.FullName" selected="@(type.FullName == selectedIntegrator)">@FormatClassName(type)</option>
}
</select>
</div>
</div>

<div class="integrator-container">
@if (CurrentIntegrator != null)
{
var groups = GetParameterGroups(CurrentIntegrator);

@foreach (var group in groups)
{
<details open>
<summary>
@group.Title
</summary>

<div>
@foreach (var prop in group.Properties)
{
<RenderSetting
Member="@prop"
Getter="() => prop.GetValue(CurrentIntegrator)"
Setter="v => { prop.SetValue(CurrentIntegrator, v); StateHasChanged(); }" />
}
@foreach (var field in group.Fields)
{
<RenderSetting
Member="@field"
Getter="() => field.GetValue(CurrentIntegrator)"
Setter="v => { field.SetValue(CurrentIntegrator, v); StateHasChanged(); }" />
}
</div>
</details>
}
}
</div>
88 changes: 88 additions & 0 deletions SeeSharp.ReferenceManager/Pages/IntegratorSelector.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Components;

namespace SeeSharp.ReferenceManager.Pages;

public partial class IntegratorSelector : ComponentBase
{
[Parameter] public Scene scene { get; set; } = default!;

public List<Integrator> addedIntegrators { get; private set; } = new();

public Integrator? CurrentIntegrator => addedIntegrators.FirstOrDefault();

Type[] integratorTypes = Array.Empty<Type>();
string? selectedIntegrator;
private string lastIntegrator;

protected override void OnInitialized()
{
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsClass && !type.IsAbstract && typeof(Integrator).IsAssignableFrom(type) &&
!type.ContainsGenericParameters && !typeof(DebugVisualizer).IsAssignableFrom(type));
integratorTypes = types.Where(t => !types.Any(other => other.IsSubclassOf(t))).ToArray();

if (integratorTypes.Length > 0) {
selectedIntegrator = integratorTypes[0].FullName;
lastIntegrator = selectedIntegrator;
ReplaceIntegrator();
}
DocumentationReader.LoadXmlDocumentation(typeof(Integrator).Assembly);
}

void OnSelectionChanged(ChangeEventArgs e)
{
selectedIntegrator = e.Value?.ToString();
if (!string.IsNullOrEmpty(selectedIntegrator))
{
lastIntegrator = selectedIntegrator;
ReplaceIntegrator();
}
}

void ReplaceIntegrator()
{
if (string.IsNullOrEmpty(selectedIntegrator)) return;
var type = integratorTypes.FirstOrDefault(t => t.FullName == selectedIntegrator);
if (type == null) return;

addedIntegrators.Clear();

var integrator = (Integrator)Activator.CreateInstance(type)!;
addedIntegrators.Add(integrator);

StateHasChanged();
}

protected List<ParameterGroup> GetParameterGroups(Integrator integrator)
=> IntegratorUtils.GetParameterGroups(integrator);

protected string FormatClassName(Type t) => IntegratorUtils.FormatClassName(t);

public void TriggerReset()
{
selectedIntegrator = lastIntegrator;
ReplaceIntegrator();
}

public bool TrySelectIntegrator(string simpleName)
{
var targetType = integratorTypes.FirstOrDefault(t => t.Name == simpleName || t.Name == simpleName + "`1");

if (targetType != null && targetType.FullName != selectedIntegrator)
{
selectedIntegrator = targetType.FullName;
ReplaceIntegrator();
return true;
}
return targetType != null;
}

public Type? GetIntegratorType(string simpleName)
{
if (string.IsNullOrEmpty(simpleName)) return null;
return integratorTypes.FirstOrDefault(t =>
t.Name.Equals(simpleName, StringComparison.OrdinalIgnoreCase) ||
t.Name.Equals(simpleName + "`1", StringComparison.OrdinalIgnoreCase));
}
}
46 changes: 46 additions & 0 deletions SeeSharp.ReferenceManager/Pages/IntegratorSelector.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.integrator-selector {
display: flex;
flex-direction: column;
gap: 0.4em;
margin-bottom: 10px;
}

.integrator-container {
display: flex;
flex-direction: column;
gap: 0.5em;
}

summary {
cursor: pointer;
font-weight: 600;
font-size: 0.85em;
color: #000;
outline: none;
display: flex;
align-items: center;
padding: 4px 0;
}
summary::before {
content: '▶';
font-size: 0.7em;
margin-right: 6px;
display: inline-block;
transition: transform 0.2s;
color: #000;
}
details[open] > summary::before { transform: rotate(90deg); }
summary::-webkit-details-marker { display: none; }


details > div {
margin-left: 6px;
padding-left: 12px;
border-left: 2px solid #e0e0e0;
margin-top: 2px;
margin-bottom: 2px;
}
details:hover > div {
border-left-color: #ccc;
}

80 changes: 80 additions & 0 deletions SeeSharp.ReferenceManager/Pages/IntegratorUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Reflection;

namespace SeeSharp.ReferenceManager.Pages;

public class ParameterGroup {
public string Title { get; set; } = "";
public List<PropertyInfo> Properties { get; set; } = new();
public List<FieldInfo> Fields { get; set; } = new();
public bool HasParameters => Properties.Any() || Fields.Any();
}

public static class IntegratorUtils {
public static List<ParameterGroup> GetParameterGroups(Integrator integrator)
{
var groups = new List<ParameterGroup>();
var currentType = integrator.GetType();

var allProps = GetFilteredProps(currentType);
var allFields = GetFilteredFields(currentType);

while (currentType != null && currentType != typeof(object)) {
bool IsCurrentDeclared(MemberInfo m)
{
var d = m.DeclaringType;
var cur = currentType;
if (d != null && d.IsGenericType && !d.IsGenericTypeDefinition) d = d.GetGenericTypeDefinition();
if (cur != null && cur.IsGenericType && !cur.IsGenericTypeDefinition) cur = cur.GetGenericTypeDefinition();
return d == cur;
}

string title = FormatClassName(currentType);

bool isGlobalSettings = (currentType == typeof(Integrator));
if (isGlobalSettings) title = "Global Settings";

var group = new ParameterGroup {
Title = title,
Properties = allProps.Where(p => IsCurrentDeclared(p))
.Where(p => !isGlobalSettings || (p.Name != "MaxDepth" && p.Name != "MinDepth")).ToList(),
Fields = allFields.Where(f => IsCurrentDeclared(f)).ToList()
};

if (group.HasParameters)
groups.Add(group);

currentType = currentType.BaseType;
}

return groups;
}

public static string FormatClassName(Type t)
{
string name = t.Name;
if (name.Contains('`')) name = name.Substring(0, name.IndexOf('`'));
return System.Text.RegularExpressions.Regex.Replace(name, "(\\B[A-Z])", " $1");
}

public static bool IsVisible(MemberInfo m)
{
if (m is PropertyInfo p && (!p.CanRead || !p.CanWrite)) return false;
if (m is FieldInfo f && (f.IsLiteral || f.IsInitOnly)) return false;

Type t = (m is PropertyInfo pi) ? pi.PropertyType : ((FieldInfo)m).FieldType;
Type underlyingType = Nullable.GetUnderlyingType(t) ?? t;

return underlyingType.IsPrimitive;
}

public static IEnumerable<PropertyInfo> GetFilteredProps(Type type) =>
type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(IsVisible);

public static IEnumerable<FieldInfo> GetFilteredFields(Type type) =>
type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(IsVisible);

public static string GetDescription(MemberInfo member)
{
return DocumentationReader.GetSummary(member) ?? "";
}
}
Loading