q5 2.9.21 → 2.9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/launch.json +26 -0
- package/bun.lockb +0 -0
- package/p5-tests/js/chai_helpers.js +20 -0
- package/p5-tests/js/mocha_setup.js +2 -0
- package/p5-tests/js/modernizr.js +5 -0
- package/p5-tests/js/p5_helpers.js +135 -0
- package/p5-tests/js/sinon.js +5949 -0
- package/p5-tests/mocha.css +289 -0
- package/p5-tests/test.html +71 -0
- package/p5-tests/unit/color/color_conversion.js +68 -0
- package/p5-tests/unit/color/creating_reading.js +217 -0
- package/p5-tests/unit/color/p5.Color.js +1000 -0
- package/p5-tests/unit/color/setting.js +289 -0
- package/p5-tests/unit/core/2d_primitives.js +490 -0
- package/p5-tests/unit/core/attributes.js +115 -0
- package/p5-tests/unit/core/curves.js +139 -0
- package/p5-tests/unit/core/environment.js +248 -0
- package/p5-tests/unit/core/error_helpers.js +1158 -0
- package/p5-tests/unit/core/main.js +340 -0
- package/p5-tests/unit/core/p5.Element.js +773 -0
- package/p5-tests/unit/core/p5.Graphics.js +179 -0
- package/p5-tests/unit/core/preload.js +285 -0
- package/p5-tests/unit/core/rendering.js +116 -0
- package/p5-tests/unit/core/structure.js +293 -0
- package/p5-tests/unit/core/transform.js +144 -0
- package/p5-tests/unit/core/version.js +28 -0
- package/p5-tests/unit/core/vertex.js +137 -0
- package/p5-tests/unit/dom/dom.js +2146 -0
- package/p5-tests/unit/events/acceleration.js +213 -0
- package/p5-tests/unit/events/keyboard.js +179 -0
- package/p5-tests/unit/events/mouse.js +487 -0
- package/p5-tests/unit/events/touch.js +180 -0
- package/p5-tests/unit/image/downloading.js +379 -0
- package/p5-tests/unit/image/filters.js +92 -0
- package/p5-tests/unit/image/loading.js +413 -0
- package/p5-tests/unit/image/p5.Image.js +201 -0
- package/p5-tests/unit/image/pixels.js +234 -0
- package/p5-tests/unit/io/files.js +378 -0
- package/p5-tests/unit/io/loadBytes.js +149 -0
- package/p5-tests/unit/io/loadImage.js +123 -0
- package/p5-tests/unit/io/loadJSON.js +185 -0
- package/p5-tests/unit/io/loadModel.js +215 -0
- package/p5-tests/unit/io/loadShader.js +176 -0
- package/p5-tests/unit/io/loadStrings.js +140 -0
- package/p5-tests/unit/io/loadTable.js +183 -0
- package/p5-tests/unit/io/loadXML.js +127 -0
- package/p5-tests/unit/io/saveModel.js +113 -0
- package/p5-tests/unit/io/saveTable.js +142 -0
- package/p5-tests/unit/math/calculation.js +452 -0
- package/p5-tests/unit/math/noise.js +66 -0
- package/p5-tests/unit/math/p5.Vector.js +1886 -0
- package/p5-tests/unit/math/random.js +177 -0
- package/p5-tests/unit/math/trigonometry.js +144 -0
- package/p5-tests/unit/spec.js +50 -0
- package/p5-tests/unit/typography/attributes.js +120 -0
- package/p5-tests/unit/typography/loadFont.js +162 -0
- package/p5-tests/unit/typography/p5.Font.js +63 -0
- package/p5-tests/unit/utilities/conversion.js +329 -0
- package/p5-tests/unit/utilities/time_date.js +133 -0
- package/package.json +1 -1
- package/q5.js +158 -54
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +8 -2
- package/src/q5-2d-drawing.js +20 -7
- package/src/q5-2d-image.js +4 -1
- package/src/q5-2d-text.js +7 -0
- package/src/q5-canvas.js +6 -5
- package/src/q5-color.js +5 -0
- package/src/q5-core.js +3 -1
- package/src/q5-input.js +12 -0
- package/src/q5-math.js +11 -3
- package/src/q5-record.js +2 -0
- package/src/q5-vector.js +33 -0
- package/src/q5-webgpu-canvas.js +11 -7
- package/src/q5-webgpu-drawing.js +15 -12
- package/src/q5-webgpu-image.js +1 -1
- package/src/q5-webgpu-text.js +22 -15
package/q5.js
CHANGED
|
@@ -118,7 +118,7 @@ function Q5(scope, parent, renderer) {
|
|
|
118
118
|
};
|
|
119
119
|
$.loop = () => {
|
|
120
120
|
$._loop = true;
|
|
121
|
-
if (looper == null) $._draw();
|
|
121
|
+
if ($._setupDone && looper == null) $._draw();
|
|
122
122
|
};
|
|
123
123
|
$.isLooping = () => $._loop;
|
|
124
124
|
$.redraw = (n = 1) => {
|
|
@@ -309,6 +309,8 @@ function createCanvas(w, h, opt) {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
Q5.version = Q5.VERSION = '2.9';
|
|
313
|
+
|
|
312
314
|
if (typeof document == 'object') {
|
|
313
315
|
document.addEventListener('DOMContentLoaded', () => {
|
|
314
316
|
if (!Q5._hasGlobal) new Q5('auto');
|
|
@@ -462,7 +464,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
462
464
|
return g;
|
|
463
465
|
};
|
|
464
466
|
|
|
465
|
-
|
|
467
|
+
async function saveFile(data, name, ext) {
|
|
466
468
|
name = name || 'untitled';
|
|
467
469
|
ext = ext || 'png';
|
|
468
470
|
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
@@ -490,18 +492,19 @@ Q5.modules.canvas = ($, q) => {
|
|
|
490
492
|
a.download = name + '.' + ext;
|
|
491
493
|
a.click();
|
|
492
494
|
URL.revokeObjectURL(a.href);
|
|
493
|
-
}
|
|
495
|
+
}
|
|
496
|
+
|
|
494
497
|
$.save = (a, b, c) => {
|
|
495
498
|
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
496
499
|
c = b;
|
|
497
500
|
b = a;
|
|
498
501
|
a = $.canvas;
|
|
499
502
|
}
|
|
500
|
-
if (c) return
|
|
503
|
+
if (c) return saveFile(a, b, c);
|
|
501
504
|
if (b) {
|
|
502
505
|
b = b.split('.');
|
|
503
|
-
|
|
504
|
-
} else
|
|
506
|
+
saveFile(a, b[0], b.at(-1));
|
|
507
|
+
} else saveFile(a);
|
|
505
508
|
};
|
|
506
509
|
|
|
507
510
|
$._setCanvasSize = (w, h) => {
|
|
@@ -716,7 +719,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
716
719
|
}
|
|
717
720
|
$.ctx.fillStyle = $._fill = c.toString();
|
|
718
721
|
};
|
|
719
|
-
|
|
722
|
+
|
|
720
723
|
$.stroke = function (c) {
|
|
721
724
|
$._doStroke = $._strokeSet = true;
|
|
722
725
|
if (Q5.Color) {
|
|
@@ -728,13 +731,15 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
728
731
|
}
|
|
729
732
|
$.ctx.strokeStyle = $._stroke = c.toString();
|
|
730
733
|
};
|
|
734
|
+
|
|
731
735
|
$.strokeWeight = (n) => {
|
|
732
736
|
if (!n) $._doStroke = false;
|
|
733
737
|
if ($._da) n *= $._da;
|
|
734
738
|
$.ctx.lineWidth = $._strokeWeight = n || 0.0001;
|
|
735
739
|
};
|
|
736
|
-
$.noStroke = () => ($._doStroke = false);
|
|
737
740
|
|
|
741
|
+
$.noFill = () => ($._doFill = false);
|
|
742
|
+
$.noStroke = () => ($._doStroke = false);
|
|
738
743
|
$.opacity = (a) => ($.ctx.globalAlpha = a);
|
|
739
744
|
|
|
740
745
|
// DRAWING MATRIX
|
|
@@ -746,10 +751,12 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
746
751
|
}
|
|
747
752
|
$.ctx.translate(x, y);
|
|
748
753
|
};
|
|
754
|
+
|
|
749
755
|
$.rotate = (r) => {
|
|
750
756
|
if ($._angleMode) r = $.radians(r);
|
|
751
757
|
$.ctx.rotate(r);
|
|
752
758
|
};
|
|
759
|
+
|
|
753
760
|
$.scale = (x, y) => {
|
|
754
761
|
if (x.x) {
|
|
755
762
|
y = x.y;
|
|
@@ -758,9 +765,11 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
758
765
|
y ??= x;
|
|
759
766
|
$.ctx.scale(x, y);
|
|
760
767
|
};
|
|
768
|
+
|
|
761
769
|
$.applyMatrix = (a, b, c, d, e, f) => $.ctx.transform(a, b, c, d, e, f);
|
|
762
770
|
$.shearX = (ang) => $.ctx.transform(1, 0, $.tan(ang), 1, 0, 0);
|
|
763
771
|
$.shearY = (ang) => $.ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
|
|
772
|
+
|
|
764
773
|
$.resetMatrix = () => {
|
|
765
774
|
if ($.ctx) {
|
|
766
775
|
$.ctx.resetTransform();
|
|
@@ -852,12 +861,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
852
861
|
|
|
853
862
|
$.line = (x0, y0, x1, y1) => {
|
|
854
863
|
if ($._doStroke) {
|
|
855
|
-
|
|
856
|
-
x0 *= $._da;
|
|
857
|
-
y0 *= $._da;
|
|
858
|
-
x1 *= $._da;
|
|
859
|
-
y1 *= $._da;
|
|
860
|
-
}
|
|
864
|
+
$._da && ((x0 *= $._da), (y0 *= $._da), (x1 *= $._da), (y1 *= $._da));
|
|
861
865
|
$.ctx.beginPath();
|
|
862
866
|
$.ctx.moveTo(x0, y0);
|
|
863
867
|
$.ctx.lineTo(x1, y1);
|
|
@@ -897,6 +901,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
897
901
|
$.ctx.stroke();
|
|
898
902
|
}
|
|
899
903
|
}
|
|
904
|
+
|
|
900
905
|
$.arc = (x, y, w, h, start, stop, mode) => {
|
|
901
906
|
if (start == stop) return $.ellipse(x, y, w, h);
|
|
902
907
|
|
|
@@ -906,7 +911,6 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
906
911
|
w *= $._da;
|
|
907
912
|
h *= $._da;
|
|
908
913
|
}
|
|
909
|
-
|
|
910
914
|
mode ??= $.PIE_OPEN;
|
|
911
915
|
|
|
912
916
|
if ($._ellipseMode == $.CENTER) {
|
|
@@ -925,6 +929,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
925
929
|
$.ctx.ellipse(x, y, w / 2, h / 2, 0, 0, $.TAU);
|
|
926
930
|
ink();
|
|
927
931
|
}
|
|
932
|
+
|
|
928
933
|
$.ellipse = (x, y, w, h) => {
|
|
929
934
|
h ??= w;
|
|
930
935
|
if ($._da) {
|
|
@@ -943,6 +948,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
943
948
|
ellipse((x + w) / 2, (y + h) / 2, w - x, h - y);
|
|
944
949
|
}
|
|
945
950
|
};
|
|
951
|
+
|
|
946
952
|
$.circle = (x, y, d) => {
|
|
947
953
|
if ($._ellipseMode == $.CENTER) {
|
|
948
954
|
if ($._da) {
|
|
@@ -984,6 +990,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
984
990
|
$.ctx.rect(x, y, w, h);
|
|
985
991
|
ink();
|
|
986
992
|
}
|
|
993
|
+
|
|
987
994
|
function roundedRect(x, y, w, h, tl, tr, br, bl) {
|
|
988
995
|
if (tl === undefined) {
|
|
989
996
|
return rect(x, y, w, h);
|
|
@@ -1016,6 +1023,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1016
1023
|
roundedRect(x, y, w - x, h - y, tl, tr, br, bl);
|
|
1017
1024
|
}
|
|
1018
1025
|
};
|
|
1026
|
+
|
|
1019
1027
|
$.square = (x, y, s, tl, tr, br, bl) => {
|
|
1020
1028
|
return $.rect(x, y, s, s, tl, tr, br, bl);
|
|
1021
1029
|
};
|
|
@@ -1025,15 +1033,18 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1025
1033
|
$.ctx.beginPath();
|
|
1026
1034
|
firstVertex = true;
|
|
1027
1035
|
};
|
|
1036
|
+
|
|
1028
1037
|
$.beginContour = () => {
|
|
1029
1038
|
$.ctx.closePath();
|
|
1030
1039
|
curveBuff.length = 0;
|
|
1031
1040
|
firstVertex = true;
|
|
1032
1041
|
};
|
|
1042
|
+
|
|
1033
1043
|
$.endContour = () => {
|
|
1034
1044
|
curveBuff.length = 0;
|
|
1035
1045
|
firstVertex = true;
|
|
1036
1046
|
};
|
|
1047
|
+
|
|
1037
1048
|
$.vertex = (x, y) => {
|
|
1038
1049
|
if ($._da) {
|
|
1039
1050
|
x *= $._da;
|
|
@@ -1047,6 +1058,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1047
1058
|
}
|
|
1048
1059
|
firstVertex = false;
|
|
1049
1060
|
};
|
|
1061
|
+
|
|
1050
1062
|
$.bezierVertex = (cp1x, cp1y, cp2x, cp2y, x, y) => {
|
|
1051
1063
|
if ($._da) {
|
|
1052
1064
|
cp1x *= $._da;
|
|
@@ -1059,6 +1071,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1059
1071
|
curveBuff.length = 0;
|
|
1060
1072
|
$.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
1061
1073
|
};
|
|
1074
|
+
|
|
1062
1075
|
$.quadraticVertex = (cp1x, cp1y, x, y) => {
|
|
1063
1076
|
if ($._da) {
|
|
1064
1077
|
cp1x *= $._da;
|
|
@@ -1069,12 +1082,14 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1069
1082
|
curveBuff.length = 0;
|
|
1070
1083
|
$.ctx.quadraticCurveTo(cp1x, cp1y, x, y);
|
|
1071
1084
|
};
|
|
1085
|
+
|
|
1072
1086
|
$.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
1073
1087
|
$.beginShape();
|
|
1074
1088
|
$.vertex(x1, y1);
|
|
1075
1089
|
$.bezierVertex(x2, y2, x3, y3, x4, y4);
|
|
1076
1090
|
$.endShape();
|
|
1077
1091
|
};
|
|
1092
|
+
|
|
1078
1093
|
$.triangle = (x1, y1, x2, y2, x3, y3) => {
|
|
1079
1094
|
$.beginShape();
|
|
1080
1095
|
$.vertex(x1, y1);
|
|
@@ -1082,6 +1097,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1082
1097
|
$.vertex(x3, y3);
|
|
1083
1098
|
$.endShape($.CLOSE);
|
|
1084
1099
|
};
|
|
1100
|
+
|
|
1085
1101
|
$.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
1086
1102
|
$.beginShape();
|
|
1087
1103
|
$.vertex(x1, y1);
|
|
@@ -1090,6 +1106,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1090
1106
|
$.vertex(x4, y4);
|
|
1091
1107
|
$.endShape($.CLOSE);
|
|
1092
1108
|
};
|
|
1109
|
+
|
|
1093
1110
|
$.endShape = (close) => {
|
|
1094
1111
|
curveBuff.length = 0;
|
|
1095
1112
|
if (close) $.ctx.closePath();
|
|
@@ -1120,6 +1137,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1120
1137
|
}
|
|
1121
1138
|
$.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2[0], p2[1]);
|
|
1122
1139
|
};
|
|
1140
|
+
|
|
1123
1141
|
$.curve = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
1124
1142
|
$.beginShape();
|
|
1125
1143
|
$.curveVertex(x1, y1);
|
|
@@ -1128,6 +1146,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1128
1146
|
$.curveVertex(x4, y4);
|
|
1129
1147
|
$.endShape();
|
|
1130
1148
|
};
|
|
1149
|
+
|
|
1131
1150
|
$.curvePoint = (a, b, c, d, t) => {
|
|
1132
1151
|
const t3 = t * t * t,
|
|
1133
1152
|
t2 = t * t,
|
|
@@ -1137,6 +1156,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1137
1156
|
f4 = 0.5 * t3 - 0.5 * t2;
|
|
1138
1157
|
return a * f1 + b * f2 + c * f3 + d * f4;
|
|
1139
1158
|
};
|
|
1159
|
+
|
|
1140
1160
|
$.bezierPoint = (a, b, c, d, t) => {
|
|
1141
1161
|
const adjustedT = 1 - t;
|
|
1142
1162
|
return (
|
|
@@ -1146,6 +1166,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1146
1166
|
Math.pow(t, 3) * d
|
|
1147
1167
|
);
|
|
1148
1168
|
};
|
|
1169
|
+
|
|
1149
1170
|
$.curveTangent = (a, b, c, d, t) => {
|
|
1150
1171
|
const t2 = t * t,
|
|
1151
1172
|
f1 = (-3 * t2) / 2 + 2 * t - 0.5,
|
|
@@ -1154,6 +1175,7 @@ Q5.renderers.q2d.drawing = ($) => {
|
|
|
1154
1175
|
f4 = (3 * t2) / 2 - t;
|
|
1155
1176
|
return a * f1 + b * f2 + c * f3 + d * f4;
|
|
1156
1177
|
};
|
|
1178
|
+
|
|
1157
1179
|
$.bezierTangent = (a, b, c, d, t) => {
|
|
1158
1180
|
const adjustedT = 1 - t;
|
|
1159
1181
|
return (
|
|
@@ -1228,7 +1250,9 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1228
1250
|
$.loadImage = function (url, cb, opt) {
|
|
1229
1251
|
if (url.canvas) return url;
|
|
1230
1252
|
if (url.slice(-3).toLowerCase() == 'gif') {
|
|
1231
|
-
throw new Error(
|
|
1253
|
+
throw new Error(
|
|
1254
|
+
`q5 doesn't support GIFs. Use a video or p5play animation instead. https://github.com/q5js/q5.js/issues/84`
|
|
1255
|
+
);
|
|
1232
1256
|
}
|
|
1233
1257
|
q._preloadCount++;
|
|
1234
1258
|
let last = [...arguments].at(-1);
|
|
@@ -1271,6 +1295,7 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1271
1295
|
};
|
|
1272
1296
|
|
|
1273
1297
|
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1298
|
+
|
|
1274
1299
|
$.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
1275
1300
|
if (!img) return;
|
|
1276
1301
|
let drawable = img?.canvas || img;
|
|
@@ -1512,6 +1537,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1512
1537
|
fontMod = true;
|
|
1513
1538
|
styleHash = -1;
|
|
1514
1539
|
};
|
|
1540
|
+
|
|
1515
1541
|
$.textSize = (x) => {
|
|
1516
1542
|
if (x == undefined || x == $._textSize) return $._textSize;
|
|
1517
1543
|
if ($._da) x *= $._da;
|
|
@@ -1523,12 +1549,14 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1523
1549
|
leadDiff = leading - x;
|
|
1524
1550
|
}
|
|
1525
1551
|
};
|
|
1552
|
+
|
|
1526
1553
|
$.textStyle = (x) => {
|
|
1527
1554
|
if (!x || x == emphasis) return emphasis;
|
|
1528
1555
|
emphasis = x;
|
|
1529
1556
|
fontMod = true;
|
|
1530
1557
|
styleHash = -1;
|
|
1531
1558
|
};
|
|
1559
|
+
|
|
1532
1560
|
$.textLeading = (x) => {
|
|
1533
1561
|
leadingSet = true;
|
|
1534
1562
|
if (x == undefined || x == leading) return leading;
|
|
@@ -1537,6 +1565,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1537
1565
|
leadDiff = x - $._textSize;
|
|
1538
1566
|
styleHash = -1;
|
|
1539
1567
|
};
|
|
1568
|
+
|
|
1540
1569
|
$.textAlign = (horiz, vert) => {
|
|
1541
1570
|
$.ctx.textAlign = $._textAlign = horiz;
|
|
1542
1571
|
if (vert) {
|
|
@@ -1566,6 +1595,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1566
1595
|
if (enable !== undefined) useCache = enable;
|
|
1567
1596
|
return useCache;
|
|
1568
1597
|
};
|
|
1598
|
+
|
|
1569
1599
|
$.createTextImage = (str, w, h) => {
|
|
1570
1600
|
genTextImage = true;
|
|
1571
1601
|
img = $.text(str, 0, 0, w, h);
|
|
@@ -1574,6 +1604,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1574
1604
|
};
|
|
1575
1605
|
|
|
1576
1606
|
let lines = [];
|
|
1607
|
+
|
|
1577
1608
|
$.text = (str, x, y, w, h) => {
|
|
1578
1609
|
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
1579
1610
|
str = str.toString();
|
|
@@ -1699,6 +1730,7 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1699
1730
|
$.textImage(img, x, y);
|
|
1700
1731
|
}
|
|
1701
1732
|
};
|
|
1733
|
+
|
|
1702
1734
|
$.textImage = (img, x, y) => {
|
|
1703
1735
|
if (typeof img == 'string') img = $.createTextImage(img);
|
|
1704
1736
|
|
|
@@ -1942,6 +1974,7 @@ Q5.Color = class {
|
|
|
1942
1974
|
this._q5Color = true;
|
|
1943
1975
|
}
|
|
1944
1976
|
};
|
|
1977
|
+
|
|
1945
1978
|
Q5.ColorOKLCH = class extends Q5.Color {
|
|
1946
1979
|
constructor(l, c, h, a) {
|
|
1947
1980
|
super();
|
|
@@ -1954,6 +1987,7 @@ Q5.ColorOKLCH = class extends Q5.Color {
|
|
|
1954
1987
|
return `oklch(${this.l} ${this.c} ${this.h} / ${this.a})`;
|
|
1955
1988
|
}
|
|
1956
1989
|
};
|
|
1990
|
+
|
|
1957
1991
|
Q5.ColorRGBA = class extends Q5.Color {
|
|
1958
1992
|
constructor(r, g, b, a) {
|
|
1959
1993
|
super();
|
|
@@ -1969,11 +2003,13 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
1969
2003
|
return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
1970
2004
|
}
|
|
1971
2005
|
};
|
|
2006
|
+
|
|
1972
2007
|
Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
|
|
1973
2008
|
toString() {
|
|
1974
2009
|
return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
1975
2010
|
}
|
|
1976
2011
|
};
|
|
2012
|
+
|
|
1977
2013
|
// legacy 8-bit (0-255) integer color format
|
|
1978
2014
|
Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
1979
2015
|
constructor(r, g, b, a) {
|
|
@@ -1998,6 +2034,7 @@ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
|
1998
2034
|
return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
|
|
1999
2035
|
}
|
|
2000
2036
|
};
|
|
2037
|
+
|
|
2001
2038
|
// p3 10-bit color in integer color format, for backwards compatibility
|
|
2002
2039
|
Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
|
|
2003
2040
|
constructor(r, g, b, a) {
|
|
@@ -2206,6 +2243,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2206
2243
|
q.moveX = e.movementX;
|
|
2207
2244
|
q.moveY = e.movementY;
|
|
2208
2245
|
};
|
|
2246
|
+
|
|
2209
2247
|
$._onmousedown = (e) => {
|
|
2210
2248
|
$._startAudio();
|
|
2211
2249
|
$._updateMouse(e);
|
|
@@ -2213,22 +2251,26 @@ Q5.modules.input = ($, q) => {
|
|
|
2213
2251
|
q.mouseButton = mouseBtns[e.button];
|
|
2214
2252
|
$.mousePressed(e);
|
|
2215
2253
|
};
|
|
2254
|
+
|
|
2216
2255
|
$._onmousemove = (e) => {
|
|
2217
2256
|
$._updateMouse(e);
|
|
2218
2257
|
if ($.mouseIsPressed) $.mouseDragged(e);
|
|
2219
2258
|
else $.mouseMoved(e);
|
|
2220
2259
|
};
|
|
2260
|
+
|
|
2221
2261
|
$._onmouseup = (e) => {
|
|
2222
2262
|
$._updateMouse(e);
|
|
2223
2263
|
q.mouseIsPressed = false;
|
|
2224
2264
|
$.mouseReleased(e);
|
|
2225
2265
|
};
|
|
2266
|
+
|
|
2226
2267
|
$._onclick = (e) => {
|
|
2227
2268
|
$._updateMouse(e);
|
|
2228
2269
|
q.mouseIsPressed = true;
|
|
2229
2270
|
$.mouseClicked(e);
|
|
2230
2271
|
q.mouseIsPressed = false;
|
|
2231
2272
|
};
|
|
2273
|
+
|
|
2232
2274
|
$._onwheel = (e) => {
|
|
2233
2275
|
$._updateMouse(e);
|
|
2234
2276
|
e.delta = e.deltaY;
|
|
@@ -2246,9 +2288,11 @@ Q5.modules.input = ($, q) => {
|
|
|
2246
2288
|
}
|
|
2247
2289
|
$.canvas.style.cursor = name + pfx;
|
|
2248
2290
|
};
|
|
2291
|
+
|
|
2249
2292
|
$.noCursor = () => {
|
|
2250
2293
|
$.canvas.style.cursor = 'none';
|
|
2251
2294
|
};
|
|
2295
|
+
|
|
2252
2296
|
if (window) {
|
|
2253
2297
|
$.requestPointerLock = document.body?.requestPointerLock;
|
|
2254
2298
|
$.exitPointerLock = document.exitPointerLock;
|
|
@@ -2264,6 +2308,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2264
2308
|
$.keyPressed(e);
|
|
2265
2309
|
if (e.key.length == 1) $.keyTyped(e);
|
|
2266
2310
|
};
|
|
2311
|
+
|
|
2267
2312
|
$._onkeyup = (e) => {
|
|
2268
2313
|
q.keyIsPressed = false;
|
|
2269
2314
|
q.key = e.key;
|
|
@@ -2271,6 +2316,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2271
2316
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
|
|
2272
2317
|
$.keyReleased(e);
|
|
2273
2318
|
};
|
|
2319
|
+
|
|
2274
2320
|
$.keyIsDown = (v) => !!keysHeld[typeof v == 'string' ? v.toLowerCase() : v];
|
|
2275
2321
|
|
|
2276
2322
|
function getTouchInfo(touch) {
|
|
@@ -2283,6 +2329,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2283
2329
|
id: touch.identifier
|
|
2284
2330
|
};
|
|
2285
2331
|
}
|
|
2332
|
+
|
|
2286
2333
|
$._ontouchstart = (e) => {
|
|
2287
2334
|
$._startAudio();
|
|
2288
2335
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
@@ -2295,6 +2342,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2295
2342
|
}
|
|
2296
2343
|
if (!$.touchStarted(e)) e.preventDefault();
|
|
2297
2344
|
};
|
|
2345
|
+
|
|
2298
2346
|
$._ontouchmove = (e) => {
|
|
2299
2347
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
2300
2348
|
if (!$._isTouchAware) {
|
|
@@ -2304,6 +2352,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2304
2352
|
}
|
|
2305
2353
|
if (!$.touchMoved(e)) e.preventDefault();
|
|
2306
2354
|
};
|
|
2355
|
+
|
|
2307
2356
|
$._ontouchend = (e) => {
|
|
2308
2357
|
q.touches = [...e.touches].map(getTouchInfo);
|
|
2309
2358
|
if (!$._isTouchAware && !$.touches.length) {
|
|
@@ -2358,6 +2407,7 @@ Q5.modules.math = ($, q) => {
|
|
|
2358
2407
|
|
|
2359
2408
|
$.angleMode = (mode) => {
|
|
2360
2409
|
angleMode = $._angleMode = mode == 0 || mode == 'radians' ? 0 : 1;
|
|
2410
|
+
return !angleMode ? 'radians' : 'degrees';
|
|
2361
2411
|
};
|
|
2362
2412
|
let DEGTORAD = ($._DEGTORAD = Math.PI / 180);
|
|
2363
2413
|
let RADTODEG = ($._RADTODEG = 180 / Math.PI);
|
|
@@ -2375,13 +2425,15 @@ Q5.modules.math = ($, q) => {
|
|
|
2375
2425
|
return Math.min(Math.max(val, ostop), ostart);
|
|
2376
2426
|
}
|
|
2377
2427
|
};
|
|
2378
|
-
|
|
2379
|
-
$.constrain = (x, lo, hi) => Math.min(Math.max(x, lo), hi);
|
|
2428
|
+
|
|
2380
2429
|
$.dist = function () {
|
|
2381
2430
|
let a = arguments;
|
|
2382
2431
|
if (a.length == 4) return Math.hypot(a[0] - a[2], a[1] - a[3]);
|
|
2383
2432
|
else return Math.hypot(a[0] - a[3], a[1] - a[4], a[2] - a[5]);
|
|
2384
2433
|
};
|
|
2434
|
+
|
|
2435
|
+
$.lerp = (a, b, t) => a * (1 - t) + b * t;
|
|
2436
|
+
$.constrain = (x, lo, hi) => Math.min(Math.max(x, lo), hi);
|
|
2385
2437
|
$.norm = (value, start, stop) => $.map(value, start, stop, 0, 1);
|
|
2386
2438
|
$.sq = (x) => x * x;
|
|
2387
2439
|
$.fract = (x) => x - Math.floor(x);
|
|
@@ -2402,7 +2454,6 @@ Q5.modules.math = ($, q) => {
|
|
|
2402
2454
|
let a = Math.atan(x);
|
|
2403
2455
|
return !angleMode ? a : a * RADTODEG;
|
|
2404
2456
|
};
|
|
2405
|
-
|
|
2406
2457
|
$.atan2 = (y, x) => {
|
|
2407
2458
|
let a = Math.atan2(y, x);
|
|
2408
2459
|
return !angleMode ? a : a * RADTODEG;
|
|
@@ -2426,6 +2477,7 @@ Q5.modules.math = ($, q) => {
|
|
|
2426
2477
|
}
|
|
2427
2478
|
};
|
|
2428
2479
|
}
|
|
2480
|
+
|
|
2429
2481
|
function shr3() {
|
|
2430
2482
|
let jsr, seed;
|
|
2431
2483
|
let m = 4294967295;
|
|
@@ -2444,6 +2496,7 @@ Q5.modules.math = ($, q) => {
|
|
|
2444
2496
|
}
|
|
2445
2497
|
};
|
|
2446
2498
|
}
|
|
2499
|
+
|
|
2447
2500
|
let rng1 = shr3();
|
|
2448
2501
|
rng1.setSeed();
|
|
2449
2502
|
|
|
@@ -2460,6 +2513,7 @@ Q5.modules.math = ($, q) => {
|
|
|
2460
2513
|
return a[Math.trunc(a.length * rng1.rand())];
|
|
2461
2514
|
}
|
|
2462
2515
|
};
|
|
2516
|
+
|
|
2463
2517
|
$.randomGenerator = (method) => {
|
|
2464
2518
|
if (method == $.LCG) rng1 = lcg();
|
|
2465
2519
|
else if (method == $.SHR3) rng1 = shr3();
|
|
@@ -2615,13 +2669,16 @@ Q5.modules.math = ($, q) => {
|
|
|
2615
2669
|
q.Noise = Q5[mode[0].toUpperCase() + mode.slice(1) + 'Noise'];
|
|
2616
2670
|
_noise = null;
|
|
2617
2671
|
};
|
|
2672
|
+
|
|
2618
2673
|
$.noiseSeed = (seed) => {
|
|
2619
2674
|
_noise = new $.Noise(seed);
|
|
2620
2675
|
};
|
|
2676
|
+
|
|
2621
2677
|
$.noise = (x = 0, y = 0, z = 0) => {
|
|
2622
2678
|
_noise ??= new $.Noise();
|
|
2623
2679
|
return _noise.noise(x, y, z);
|
|
2624
2680
|
};
|
|
2681
|
+
|
|
2625
2682
|
$.noiseDetail = (lod, falloff) => {
|
|
2626
2683
|
_noise ??= new $.Noise();
|
|
2627
2684
|
if (lod > 0) _noise.octaves = lod;
|
|
@@ -2857,15 +2914,18 @@ Q5.Vector = class {
|
|
|
2857
2914
|
this._cn = null;
|
|
2858
2915
|
this._cnsq = null;
|
|
2859
2916
|
}
|
|
2917
|
+
|
|
2860
2918
|
set(x, y, z) {
|
|
2861
2919
|
this.x = x?.x || x || 0;
|
|
2862
2920
|
this.y = x?.y || y || 0;
|
|
2863
2921
|
this.z = x?.z || z || 0;
|
|
2864
2922
|
return this;
|
|
2865
2923
|
}
|
|
2924
|
+
|
|
2866
2925
|
copy() {
|
|
2867
2926
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
2868
2927
|
}
|
|
2928
|
+
|
|
2869
2929
|
_arg2v(x, y, z) {
|
|
2870
2930
|
if (x?.x !== undefined) return x;
|
|
2871
2931
|
if (y !== undefined) {
|
|
@@ -2873,10 +2933,12 @@ Q5.Vector = class {
|
|
|
2873
2933
|
}
|
|
2874
2934
|
return { x: x, y: x, z: x };
|
|
2875
2935
|
}
|
|
2936
|
+
|
|
2876
2937
|
_calcNorm() {
|
|
2877
2938
|
this._cnsq = this.x * this.x + this.y * this.y + this.z * this.z;
|
|
2878
2939
|
this._cn = Math.sqrt(this._cnsq);
|
|
2879
2940
|
}
|
|
2941
|
+
|
|
2880
2942
|
add() {
|
|
2881
2943
|
let u = this._arg2v(...arguments);
|
|
2882
2944
|
this.x += u.x;
|
|
@@ -2884,6 +2946,7 @@ Q5.Vector = class {
|
|
|
2884
2946
|
this.z += u.z;
|
|
2885
2947
|
return this;
|
|
2886
2948
|
}
|
|
2949
|
+
|
|
2887
2950
|
rem() {
|
|
2888
2951
|
let u = this._arg2v(...arguments);
|
|
2889
2952
|
this.x %= u.x;
|
|
@@ -2891,6 +2954,7 @@ Q5.Vector = class {
|
|
|
2891
2954
|
this.z %= u.z;
|
|
2892
2955
|
return this;
|
|
2893
2956
|
}
|
|
2957
|
+
|
|
2894
2958
|
sub() {
|
|
2895
2959
|
let u = this._arg2v(...arguments);
|
|
2896
2960
|
this.x -= u.x;
|
|
@@ -2898,6 +2962,7 @@ Q5.Vector = class {
|
|
|
2898
2962
|
this.z -= u.z;
|
|
2899
2963
|
return this;
|
|
2900
2964
|
}
|
|
2965
|
+
|
|
2901
2966
|
mult() {
|
|
2902
2967
|
let u = this._arg2v(...arguments);
|
|
2903
2968
|
this.x *= u.x;
|
|
@@ -2905,6 +2970,7 @@ Q5.Vector = class {
|
|
|
2905
2970
|
this.z *= u.z;
|
|
2906
2971
|
return this;
|
|
2907
2972
|
}
|
|
2973
|
+
|
|
2908
2974
|
div() {
|
|
2909
2975
|
let u = this._arg2v(...arguments);
|
|
2910
2976
|
if (u.x) this.x /= u.x;
|
|
@@ -2915,18 +2981,22 @@ Q5.Vector = class {
|
|
|
2915
2981
|
else this.z = 0;
|
|
2916
2982
|
return this;
|
|
2917
2983
|
}
|
|
2984
|
+
|
|
2918
2985
|
mag() {
|
|
2919
2986
|
this._calcNorm();
|
|
2920
2987
|
return this._cn;
|
|
2921
2988
|
}
|
|
2989
|
+
|
|
2922
2990
|
magSq() {
|
|
2923
2991
|
this._calcNorm();
|
|
2924
2992
|
return this._cnsq;
|
|
2925
2993
|
}
|
|
2994
|
+
|
|
2926
2995
|
dot() {
|
|
2927
2996
|
let u = this._arg2v(...arguments);
|
|
2928
2997
|
return this.x * u.x + this.y * u.y + this.z * u.z;
|
|
2929
2998
|
}
|
|
2999
|
+
|
|
2930
3000
|
dist() {
|
|
2931
3001
|
let u = this._arg2v(...arguments);
|
|
2932
3002
|
let x = this.x - u.x;
|
|
@@ -2934,6 +3004,7 @@ Q5.Vector = class {
|
|
|
2934
3004
|
let z = this.z - u.z;
|
|
2935
3005
|
return Math.sqrt(x * x + y * y + z * z);
|
|
2936
3006
|
}
|
|
3007
|
+
|
|
2937
3008
|
cross() {
|
|
2938
3009
|
let u = this._arg2v(...arguments);
|
|
2939
3010
|
let x = this.y * u.z - this.z * u.y;
|
|
@@ -2944,6 +3015,7 @@ Q5.Vector = class {
|
|
|
2944
3015
|
this.z = z;
|
|
2945
3016
|
return this;
|
|
2946
3017
|
}
|
|
3018
|
+
|
|
2947
3019
|
normalize() {
|
|
2948
3020
|
this._calcNorm();
|
|
2949
3021
|
let n = this._cn;
|
|
@@ -2956,6 +3028,7 @@ Q5.Vector = class {
|
|
|
2956
3028
|
this._cnsq = 1;
|
|
2957
3029
|
return this;
|
|
2958
3030
|
}
|
|
3031
|
+
|
|
2959
3032
|
limit(m) {
|
|
2960
3033
|
this._calcNorm();
|
|
2961
3034
|
let n = this._cn;
|
|
@@ -2969,6 +3042,7 @@ Q5.Vector = class {
|
|
|
2969
3042
|
}
|
|
2970
3043
|
return this;
|
|
2971
3044
|
}
|
|
3045
|
+
|
|
2972
3046
|
setMag(m) {
|
|
2973
3047
|
this._calcNorm();
|
|
2974
3048
|
let n = this._cn;
|
|
@@ -2980,15 +3054,18 @@ Q5.Vector = class {
|
|
|
2980
3054
|
this._cnsq = m * m;
|
|
2981
3055
|
return this;
|
|
2982
3056
|
}
|
|
3057
|
+
|
|
2983
3058
|
heading() {
|
|
2984
3059
|
return this._$.atan2(this.y, this.x);
|
|
2985
3060
|
}
|
|
3061
|
+
|
|
2986
3062
|
setHeading(ang) {
|
|
2987
3063
|
let mag = this.mag();
|
|
2988
3064
|
this.x = mag * this._$.cos(ang);
|
|
2989
3065
|
this.y = mag * this._$.sin(ang);
|
|
2990
3066
|
return this;
|
|
2991
3067
|
}
|
|
3068
|
+
|
|
2992
3069
|
rotate(ang) {
|
|
2993
3070
|
let costh = this._$.cos(ang);
|
|
2994
3071
|
let sinth = this._$.sin(ang);
|
|
@@ -2998,12 +3075,14 @@ Q5.Vector = class {
|
|
|
2998
3075
|
this.y = vy;
|
|
2999
3076
|
return this;
|
|
3000
3077
|
}
|
|
3078
|
+
|
|
3001
3079
|
angleBetween() {
|
|
3002
3080
|
let u = this._arg2v(...arguments);
|
|
3003
3081
|
let o = Q5.Vector.cross(this, u);
|
|
3004
3082
|
let ang = this._$.atan2(o.mag(), this.dot(u));
|
|
3005
3083
|
return ang * Math.sign(o.z || 1);
|
|
3006
3084
|
}
|
|
3085
|
+
|
|
3007
3086
|
lerp() {
|
|
3008
3087
|
let args = [...arguments];
|
|
3009
3088
|
let amt = args.at(-1);
|
|
@@ -3014,6 +3093,7 @@ Q5.Vector = class {
|
|
|
3014
3093
|
this.z += (u.z - this.z) * amt;
|
|
3015
3094
|
return this;
|
|
3016
3095
|
}
|
|
3096
|
+
|
|
3017
3097
|
slerp() {
|
|
3018
3098
|
let args = [...arguments];
|
|
3019
3099
|
let amt = args.at(-1);
|
|
@@ -3052,17 +3132,21 @@ Q5.Vector = class {
|
|
|
3052
3132
|
this.z = this.z * cosMultiplier + ey.z * sinMultiplier;
|
|
3053
3133
|
return this;
|
|
3054
3134
|
}
|
|
3135
|
+
|
|
3055
3136
|
reflect(n) {
|
|
3056
3137
|
n.normalize();
|
|
3057
3138
|
return this.sub(n.mult(2 * this.dot(n)));
|
|
3058
3139
|
}
|
|
3140
|
+
|
|
3059
3141
|
array() {
|
|
3060
3142
|
return [this.x, this.y, this.z];
|
|
3061
3143
|
}
|
|
3144
|
+
|
|
3062
3145
|
equals(u, epsilon) {
|
|
3063
3146
|
epsilon ??= Number.EPSILON || 0;
|
|
3064
3147
|
return Math.abs(u.x - this.x) < epsilon && Math.abs(u.y - this.y) < epsilon && Math.abs(u.z - this.z) < epsilon;
|
|
3065
3148
|
}
|
|
3149
|
+
|
|
3066
3150
|
fromAngle(th, l) {
|
|
3067
3151
|
if (l === undefined) l = 1;
|
|
3068
3152
|
this._cn = l;
|
|
@@ -3072,6 +3156,7 @@ Q5.Vector = class {
|
|
|
3072
3156
|
this.z = 0;
|
|
3073
3157
|
return this;
|
|
3074
3158
|
}
|
|
3159
|
+
|
|
3075
3160
|
fromAngles(th, ph, l) {
|
|
3076
3161
|
if (l === undefined) l = 1;
|
|
3077
3162
|
this._cn = l;
|
|
@@ -3085,18 +3170,22 @@ Q5.Vector = class {
|
|
|
3085
3170
|
this.z = l * sinth * cosph;
|
|
3086
3171
|
return this;
|
|
3087
3172
|
}
|
|
3173
|
+
|
|
3088
3174
|
random2D() {
|
|
3089
3175
|
this._cn = this._cnsq = 1;
|
|
3090
3176
|
return this.fromAngle(Math.random() * Math.PI * 2);
|
|
3091
3177
|
}
|
|
3178
|
+
|
|
3092
3179
|
random3D() {
|
|
3093
3180
|
this._cn = this._cnsq = 1;
|
|
3094
3181
|
return this.fromAngles(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
|
|
3095
3182
|
}
|
|
3183
|
+
|
|
3096
3184
|
toString() {
|
|
3097
3185
|
return `[${this.x}, ${this.y}, ${this.z}]`;
|
|
3098
3186
|
}
|
|
3099
3187
|
};
|
|
3188
|
+
|
|
3100
3189
|
Q5.Vector.add = (v, u) => v.copy().add(u);
|
|
3101
3190
|
Q5.Vector.cross = (v, u) => v.copy().cross(u);
|
|
3102
3191
|
Q5.Vector.dist = (v, u) => Math.hypot(v.x - u.x, v.y - u.y, v.z - u.z);
|
|
@@ -3113,6 +3202,7 @@ Q5.Vector.mult = (v, u) => v.copy().mult(u);
|
|
|
3113
3202
|
Q5.Vector.normalize = (v) => v.copy().normalize();
|
|
3114
3203
|
Q5.Vector.rem = (v, u) => v.copy().rem(u);
|
|
3115
3204
|
Q5.Vector.sub = (v, u) => v.copy().sub(u);
|
|
3205
|
+
|
|
3116
3206
|
for (let k of ['fromAngle', 'fromAngles', 'random2D', 'random3D']) {
|
|
3117
3207
|
Q5.Vector[k] = (u, v, t) => new Q5.Vector()[k](u, v, t);
|
|
3118
3208
|
}
|
|
@@ -3286,7 +3376,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3286
3376
|
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
3287
3377
|
|
|
3288
3378
|
$.resetMatrix = () => {
|
|
3289
|
-
//
|
|
3379
|
+
// initialize the transformation matrix as 4x4 identity matrix
|
|
3290
3380
|
|
|
3291
3381
|
// prettier-ignore
|
|
3292
3382
|
$._matrix = [
|
|
@@ -3310,7 +3400,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3310
3400
|
|
|
3311
3401
|
$.translate = (x, y, z) => {
|
|
3312
3402
|
if (!x && !y && !z) return;
|
|
3313
|
-
//
|
|
3403
|
+
// update the translation values
|
|
3314
3404
|
$._matrix[12] += x;
|
|
3315
3405
|
$._matrix[13] -= y;
|
|
3316
3406
|
$._matrix[14] += z || 0;
|
|
@@ -3409,34 +3499,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3409
3499
|
else m = args;
|
|
3410
3500
|
|
|
3411
3501
|
if (m.length == 9) {
|
|
3412
|
-
//
|
|
3502
|
+
// convert 3x3 matrix to 4x4 matrix
|
|
3413
3503
|
m = [m[0], m[1], 0, m[2], m[3], m[4], 0, m[5], 0, 0, 1, 0, m[6], m[7], 0, m[8]];
|
|
3414
3504
|
} else if (m.length != 16) {
|
|
3415
3505
|
throw new Error('Matrix must be a 3x3 or 4x4 array.');
|
|
3416
3506
|
}
|
|
3417
3507
|
|
|
3418
|
-
//
|
|
3508
|
+
// overwrite the current transformation matrix
|
|
3419
3509
|
$._matrix = m.slice();
|
|
3420
3510
|
$._matrixDirty = true;
|
|
3421
3511
|
};
|
|
3422
3512
|
|
|
3423
|
-
//
|
|
3513
|
+
// function to save the current matrix state if dirty
|
|
3424
3514
|
$._saveMatrix = () => {
|
|
3425
3515
|
transformStates.push($._matrix.slice());
|
|
3426
3516
|
$._transformIndex = transformStates.length - 1;
|
|
3427
3517
|
$._matrixDirty = false;
|
|
3428
3518
|
};
|
|
3429
3519
|
|
|
3430
|
-
//
|
|
3520
|
+
// push the current matrix index onto the stack
|
|
3431
3521
|
$.pushMatrix = () => {
|
|
3432
3522
|
if ($._matrixDirty) $._saveMatrix();
|
|
3433
3523
|
$._transformIndexStack.push($._transformIndex);
|
|
3434
3524
|
};
|
|
3525
|
+
|
|
3435
3526
|
$.popMatrix = () => {
|
|
3436
3527
|
if (!$._transformIndexStack.length) {
|
|
3437
3528
|
return console.warn('Matrix index stack is empty!');
|
|
3438
3529
|
}
|
|
3439
|
-
//
|
|
3530
|
+
// pop the last matrix index and set it as the current matrix index
|
|
3440
3531
|
let idx = $._transformIndexStack.pop();
|
|
3441
3532
|
$._matrix = transformStates[idx].slice();
|
|
3442
3533
|
$._transformIndex = idx;
|
|
@@ -3447,6 +3538,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3447
3538
|
$.pushMatrix();
|
|
3448
3539
|
$.pushStyles();
|
|
3449
3540
|
};
|
|
3541
|
+
|
|
3450
3542
|
$.pop = () => {
|
|
3451
3543
|
$.popMatrix();
|
|
3452
3544
|
$.popStyles();
|
|
@@ -3498,6 +3590,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3498
3590
|
'max' // 4
|
|
3499
3591
|
];
|
|
3500
3592
|
|
|
3593
|
+
// other blend modes are not supported yet
|
|
3501
3594
|
const blendModes = {
|
|
3502
3595
|
normal: [2, 3, 0, 2, 3, 0],
|
|
3503
3596
|
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
@@ -3528,6 +3621,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3528
3621
|
}
|
|
3529
3622
|
|
|
3530
3623
|
$._blendMode = 'normal';
|
|
3624
|
+
|
|
3531
3625
|
$.blendMode = (mode) => {
|
|
3532
3626
|
if (mode == $._blendMode) return;
|
|
3533
3627
|
if (mode == 'source-over') mode = 'normal';
|
|
@@ -3960,19 +4054,22 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
3960
4054
|
if ($._matrixDirty) $._saveMatrix();
|
|
3961
4055
|
let ti = $._transformIndex,
|
|
3962
4056
|
ci = $._stroke,
|
|
3963
|
-
sw = $._strokeWeight
|
|
3964
|
-
hsw = sw / 2;
|
|
4057
|
+
sw = $._strokeWeight;
|
|
3965
4058
|
|
|
3966
4059
|
if (sw < 2) {
|
|
3967
4060
|
let [l, r, t, b] = $._calcBox(x, y, sw, sw, 'corner');
|
|
3968
4061
|
addRect(l, t, r, t, r, b, l, b, ci, ti);
|
|
3969
4062
|
} else {
|
|
3970
4063
|
let n = getArcSegments(sw);
|
|
3971
|
-
|
|
4064
|
+
sw /= 2;
|
|
4065
|
+
addEllipse(x, y, sw, sw, n, ci, ti);
|
|
3972
4066
|
}
|
|
3973
4067
|
};
|
|
3974
4068
|
|
|
3975
|
-
|
|
4069
|
+
$.stokeJoin = (x) => {
|
|
4070
|
+
$.log("q5 WebGPU doesn't support changing stroke join style.");
|
|
4071
|
+
};
|
|
4072
|
+
|
|
3976
4073
|
$.line = (x1, y1, x2, y2) => {
|
|
3977
4074
|
if ($._matrixDirty) $._saveMatrix();
|
|
3978
4075
|
let ti = $._transformIndex,
|
|
@@ -3980,12 +4077,12 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
3980
4077
|
sw = $._strokeWeight,
|
|
3981
4078
|
hsw = sw / 2;
|
|
3982
4079
|
|
|
3983
|
-
//
|
|
4080
|
+
// calculate the direction vector and length
|
|
3984
4081
|
let dx = x2 - x1,
|
|
3985
4082
|
dy = y2 - y1,
|
|
3986
4083
|
length = Math.hypot(dx, dy);
|
|
3987
4084
|
|
|
3988
|
-
//
|
|
4085
|
+
// calculate the perpendicular vector for line thickness
|
|
3989
4086
|
let px = -(dy / length) * hsw,
|
|
3990
4087
|
py = (dx / length) * hsw;
|
|
3991
4088
|
|
|
@@ -4021,10 +4118,10 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
4021
4118
|
|
|
4022
4119
|
$.endShape = (close) => {
|
|
4023
4120
|
if (curveVertices.length > 0) {
|
|
4024
|
-
//
|
|
4121
|
+
// duplicate start and end points if necessary
|
|
4025
4122
|
let points = [...curveVertices];
|
|
4026
4123
|
if (points.length < 4) {
|
|
4027
|
-
//
|
|
4124
|
+
// duplicate first and last points
|
|
4028
4125
|
while (points.length < 4) {
|
|
4029
4126
|
points.unshift(points[0]);
|
|
4030
4127
|
points.push(points[points.length - 1]);
|
|
@@ -4065,7 +4162,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
4065
4162
|
throw new Error('A shape must have at least 3 vertices.');
|
|
4066
4163
|
}
|
|
4067
4164
|
|
|
4068
|
-
//
|
|
4165
|
+
// close the shape if requested
|
|
4069
4166
|
if (close) {
|
|
4070
4167
|
let firstIndex = 0;
|
|
4071
4168
|
let lastIndex = (shapeVertCount - 1) * 4;
|
|
@@ -4082,7 +4179,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
4082
4179
|
}
|
|
4083
4180
|
|
|
4084
4181
|
if ($._doFill) {
|
|
4085
|
-
//
|
|
4182
|
+
// triangulate the shape
|
|
4086
4183
|
for (let i = 1; i < shapeVertCount - 1; i++) {
|
|
4087
4184
|
let v0 = 0;
|
|
4088
4185
|
let v1 = i * 4;
|
|
@@ -4096,7 +4193,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
4096
4193
|
}
|
|
4097
4194
|
|
|
4098
4195
|
if ($._doStroke) {
|
|
4099
|
-
//
|
|
4196
|
+
// draw lines between vertices
|
|
4100
4197
|
for (let i = 0; i < shapeVertCount - 1; i++) {
|
|
4101
4198
|
let v1 = i * 4;
|
|
4102
4199
|
let v2 = (i + 1) * 4;
|
|
@@ -4109,7 +4206,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
|
4109
4206
|
}
|
|
4110
4207
|
}
|
|
4111
4208
|
|
|
4112
|
-
//
|
|
4209
|
+
// reset for the next shape
|
|
4113
4210
|
shapeVertCount = 0;
|
|
4114
4211
|
sv = [];
|
|
4115
4212
|
curveVertices = [];
|
|
@@ -4324,7 +4421,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4324
4421
|
|
|
4325
4422
|
tIdx = (tIdx + 1) % MAX_TEXTURES;
|
|
4326
4423
|
|
|
4327
|
-
//
|
|
4424
|
+
// if the texture array is full, destroy the oldest texture
|
|
4328
4425
|
if ($._textures[tIdx]) {
|
|
4329
4426
|
$._textures[tIdx].destroy();
|
|
4330
4427
|
delete $._textures[tIdx];
|
|
@@ -4537,6 +4634,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4537
4634
|
mipmapFilter: 'linear',
|
|
4538
4635
|
maxAnisotropy: 16
|
|
4539
4636
|
});
|
|
4637
|
+
|
|
4540
4638
|
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4541
4639
|
label: 'MSDF font group layout',
|
|
4542
4640
|
entries: [
|
|
@@ -4574,6 +4672,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4574
4672
|
primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' },
|
|
4575
4673
|
multisample: { count: 4 }
|
|
4576
4674
|
};
|
|
4675
|
+
|
|
4577
4676
|
$._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
|
|
4578
4677
|
|
|
4579
4678
|
class MsdfFont {
|
|
@@ -4589,8 +4688,9 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4589
4688
|
getChar(charCode) {
|
|
4590
4689
|
return this.chars[charCode] ?? this.defaultChar;
|
|
4591
4690
|
}
|
|
4592
|
-
// Gets the distance in pixels a line should advance for a given
|
|
4593
|
-
// character code
|
|
4691
|
+
// Gets the distance in pixels a line should advance for a given
|
|
4692
|
+
// character code. If the upcoming character code is given any
|
|
4693
|
+
// kerning between the two characters will be taken into account.
|
|
4594
4694
|
getXAdvance(charCode, nextCharCode = -1) {
|
|
4595
4695
|
let char = this.getChar(charCode);
|
|
4596
4696
|
if (nextCharCode >= 0) {
|
|
@@ -4632,8 +4732,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4632
4732
|
});
|
|
4633
4733
|
Q5.device.queue.copyExternalImageToTexture({ source: img }, { texture }, imgSize);
|
|
4634
4734
|
|
|
4635
|
-
//
|
|
4636
|
-
//
|
|
4735
|
+
// chars and kernings can be stored as csv strings, making the file
|
|
4736
|
+
// size smaller, but they need to be parsed into arrays of objects
|
|
4637
4737
|
if (typeof atlas.chars == 'string') {
|
|
4638
4738
|
atlas.chars = $.CSV.parse(atlas.chars, ' ');
|
|
4639
4739
|
atlas.kernings = $.CSV.parse(atlas.kernings, ' ');
|
|
@@ -4722,6 +4822,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4722
4822
|
$.textFont = (fontName) => {
|
|
4723
4823
|
$._font = fonts[fontName];
|
|
4724
4824
|
};
|
|
4825
|
+
|
|
4725
4826
|
$.textSize = (size) => {
|
|
4726
4827
|
$._textSize = size;
|
|
4727
4828
|
if (!leadingSet) {
|
|
@@ -4729,12 +4830,14 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4729
4830
|
leadDiff = leading - size;
|
|
4730
4831
|
}
|
|
4731
4832
|
};
|
|
4833
|
+
|
|
4732
4834
|
$.textLeading = (lineHeight) => {
|
|
4733
4835
|
$._font.lineHeight = leading = lineHeight;
|
|
4734
4836
|
leadDiff = leading - $._textSize;
|
|
4735
4837
|
leadPercent = leading / $._textSize;
|
|
4736
4838
|
leadingSet = true;
|
|
4737
4839
|
};
|
|
4840
|
+
|
|
4738
4841
|
$.textAlign = (horiz, vert) => {
|
|
4739
4842
|
$._textAlign = horiz;
|
|
4740
4843
|
if (vert) $._textBaseline = vert;
|
|
@@ -4756,7 +4859,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4756
4859
|
let charCode = nextCharCode;
|
|
4757
4860
|
nextCharCode = i < text.length - 1 ? text.charCodeAt(i + 1) : -1;
|
|
4758
4861
|
switch (charCode) {
|
|
4759
|
-
case 10: //
|
|
4862
|
+
case 10: // newline
|
|
4760
4863
|
lineWidths.push(offsetX);
|
|
4761
4864
|
line++;
|
|
4762
4865
|
maxWidth = Math.max(maxWidth, offsetX);
|
|
@@ -4765,11 +4868,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4765
4868
|
break;
|
|
4766
4869
|
case 13: // CR
|
|
4767
4870
|
break;
|
|
4768
|
-
case 32: //
|
|
4871
|
+
case 32: // space
|
|
4769
4872
|
// advance the offset without actually adding a character
|
|
4770
4873
|
offsetX += font.getXAdvance(charCode);
|
|
4771
4874
|
break;
|
|
4772
|
-
case 9: //
|
|
4875
|
+
case 9: // tab
|
|
4773
4876
|
offsetX += font.getXAdvance(charCode) * 2;
|
|
4774
4877
|
break;
|
|
4775
4878
|
default:
|
|
@@ -4794,7 +4897,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4794
4897
|
|
|
4795
4898
|
$.text = (str, x, y, w, h) => {
|
|
4796
4899
|
if (!$._font) {
|
|
4797
|
-
// check if online and loading the default font
|
|
4900
|
+
// check if online and loading the default font
|
|
4901
|
+
// hasn't been attempted yet
|
|
4798
4902
|
if (navigator.onLine && !initLoadDefaultFont) {
|
|
4799
4903
|
initLoadDefaultFont = true;
|
|
4800
4904
|
$.loadFont('https://q5js.org/fonts/YaHei-msdf.json');
|
|
@@ -4953,27 +5057,27 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4953
5057
|
$._hooks.preRender.push(() => {
|
|
4954
5058
|
if (!$._charStack.length) return;
|
|
4955
5059
|
|
|
4956
|
-
//
|
|
5060
|
+
// calculate total buffer size for text data
|
|
4957
5061
|
let totalTextSize = 0;
|
|
4958
5062
|
for (let charsData of $._charStack) {
|
|
4959
5063
|
totalTextSize += charsData.length * 4;
|
|
4960
5064
|
}
|
|
4961
5065
|
|
|
4962
|
-
//
|
|
5066
|
+
// create a single buffer for all the char data
|
|
4963
5067
|
let charBuffer = Q5.device.createBuffer({
|
|
4964
5068
|
size: totalTextSize,
|
|
4965
5069
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
4966
5070
|
mappedAtCreation: true
|
|
4967
5071
|
});
|
|
4968
5072
|
|
|
4969
|
-
//
|
|
5073
|
+
// copy all the text data into the buffer
|
|
4970
5074
|
new Float32Array(charBuffer.getMappedRange()).set($._charStack.flat());
|
|
4971
5075
|
charBuffer.unmap();
|
|
4972
5076
|
|
|
4973
|
-
//
|
|
5077
|
+
// calculate total buffer size for metadata
|
|
4974
5078
|
let totalMetadataSize = $._textStack.length * 6 * 4;
|
|
4975
5079
|
|
|
4976
|
-
//
|
|
5080
|
+
// create a single buffer for all metadata
|
|
4977
5081
|
let textBuffer = Q5.device.createBuffer({
|
|
4978
5082
|
label: 'textBuffer',
|
|
4979
5083
|
size: totalMetadataSize,
|
|
@@ -4981,11 +5085,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4981
5085
|
mappedAtCreation: true
|
|
4982
5086
|
});
|
|
4983
5087
|
|
|
4984
|
-
//
|
|
5088
|
+
// copy all metadata into the buffer
|
|
4985
5089
|
new Float32Array(textBuffer.getMappedRange()).set($._textStack.flat());
|
|
4986
5090
|
textBuffer.unmap();
|
|
4987
5091
|
|
|
4988
|
-
//
|
|
5092
|
+
// create a single bind group for the text buffer and metadata buffer
|
|
4989
5093
|
$._textBindGroup = Q5.device.createBindGroup({
|
|
4990
5094
|
label: 'msdf text bind group',
|
|
4991
5095
|
layout: textBindGroupLayout,
|