11import assert from "node:assert/strict" ;
22import { describe , it } from "node:test" ;
33import { GaussKernel } from "postprocessing" ;
4+ import { assertClose , assertCloseSequence } from "../../support/assert.ts" ;
45
56describe ( "GaussKernel" , ( ) => {
67
@@ -10,4 +11,130 @@ describe("GaussKernel", () => {
1011
1112 } ) ;
1213
14+ it ( "produces the expected discrete offsets and weights" , ( ) => {
15+
16+ const kernel = GaussKernel . create ( 9 , 1 ) ;
17+
18+ assert . equal ( kernel . steps , 5 ) ;
19+
20+ // Offsets run from the center (index 0) outwards by one sample each step.
21+ assertCloseSequence ( kernel . offsets , [ 0 , 1 , 2 , 3 , 4 ] ) ;
22+
23+ // Center-aligned discrete Gaussian for sigma = 1, normalized so the symmetric kernel sums to 1.
24+ // The center weight is exp(0) / sum = 1 / 2.5066...
25+ assertCloseSequence ( kernel . weights , [
26+ 0.39894346935609781 ,
27+ 0.24197144565660075 ,
28+ 0.053991127420704416 ,
29+ 0.0044318616200312664 ,
30+ 0.00013383062461474178
31+ ] ) ;
32+
33+ } ) ;
34+
35+ it ( "produces the expected linear offsets and weights" , ( ) => {
36+
37+ const kernel = GaussKernel . create ( 9 , 1 ) ;
38+
39+ assert . equal ( kernel . linearSteps , 3 ) ;
40+
41+ // linearWeights combine the adjacent discrete samples for bilinear filtering,
42+ // and linearOffsets are their weighted centroids.
43+ assertCloseSequence ( kernel . linearWeights , [
44+ 0.39894346935609781 ,
45+ 0.29596257307730517 ,
46+ 0.0045656922446460080
47+ ] ) ;
48+
49+ assertCloseSequence ( kernel . linearOffsets , [
50+ 0 ,
51+ 1.1824255238063563 ,
52+ 3.0293122307513562
53+ ] ) ;
54+
55+ } ) ;
56+
57+ it ( "normalizes the symmetric kernel to sum to one" , ( ) => {
58+
59+ for ( const [ kernelSize , sigma ] of [ [ 3 , 0.7 ] , [ 5 , 1.5 ] , [ 9 , 1 ] , [ 15 , 2 ] , [ 9 , 3 ] ] as const ) {
60+
61+ const kernel = GaussKernel . create ( kernelSize , sigma ) ;
62+
63+ // The stored weights cover only the center and one side;
64+ // the full symmetric kernel therefore sums as weights[0] + 2 * (sum of the remaining weights).
65+ const sum = Array . from ( kernel . weights ) . reduce (
66+ ( total , weight , index ) => total + weight * ( index === 0 ? 1 : 2 ) ,
67+ 0.0
68+ ) ;
69+
70+ assertClose ( sum , 1.0 ) ;
71+
72+ }
73+
74+ } ) ;
75+
76+ it ( "keeps the weights centered, positive, and monotonically decreasing" , ( ) => {
77+
78+ const kernel = GaussKernel . create ( 15 , 1.5 ) ;
79+
80+ assert . equal ( kernel . offsets [ 0 ] , 0 ) ;
81+
82+ for ( let i = 0 ; i < kernel . steps ; ++ i ) {
83+
84+ const weight = kernel . weights [ i ] ;
85+
86+ assert . ok ( weight > 0.0 , `weight[${ i } ] should be positive` ) ;
87+
88+ if ( i > 0 ) {
89+
90+ const previous = kernel . weights [ i - 1 ] ;
91+ assert . ok ( weight < previous , `weight[${ i } ] should decrease from weight[${ i - 1 } ]` ) ;
92+
93+ }
94+
95+ }
96+
97+ } ) ;
98+
99+ it ( "partitions the half-kernel energy across the linear weights" , ( ) => {
100+
101+ for ( const [ kernelSize , sigma ] of [ [ 5 , 2 ] , [ 7 , 0.5 ] , [ 9 , 1 ] , [ 11 , 1 ] , [ 9 , 3 ] ] as const ) {
102+
103+ const kernel = GaussKernel . create ( kernelSize , sigma ) ;
104+
105+ const linearSum = Array . from ( kernel . linearWeights ) . reduce (
106+ ( total , weight ) => total + weight ,
107+ 0.0
108+ ) ;
109+
110+ // The linear weights must account for exactly the discrete half-kernel
111+ // (center plus one side), which is (1 + centerWeight) / 2.
112+ assertClose ( linearSum , ( 1.0 + kernel . weights [ 0 ] ) / 2.0 ) ;
113+
114+ }
115+
116+ } ) ;
117+
118+ it ( "rejects invalid kernel sizes and sigma" , ( ) => {
119+
120+ for ( const kernelSize of [ 0 , 2 , 4 , - 1 , 9.5 , 1021 , Number . NaN , Number . POSITIVE_INFINITY ] ) {
121+
122+ assert . throws (
123+ ( ) => GaussKernel . create ( kernelSize , 1.0 ) ,
124+ { name : "Error" , message : / k e r n e l s i z e / i }
125+ ) ;
126+
127+ }
128+
129+ for ( const sigma of [ 0 , - 1 , Number . NaN , Number . POSITIVE_INFINITY ] ) {
130+
131+ assert . throws (
132+ ( ) => GaussKernel . create ( 9 , sigma ) ,
133+ { name : "Error" , message : / s i g m a / i }
134+ ) ;
135+
136+ }
137+
138+ } ) ;
139+
13140} ) ;
0 commit comments