@flighthq/effects 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autoExposureEffect.d.ts +3 -0
- package/dist/autoExposureEffect.d.ts.map +1 -0
- package/dist/autoExposureEffect.js +4 -0
- package/dist/autoExposureEffect.js.map +1 -0
- package/dist/barrelDistortionEffect.d.ts +3 -0
- package/dist/barrelDistortionEffect.d.ts.map +1 -0
- package/dist/barrelDistortionEffect.js +4 -0
- package/dist/barrelDistortionEffect.js.map +1 -0
- package/dist/bloomEffect.d.ts +6 -0
- package/dist/bloomEffect.d.ts.map +1 -0
- package/dist/bloomEffect.js +16 -0
- package/dist/bloomEffect.js.map +1 -0
- package/dist/bokehDepthOfFieldEffect.d.ts +3 -0
- package/dist/bokehDepthOfFieldEffect.d.ts.map +1 -0
- package/dist/bokehDepthOfFieldEffect.js +4 -0
- package/dist/bokehDepthOfFieldEffect.js.map +1 -0
- package/dist/brightnessContrastEffect.d.ts +3 -0
- package/dist/brightnessContrastEffect.d.ts.map +1 -0
- package/dist/brightnessContrastEffect.js +4 -0
- package/dist/brightnessContrastEffect.js.map +1 -0
- package/dist/cameraMotionBlurEffect.d.ts +3 -0
- package/dist/cameraMotionBlurEffect.d.ts.map +1 -0
- package/dist/cameraMotionBlurEffect.js +4 -0
- package/dist/cameraMotionBlurEffect.js.map +1 -0
- package/dist/channelMixerEffect.d.ts +3 -0
- package/dist/channelMixerEffect.d.ts.map +1 -0
- package/dist/channelMixerEffect.js +4 -0
- package/dist/channelMixerEffect.js.map +1 -0
- package/dist/chromaticAberrationEffect.d.ts +3 -0
- package/dist/chromaticAberrationEffect.d.ts.map +1 -0
- package/dist/chromaticAberrationEffect.js +4 -0
- package/dist/chromaticAberrationEffect.js.map +1 -0
- package/dist/colorBlindSimulationEffect.d.ts +3 -0
- package/dist/colorBlindSimulationEffect.d.ts.map +1 -0
- package/dist/colorBlindSimulationEffect.js +4 -0
- package/dist/colorBlindSimulationEffect.js.map +1 -0
- package/dist/colorGradeEffect.d.ts +3 -0
- package/dist/colorGradeEffect.d.ts.map +1 -0
- package/dist/colorGradeEffect.js +4 -0
- package/dist/colorGradeEffect.js.map +1 -0
- package/dist/colorScienceMath.d.ts +9 -0
- package/dist/colorScienceMath.d.ts.map +1 -0
- package/dist/colorScienceMath.js +135 -0
- package/dist/colorScienceMath.js.map +1 -0
- package/dist/colorTemperatureMath.d.ts +5 -0
- package/dist/colorTemperatureMath.d.ts.map +1 -0
- package/dist/colorTemperatureMath.js +45 -0
- package/dist/colorTemperatureMath.js.map +1 -0
- package/dist/contactShadowsEffect.d.ts +3 -0
- package/dist/contactShadowsEffect.d.ts.map +1 -0
- package/dist/contactShadowsEffect.js +4 -0
- package/dist/contactShadowsEffect.js.map +1 -0
- package/dist/crtEffect.d.ts +3 -0
- package/dist/crtEffect.d.ts.map +1 -0
- package/dist/crtEffect.js +4 -0
- package/dist/crtEffect.js.map +1 -0
- package/dist/customShaderEffect.d.ts +3 -0
- package/dist/customShaderEffect.d.ts.map +1 -0
- package/dist/customShaderEffect.js +4 -0
- package/dist/customShaderEffect.js.map +1 -0
- package/dist/depthMath.d.ts +4 -0
- package/dist/depthMath.d.ts.map +1 -0
- package/dist/depthMath.js +55 -0
- package/dist/depthMath.js.map +1 -0
- package/dist/directionalBlurEffect.d.ts +3 -0
- package/dist/directionalBlurEffect.d.ts.map +1 -0
- package/dist/directionalBlurEffect.js +4 -0
- package/dist/directionalBlurEffect.js.map +1 -0
- package/dist/displacementEffect.d.ts +3 -0
- package/dist/displacementEffect.d.ts.map +1 -0
- package/dist/displacementEffect.js +4 -0
- package/dist/displacementEffect.js.map +1 -0
- package/dist/ditherEffect.d.ts +3 -0
- package/dist/ditherEffect.d.ts.map +1 -0
- package/dist/ditherEffect.js +4 -0
- package/dist/ditherEffect.js.map +1 -0
- package/dist/edgeDetectMath.d.ts +6 -0
- package/dist/edgeDetectMath.d.ts.map +1 -0
- package/dist/edgeDetectMath.js +72 -0
- package/dist/edgeDetectMath.js.map +1 -0
- package/dist/exposureEffect.d.ts +3 -0
- package/dist/exposureEffect.d.ts.map +1 -0
- package/dist/exposureEffect.js +4 -0
- package/dist/exposureEffect.js.map +1 -0
- package/dist/filmEmulationEffect.d.ts +3 -0
- package/dist/filmEmulationEffect.d.ts.map +1 -0
- package/dist/filmEmulationEffect.js +4 -0
- package/dist/filmEmulationEffect.js.map +1 -0
- package/dist/filmGrainEffect.d.ts +3 -0
- package/dist/filmGrainEffect.d.ts.map +1 -0
- package/dist/filmGrainEffect.js +4 -0
- package/dist/filmGrainEffect.js.map +1 -0
- package/dist/fxaaEffect.d.ts +3 -0
- package/dist/fxaaEffect.d.ts.map +1 -0
- package/dist/fxaaEffect.js +4 -0
- package/dist/fxaaEffect.js.map +1 -0
- package/dist/gaussianMath.d.ts +5 -0
- package/dist/gaussianMath.d.ts.map +1 -0
- package/dist/gaussianMath.js +42 -0
- package/dist/gaussianMath.js.map +1 -0
- package/dist/glitchEffect.d.ts +3 -0
- package/dist/glitchEffect.d.ts.map +1 -0
- package/dist/glitchEffect.js +4 -0
- package/dist/glitchEffect.js.map +1 -0
- package/dist/godRaysEffect.d.ts +3 -0
- package/dist/godRaysEffect.d.ts.map +1 -0
- package/dist/godRaysEffect.js +4 -0
- package/dist/godRaysEffect.js.map +1 -0
- package/dist/godRaysMath.d.ts +8 -0
- package/dist/godRaysMath.d.ts.map +1 -0
- package/dist/godRaysMath.js +49 -0
- package/dist/godRaysMath.js.map +1 -0
- package/dist/grayscaleEffect.d.ts +3 -0
- package/dist/grayscaleEffect.d.ts.map +1 -0
- package/dist/grayscaleEffect.js +4 -0
- package/dist/grayscaleEffect.js.map +1 -0
- package/dist/halftoneEffect.d.ts +3 -0
- package/dist/halftoneEffect.d.ts.map +1 -0
- package/dist/halftoneEffect.js +4 -0
- package/dist/halftoneEffect.js.map +1 -0
- package/dist/hueSaturationEffect.d.ts +3 -0
- package/dist/hueSaturationEffect.d.ts.map +1 -0
- package/dist/hueSaturationEffect.js +4 -0
- package/dist/hueSaturationEffect.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/invertEffect.d.ts +3 -0
- package/dist/invertEffect.d.ts.map +1 -0
- package/dist/invertEffect.js +4 -0
- package/dist/invertEffect.js.map +1 -0
- package/dist/kuwaharaEffect.d.ts +3 -0
- package/dist/kuwaharaEffect.d.ts.map +1 -0
- package/dist/kuwaharaEffect.js +4 -0
- package/dist/kuwaharaEffect.js.map +1 -0
- package/dist/kuwaharaMath.d.ts +6 -0
- package/dist/kuwaharaMath.d.ts.map +1 -0
- package/dist/kuwaharaMath.js +70 -0
- package/dist/kuwaharaMath.js.map +1 -0
- package/dist/lensDirtEffect.d.ts +3 -0
- package/dist/lensDirtEffect.d.ts.map +1 -0
- package/dist/lensDirtEffect.js +4 -0
- package/dist/lensDirtEffect.js.map +1 -0
- package/dist/lensDistortionEffect.d.ts +3 -0
- package/dist/lensDistortionEffect.d.ts.map +1 -0
- package/dist/lensDistortionEffect.js +4 -0
- package/dist/lensDistortionEffect.js.map +1 -0
- package/dist/lensFlareEffect.d.ts +3 -0
- package/dist/lensFlareEffect.d.ts.map +1 -0
- package/dist/lensFlareEffect.js +4 -0
- package/dist/lensFlareEffect.js.map +1 -0
- package/dist/liftGammaGainEffect.d.ts +3 -0
- package/dist/liftGammaGainEffect.d.ts.map +1 -0
- package/dist/liftGammaGainEffect.js +4 -0
- package/dist/liftGammaGainEffect.js.map +1 -0
- package/dist/lookupTableGradeEffect.d.ts +3 -0
- package/dist/lookupTableGradeEffect.d.ts.map +1 -0
- package/dist/lookupTableGradeEffect.js +4 -0
- package/dist/lookupTableGradeEffect.js.map +1 -0
- package/dist/lutMath.d.ts +7 -0
- package/dist/lutMath.d.ts.map +1 -0
- package/dist/lutMath.js +39 -0
- package/dist/lutMath.js.map +1 -0
- package/dist/motionBlurEffect.d.ts +3 -0
- package/dist/motionBlurEffect.d.ts.map +1 -0
- package/dist/motionBlurEffect.js +4 -0
- package/dist/motionBlurEffect.js.map +1 -0
- package/dist/outlineEffect.d.ts +3 -0
- package/dist/outlineEffect.d.ts.map +1 -0
- package/dist/outlineEffect.js +4 -0
- package/dist/outlineEffect.js.map +1 -0
- package/dist/panniniProjectionEffect.d.ts +3 -0
- package/dist/panniniProjectionEffect.d.ts.map +1 -0
- package/dist/panniniProjectionEffect.js +4 -0
- package/dist/panniniProjectionEffect.js.map +1 -0
- package/dist/pixelateEffect.d.ts +3 -0
- package/dist/pixelateEffect.d.ts.map +1 -0
- package/dist/pixelateEffect.js +4 -0
- package/dist/pixelateEffect.js.map +1 -0
- package/dist/posterizeEffect.d.ts +3 -0
- package/dist/posterizeEffect.d.ts.map +1 -0
- package/dist/posterizeEffect.js +4 -0
- package/dist/posterizeEffect.js.map +1 -0
- package/dist/radialBlurEffect.d.ts +3 -0
- package/dist/radialBlurEffect.d.ts.map +1 -0
- package/dist/radialBlurEffect.js +4 -0
- package/dist/radialBlurEffect.js.map +1 -0
- package/dist/renderEffectDefaults.d.ts +4 -0
- package/dist/renderEffectDefaults.d.ts.map +1 -0
- package/dist/renderEffectDefaults.js +111 -0
- package/dist/renderEffectDefaults.js.map +1 -0
- package/dist/renderEffectInputs.d.ts +5 -0
- package/dist/renderEffectInputs.d.ts.map +1 -0
- package/dist/renderEffectInputs.js +87 -0
- package/dist/renderEffectInputs.js.map +1 -0
- package/dist/renderEffectInterpolation.d.ts +4 -0
- package/dist/renderEffectInterpolation.d.ts.map +1 -0
- package/dist/renderEffectInterpolation.js +75 -0
- package/dist/renderEffectInterpolation.js.map +1 -0
- package/dist/renderEffectValidation.d.ts +3 -0
- package/dist/renderEffectValidation.d.ts.map +1 -0
- package/dist/renderEffectValidation.js +18 -0
- package/dist/renderEffectValidation.js.map +1 -0
- package/dist/scanlinesEffect.d.ts +3 -0
- package/dist/scanlinesEffect.d.ts.map +1 -0
- package/dist/scanlinesEffect.js +4 -0
- package/dist/scanlinesEffect.js.map +1 -0
- package/dist/screenSpaceFogEffect.d.ts +3 -0
- package/dist/screenSpaceFogEffect.d.ts.map +1 -0
- package/dist/screenSpaceFogEffect.js +4 -0
- package/dist/screenSpaceFogEffect.js.map +1 -0
- package/dist/sepiaEffect.d.ts +3 -0
- package/dist/sepiaEffect.d.ts.map +1 -0
- package/dist/sepiaEffect.js +4 -0
- package/dist/sepiaEffect.js.map +1 -0
- package/dist/sharpenEffect.d.ts +3 -0
- package/dist/sharpenEffect.d.ts.map +1 -0
- package/dist/sharpenEffect.js +4 -0
- package/dist/sharpenEffect.js.map +1 -0
- package/dist/sketchEffect.d.ts +3 -0
- package/dist/sketchEffect.d.ts.map +1 -0
- package/dist/sketchEffect.js +4 -0
- package/dist/sketchEffect.js.map +1 -0
- package/dist/smaaEffect.d.ts +3 -0
- package/dist/smaaEffect.d.ts.map +1 -0
- package/dist/smaaEffect.js +4 -0
- package/dist/smaaEffect.js.map +1 -0
- package/dist/ssaoEffect.d.ts +3 -0
- package/dist/ssaoEffect.d.ts.map +1 -0
- package/dist/ssaoEffect.js +4 -0
- package/dist/ssaoEffect.js.map +1 -0
- package/dist/ssrEffect.d.ts +3 -0
- package/dist/ssrEffect.d.ts.map +1 -0
- package/dist/ssrEffect.js +4 -0
- package/dist/ssrEffect.js.map +1 -0
- package/dist/stylizeMath.d.ts +9 -0
- package/dist/stylizeMath.d.ts.map +1 -0
- package/dist/stylizeMath.js +77 -0
- package/dist/stylizeMath.js.map +1 -0
- package/dist/taaEffect.d.ts +3 -0
- package/dist/taaEffect.d.ts.map +1 -0
- package/dist/taaEffect.js +4 -0
- package/dist/taaEffect.js.map +1 -0
- package/dist/tiltShiftEffect.d.ts +3 -0
- package/dist/tiltShiftEffect.d.ts.map +1 -0
- package/dist/tiltShiftEffect.js +4 -0
- package/dist/tiltShiftEffect.js.map +1 -0
- package/dist/toneMapEffect.d.ts +3 -0
- package/dist/toneMapEffect.d.ts.map +1 -0
- package/dist/toneMapEffect.js +4 -0
- package/dist/toneMapEffect.js.map +1 -0
- package/dist/toneMapMath.d.ts +12 -0
- package/dist/toneMapMath.d.ts.map +1 -0
- package/dist/toneMapMath.js +110 -0
- package/dist/toneMapMath.js.map +1 -0
- package/dist/vignetteEffect.d.ts +3 -0
- package/dist/vignetteEffect.d.ts.map +1 -0
- package/dist/vignetteEffect.js +4 -0
- package/dist/vignetteEffect.js.map +1 -0
- package/dist/volumetricLightEffect.d.ts +3 -0
- package/dist/volumetricLightEffect.d.ts.map +1 -0
- package/dist/volumetricLightEffect.js +4 -0
- package/dist/volumetricLightEffect.js.map +1 -0
- package/dist/whiteBalanceEffect.d.ts +3 -0
- package/dist/whiteBalanceEffect.d.ts.map +1 -0
- package/dist/whiteBalanceEffect.js +4 -0
- package/dist/whiteBalanceEffect.js.map +1 -0
- package/package.json +37 -0
- package/src/autoExposureEffect.test.ts +11 -0
- package/src/barrelDistortionEffect.test.ts +11 -0
- package/src/bloomEffect.test.ts +41 -0
- package/src/bokehDepthOfFieldEffect.test.ts +15 -0
- package/src/brightnessContrastEffect.test.ts +14 -0
- package/src/cameraMotionBlurEffect.test.ts +14 -0
- package/src/channelMixerEffect.test.ts +12 -0
- package/src/chromaticAberrationEffect.test.ts +14 -0
- package/src/colorBlindSimulationEffect.test.ts +13 -0
- package/src/colorGradeEffect.test.ts +11 -0
- package/src/colorScienceMath.test.ts +160 -0
- package/src/colorTemperatureMath.test.ts +67 -0
- package/src/contactShadowsEffect.test.ts +14 -0
- package/src/crtEffect.test.ts +16 -0
- package/src/customShaderEffect.test.ts +14 -0
- package/src/depthMath.test.ts +71 -0
- package/src/directionalBlurEffect.test.ts +11 -0
- package/src/displacementEffect.test.ts +15 -0
- package/src/ditherEffect.test.ts +11 -0
- package/src/edgeDetectMath.test.ts +84 -0
- package/src/exposureEffect.test.ts +7 -0
- package/src/filmEmulationEffect.test.ts +14 -0
- package/src/filmGrainEffect.test.ts +15 -0
- package/src/fxaaEffect.test.ts +14 -0
- package/src/gaussianMath.test.ts +93 -0
- package/src/glitchEffect.test.ts +16 -0
- package/src/godRaysEffect.test.ts +15 -0
- package/src/godRaysMath.test.ts +63 -0
- package/src/grayscaleEffect.test.ts +11 -0
- package/src/halftoneEffect.test.ts +11 -0
- package/src/hueSaturationEffect.test.ts +15 -0
- package/src/invertEffect.test.ts +11 -0
- package/src/kuwaharaEffect.test.ts +11 -0
- package/src/kuwaharaMath.test.ts +65 -0
- package/src/lensDirtEffect.test.ts +15 -0
- package/src/lensDistortionEffect.test.ts +11 -0
- package/src/lensFlareEffect.test.ts +16 -0
- package/src/liftGammaGainEffect.test.ts +15 -0
- package/src/lookupTableGradeEffect.test.ts +11 -0
- package/src/lutMath.test.ts +67 -0
- package/src/motionBlurEffect.test.ts +11 -0
- package/src/outlineEffect.test.ts +15 -0
- package/src/panniniProjectionEffect.test.ts +14 -0
- package/src/pixelateEffect.test.ts +11 -0
- package/src/posterizeEffect.test.ts +11 -0
- package/src/radialBlurEffect.test.ts +15 -0
- package/src/renderEffectDefaults.test.ts +70 -0
- package/src/renderEffectInputs.test.ts +60 -0
- package/src/renderEffectInterpolation.test.ts +71 -0
- package/src/renderEffectValidation.test.ts +37 -0
- package/src/scanlinesEffect.test.ts +11 -0
- package/src/screenSpaceFogEffect.test.ts +14 -0
- package/src/sepiaEffect.test.ts +11 -0
- package/src/sharpenEffect.test.ts +11 -0
- package/src/sketchEffect.test.ts +11 -0
- package/src/smaaEffect.test.ts +11 -0
- package/src/ssaoEffect.test.ts +11 -0
- package/src/ssrEffect.test.ts +11 -0
- package/src/stylizeMath.test.ts +99 -0
- package/src/taaEffect.test.ts +11 -0
- package/src/tiltShiftEffect.test.ts +15 -0
- package/src/toneMapEffect.test.ts +7 -0
- package/src/toneMapMath.test.ts +145 -0
- package/src/vignetteEffect.test.ts +16 -0
- package/src/volumetricLightEffect.test.ts +14 -0
- package/src/whiteBalanceEffect.test.ts +11 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createPosterizeEffect } from './posterizeEffect';
|
|
2
|
+
|
|
3
|
+
describe('createPosterizeEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createPosterizeEffect().kind).toBe('PosterizeEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createPosterizeEffect({ levels: 4 })).toMatchObject({ levels: 4 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRadialBlurEffect } from './radialBlurEffect';
|
|
2
|
+
|
|
3
|
+
describe('createRadialBlurEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createRadialBlurEffect().kind).toBe('RadialBlurEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createRadialBlurEffect({ centerX: 0.5, centerY: 0.5, strength: 0.2 })).toMatchObject({
|
|
10
|
+
centerX: 0.5,
|
|
11
|
+
centerY: 0.5,
|
|
12
|
+
strength: 0.2,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { BloomEffect, GrayscaleEffect, RenderEffect, ToneMapEffect, VignetteEffect } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { createBloomEffect } from './bloomEffect';
|
|
4
|
+
import { createGrayscaleEffect } from './grayscaleEffect';
|
|
5
|
+
import { getRenderEffectDefaults, normalizeRenderEffect } from './renderEffectDefaults';
|
|
6
|
+
import { createToneMapEffect } from './toneMapEffect';
|
|
7
|
+
import { createVignetteEffect } from './vignetteEffect';
|
|
8
|
+
|
|
9
|
+
describe('getRenderEffectDefaults', () => {
|
|
10
|
+
it('returns documented defaults for BloomEffect', () => {
|
|
11
|
+
const d = getRenderEffectDefaults('BloomEffect');
|
|
12
|
+
expect(d.brightness).toBe(1);
|
|
13
|
+
expect(d.threshold).toBe(0.8);
|
|
14
|
+
expect(d.thresholdKnee).toBe(0.5);
|
|
15
|
+
expect(d.radius).toBe(8);
|
|
16
|
+
expect(d.passes).toBe(1);
|
|
17
|
+
});
|
|
18
|
+
it('returns documented defaults for ToneMapEffect', () => {
|
|
19
|
+
const d = getRenderEffectDefaults('ToneMapEffect');
|
|
20
|
+
expect(d.operator).toBe('aces');
|
|
21
|
+
expect(d.exposure).toBe(0);
|
|
22
|
+
});
|
|
23
|
+
it('returns a fresh copy each call (mutations do not persist)', () => {
|
|
24
|
+
const d1 = getRenderEffectDefaults('BloomEffect');
|
|
25
|
+
const d2 = getRenderEffectDefaults('BloomEffect');
|
|
26
|
+
d1.threshold = 99;
|
|
27
|
+
expect(d2.threshold).toBe(0.8);
|
|
28
|
+
});
|
|
29
|
+
it('returns empty object for unknown kind', () => {
|
|
30
|
+
expect(getRenderEffectDefaults('acme.UnknownEffect')).toEqual({});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('normalizeRenderEffect', () => {
|
|
35
|
+
it('fills in missing fields from defaults', () => {
|
|
36
|
+
const effect = createBloomEffect({ threshold: 0.9 });
|
|
37
|
+
const out = { kind: 'BloomEffect' } as BloomEffect;
|
|
38
|
+
const ok = normalizeRenderEffect(effect, out);
|
|
39
|
+
expect(ok).toBe(true);
|
|
40
|
+
expect(out.threshold).toBe(0.9); // preserved
|
|
41
|
+
expect((out as unknown as Record<string, unknown>).brightness).toBe(1); // filled from defaults
|
|
42
|
+
expect(out.radius).toBe(8); // filled from defaults
|
|
43
|
+
});
|
|
44
|
+
it('preserves explicitly set zero and false', () => {
|
|
45
|
+
const effect = createGrayscaleEffect({ intensity: 0 });
|
|
46
|
+
const out = { kind: 'GrayscaleEffect' } as GrayscaleEffect;
|
|
47
|
+
normalizeRenderEffect(effect, out);
|
|
48
|
+
expect(out.intensity).toBe(0); // 0 is explicit, should not be replaced by default 1
|
|
49
|
+
});
|
|
50
|
+
it('carries over fields not in the defaults table', () => {
|
|
51
|
+
const effect = createVignetteEffect({ intensity: 0.3 });
|
|
52
|
+
const out = { kind: 'VignetteEffect' } as VignetteEffect;
|
|
53
|
+
normalizeRenderEffect(effect, out);
|
|
54
|
+
expect(out.intensity).toBe(0.3);
|
|
55
|
+
expect(out.radius).toBe(1);
|
|
56
|
+
expect(out.softness).toBe(0.5);
|
|
57
|
+
});
|
|
58
|
+
it('returns false for unknown kind', () => {
|
|
59
|
+
const effect = { kind: 'acme.UnknownEffect' } as RenderEffect;
|
|
60
|
+
const out = { kind: 'acme.UnknownEffect' } as RenderEffect;
|
|
61
|
+
expect(normalizeRenderEffect(effect, out)).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('is alias-safe when out === effect', () => {
|
|
64
|
+
const effect = createToneMapEffect({ exposure: 1 }) as ToneMapEffect;
|
|
65
|
+
const ok = normalizeRenderEffect(effect, effect);
|
|
66
|
+
expect(ok).toBe(true);
|
|
67
|
+
expect(effect.exposure).toBe(1);
|
|
68
|
+
expect(effect.operator).toBe('aces');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createBloomEffect } from './bloomEffect';
|
|
2
|
+
import { createBokehDepthOfFieldEffect } from './bokehDepthOfFieldEffect';
|
|
3
|
+
import { createColorGradeEffect } from './colorGradeEffect';
|
|
4
|
+
import { createGrayscaleEffect } from './grayscaleEffect';
|
|
5
|
+
import { getRenderEffectInputs, getRenderEffectKinds, RENDER_EFFECT_KINDS } from './renderEffectInputs';
|
|
6
|
+
import { createSsaoEffect } from './ssaoEffect';
|
|
7
|
+
import { createTaaEffect } from './taaEffect';
|
|
8
|
+
import { createToneMapEffect } from './toneMapEffect';
|
|
9
|
+
|
|
10
|
+
describe('getRenderEffectInputs', () => {
|
|
11
|
+
it('returns Hdr for BloomEffect', () => {
|
|
12
|
+
expect(getRenderEffectInputs(createBloomEffect())).toEqual(['Hdr']);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns Hdr for ToneMapEffect', () => {
|
|
16
|
+
expect(getRenderEffectInputs(createToneMapEffect())).toEqual(['Hdr']);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns Depth for SsaoEffect', () => {
|
|
20
|
+
expect(getRenderEffectInputs(createSsaoEffect())).toEqual(['Depth']);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns Depth for BokehDepthOfFieldEffect', () => {
|
|
24
|
+
expect(getRenderEffectInputs(createBokehDepthOfFieldEffect())).toEqual(['Depth']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns Temporal for TaaEffect', () => {
|
|
28
|
+
expect(getRenderEffectInputs(createTaaEffect())).toEqual(['Temporal']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns empty array for effects that need only the color buffer', () => {
|
|
32
|
+
expect(getRenderEffectInputs(createGrayscaleEffect())).toEqual([]);
|
|
33
|
+
expect(getRenderEffectInputs(createColorGradeEffect())).toEqual([]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns empty array for unknown kind', () => {
|
|
37
|
+
expect(getRenderEffectInputs({ kind: 'acme.UnknownEffect' } as never)).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('getRenderEffectKinds', () => {
|
|
42
|
+
it('returns an array containing known effect kinds', () => {
|
|
43
|
+
const kinds = getRenderEffectKinds();
|
|
44
|
+
expect(kinds).toContain('BloomEffect');
|
|
45
|
+
expect(kinds).toContain('ToneMapEffect');
|
|
46
|
+
expect(kinds).toContain('SsaoEffect');
|
|
47
|
+
expect(kinds).toContain('GrayscaleEffect');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('returns the same reference as RENDER_EFFECT_KINDS', () => {
|
|
51
|
+
expect(getRenderEffectKinds()).toBe(RENDER_EFFECT_KINDS);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('kinds are in alphabetical order', () => {
|
|
55
|
+
const kinds = getRenderEffectKinds();
|
|
56
|
+
for (let i = 1; i < kinds.length; i++) {
|
|
57
|
+
expect(kinds[i - 1].localeCompare(kinds[i])).toBeLessThan(0);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { GrayscaleEffect } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { createBloomEffect } from './bloomEffect';
|
|
4
|
+
import { createColorGradeEffect } from './colorGradeEffect';
|
|
5
|
+
import { createGrayscaleEffect } from './grayscaleEffect';
|
|
6
|
+
import { canLerpRenderEffects, lerpRenderEffect } from './renderEffectInterpolation';
|
|
7
|
+
|
|
8
|
+
describe('canLerpRenderEffects', () => {
|
|
9
|
+
it('returns true for same kind', () => {
|
|
10
|
+
expect(canLerpRenderEffects(createBloomEffect(), createBloomEffect())).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('returns false for different kinds', () => {
|
|
13
|
+
expect(canLerpRenderEffects(createBloomEffect(), createColorGradeEffect())).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('lerpRenderEffect', () => {
|
|
18
|
+
it('returns false and leaves out unchanged for mismatched kinds', () => {
|
|
19
|
+
const a = createBloomEffect({ threshold: 0.5 });
|
|
20
|
+
const b = createColorGradeEffect({ exposure: 1 });
|
|
21
|
+
const out = createBloomEffect({ threshold: 0.99 });
|
|
22
|
+
const result = lerpRenderEffect(a, b, 0.5, out);
|
|
23
|
+
expect(result).toBe(false);
|
|
24
|
+
expect(out.threshold).toBe(0.99);
|
|
25
|
+
});
|
|
26
|
+
it('at t=0 writes a values', () => {
|
|
27
|
+
const a = createBloomEffect({ threshold: 0.2, radius: 4 });
|
|
28
|
+
const b = createBloomEffect({ threshold: 0.8, radius: 16 });
|
|
29
|
+
const out = createBloomEffect();
|
|
30
|
+
lerpRenderEffect(a, b, 0, out);
|
|
31
|
+
expect(out.threshold).toBeCloseTo(0.2, 5);
|
|
32
|
+
expect(out.radius).toBeCloseTo(4, 5);
|
|
33
|
+
});
|
|
34
|
+
it('at t=1 writes b values', () => {
|
|
35
|
+
const a = createBloomEffect({ threshold: 0.2, radius: 4 });
|
|
36
|
+
const b = createBloomEffect({ threshold: 0.8, radius: 16 });
|
|
37
|
+
const out = createBloomEffect();
|
|
38
|
+
lerpRenderEffect(a, b, 1, out);
|
|
39
|
+
expect(out.threshold).toBeCloseTo(0.8, 5);
|
|
40
|
+
expect(out.radius).toBeCloseTo(16, 5);
|
|
41
|
+
});
|
|
42
|
+
it('at t=0.5 interpolates numeric fields', () => {
|
|
43
|
+
const a = createBloomEffect({ threshold: 0, radius: 0 });
|
|
44
|
+
const b = createBloomEffect({ threshold: 1, radius: 10 });
|
|
45
|
+
const out = createBloomEffect();
|
|
46
|
+
lerpRenderEffect(a, b, 0.5, out);
|
|
47
|
+
expect(out.threshold).toBeCloseTo(0.5, 5);
|
|
48
|
+
expect(out.radius).toBeCloseTo(5, 5);
|
|
49
|
+
});
|
|
50
|
+
it('returns true on success', () => {
|
|
51
|
+
const a = createBloomEffect();
|
|
52
|
+
const b = createBloomEffect();
|
|
53
|
+
const out = createBloomEffect();
|
|
54
|
+
expect(lerpRenderEffect(a, b, 0.5, out)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it('is alias-safe when out === a', () => {
|
|
57
|
+
const a = createBloomEffect({ threshold: 0, radius: 0 });
|
|
58
|
+
const b = createBloomEffect({ threshold: 1, radius: 10 });
|
|
59
|
+
lerpRenderEffect(a, b, 0.5, a);
|
|
60
|
+
expect(a.threshold).toBeCloseTo(0.5, 5);
|
|
61
|
+
});
|
|
62
|
+
it('boolean fields snap at t=0.5 boundary', () => {
|
|
63
|
+
const a = createGrayscaleEffect({ enabled: false } as Partial<GrayscaleEffect>);
|
|
64
|
+
const b = createGrayscaleEffect({ enabled: true } as Partial<GrayscaleEffect>);
|
|
65
|
+
const out = createGrayscaleEffect();
|
|
66
|
+
lerpRenderEffect(a, b, 0.4, out);
|
|
67
|
+
expect((out as unknown as Record<string, unknown>).enabled).toBe(false);
|
|
68
|
+
lerpRenderEffect(a, b, 0.5, out);
|
|
69
|
+
expect((out as unknown as Record<string, unknown>).enabled).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createBloomEffect } from './bloomEffect';
|
|
2
|
+
import { createColorGradeEffect } from './colorGradeEffect';
|
|
3
|
+
import { validateRenderEffectList } from './renderEffectValidation';
|
|
4
|
+
import { createSsaoEffect } from './ssaoEffect';
|
|
5
|
+
import { createToneMapEffect } from './toneMapEffect';
|
|
6
|
+
|
|
7
|
+
describe('validateRenderEffectList', () => {
|
|
8
|
+
it('returns null when all required inputs are available', () => {
|
|
9
|
+
const effects = [createBloomEffect(), createSsaoEffect()];
|
|
10
|
+
expect(validateRenderEffectList(effects, ['Hdr', 'Depth'])).toBeNull();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns null for effects that need only the color buffer', () => {
|
|
14
|
+
const effects = [createColorGradeEffect()];
|
|
15
|
+
expect(validateRenderEffectList(effects, [])).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('returns the missing input when Hdr is required but unavailable', () => {
|
|
19
|
+
const effects = [createToneMapEffect()];
|
|
20
|
+
expect(validateRenderEffectList(effects, [])).toBe('Hdr');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns the missing input when Depth is required but unavailable', () => {
|
|
24
|
+
const effects = [createSsaoEffect()];
|
|
25
|
+
expect(validateRenderEffectList(effects, ['Hdr'])).toBe('Depth');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns null for an empty effect list', () => {
|
|
29
|
+
expect(validateRenderEffectList([], [])).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('returns the first missing input in order', () => {
|
|
33
|
+
const effects = [createBloomEffect(), createSsaoEffect()];
|
|
34
|
+
// Only Hdr available — Bloom ok, Ssao fails.
|
|
35
|
+
expect(validateRenderEffectList(effects, ['Hdr'])).toBe('Depth');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createScanlinesEffect } from './scanlinesEffect';
|
|
2
|
+
|
|
3
|
+
describe('createScanlinesEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createScanlinesEffect().kind).toBe('ScanlinesEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createScanlinesEffect({ count: 240, intensity: 0.4 })).toMatchObject({ count: 240, intensity: 0.4 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createScreenSpaceFogEffect } from './screenSpaceFogEffect';
|
|
2
|
+
|
|
3
|
+
describe('createScreenSpaceFogEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createScreenSpaceFogEffect().kind).toBe('ScreenSpaceFogEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createScreenSpaceFogEffect({ color: 0xaabbccff, density: 0.4 })).toMatchObject({
|
|
10
|
+
color: 0xaabbccff,
|
|
11
|
+
density: 0.4,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSepiaEffect } from './sepiaEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSepiaEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSepiaEffect().kind).toBe('SepiaEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSepiaEffect({ intensity: 0.6 })).toMatchObject({ intensity: 0.6 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSharpenEffect } from './sharpenEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSharpenEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSharpenEffect().kind).toBe('SharpenEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSharpenEffect({ amount: 0.6 })).toMatchObject({ amount: 0.6 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSketchEffect } from './sketchEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSketchEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSketchEffect().kind).toBe('SketchEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSketchEffect({ strength: 0.8 })).toMatchObject({ strength: 0.8 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSmaaEffect } from './smaaEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSmaaEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSmaaEffect().kind).toBe('SmaaEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSmaaEffect({ threshold: 0.1 })).toMatchObject({ threshold: 0.1 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSsaoEffect } from './ssaoEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSsaoEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSsaoEffect().kind).toBe('SsaoEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSsaoEffect({ radius: 4, intensity: 1.5 })).toMatchObject({ radius: 4, intensity: 1.5 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createSsrEffect } from './ssrEffect';
|
|
2
|
+
|
|
3
|
+
describe('createSsrEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createSsrEffect().kind).toBe('SsrEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createSsrEffect({ maxDistance: 10, steps: 32 })).toMatchObject({ maxDistance: 10, steps: 32 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeCrtMaskParams,
|
|
3
|
+
computeHalftoneCellParams,
|
|
4
|
+
computeScanlineParams,
|
|
5
|
+
createBayerMatrix,
|
|
6
|
+
} from './stylizeMath';
|
|
7
|
+
|
|
8
|
+
describe('computeCrtMaskParams', () => {
|
|
9
|
+
it('writes two values', () => {
|
|
10
|
+
const out: [number, number] = [0, 0];
|
|
11
|
+
computeCrtMaskParams(720, 0.5, out);
|
|
12
|
+
expect(typeof out[0]).toBe('number');
|
|
13
|
+
expect(typeof out[1]).toBe('number');
|
|
14
|
+
});
|
|
15
|
+
it('higher resolution produces larger mask scale', () => {
|
|
16
|
+
const out1: [number, number] = [0, 0];
|
|
17
|
+
const out2: [number, number] = [0, 0];
|
|
18
|
+
computeCrtMaskParams(360, 0, out1);
|
|
19
|
+
computeCrtMaskParams(720, 0, out2);
|
|
20
|
+
expect(out2[0]).toBeGreaterThan(out1[0]);
|
|
21
|
+
});
|
|
22
|
+
it('curvature is clamped to [0, 0.1]', () => {
|
|
23
|
+
const out: [number, number] = [0, 0];
|
|
24
|
+
computeCrtMaskParams(360, 2, out);
|
|
25
|
+
expect(out[1]).toBeCloseTo(0.1, 5);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('computeHalftoneCellParams', () => {
|
|
30
|
+
it('writes cellSize, cosAngle, sinAngle', () => {
|
|
31
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
32
|
+
computeHalftoneCellParams(1 / 8, Math.PI / 4, out);
|
|
33
|
+
expect(out[0]).toBeCloseTo(8, 4);
|
|
34
|
+
expect(out[1]).toBeCloseTo(Math.SQRT1_2, 4);
|
|
35
|
+
expect(out[2]).toBeCloseTo(Math.SQRT1_2, 4);
|
|
36
|
+
});
|
|
37
|
+
it('0 angle produces cos=1, sin=0', () => {
|
|
38
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
39
|
+
computeHalftoneCellParams(1 / 4, 0, out);
|
|
40
|
+
expect(out[1]).toBeCloseTo(1, 5);
|
|
41
|
+
expect(out[2]).toBeCloseTo(0, 5);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('computeScanlineParams', () => {
|
|
46
|
+
it('writes scale and intensity', () => {
|
|
47
|
+
const out: [number, number] = [0, 0];
|
|
48
|
+
computeScanlineParams(480, 0.5, out);
|
|
49
|
+
expect(out[0]).toBeCloseTo(1, 4);
|
|
50
|
+
expect(out[1]).toBeCloseTo(0.5, 4);
|
|
51
|
+
});
|
|
52
|
+
it('clamps intensity to [0, 1]', () => {
|
|
53
|
+
const out: [number, number] = [0, 0];
|
|
54
|
+
computeScanlineParams(480, 2, out);
|
|
55
|
+
expect(out[1]).toBeCloseTo(1, 5);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('createBayerMatrix', () => {
|
|
60
|
+
it('returns size 2 for order 1', () => {
|
|
61
|
+
const out = new Float32Array(4);
|
|
62
|
+
const size = createBayerMatrix(1, out);
|
|
63
|
+
expect(size).toBe(2);
|
|
64
|
+
});
|
|
65
|
+
it('returns size 4 for order 2', () => {
|
|
66
|
+
const out = new Float32Array(16);
|
|
67
|
+
const size = createBayerMatrix(2, out);
|
|
68
|
+
expect(size).toBe(4);
|
|
69
|
+
});
|
|
70
|
+
it('all values are in [0, 1)', () => {
|
|
71
|
+
const out = new Float32Array(16);
|
|
72
|
+
const size = createBayerMatrix(2, out);
|
|
73
|
+
for (let i = 0; i < size * size; i++) {
|
|
74
|
+
expect(out[i]).toBeGreaterThanOrEqual(0);
|
|
75
|
+
expect(out[i]).toBeLessThan(1);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it('all values in 2x2 matrix are unique', () => {
|
|
79
|
+
const out = new Float32Array(4);
|
|
80
|
+
createBayerMatrix(1, out);
|
|
81
|
+
const unique = new Set(Array.from(out));
|
|
82
|
+
expect(unique.size).toBe(4);
|
|
83
|
+
});
|
|
84
|
+
it('all values in 4x4 matrix are unique', () => {
|
|
85
|
+
const out = new Float32Array(16);
|
|
86
|
+
createBayerMatrix(2, out);
|
|
87
|
+
const unique = new Set(Array.from(out));
|
|
88
|
+
expect(unique.size).toBe(16);
|
|
89
|
+
});
|
|
90
|
+
it('is deterministic', () => {
|
|
91
|
+
const out1 = new Float32Array(16);
|
|
92
|
+
const out2 = new Float32Array(16);
|
|
93
|
+
createBayerMatrix(2, out1);
|
|
94
|
+
createBayerMatrix(2, out2);
|
|
95
|
+
for (let i = 0; i < 16; i++) {
|
|
96
|
+
expect(out1[i]).toBeCloseTo(out2[i], 5);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createTaaEffect } from './taaEffect';
|
|
2
|
+
|
|
3
|
+
describe('createTaaEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createTaaEffect().kind).toBe('TaaEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createTaaEffect({ feedback: 0.9 })).toMatchObject({ feedback: 0.9 });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createTiltShiftEffect } from './tiltShiftEffect';
|
|
2
|
+
|
|
3
|
+
describe('createTiltShiftEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createTiltShiftEffect().kind).toBe('TiltShiftEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createTiltShiftEffect({ center: 0.5, width: 0.2, blur: 4 })).toMatchObject({
|
|
10
|
+
center: 0.5,
|
|
11
|
+
width: 0.2,
|
|
12
|
+
blur: 4,
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createToneMapEffect } from './toneMapEffect';
|
|
2
|
+
|
|
3
|
+
describe('createToneMapEffect', () => {
|
|
4
|
+
it('tags the intent type and operator', () => {
|
|
5
|
+
expect(createToneMapEffect({ operator: 'aces' })).toMatchObject({ kind: 'ToneMapEffect', operator: 'aces' });
|
|
6
|
+
});
|
|
7
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeAcesToneMap,
|
|
3
|
+
computeAgxToneMap,
|
|
4
|
+
computeExposureScale,
|
|
5
|
+
computeFilmicToneMap,
|
|
6
|
+
computeReinhardExtendedToneMap,
|
|
7
|
+
computeReinhardToneMap,
|
|
8
|
+
computeUncharted2ToneMap,
|
|
9
|
+
getAcesInputMatrix,
|
|
10
|
+
getAcesOutputMatrix,
|
|
11
|
+
} from './toneMapMath';
|
|
12
|
+
|
|
13
|
+
describe('computeAcesToneMap', () => {
|
|
14
|
+
it('maps 0 to approximately 0', () => {
|
|
15
|
+
expect(computeAcesToneMap(0)).toBeCloseTo(0, 2);
|
|
16
|
+
});
|
|
17
|
+
it('output is clamped to [0, 1]', () => {
|
|
18
|
+
expect(computeAcesToneMap(100)).toBeLessThanOrEqual(1);
|
|
19
|
+
expect(computeAcesToneMap(-1)).toBeGreaterThanOrEqual(0);
|
|
20
|
+
});
|
|
21
|
+
it('x=1 returns a known reference value', () => {
|
|
22
|
+
// At x=1: (2.51 + 0.03) / (2.43 + 0.59 + 0.14) ≈ 0.8016
|
|
23
|
+
const v = computeAcesToneMap(1);
|
|
24
|
+
expect(v).toBeGreaterThan(0.7);
|
|
25
|
+
expect(v).toBeLessThan(0.9);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('computeAgxToneMap', () => {
|
|
30
|
+
it('output is in [0, 1] range', () => {
|
|
31
|
+
expect(computeAgxToneMap(0.5)).toBeGreaterThanOrEqual(0);
|
|
32
|
+
expect(computeAgxToneMap(0.5)).toBeLessThanOrEqual(1);
|
|
33
|
+
expect(computeAgxToneMap(100)).toBeLessThanOrEqual(1.1); // allow slight overshoot on sigmoid endpoints
|
|
34
|
+
});
|
|
35
|
+
it('increases monotonically in the normal range', () => {
|
|
36
|
+
expect(computeAgxToneMap(1)).toBeGreaterThan(computeAgxToneMap(0.5));
|
|
37
|
+
});
|
|
38
|
+
it('matches default when options is undefined', () => {
|
|
39
|
+
expect(computeAgxToneMap(0.5)).toBeCloseTo(computeAgxToneMap(0.5, undefined), 10);
|
|
40
|
+
expect(computeAgxToneMap(0.5)).toBeCloseTo(computeAgxToneMap(0.5, {}), 10);
|
|
41
|
+
});
|
|
42
|
+
it('custom EV range shifts the output', () => {
|
|
43
|
+
const defaultVal = computeAgxToneMap(0.5);
|
|
44
|
+
const narrow = computeAgxToneMap(0.5, { minEv: -6, maxEv: 2 });
|
|
45
|
+
expect(narrow).not.toBeCloseTo(defaultVal, 2);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('computeExposureScale', () => {
|
|
50
|
+
it('EV=0 returns 1', () => {
|
|
51
|
+
expect(computeExposureScale(0)).toBeCloseTo(1, 5);
|
|
52
|
+
});
|
|
53
|
+
it('EV=1 doubles brightness', () => {
|
|
54
|
+
expect(computeExposureScale(1)).toBeCloseTo(2, 5);
|
|
55
|
+
});
|
|
56
|
+
it('EV=-1 halves brightness', () => {
|
|
57
|
+
expect(computeExposureScale(-1)).toBeCloseTo(0.5, 5);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('computeFilmicToneMap', () => {
|
|
62
|
+
it('output is non-negative for non-negative input', () => {
|
|
63
|
+
expect(computeFilmicToneMap(0)).toBeGreaterThanOrEqual(0);
|
|
64
|
+
expect(computeFilmicToneMap(1)).toBeGreaterThanOrEqual(0);
|
|
65
|
+
});
|
|
66
|
+
it('output increases with input in the linear range', () => {
|
|
67
|
+
expect(computeFilmicToneMap(0.5)).toBeGreaterThan(computeFilmicToneMap(0.1));
|
|
68
|
+
});
|
|
69
|
+
it('matches default when options is undefined', () => {
|
|
70
|
+
expect(computeFilmicToneMap(0.5)).toBeCloseTo(computeFilmicToneMap(0.5, undefined), 10);
|
|
71
|
+
expect(computeFilmicToneMap(0.5)).toBeCloseTo(computeFilmicToneMap(0.5, {}), 10);
|
|
72
|
+
});
|
|
73
|
+
it('higher contrast steepens the curve', () => {
|
|
74
|
+
const defaultVal = computeFilmicToneMap(0.5);
|
|
75
|
+
const highContrast = computeFilmicToneMap(0.5, { contrast: 2.0 });
|
|
76
|
+
expect(highContrast).not.toBeCloseTo(defaultVal, 2);
|
|
77
|
+
});
|
|
78
|
+
it('pedestal lifts the black level', () => {
|
|
79
|
+
const noPedestal = computeFilmicToneMap(0.01);
|
|
80
|
+
const withPedestal = computeFilmicToneMap(0.01, { pedestal: 0.05 });
|
|
81
|
+
expect(withPedestal).toBeGreaterThan(noPedestal);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('computeReinhardExtendedToneMap', () => {
|
|
86
|
+
it('maps 0 to 0', () => {
|
|
87
|
+
expect(computeReinhardExtendedToneMap(0, 10)).toBeCloseTo(0, 5);
|
|
88
|
+
});
|
|
89
|
+
it('at white point x==w output is 1', () => {
|
|
90
|
+
expect(computeReinhardExtendedToneMap(10, 10)).toBeCloseTo(1, 4);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('computeReinhardToneMap', () => {
|
|
95
|
+
it('maps 0 to 0', () => {
|
|
96
|
+
expect(computeReinhardToneMap(0)).toBeCloseTo(0, 5);
|
|
97
|
+
});
|
|
98
|
+
it('output is always < 1 for positive input', () => {
|
|
99
|
+
expect(computeReinhardToneMap(1)).toBeLessThan(1);
|
|
100
|
+
expect(computeReinhardToneMap(100)).toBeLessThan(1);
|
|
101
|
+
});
|
|
102
|
+
it('x=1 maps to 0.5', () => {
|
|
103
|
+
expect(computeReinhardToneMap(1)).toBeCloseTo(0.5, 5);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('computeUncharted2ToneMap', () => {
|
|
108
|
+
it('output at x=0 is near 0', () => {
|
|
109
|
+
expect(Math.abs(computeUncharted2ToneMap(0))).toBeLessThan(0.1);
|
|
110
|
+
});
|
|
111
|
+
it('output increases with input', () => {
|
|
112
|
+
expect(computeUncharted2ToneMap(2)).toBeGreaterThan(computeUncharted2ToneMap(1));
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('getAcesInputMatrix', () => {
|
|
117
|
+
it('writes 9 values', () => {
|
|
118
|
+
const out = new Float32Array(9);
|
|
119
|
+
getAcesInputMatrix(out);
|
|
120
|
+
let sum = 0;
|
|
121
|
+
for (let i = 0; i < 9; i++) {
|
|
122
|
+
sum += out[i];
|
|
123
|
+
}
|
|
124
|
+
expect(sum).toBeGreaterThan(0);
|
|
125
|
+
});
|
|
126
|
+
it('diagonal values are largest in each column', () => {
|
|
127
|
+
const out = new Float32Array(9);
|
|
128
|
+
getAcesInputMatrix(out);
|
|
129
|
+
// Column 0: out[0] > out[1], out[0] > out[2]
|
|
130
|
+
expect(out[0]).toBeGreaterThan(out[1]);
|
|
131
|
+
expect(out[0]).toBeGreaterThan(out[2]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('getAcesOutputMatrix', () => {
|
|
136
|
+
it('writes 9 values', () => {
|
|
137
|
+
const out = new Float32Array(9);
|
|
138
|
+
getAcesOutputMatrix(out);
|
|
139
|
+
let hasNonZero = false;
|
|
140
|
+
for (let i = 0; i < 9; i++) {
|
|
141
|
+
if (out[i] !== 0) hasNonZero = true;
|
|
142
|
+
}
|
|
143
|
+
expect(hasNonZero).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createVignetteEffect } from './vignetteEffect';
|
|
2
|
+
|
|
3
|
+
describe('createVignetteEffect', () => {
|
|
4
|
+
it('tags the intent type', () => {
|
|
5
|
+
expect(createVignetteEffect().kind).toBe('VignetteEffect');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('carries options', () => {
|
|
9
|
+
expect(createVignetteEffect({ intensity: 1, radius: 0.7, softness: 0.4, color: 0x000000ff })).toMatchObject({
|
|
10
|
+
intensity: 1,
|
|
11
|
+
radius: 0.7,
|
|
12
|
+
softness: 0.4,
|
|
13
|
+
color: 0x000000ff,
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|