@@ -41,18 +41,22 @@ const map = (n, start1, stop1, start2, stop2, clamp) => {
4141 return result ;
4242}
4343
44- const serializationMap = { } ;
44+ const toHexComponent = ( v ) => {
45+ const vInt = ~ ~ ( v * 255 ) ;
46+ const hex = vInt . toString ( 16 )
47+ if ( hex . length < 2 ) {
48+ return '0' + hex
49+ } else {
50+ return hex
51+ }
52+ }
4553
54+ const serializationMap = new Map ( ) ;
4655
4756
4857
49- class Color {
50- // Reference to underlying color object depending on implementation
51- // Not meant to be used publicly unless the implementation is known for sure
52- _color ;
53- // Color mode of the Color object, uses p5 color modes
54- mode ;
5558
59+ class Color {
5660 static colorMap = { } ;
5761 static #colorjsMaxes = { } ;
5862 static #grayscaleMap = { } ;
@@ -77,40 +81,47 @@ class Color {
7781
7882 constructor ( vals , colorMode , colorMaxes , { clamp = false } = { } ) {
7983 // This changes with the color object
80- this . mode = colorMode || RGB ;
84+ this . _cachedMode = colorMode || RGB ;
8185
8286 if ( vals instanceof Color ) {
8387 // Received Color object to be used for color mode conversion
8488 const mode = colorMode ?
8589 Color . colorMap [ colorMode ] :
8690 Color . colorMap [ vals . mode ] ;
87- this . _color = to ( vals . _color , mode ) ;
88- this . mode = mode ;
91+ this . _initialize = ( ) => {
92+ this . _cachedColor = to ( vals . _color , mode ) ;
93+ this . _cachedMode = mode ;
94+ } ;
8995
9096 } else if ( typeof vals === 'object' && ! Array . isArray ( vals ) && vals !== null ) {
9197 // Received color.js object to be used internally
9298 const mode = colorMode ?
9399 Color . colorMap [ colorMode ] :
94100 vals . spaceId ;
95- this . _color = to ( vals , mode ) ;
96- this . mode = colorMode || Object . entries ( Color . colorMap )
97- . find ( ( [ key , val ] ) => {
98- return val === this . _color . spaceId ;
99- } ) ;
101+ this . _initialize = ( ) => {
102+ this . _cachedColor = to ( vals , mode ) ;
103+ this . _cachedMode = colorMode || Object . entries ( Color . colorMap )
104+ . find ( ( [ key , val ] ) => {
105+ return val === this . _cachedColor . spaceId ;
106+ } ) ;
107+ } ;
100108
101109 } else if ( typeof vals [ 0 ] === 'string' ) {
102110 // Received string
103- try {
104- this . _color = parse ( vals [ 0 ] ) ;
105- const [ mode ] = Object . entries ( Color . colorMap ) . find ( ( [ key , val ] ) => {
106- return val === this . _color . spaceId ;
107- } ) ;
108- this . mode = mode ;
109- this . _color = to ( this . _color , this . _color . spaceId ) ;
110- } catch ( err ) {
111- // TODO: Invalid color string
112- throw new Error ( 'Invalid color string' ) ;
113- }
111+ this . _defaultStringValue = vals [ 0 ] ;
112+ this . _initialize = ( ) => {
113+ try {
114+ this . _cachedColor = parse ( vals [ 0 ] ) ;
115+ const [ mode ] = Object . entries ( Color . colorMap ) . find ( ( [ key , val ] ) => {
116+ return val === this . _cachedColor . spaceId ;
117+ } ) ;
118+ this . _cachedMode = mode ;
119+ this . _cachedColor = to ( this . _cachedColor , this . _cachedColor . spaceId ) ;
120+ } catch ( err ) {
121+ // TODO: Invalid color string
122+ throw new Error ( 'Invalid color string' ) ;
123+ }
124+ } ;
114125
115126 } else {
116127 // Received individual channel values
@@ -119,27 +130,27 @@ class Color {
119130 if ( colorMaxes ) {
120131 // NOTE: need to consider different number of arguments (eg. CMYK)
121132 if ( vals . length === 4 ) {
122- mappedVals = Color . mapColorRange ( vals , this . mode , colorMaxes , clamp ) ;
133+ mappedVals = Color . mapColorRange ( vals , this . _cachedMode , colorMaxes , clamp ) ;
123134 } else if ( vals . length === 3 ) {
124135 mappedVals = Color . mapColorRange (
125136 [ vals [ 0 ] , vals [ 1 ] , vals [ 2 ] ] ,
126- this . mode ,
137+ this . _cachedMode ,
127138 colorMaxes ,
128139 clamp
129140 ) ;
130141 mappedVals . push ( 1 ) ;
131142 } else if ( vals . length === 2 ) {
132143 // Grayscale with alpha
133- if ( Color . #grayscaleMap[ this . mode ] ) {
134- mappedVals = Color . #grayscaleMap[ this . mode ] (
144+ if ( Color . #grayscaleMap[ this . _cachedMode ] ) {
145+ mappedVals = Color . #grayscaleMap[ this . _cachedMode ] (
135146 vals [ 0 ] ,
136147 colorMaxes ,
137148 clamp
138149 ) ;
139150 } else {
140151 mappedVals = Color . mapColorRange (
141152 [ vals [ 0 ] , vals [ 0 ] , vals [ 0 ] ] ,
142- this . mode ,
153+ this . _cachedMode ,
143154 colorMaxes ,
144155 clamp
145156 ) ;
@@ -159,16 +170,16 @@ class Color {
159170 ) ;
160171 } else if ( vals . length === 1 ) {
161172 // Grayscale only
162- if ( Color . #grayscaleMap[ this . mode ] ) {
163- mappedVals = Color . #grayscaleMap[ this . mode ] (
173+ if ( Color . #grayscaleMap[ this . _cachedMode ] ) {
174+ mappedVals = Color . #grayscaleMap[ this . _cachedMode ] (
164175 vals [ 0 ] ,
165176 colorMaxes ,
166177 clamp
167178 ) ;
168179 } else {
169180 mappedVals = Color . mapColorRange (
170181 [ vals [ 0 ] , vals [ 0 ] , vals [ 0 ] ] ,
171- this . mode ,
182+ this . _cachedMode ,
172183 colorMaxes ,
173184 clamp
174185 ) ;
@@ -180,19 +191,54 @@ class Color {
180191 } else {
181192 mappedVals = vals ;
182193 }
194+ if ( this . _cachedMode === RGB ) {
195+ if ( mappedVals [ 3 ] === 1 ) {
196+ // Faster for the browser to parse than rgba
197+ this . _defaultStringValue = '#' + toHexComponent ( mappedVals [ 0 ] ) + toHexComponent ( mappedVals [ 1 ] ) + toHexComponent ( mappedVals [ 2 ] ) ;
198+ } else {
199+ this . _defaultStringValue = '#' + toHexComponent ( mappedVals [ 0 ] ) + toHexComponent ( mappedVals [ 1 ] ) + toHexComponent ( mappedVals [ 2 ] ) + toHexComponent ( mappedVals [ 3 ] ) ; ;
200+ }
201+ }
183202
184- const space = Color . colorMap [ this . mode ] || console . error ( 'Invalid color mode' ) ;
185- const coords = mappedVals . slice ( 0 , 3 ) ;
203+ this . _initialize = ( ) => {
204+ const space = Color . colorMap [ this . _cachedMode ] || console . error ( 'Invalid color mode' ) ;
205+ const coords = mappedVals . slice ( 0 , 3 ) ;
186206
187- const color = {
188- space,
189- coords,
190- alpha : mappedVals [ 3 ]
207+ const color = {
208+ space,
209+ coords,
210+ alpha : mappedVals [ 3 ]
211+ } ;
212+ this . _cachedColor = to ( color , space ) ;
191213 } ;
192- this . _color = to ( color , space ) ;
193214 }
194215 }
195216
217+ // Color mode of the Color object, uses p5 color modes
218+ get mode ( ) {
219+ if ( this . _initialize ) {
220+ this . _initialize ( ) ;
221+ this . _initialize = undefined ;
222+ }
223+ return this . _cachedMode ;
224+ }
225+ // Reference to underlying color object depending on implementation
226+ // Not meant to be used publicly unless the implementation is known for sure
227+ get _color ( ) {
228+ if ( this . _initialize ) {
229+ this . _initialize ( ) ;
230+ this . _initialize = undefined ;
231+ }
232+ return this . _cachedColor ;
233+ }
234+ set _color ( newColor ) {
235+ if ( this . _initialize ) {
236+ this . _initialize ( ) ;
237+ this . _initialize = undefined ;
238+ }
239+ this . _cachedColor = newColor ;
240+ }
241+
196242 // Convert from p5 color range to color.js color range
197243 static mapColorRange ( origin , mode , maxes , clamp ) {
198244 const p5Maxes = maxes . map ( max => {
@@ -319,14 +365,21 @@ class Color {
319365 * </div>
320366 */
321367 toString ( format ) {
368+ if ( format === undefined && this . _defaultStringValue !== undefined ) {
369+ return this . _defaultStringValue ;
370+ }
371+
322372 const key = `${ this . _color . space . id } -${ this . _color . coords . join ( ',' ) } -${ this . _color . alpha } -${ format } ` ;
323- let colorString = serializationMap [ key ] ;
373+ let colorString = serializationMap . get ( key ) ;
324374
325375 if ( ! colorString ) {
326376 colorString = serialize ( this . _color , {
327377 format
328378 } ) ;
329- serializationMap [ key ] = colorString ;
379+ if ( serializationMap . size > 1000 ) {
380+ serializationMap . delete ( serializationMap . keys ( ) . next ( ) . value )
381+ }
382+ serializationMap . set ( key , colorString ) ;
330383 }
331384 return colorString ;
332385 }
@@ -487,6 +540,7 @@ class Color {
487540 * </div>
488541 */
489542 setRed ( new_red , max = [ 0 , 1 ] ) {
543+ this . _defaultStringValue = undefined ;
490544 if ( ! Array . isArray ( max ) ) {
491545 max = [ 0 , max ] ;
492546 }
@@ -542,6 +596,7 @@ class Color {
542596 * </div>
543597 */
544598 setGreen ( new_green , max = [ 0 , 1 ] ) {
599+ this . _defaultStringValue = undefined ;
545600 if ( ! Array . isArray ( max ) ) {
546601 max = [ 0 , max ] ;
547602 }
@@ -597,6 +652,7 @@ class Color {
597652 * </div>
598653 */
599654 setBlue ( new_blue , max = [ 0 , 1 ] ) {
655+ this . _defaultStringValue = undefined ;
600656 if ( ! Array . isArray ( max ) ) {
601657 max = [ 0 , max ] ;
602658 }
@@ -653,6 +709,7 @@ class Color {
653709 * </div>
654710 */
655711 setAlpha ( new_alpha , max = [ 0 , 1 ] ) {
712+ this . _defaultStringValue = undefined ;
656713 if ( ! Array . isArray ( max ) ) {
657714 max = [ 0 , max ] ;
658715 }
0 commit comments