Skip to content
Open
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
239 changes: 239 additions & 0 deletions test/unit/color/hsb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/**
* Tests for the HSB (Hue, Saturation, Brightness) color space.
*
* The HSB color space is defined in src/color/color_spaces/hsb.js and provides
* conversions between RGB (base) and HSB color representations.
*
* HSB ranges:
* - Hue: 0-360 (degrees on color wheel)
* - Saturation: 0-100 (percentage)
* - Brightness: 0-100 (percentage)
*
* RGB ranges (in colorjs.io):
* - R, G, B: 0-1 (normalized)
*/

import HSBSpace from '../../../src/color/color_spaces/hsb.js';

suite('color/HSB Color Space', function() {
const accuracy = 0.01;

suite('HSB to RGB conversion (toBase)', function() {
suite('Primary Colors', function() {
test('pure red: HSB(0, 100, 100) → RGB(1, 0, 0)', function() {
const result = HSBSpace.toBase([0, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('pure green: HSB(120, 100, 100) → RGB(0, 1, 0)', function() {
const result = HSBSpace.toBase([120, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('pure blue: HSB(240, 100, 100) → RGB(0, 0, 1)', function() {
const result = HSBSpace.toBase([240, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 1, accuracy);
});
});

suite('Secondary Colors', function() {
test('yellow: HSB(60, 100, 100) → RGB(1, 1, 0)', function() {
const result = HSBSpace.toBase([60, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('cyan: HSB(180, 100, 100) → RGB(0, 1, 1)', function() {
const result = HSBSpace.toBase([180, 100, 100]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 1, accuracy);
});

test('magenta: HSB(300, 100, 100) → RGB(1, 0, 1)', function() {
const result = HSBSpace.toBase([300, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 1, accuracy);
});
});

suite('Grayscale (Saturation = 0)', function() {
test('white: HSB(0, 0, 100) → RGB(1, 1, 1)', function() {
const result = HSBSpace.toBase([0, 0, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 1, accuracy);
assert.approximately(result[2], 1, accuracy);
});

test('black: HSB(0, 0, 0) → RGB(0, 0, 0)', function() {
const result = HSBSpace.toBase([0, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('mid gray: HSB(0, 0, 50) → RGB(0.5, 0.5, 0.5)', function() {
const result = HSBSpace.toBase([0, 0, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0.5, accuracy);
assert.approximately(result[2], 0.5, accuracy);
});

test('grayscale ignores hue: HSB(180, 0, 50) → RGB(0.5, 0.5, 0.5)', function() {
const result = HSBSpace.toBase([180, 0, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0.5, accuracy);
assert.approximately(result[2], 0.5, accuracy);
});
});

suite('Partial Saturation', function() {
test('50% saturation red: HSB(0, 50, 100) → RGB(1, 0.5, 0.5)', function() {
const result = HSBSpace.toBase([0, 50, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0.5, accuracy);
assert.approximately(result[2], 0.5, accuracy);
});

test('50% brightness red: HSB(0, 100, 50) → RGB(0.5, 0, 0)', function() {
const result = HSBSpace.toBase([0, 100, 50]);
assert.approximately(result[0], 0.5, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
});

suite('Edge Cases', function() {
test('hue at 360 should be same as 0 (red)', function() {
const result = HSBSpace.toBase([360, 100, 100]);
assert.approximately(result[0], 1, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('brightness 0 produces black regardless of hue/saturation', function() {
const result = HSBSpace.toBase([120, 100, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});
});
});

suite('RGB to HSB conversion (fromBase)', function() {
suite('Primary Colors', function() {
test('pure red: RGB(1, 0, 0) → HSB(0, 100, 100)', function() {
const result = HSBSpace.fromBase([1, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});

test('pure green: RGB(0, 1, 0) → HSB(120, 100, 100)', function() {
const result = HSBSpace.fromBase([0, 1, 0]);
assert.approximately(result[0], 120, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});

test('pure blue: RGB(0, 0, 1) → HSB(240, 100, 100)', function() {
const result = HSBSpace.fromBase([0, 0, 1]);
assert.approximately(result[0], 240, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
});

suite('Secondary Colors', function() {
test('yellow: RGB(1, 1, 0) → HSB(60, 100, 100)', function() {
const result = HSBSpace.fromBase([1, 1, 0]);
assert.approximately(result[0], 60, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});

test('cyan: RGB(0, 1, 1) → HSB(180, 100, 100)', function() {
const result = HSBSpace.fromBase([0, 1, 1]);
assert.approximately(result[0], 180, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});

test('magenta: RGB(1, 0, 1) → HSB(300, 100, 100)', function() {
const result = HSBSpace.fromBase([1, 0, 1]);
assert.approximately(result[0], 300, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 100, accuracy);
});
});

suite('Grayscale', function() {
test('white: RGB(1, 1, 1) → HSB(0, 0, 100)', function() {
const result = HSBSpace.fromBase([1, 1, 1]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 100, accuracy);
});

test('black: RGB(0, 0, 0) → HSB(0, 0, 0)', function() {
const result = HSBSpace.fromBase([0, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 0, accuracy);
});

test('mid gray: RGB(0.5, 0.5, 0.5) → HSB(0, 0, 50)', function() {
const result = HSBSpace.fromBase([0.5, 0.5, 0.5]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 0, accuracy);
assert.approximately(result[2], 50, accuracy);
});
});

suite('Partial Values', function() {
test('dark red: RGB(0.5, 0, 0) → HSB(0, 100, 50)', function() {
const result = HSBSpace.fromBase([0.5, 0, 0]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 100, accuracy);
assert.approximately(result[2], 50, accuracy);
});

test('light red (pink): RGB(1, 0.5, 0.5) → HSB(0, 50, 100)', function() {
const result = HSBSpace.fromBase([1, 0.5, 0.5]);
assert.approximately(result[0], 0, accuracy);
assert.approximately(result[1], 50, accuracy);
assert.approximately(result[2], 100, accuracy);
});
});
});

suite('Round-trip conversion', function() {
test('HSB → RGB → HSB should preserve values', function() {
const originalHSB = [210, 75, 80];
const rgb = HSBSpace.toBase(originalHSB);
const resultHSB = HSBSpace.fromBase(rgb);

assert.approximately(resultHSB[0], originalHSB[0], 0.1);
assert.approximately(resultHSB[1], originalHSB[1], 0.1);
assert.approximately(resultHSB[2], originalHSB[2], 0.1);
});

test('RGB → HSB → RGB should preserve values', function() {
const originalRGB = [0.7, 0.3, 0.5];
const hsb = HSBSpace.fromBase(originalRGB);
const resultRGB = HSBSpace.toBase(hsb);

assert.approximately(resultRGB[0], originalRGB[0], 0.01);
assert.approximately(resultRGB[1], originalRGB[1], 0.01);
assert.approximately(resultRGB[2], originalRGB[2], 0.01);
});
});
});
Loading