import XCTest @testable import ForgeColor final class Lut3DTests: XCTestCase { // Identity lattice is a no-op within interp tolerance. func testIdentityNoOp() { let lut = Lut3D.identity(size: 17) for px in [SIMD3(0, 0, 0), SIMD3(1, 1, 1), SIMD3(0.5, 0.25, 0.75), SIMD3(0.18, 0.18, 0.18)] { let out = lut.sample(px) XCTAssertEqual(out.x, px.x, accuracy: 1e-5) XCTAssertEqual(out.y, px.y, accuracy: 1e-5) XCTAssertEqual(out.z, px.z, accuracy: 1e-5) } } // Lattice built from an analytic function: interp matches direct eval on off-lattice points. func testTrilinearMatchesDirectEval() { // f = gain 0.5 (linear function -> trilinear should be near-exact) let f: (SIMD3) -> SIMD3 = { $0 * 0.5 } let lut = Lut3D.build(size: 9, function: f) for px in [SIMD3(0.13, 0.57, 0.91), SIMD3(0.9, 0.05, 0.5)] { let out = lut.sample(px) let direct = f(px) XCTAssertEqual(out.x, direct.x, accuracy: 1e-4) XCTAssertEqual(out.y, direct.y, accuracy: 1e-4) XCTAssertEqual(out.z, direct.z, accuracy: 1e-4) } } // Nonlinear function: 33^3 approx within 1/1024 where trilinear error bound // (max|f''|·h²/8, h=1/32) actually permits it. pow(x, 0.6) has f''→∞ at 0; // at x=0.15, |f''|≈3.4 → bound ≈4e-4. Real flattening runs in log space where // curvature is gentle everywhere. Samples in [0.15, 1]. func testNonlinearApproxWithinTolerance() { let f: (SIMD3) -> SIMD3 = { SIMD3(pow($0.x, 2.2), pow($0.y, 1.8), pow($0.z, 0.6)) } let lut = Lut3D.build(size: 33, function: f) var rng = SystemRandomNumberGenerator() for _ in 0..<200 { let px = SIMD3( Float.random(in: 0.15...1, using: &rng), Float.random(in: 0.15...1, using: &rng), Float.random(in: 0.15...1, using: &rng)) let out = lut.sample(px) let direct = f(px) XCTAssertEqual(out.x, direct.x, accuracy: 1.0 / 1024) XCTAssertEqual(out.y, direct.y, accuracy: 1.0 / 1024) XCTAssertEqual(out.z, direct.z, accuracy: 1.0 / 1024) } } // Out-of-range input clamps to lattice bounds. func testInputClamped() { let lut = Lut3D.identity(size: 5) let out = lut.sample(SIMD3(-0.5, 1.5, 0.5)) XCTAssertEqual(out.x, 0, accuracy: 1e-5) XCTAssertEqual(out.y, 1, accuracy: 1e-5) XCTAssertEqual(out.z, 0.5, accuracy: 1e-5) } // MARK: CUBE I/O func testCubeRoundTrip() throws { let lut = Lut3D.build(size: 5) { $0 * 0.8 + SIMD3(0.05, 0, 0.1) } let text = CubeFile.write(lut, title: "Forge Test") let parsed = try CubeFile.parse(text) XCTAssertEqual(parsed.size, 5) for i in 0..