Skip to content

Commit 9efb13c

Browse files
authored
Merge branch 'dev-2.0' into fix/buildgeom-no-faces
2 parents 98d2664 + dc448e5 commit 9efb13c

File tree

15 files changed

+256
-18
lines changed

15 files changed

+256
-18
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"test/**/*.js": "eslint",
2020
"utils/**/*.{js,mjs}": "eslint"
2121
},
22-
"version": "2.1.0-rc.0",
22+
"version": "2.1.0-rc.1",
2323
"dependencies": {
2424
"@davepagurek/bezier-path": "^0.0.2",
2525
"@japont/unicode-range": "^1.0.0",

src/color/p5.Color.js

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { RGB, RGBHDR, HSL, HSB, HWB, LAB, LCH, OKLAB, OKLCH } from './creating_reading';
1010

11+
1112
import {
1213
ColorSpace,
1314
to,
@@ -25,7 +26,8 @@ import {
2526

2627
OKLab,
2728
OKLCH as OKLCHSpace,
28-
29+
contrastWCAG21,
30+
contrastAPCA,
2931
P3
3032
} from 'colorjs.io/fn';
3133
import HSBSpace from './color_spaces/hsb.js';
@@ -41,6 +43,9 @@ const map = (n, start1, stop1, start2, stop2, clamp) => {
4143

4244
const serializationMap = {};
4345

46+
47+
48+
4449
class Color {
4550
// Reference to underlying color object depending on implementation
4651
// Not meant to be used publicly unless the implementation is known for sure
@@ -326,6 +331,125 @@ class Color {
326331
return colorString;
327332
}
328333

334+
/**
335+
* Checks the contrast between two colors. This method returns a boolean
336+
* value to indicate if the two color has enough contrast. `true` means that
337+
* the colors has enough contrast to be used as background color and body
338+
* text color. `false` means there is not enough contrast.
339+
*
340+
* A second argument can be passed to the method, `options` , which defines
341+
* the algorithm to be used. The algorithms currently supported are
342+
* WCAG 2.1 (`'WCAG21'`) or APCA (`'APCA'`). The default is WCAG 2.1. If a
343+
* value of `'all'` is passed to the `options` argument, an object containing
344+
* more details is returned. The details object will include the calculated
345+
* contrast value of the colors and different passing criteria.
346+
*
347+
* For more details about color contrast, you can check out
348+
* <a href="https://colorjs.io/docs/contrast">this page from color.js</a>, and the
349+
* <a href="https://webaim.org/resources/contrastchecker/">WebAIM color contrast checker.</a>
350+
*
351+
* @param {Color} other
352+
* @returns {boolean|object}
353+
* @example
354+
* <div>
355+
* <code>
356+
* let bgColor, fg1Color, fg2Color, msg1, msg2;
357+
* function setup() {
358+
* createCanvas(100, 100);
359+
* bgColor = color(0);
360+
* fg1Color = color(100);
361+
* fg2Color = color(220);
362+
*
363+
* if(bgColor.contrast(fg1Color)){
364+
* msg1 = 'good';
365+
* }else{
366+
* msg1 = 'bad';
367+
* }
368+
*
369+
* if(bgColor.contrast(fg2Color)){
370+
* msg2 = 'good';
371+
* }else{
372+
* msg2 = 'bad';
373+
* }
374+
*
375+
* describe('A black canvas with a faint grey word saying "bad" at the top left and a brighter light grey word saying "good" in the middle of the canvas.');
376+
* }
377+
*
378+
* function draw(){
379+
* background(bgColor);
380+
*
381+
* textSize(18);
382+
*
383+
* fill(fg1Color);
384+
* text(msg1, 10, 30);
385+
*
386+
* fill(fg2Color);
387+
* text(msg2, 10, 60);
388+
* }
389+
* </code>
390+
* </div>
391+
*
392+
* <div>
393+
* <code>
394+
* let bgColor, fgColor, contrast;
395+
* function setup() {
396+
* createCanvas(100, 100);
397+
* bgColor = color(0);
398+
* fgColor = color(200);
399+
* contrast = bgColor.contrast(fgColor, 'all');
400+
*
401+
* describe('A black canvas with four short lines of grey text that respectively says: "WCAG 2.1", "12.55", "APCA", and "-73.30".');
402+
* }
403+
*
404+
* function draw(){
405+
* background(bgColor);
406+
*
407+
* textSize(14);
408+
*
409+
* fill(fgColor);
410+
* text('WCAG 2.1', 10, 25);
411+
* text(nf(contrast.WCAG21.value, 0, 2), 10, 40);
412+
*
413+
* text('APCA', 10, 70);
414+
* text(nf(contrast.APCA.value, 0, 2), 10, 85);
415+
* }
416+
* </code>
417+
* </div>
418+
*/
419+
contrast(other_color, options='WCAG21') {
420+
if(options !== 'all'){
421+
let contrastVal, minimum;
422+
switch(options){
423+
case 'WCAG21':
424+
contrastVal = contrastWCAG21(this._color, other_color._color);
425+
minimum = 4.5;
426+
break;
427+
case 'APCA':
428+
contrastVal = Math.abs(contrastAPCA(this._color, other_color._color));
429+
minimum = 75;
430+
break;
431+
default:
432+
return null;
433+
}
434+
435+
return contrastVal >= minimum;
436+
}else{
437+
const wcag21Value = contrastWCAG21(this._color, other_color._color);
438+
const apcaValue = contrastAPCA(this._color, other_color._color);
439+
return {
440+
WCAG21: {
441+
value: wcag21Value,
442+
passedMinimum: wcag21Value >= 4.5,
443+
passedAAA: wcag21Value >= 7
444+
},
445+
APCA: {
446+
value: apcaValue,
447+
passedMinimum: Math.abs(apcaValue) >= 75
448+
}
449+
};
450+
}
451+
};
452+
329453
/**
330454
* Sets the red component of a color.
331455
*

src/core/friendly_errors/fes_core.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ function fesCore(p5, fn){
593593

594594
// get the function just above the topmost frame in the friendlyStack.
595595
// i.e the name of the library function called from user's code
596-
const func = stacktrace[friendlyStack[0].frameIndex - 2].functionName
596+
const func = stacktrace[friendlyStack[0].frameIndex - 1].functionName
597597
.split('.')
598598
.slice(-1)[0];
599599

src/dom/p5.MediaElement.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,13 @@ class MediaElement extends Element {
935935

936936
/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
937937

938+
_getAudioContext() {
939+
return undefined;
940+
}
941+
_getSoundOut() {
942+
return undefined;
943+
}
944+
938945
/**
939946
* Sends the element's audio to an output.
940947
*
@@ -954,9 +961,9 @@ class MediaElement extends Element {
954961
let audioContext, mainOutput;
955962

956963
// if p5.sound exists, same audio context
957-
if (typeof fn.getAudioContext === 'function') {
958-
audioContext = fn.getAudioContext();
959-
mainOutput = p5.soundOut.input;
964+
if (this._getAudioContext() && this._getSoundOut()) {
965+
audioContext = this._getAudioContext();
966+
mainOutput = this._getSoundOut().input;
960967
} else {
961968
try {
962969
audioContext = obj.context;
@@ -1792,6 +1799,19 @@ function media(p5, fn){
17921799
*/
17931800
p5.MediaElement = MediaElement;
17941801

1802+
// Patch MediaElement to give it access to fn, which p5.sound may attach things to
1803+
// if present in a sketch
1804+
MediaElement.prototype._getSoundOut = function() {
1805+
return p5.soundOut;
1806+
}
1807+
MediaElement.prototype._getAudioContext = function() {
1808+
if (typeof fn.getAudioContext === 'function') {
1809+
return fn.getAudioContext();
1810+
} else {
1811+
return undefined;
1812+
}
1813+
}
1814+
17951815
/**
17961816
* Path to the media element's source as a string.
17971817
*

src/type/p5.Font.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,15 @@ export class Font {
234234
* path and are more precise.
235235
*
236236
* `simplifyThreshold` removes collinear points if it's set to a number other
237-
* than 0. The value represents the threshold angle to use when determining
237+
* than 0. The value represents the threshold angle in radians to use when determining
238238
* whether two edges are collinear.
239239
*
240240
* @param {String} str string of text.
241241
* @param {Number} x x-coordinate of the text.
242242
* @param {Number} y y-coordinate of the text.
243243
* @param {Object} [options] Configuration:
244244
* @param {Number} [options.sampleFactor=0.1] The ratio of the text's path length to the number of samples.
245-
* @param {Number} [options.simplifyThreshold=0] A minmum angle between two segments. Segments with a shallower angle will be merged.
245+
* @param {Number} [options.simplifyThreshold=0] A minmum angle in radian sbetween two segments. Segments with a shallower angle will be merged.
246246
* @return {Array<Object>} array of point objects, each with `x`, `y`, and `alpha` (path angle) properties.
247247
*
248248
* @example
@@ -306,15 +306,15 @@ export class Font {
306306
* path and are more precise.
307307
*
308308
* `simplifyThreshold` removes collinear points if it's set to a number other
309-
* than 0. The value represents the threshold angle to use when determining
309+
* than 0. The value represents the threshold angle in radians to use when determining
310310
* whether two edges are collinear.
311311
*
312312
* @param {String} str string of text.
313313
* @param {Number} x x-coordinate of the text.
314314
* @param {Number} y y-coordinate of the text.
315315
* @param {Object} [options] Configuration options:
316316
* @param {Number} [options.sampleFactor=0.1] The ratio of the text's path length to the number of samples.
317-
* @param {Number} [options.simplifyThreshold=0] A minmum angle between two segments. Segments with a shallower angle will be merged.
317+
* @param {Number} [options.simplifyThreshold=0] A minmum angle in radians between two segments. Segments with a shallower angle will be merged.
318318
* @return {Array<Array<Object>>} array of point objects, each with `x`, `y`, and `alpha` (path angle) properties.
319319
*
320320
* @example
@@ -1092,6 +1092,35 @@ function pathToPoints(cmds, options, font) {
10921092
return num;
10931093
};
10941094

1095+
const collinear = (a, b, c, thresholdAngle) => {
1096+
if (!thresholdAngle) {
1097+
return areaTriangle(a, b, c) === 0;
1098+
}
1099+
1100+
if (typeof collinear.tmpPoint1 === 'undefined') {
1101+
collinear.tmpPoint1 = [];
1102+
collinear.tmpPoint2 = [];
1103+
}
1104+
1105+
const ab = collinear.tmpPoint1,
1106+
bc = collinear.tmpPoint2;
1107+
ab.x = b.x - a.x;
1108+
ab.y = b.y - a.y;
1109+
bc.x = c.x - b.x;
1110+
bc.y = c.y - b.y;
1111+
1112+
const dot = ab.x * bc.x + ab.y * bc.y,
1113+
magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y),
1114+
magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y),
1115+
angle = Math.acos(dot / (magA * magB));
1116+
1117+
return angle < thresholdAngle;
1118+
};
1119+
1120+
const areaTriangle = (a, b, c) => {
1121+
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
1122+
};
1123+
10951124
const path = createFromCommands(arrayCommandsToObjects(cmds));
10961125
let opts = parseOpts(options, {
10971126
sampleFactor: 0.1,

src/webgl/3d_primitives.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,7 +1726,7 @@ function primitives3D(p5, fn){
17261726

17271727
this.states.setValue('uModelMatrix', mult);
17281728

1729-
this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid));
1729+
this.model(this.geometryBufferCache.getGeometryByID(gid));
17301730
} finally {
17311731
this.states.setValue('uModelMatrix', uModelMatrix);
17321732
}
@@ -1858,7 +1858,7 @@ function primitives3D(p5, fn){
18581858
this.states.uModelMatrix.translate([x, y, 0]);
18591859
this.states.uModelMatrix.scale(width, height, 1);
18601860

1861-
this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid));
1861+
this.model(this.geometryBufferCache.getGeometryByID(gid));
18621862
} finally {
18631863
this.states.setValue('uModelMatrix', uModelMatrix);
18641864
}
@@ -1919,7 +1919,7 @@ function primitives3D(p5, fn){
19191919
this.states.uModelMatrix.translate([x, y, 0]);
19201920
this.states.uModelMatrix.scale(width, height, 1);
19211921

1922-
this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid));
1922+
this.model(this.geometryBufferCache.getGeometryByID(gid));
19231923
} finally {
19241924
this.states.setValue('uModelMatrix', uModelMatrix);
19251925
}
@@ -1967,7 +1967,7 @@ function primitives3D(p5, fn){
19671967
const prevOrder = this.bezierOrder();
19681968
this.bezierOrder(2);
19691969
this.beginShape();
1970-
const addUVs = (x, y) => [x, y, (x - x1)/width, (y - y1)/height];
1970+
const addUVs = (x, y) => [x, y, 0, (x - x1)/width, (y - y1)/height];
19711971
if (tr !== 0) {
19721972
this.vertex(...addUVs(x2 - tr, y1));
19731973
this.bezierVertex(...addUVs(x2, y1));
@@ -2069,7 +2069,7 @@ function primitives3D(p5, fn){
20692069
quadGeom.gid = gid;
20702070
this.geometryBufferCache.ensureCached(quadGeom);
20712071
}
2072-
this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid));
2072+
this.model(this.geometryBufferCache.getGeometryByID(gid));
20732073
return this;
20742074
};
20752075

src/webgl/p5.RendererGL.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,11 @@ class RendererGL extends Renderer {
511511
);
512512
}
513513
const geometry = this.geometryBuilder.finish();
514-
this.fill(this.geometryBuilder.prevFillColor);
514+
if (this.geometryBuilder.prevFillColor) {
515+
this.fill(this.geometryBuilder.prevFillColor);
516+
} else {
517+
this.noFill();
518+
}
515519
this.geometryBuilder = undefined;
516520
return geometry;
517521
}

test/unit/type/p5.Font.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ suite('p5.Font', function () {
6060
expect(pt.y).not.toBeNaN();
6161
}
6262
});
63+
64+
test('simplifies collinear points', async () => {
65+
const font = await myp5.loadFont(fontFile);
66+
myp5.textSize(50);
67+
const pts = font.textToPoints('T', 0, 0);
68+
const simplifiedPts = font.textToPoints('T', 0, 0, { simplifyThreshold: Math.PI * 0.01 });
69+
expect(pts.length).toBeGreaterThan(simplifiedPts.length);
70+
});
6371
});
6472
});
6573

0 commit comments

Comments
 (0)