1- import { clampChroma , interpolate , modeOkhsl , modeRgb , samples , useMode } from "culori/fn" ;
1+ import { clampChroma , interpolate , modeOkhsl , modeRgb , samples , useMode } from "culori/fn" ;
22import { Color } from "./color.js" ;
33import { BasePalette } from "./palette-base.js" ;
44import { Swatch } from "./swatch.js" ;
@@ -7,8 +7,43 @@ import { _black, _white } from "./utilities/color-constants.js";
77const okhsl = useMode ( modeOkhsl ) ;
88const rgb = useMode ( modeRgb ) ;
99
10- const stepCount = 66 ;
11- const threeSteps = ( 1 / stepCount ) * 3 ;
10+ /**
11+ * Options to tailor the generation of PaletteOkhsl.
12+ *
13+ * @public
14+ */
15+ export interface PaletteOkhslOptions {
16+ /**
17+ * The number of steps in the generated palette.
18+ *
19+ * @defaultValue 66
20+ */
21+ stepCount : number ;
22+
23+ /**
24+ * The saturation of the color at the light end of the palette.
25+ *
26+ * @remarks Decimal value between 0 and 1; 0 means no change.
27+ *
28+ * @defaultValue 0
29+ */
30+ lightEndSaturation : number ;
31+
32+ /**
33+ * The saturation of the color at the dark end of the palette.
34+ *
35+ * @remarks Decimal value between 0 and 1; 0 means no change.
36+ *
37+ * @defaultValue 0
38+ */
39+ darkEndSaturation : number ;
40+ }
41+
42+ const defaultPaletteOkhslOptions : PaletteOkhslOptions = {
43+ stepCount : 66 ,
44+ lightEndSaturation : 0 ,
45+ darkEndSaturation : 0 ,
46+ } ;
1247
1348/**
1449 * An implementation of a {@link Palette} that uses the okhsl color model.
@@ -17,39 +52,46 @@ const threeSteps = (1 / stepCount) * 3;
1752 * @public
1853 */
1954export class PaletteOkhsl extends BasePalette < Swatch > {
20- public static from ( source : Color | string ) : PaletteOkhsl {
55+ public static from ( source : Color | string , options ?: Partial < PaletteOkhslOptions > ) : PaletteOkhsl {
2156 const color = source instanceof Color ? source : Color . parse ( source ) ;
2257 if ( ! color ) {
2358 throw new Error ( `Unable to parse source: ${ source } ` ) ;
2459 }
2560
61+ const opts = ( options === void 0 || options === null ) ? defaultPaletteOkhslOptions : { ...defaultPaletteOkhslOptions , ...options } ;
62+
63+ const oneStep = ( 1 / opts . stepCount ) ;
64+ const threeSteps = oneStep * 3 ;
65+
2666 const sourceHsl = okhsl ( color . color ) ;
2767
28- const hi = Object . assign ( { } , sourceHsl , { l : 0.999 } ) ;
29- const lo = Object . assign ( { } , sourceHsl , { l : 0.02 } ) ;
68+ const hiS = sourceHsl . s > 0 && opts . lightEndSaturation > 0 ? opts . lightEndSaturation : sourceHsl . s ;
69+ const loS = sourceHsl . s > 0 && opts . darkEndSaturation > 0 ? opts . darkEndSaturation : sourceHsl . s ;
70+ const hi = Object . assign ( { } , sourceHsl , { s : hiS , l : 1 - oneStep } ) ;
71+ const lo = Object . assign ( { } , sourceHsl , { s : loS , l : Math . max ( oneStep , 0.04 ) } ) ; // Minimum value to perceive difference
3072
3173 // Adjust the hi or lo end if the source color is too close to produce a good ramp.
3274 sourceHsl . l = Math . min ( 1 - threeSteps , sourceHsl . l ) ;
3375 sourceHsl . l = Math . max ( threeSteps , sourceHsl . l ) ;
3476
77+ const rampCount = opts . stepCount - 2 ; // Ends fixed to white and black
3578 const y = 1 - sourceHsl . l ; // Position for the source color in the ramp
36- const stepCountLeft = Math . round ( y * stepCount ) ;
37- const stepCountRight = stepCount - stepCountLeft + 1 ;
79+ const rampCountLeft = Math . round ( y * rampCount ) ;
80+ const rampCountRight = rampCount - rampCountLeft + 1 ;
3881 const colorsLeft : any = [ hi , sourceHsl ] ;
3982 const colorsRight : any = [ sourceHsl , lo ] ;
4083 const interpolateLeft = interpolate ( colorsLeft , "okhsl" ) ;
4184 const interpolateRight = interpolate ( colorsRight , "okhsl" ) ;
42- const samplesLeft = samples ( stepCountLeft ) . map ( interpolateLeft ) ;
43- const samplesRight = samples ( stepCountRight ) . map ( interpolateRight ) ;
85+ const samplesLeft = samples ( rampCountLeft ) . map ( interpolateLeft ) ;
86+ const samplesRight = samples ( rampCountRight ) . map ( interpolateRight ) ;
4487
4588 const ramp = [ ...samplesLeft , ...samplesRight . slice ( 1 ) ] ;
46- const swatches = ramp . map ( ( value ) =>
89+ const rampSwatches = ramp . map ( ( value ) =>
4790 Swatch . from ( rgb ( clampChroma ( value , "okhsl" ) ) )
4891 ) ;
4992
5093 // It's important that the ends are full white and black.
51- swatches [ 0 ] = _white ;
52- swatches [ swatches . length - 1 ] = _black ;
94+ const swatches = [ _white , ...rampSwatches , _black ] ;
5395
5496 return new PaletteOkhsl ( color , swatches ) ;
5597 }
0 commit comments