-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEventFunctions.cs
More file actions
98 lines (91 loc) · 4.4 KB
/
EventFunctions.cs
File metadata and controls
98 lines (91 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System.Numerics;
using Combobulate.Caching;
using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork;
namespace Combobulate.Rendering;
/// <summary>
/// Phase 0 (signature-based BakedAspectGraph): builds the small set of
/// scalar event functions whose <i>signs</i> uniquely determine the
/// painter signature of the model under any rotation in the supplied
/// <see cref="Matrix4x4Node"/> AST.
///
/// <para>Two kinds of events:</para>
/// <list type="bullet">
/// <item>
/// <b>Front-facing event per face</b>: <c>(M · normal).z</c>. The sign
/// determines whether the face's outward normal projects toward or away
/// from the viewer, i.e. whether the face is visible.
/// </item>
/// <item>
/// <b>Depth-pair event per ordered pair</b>: <c>(M · centroid_j).z -
/// (M · centroid_i).z</c>. The sign determines which face's centroid is
/// further from the viewer along Z, i.e. the centroid-Z painter ordering
/// between the pair.
/// </item>
/// </list>
///
/// <para>The painter signature for a configuration θ is the sign vector
/// of <i>all</i> face events plus the sign vector of all pair events
/// where <i>both</i> faces are front-facing. The runtime compositor
/// predicate for a baked signature is the conjunction of those signed
/// inequalities. For a cube under arbitrary rotation that is 6 face
/// events + at most 15 pair events = 21 scalar comparisons per
/// signature, evaluated entirely on the compositor thread. Cell
/// boundaries are now <i>exact</i> — there is no grid sampling artifact
/// because every θ has exactly one sign vector and that vector is what
/// the compositor evaluates.</para>
///
/// <para>The matrix-vector multiplication is expressed via
/// <see cref="Matrix4x4Node"/> subchannel access (Channel13/23/33/43)
/// matching <see cref="System.Numerics.Vector3.Transform(Vector3, Matrix4x4)"/>'s
/// row-major / column-vector convention used elsewhere in Combobulate.</para>
/// </summary>
internal static class EventFunctions
{
/// <summary>
/// <c>(M · point).z</c> as a typed <see cref="ScalarNode"/>:
/// <c>v.x*M13 + v.y*M23 + v.z*M33 + M43</c>. Skips terms whose
/// coefficient is exactly 0 (and avoids producing <c>-0</c> literals)
/// so the resulting Composition expression string stays as short as
/// possible — the compositor parser has a length limit and dense
/// per-cell predicates of dozens of events compound the cost
/// dramatically.
/// </summary>
public static ScalarNode TransformedPointZ(Matrix4x4Node M, Vector3 v)
{
ScalarNode? acc = null;
if (v.X != 0f) acc = AddTerm(acc, M.Channel13 * NoNegZero(v.X));
if (v.Y != 0f) acc = AddTerm(acc, M.Channel23 * NoNegZero(v.Y));
if (v.Z != 0f) acc = AddTerm(acc, M.Channel33 * NoNegZero(v.Z));
// Translation contribution always present (Channel43 may be
// animated at runtime even if at bake time it's 0).
acc = AddTerm(acc, M.Channel43);
return acc!;
}
/// <summary>
/// <c>(M · direction).z</c> as a typed <see cref="ScalarNode"/>: no
/// translation contribution. Skips zero-coefficient terms.
/// </summary>
public static ScalarNode TransformedDirectionZ(Matrix4x4Node M, Vector3 n)
{
ScalarNode? acc = null;
if (n.X != 0f) acc = AddTerm(acc, M.Channel13 * NoNegZero(n.X));
if (n.Y != 0f) acc = AddTerm(acc, M.Channel23 * NoNegZero(n.Y));
if (n.Z != 0f) acc = AddTerm(acc, M.Channel33 * NoNegZero(n.Z));
// If all components were 0 (degenerate), return a constant 0.
return acc ?? (ScalarNode)0f;
}
/// <summary>Map -0 to +0 so the toolkit doesn't emit a "-0" literal that
/// some compositor expression evaluators reject.</summary>
private static float NoNegZero(float f) => f == 0f ? 0f : f;
private static ScalarNode AddTerm(ScalarNode? acc, ScalarNode term) => acc is null ? term : acc + term;
/// <summary>
/// CPU evaluation of <c>(M · point).z</c>. Used during the bake's
/// signature-discovery sweep so we can compute event signs on the
/// CPU after evaluating the transform AST once per sample.
/// </summary>
public static float EvalPointZ(in Matrix4x4 M, Vector3 v)
=> v.X * M.M13 + v.Y * M.M23 + v.Z * M.M33 + M.M43;
/// <summary>CPU evaluation of <c>(M · direction).z</c>.</summary>
public static float EvalDirectionZ(in Matrix4x4 M, Vector3 n)
=> n.X * M.M13 + n.Y * M.M23 + n.Z * M.M33;
}