Skip to content

Commit a729956

Browse files
authored
Merge branch 'dev-2.0' into fix/strands-noise-range
2 parents d81c615 + a491a9e commit a729956

4 files changed

Lines changed: 350 additions & 50 deletions

File tree

src/color/p5.Color.js

Lines changed: 100 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/type/p5.Font.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,10 @@ export class Font {
545545
const extrude = options?.extrude || 0;
546546

547547
let contours = this.textToContours(str, x, y, width, height, options);
548+
if (!contours || contours.length === 0) {
549+
return new p5.Geometry();
550+
}
551+
548552
// Step 2: build base flat geometry - single shape
549553
const geom = this._pInst.buildGeometry(() => {
550554
const prevValidateFaces = this._pInst._renderer._validateFaces;

0 commit comments

Comments
 (0)