q5 2.14.3 → 2.14.5
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/README.md +1 -1
- package/package.json +1 -1
- package/q5.js +190 -75
- package/q5.min.js +1 -1
- package/q5js_brand.webp +0 -0
- package/q5js_icon.png +0 -0
- package/src/q5-2d-canvas.js +0 -202
- package/src/q5-2d-drawing.js +0 -398
- package/src/q5-2d-image.js +0 -330
- package/src/q5-2d-soft-filters.js +0 -145
- package/src/q5-2d-text.js +0 -279
- package/src/q5-ai.js +0 -65
- package/src/q5-canvas.js +0 -367
- package/src/q5-color.js +0 -322
- package/src/q5-core.js +0 -321
- package/src/q5-display.js +0 -101
- package/src/q5-dom.js +0 -2
- package/src/q5-input.js +0 -215
- package/src/q5-math.js +0 -423
- package/src/q5-noisier.js +0 -264
- package/src/q5-record.js +0 -366
- package/src/q5-sensors.js +0 -98
- package/src/q5-sound.js +0 -64
- package/src/q5-util.js +0 -50
- package/src/q5-vector.js +0 -305
- package/src/q5-webgpu-canvas.js +0 -556
- package/src/q5-webgpu-drawing.js +0 -525
- package/src/q5-webgpu-image.js +0 -268
- package/src/q5-webgpu-text.js +0 -594
- package/src/readme.md +0 -248
package/src/q5-2d-text.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
Q5.renderers.q2d.text = ($, q) => {
|
|
2
|
-
$._textAlign = 'left';
|
|
3
|
-
$._textBaseline = 'alphabetic';
|
|
4
|
-
$._textSize = 12;
|
|
5
|
-
|
|
6
|
-
let font = 'sans-serif',
|
|
7
|
-
leadingSet = false,
|
|
8
|
-
leading = 15,
|
|
9
|
-
leadDiff = 3,
|
|
10
|
-
emphasis = 'normal',
|
|
11
|
-
fontMod = false,
|
|
12
|
-
styleHash = 0,
|
|
13
|
-
styleHashes = [],
|
|
14
|
-
useCache = false,
|
|
15
|
-
genTextImage = false,
|
|
16
|
-
cacheSize = 0,
|
|
17
|
-
cacheMax = 12000;
|
|
18
|
-
|
|
19
|
-
let cache = ($._textCache = {});
|
|
20
|
-
|
|
21
|
-
$.loadFont = (url, cb) => {
|
|
22
|
-
q._preloadCount++;
|
|
23
|
-
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
24
|
-
let f = new FontFace(name, `url(${url})`);
|
|
25
|
-
document.fonts.add(f);
|
|
26
|
-
f.load().then(() => {
|
|
27
|
-
q._preloadCount--;
|
|
28
|
-
if (cb) cb(name);
|
|
29
|
-
});
|
|
30
|
-
$.textFont(name);
|
|
31
|
-
return name;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
$.textFont = (x) => {
|
|
35
|
-
if (!x || x == font) return font;
|
|
36
|
-
font = x;
|
|
37
|
-
fontMod = true;
|
|
38
|
-
styleHash = -1;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
$.textSize = (x) => {
|
|
42
|
-
if (x == undefined || x == $._textSize) return $._textSize;
|
|
43
|
-
if ($._da) x *= $._da;
|
|
44
|
-
$._textSize = x;
|
|
45
|
-
fontMod = true;
|
|
46
|
-
styleHash = -1;
|
|
47
|
-
if (!leadingSet) {
|
|
48
|
-
leading = x * 1.25;
|
|
49
|
-
leadDiff = leading - x;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
$.textStyle = (x) => {
|
|
54
|
-
if (!x || x == emphasis) return emphasis;
|
|
55
|
-
emphasis = x;
|
|
56
|
-
fontMod = true;
|
|
57
|
-
styleHash = -1;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
$.textLeading = (x) => {
|
|
61
|
-
if (x == undefined) return leading || $._textSize * 1.25;
|
|
62
|
-
leadingSet = true;
|
|
63
|
-
if (x == leading) return leading;
|
|
64
|
-
if ($._da) x *= $._da;
|
|
65
|
-
leading = x;
|
|
66
|
-
leadDiff = x - $._textSize;
|
|
67
|
-
styleHash = -1;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
$.textAlign = (horiz, vert) => {
|
|
71
|
-
$.ctx.textAlign = $._textAlign = horiz;
|
|
72
|
-
if (vert) {
|
|
73
|
-
$.ctx.textBaseline = $._textBaseline = vert == $.CENTER ? 'middle' : vert;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const updateFont = () => {
|
|
78
|
-
$.ctx.font = `${emphasis} ${$._textSize}px ${font}`;
|
|
79
|
-
fontMod = false;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
$.textWidth = (str) => {
|
|
83
|
-
if (fontMod) updateFont();
|
|
84
|
-
return $.ctx.measureText(str).width;
|
|
85
|
-
};
|
|
86
|
-
$.textAscent = (str) => {
|
|
87
|
-
if (fontMod) updateFont();
|
|
88
|
-
return $.ctx.measureText(str).actualBoundingBoxAscent;
|
|
89
|
-
};
|
|
90
|
-
$.textDescent = (str) => {
|
|
91
|
-
if (fontMod) updateFont();
|
|
92
|
-
return $.ctx.measureText(str).actualBoundingBoxDescent;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
$.textFill = $.fill;
|
|
96
|
-
$.textStroke = $.stroke;
|
|
97
|
-
|
|
98
|
-
let updateStyleHash = () => {
|
|
99
|
-
let styleString = font + $._textSize + emphasis + leading;
|
|
100
|
-
|
|
101
|
-
let hash = 5381;
|
|
102
|
-
for (let i = 0; i < styleString.length; i++) {
|
|
103
|
-
hash = (hash * 33) ^ styleString.charCodeAt(i);
|
|
104
|
-
}
|
|
105
|
-
styleHash = hash >>> 0;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
$.textCache = (enable, maxSize) => {
|
|
109
|
-
if (maxSize) cacheMax = maxSize;
|
|
110
|
-
if (enable !== undefined) useCache = enable;
|
|
111
|
-
return useCache;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
$.createTextImage = (str, w, h) => {
|
|
115
|
-
genTextImage = true;
|
|
116
|
-
img = $.text(str, 0, 0, w, h);
|
|
117
|
-
genTextImage = false;
|
|
118
|
-
return img;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
let lines = [];
|
|
122
|
-
|
|
123
|
-
$.text = (str, x, y, w, h) => {
|
|
124
|
-
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
125
|
-
str = str.toString();
|
|
126
|
-
if ($._da) {
|
|
127
|
-
x *= $._da;
|
|
128
|
-
y *= $._da;
|
|
129
|
-
}
|
|
130
|
-
let ctx = $.ctx;
|
|
131
|
-
let img, tX, tY;
|
|
132
|
-
|
|
133
|
-
if (fontMod) {
|
|
134
|
-
ctx.font = `${emphasis} ${$._textSize}px ${font}`;
|
|
135
|
-
fontMod = false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (useCache || genTextImage) {
|
|
139
|
-
if (styleHash == -1) updateStyleHash();
|
|
140
|
-
|
|
141
|
-
img = cache[str];
|
|
142
|
-
if (img) img = img[styleHash];
|
|
143
|
-
|
|
144
|
-
if (img) {
|
|
145
|
-
if (img._fill == $._fill && img._stroke == $._stroke && img._strokeWeight == $._strokeWeight) {
|
|
146
|
-
if (genTextImage) return img;
|
|
147
|
-
return $.textImage(img, x, y);
|
|
148
|
-
} else img.clear();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (str.indexOf('\n') == -1) lines[0] = str;
|
|
153
|
-
else lines = str.split('\n');
|
|
154
|
-
|
|
155
|
-
if (str.length > w) {
|
|
156
|
-
let wrapped = [];
|
|
157
|
-
for (let line of lines) {
|
|
158
|
-
let i = 0;
|
|
159
|
-
|
|
160
|
-
while (i < line.length) {
|
|
161
|
-
let max = i + w;
|
|
162
|
-
if (max >= line.length) {
|
|
163
|
-
wrapped.push(line.slice(i));
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
let end = line.lastIndexOf(' ', max);
|
|
167
|
-
if (end === -1 || end < i) end = max;
|
|
168
|
-
wrapped.push(line.slice(i, end));
|
|
169
|
-
i = end + 1;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
lines = wrapped;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!useCache && !genTextImage) {
|
|
176
|
-
tX = x;
|
|
177
|
-
tY = y;
|
|
178
|
-
} else {
|
|
179
|
-
tX = 0;
|
|
180
|
-
tY = leading * lines.length;
|
|
181
|
-
|
|
182
|
-
if (!img) {
|
|
183
|
-
let measure = ctx.measureText(' ');
|
|
184
|
-
let ascent = measure.fontBoundingBoxAscent;
|
|
185
|
-
let descent = measure.fontBoundingBoxDescent;
|
|
186
|
-
|
|
187
|
-
img = $.createImage.call($, Math.ceil(ctx.measureText(str).width), Math.ceil(tY + descent), {
|
|
188
|
-
pixelDensity: $._pixelDensity
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
img._ascent = ascent;
|
|
192
|
-
img._descent = descent;
|
|
193
|
-
img._top = descent + leadDiff;
|
|
194
|
-
img._middle = img._top + ascent * 0.5;
|
|
195
|
-
img._bottom = img._top + ascent;
|
|
196
|
-
img._leading = leading;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
img._fill = $._fill;
|
|
200
|
-
img._stroke = $._stroke;
|
|
201
|
-
img._strokeWeight = $._strokeWeight;
|
|
202
|
-
img.modified = true;
|
|
203
|
-
|
|
204
|
-
ctx = img.ctx;
|
|
205
|
-
|
|
206
|
-
ctx.font = $.ctx.font;
|
|
207
|
-
ctx.fillStyle = $._fill;
|
|
208
|
-
ctx.strokeStyle = $._stroke;
|
|
209
|
-
ctx.lineWidth = $.ctx.lineWidth;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
let ogFill;
|
|
213
|
-
if (!$._fillSet) {
|
|
214
|
-
ogFill = ctx.fillStyle;
|
|
215
|
-
ctx.fillStyle = 'black';
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
let lineAmount = 0;
|
|
219
|
-
for (let line of lines) {
|
|
220
|
-
if ($._doStroke && $._strokeSet) ctx.strokeText(line, tX, tY);
|
|
221
|
-
if ($._doFill) ctx.fillText(line, tX, tY);
|
|
222
|
-
tY += leading;
|
|
223
|
-
lineAmount++;
|
|
224
|
-
if (lineAmount >= h) break;
|
|
225
|
-
}
|
|
226
|
-
lines = [];
|
|
227
|
-
|
|
228
|
-
if (!$._fillSet) ctx.fillStyle = ogFill;
|
|
229
|
-
|
|
230
|
-
if (useCache || genTextImage) {
|
|
231
|
-
styleHashes.push(styleHash);
|
|
232
|
-
(cache[str] ??= {})[styleHash] = img;
|
|
233
|
-
|
|
234
|
-
cacheSize++;
|
|
235
|
-
if (cacheSize > cacheMax) {
|
|
236
|
-
let half = Math.ceil(cacheSize / 2);
|
|
237
|
-
let hashes = styleHashes.splice(0, half);
|
|
238
|
-
for (let s in cache) {
|
|
239
|
-
s = cache[s];
|
|
240
|
-
for (let h of hashes) delete s[h];
|
|
241
|
-
}
|
|
242
|
-
cacheSize -= half;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (genTextImage) return img;
|
|
246
|
-
$.textImage(img, x, y);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
$.textImage = (img, x, y) => {
|
|
251
|
-
if (typeof img == 'string') img = $.createTextImage(img);
|
|
252
|
-
|
|
253
|
-
let og = $._imageMode;
|
|
254
|
-
$._imageMode = 'corner';
|
|
255
|
-
|
|
256
|
-
let ta = $._textAlign;
|
|
257
|
-
if (ta == 'center') x -= img.canvas.hw;
|
|
258
|
-
else if (ta == 'right') x -= img.width;
|
|
259
|
-
|
|
260
|
-
let bl = $._textBaseline;
|
|
261
|
-
if (bl == 'alphabetic') y -= img._leading;
|
|
262
|
-
else if (bl == 'middle') y -= img._middle;
|
|
263
|
-
else if (bl == 'bottom') y -= img._bottom;
|
|
264
|
-
else if (bl == 'top') y -= img._top;
|
|
265
|
-
|
|
266
|
-
$.image(img, x, y);
|
|
267
|
-
$._imageMode = og;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
$.nf = (n, l, r) => {
|
|
271
|
-
let neg = n < 0;
|
|
272
|
-
n = Math.abs(n);
|
|
273
|
-
let parts = n.toFixed(r).split('.');
|
|
274
|
-
parts[0] = parts[0].padStart(l, '0');
|
|
275
|
-
let s = parts.join('.');
|
|
276
|
-
if (neg) s = '-' + s;
|
|
277
|
-
return s;
|
|
278
|
-
};
|
|
279
|
-
};
|
package/src/q5-ai.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
Q5.modules.ai = ($) => {
|
|
2
|
-
$.askAI = (question = '') => {
|
|
3
|
-
Q5.disableFriendlyErrors = false;
|
|
4
|
-
throw Error('Ask AI ✨ ' + question);
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
$._askAI = async (e) => {
|
|
8
|
-
let askAI = e.message?.includes('Ask AI ✨');
|
|
9
|
-
let stackLines = e.stack?.split('\n');
|
|
10
|
-
if (!e.stack || stackLines.length <= 1) return;
|
|
11
|
-
|
|
12
|
-
let idx = 1;
|
|
13
|
-
let sep = '(';
|
|
14
|
-
if (navigator.userAgent.indexOf('Chrome') == -1) {
|
|
15
|
-
idx = 0;
|
|
16
|
-
sep = '@';
|
|
17
|
-
}
|
|
18
|
-
while (stackLines[idx].indexOf('q5') >= 0) idx++;
|
|
19
|
-
|
|
20
|
-
let errFile = stackLines[idx].split(sep).at(-1);
|
|
21
|
-
if (errFile.startsWith('blob:')) errFile = errFile.slice(5);
|
|
22
|
-
let parts = errFile.split(':');
|
|
23
|
-
let lineNum = parseInt(parts.at(-2));
|
|
24
|
-
if (askAI) lineNum++;
|
|
25
|
-
parts[3] = parts[3].split(')')[0];
|
|
26
|
-
let fileUrl = parts.slice(0, 2).join(':');
|
|
27
|
-
let fileBase = fileUrl.split('/').at(-1);
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
let res = await (await fetch(fileUrl)).text();
|
|
31
|
-
let lines = res.split('\n');
|
|
32
|
-
let errLine = lines[lineNum - 1].trim();
|
|
33
|
-
|
|
34
|
-
let context = '';
|
|
35
|
-
let i = 1;
|
|
36
|
-
while (context.length < 1600) {
|
|
37
|
-
if (lineNum - i >= 0) {
|
|
38
|
-
context = lines[lineNum - i].trim() + '\n' + context;
|
|
39
|
-
}
|
|
40
|
-
if (lineNum + i < lines.length) {
|
|
41
|
-
context += lines[lineNum + i].trim() + '\n';
|
|
42
|
-
} else break;
|
|
43
|
-
i++;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let question =
|
|
47
|
-
askAI && e.message.length > 10 ? e.message.slice(10) : 'Whats+wrong+with+this+line%3F+short+answer';
|
|
48
|
-
|
|
49
|
-
let url =
|
|
50
|
-
'https://chatgpt.com/?q=q5.js+' +
|
|
51
|
-
question +
|
|
52
|
-
(askAI ? '' : '%0A%0A' + encodeURIComponent(e.name + ': ' + e.message)) +
|
|
53
|
-
'%0A%0ALine%3A+' +
|
|
54
|
-
encodeURIComponent(errLine) +
|
|
55
|
-
'%0A%0AExcerpt+for+context%3A%0A%0A' +
|
|
56
|
-
encodeURIComponent(context);
|
|
57
|
-
|
|
58
|
-
console.warn('Error in ' + fileBase + ' on line ' + lineNum + ':\n\n' + errLine);
|
|
59
|
-
|
|
60
|
-
console.warn('Ask AI ✨ ' + url);
|
|
61
|
-
|
|
62
|
-
if (askAI) return window.open(url, '_blank');
|
|
63
|
-
} catch (err) {}
|
|
64
|
-
};
|
|
65
|
-
};
|
package/src/q5-canvas.js
DELETED
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
Q5.modules.canvas = ($, q) => {
|
|
2
|
-
$._OffscreenCanvas =
|
|
3
|
-
window.OffscreenCanvas ||
|
|
4
|
-
function () {
|
|
5
|
-
return document.createElement('canvas');
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
if (Q5._server) {
|
|
9
|
-
if (Q5._createServerCanvas) {
|
|
10
|
-
q.canvas = Q5._createServerCanvas(100, 100);
|
|
11
|
-
}
|
|
12
|
-
} else if ($._scope == 'image' || $._scope == 'graphics') {
|
|
13
|
-
q.canvas = new $._OffscreenCanvas(100, 100);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!$.canvas) {
|
|
17
|
-
if (typeof document == 'object') {
|
|
18
|
-
q.canvas = document.createElement('canvas');
|
|
19
|
-
$.canvas.id = 'q5Canvas' + Q5._instanceCount;
|
|
20
|
-
$.canvas.classList.add('q5Canvas');
|
|
21
|
-
} else $.noCanvas();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let c = $.canvas;
|
|
25
|
-
$.width = 200;
|
|
26
|
-
$.height = 200;
|
|
27
|
-
$._pixelDensity = 1;
|
|
28
|
-
|
|
29
|
-
$.displayDensity = () => window.devicePixelRatio || 1;
|
|
30
|
-
|
|
31
|
-
if (c) {
|
|
32
|
-
c.width = 200;
|
|
33
|
-
c.height = 200;
|
|
34
|
-
if ($._scope != 'image') {
|
|
35
|
-
c.renderer = $._renderer;
|
|
36
|
-
c[$._renderer] = true;
|
|
37
|
-
|
|
38
|
-
$._pixelDensity = Math.ceil($.displayDensity());
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
$._adjustDisplay = () => {
|
|
43
|
-
if (c.style) {
|
|
44
|
-
c.style.width = c.w + 'px';
|
|
45
|
-
c.style.height = c.h + 'px';
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
$.createCanvas = function (w, h, options) {
|
|
50
|
-
if (typeof w == 'object') {
|
|
51
|
-
options = w;
|
|
52
|
-
w = null;
|
|
53
|
-
}
|
|
54
|
-
options ??= arguments[3];
|
|
55
|
-
|
|
56
|
-
let opt = Object.assign({}, Q5.canvasOptions);
|
|
57
|
-
if (typeof options == 'object') Object.assign(opt, options);
|
|
58
|
-
|
|
59
|
-
if ($._scope != 'image') {
|
|
60
|
-
if ($._scope == 'graphics') $._pixelDensity = this._pixelDensity;
|
|
61
|
-
else if (window.IntersectionObserver) {
|
|
62
|
-
let wasObserved = false;
|
|
63
|
-
new IntersectionObserver((e) => {
|
|
64
|
-
c.visible = e[0].isIntersecting;
|
|
65
|
-
if (!wasObserved) {
|
|
66
|
-
$._wasLooping = $._loop;
|
|
67
|
-
wasObserved = true;
|
|
68
|
-
}
|
|
69
|
-
if (c.visible) {
|
|
70
|
-
if ($._wasLooping && !$._loop) $.loop();
|
|
71
|
-
} else {
|
|
72
|
-
$._wasLooping = $._loop;
|
|
73
|
-
$.noLoop();
|
|
74
|
-
}
|
|
75
|
-
}).observe(c);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
$._setCanvasSize(w, h);
|
|
80
|
-
|
|
81
|
-
Object.assign(c, opt);
|
|
82
|
-
let rend = $._createCanvas(c.w, c.h, opt);
|
|
83
|
-
|
|
84
|
-
if ($._hooks) {
|
|
85
|
-
for (let m of $._hooks.postCanvas) m();
|
|
86
|
-
}
|
|
87
|
-
if ($._beginRender) $._beginRender();
|
|
88
|
-
|
|
89
|
-
return rend;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
$.createGraphics = function (w, h, opt) {
|
|
93
|
-
let g = new Q5('graphics');
|
|
94
|
-
opt ??= {};
|
|
95
|
-
opt.alpha ??= true;
|
|
96
|
-
opt.colorSpace ??= $.canvas.colorSpace;
|
|
97
|
-
g.createCanvas.call($, w, h, opt);
|
|
98
|
-
g.defaultWidth = w;
|
|
99
|
-
g.defaultHeight = h;
|
|
100
|
-
return g;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
async function saveFile(data, name, ext) {
|
|
104
|
-
name = name || 'untitled';
|
|
105
|
-
ext = ext || 'png';
|
|
106
|
-
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
107
|
-
if (data instanceof OffscreenCanvas) {
|
|
108
|
-
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
109
|
-
data = await new Promise((resolve) => {
|
|
110
|
-
const reader = new FileReader();
|
|
111
|
-
reader.onloadend = () => resolve(reader.result);
|
|
112
|
-
reader.readAsDataURL(blob);
|
|
113
|
-
});
|
|
114
|
-
} else {
|
|
115
|
-
data = data.toDataURL('image/' + ext);
|
|
116
|
-
}
|
|
117
|
-
} else {
|
|
118
|
-
let type = 'text/plain';
|
|
119
|
-
if (ext == 'json') {
|
|
120
|
-
if (typeof data != 'string') data = JSON.stringify(data);
|
|
121
|
-
type = 'text/json';
|
|
122
|
-
}
|
|
123
|
-
data = new Blob([data], { type });
|
|
124
|
-
data = URL.createObjectURL(data);
|
|
125
|
-
}
|
|
126
|
-
let a = document.createElement('a');
|
|
127
|
-
a.href = data;
|
|
128
|
-
a.download = name + '.' + ext;
|
|
129
|
-
a.click();
|
|
130
|
-
URL.revokeObjectURL(a.href);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
$.save = (a, b, c) => {
|
|
134
|
-
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
135
|
-
c = b;
|
|
136
|
-
b = a;
|
|
137
|
-
a = $.canvas;
|
|
138
|
-
}
|
|
139
|
-
if (c) return saveFile(a, b, c);
|
|
140
|
-
if (b) {
|
|
141
|
-
b = b.split('.');
|
|
142
|
-
saveFile(a, b[0], b.at(-1));
|
|
143
|
-
} else saveFile(a);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
$._setCanvasSize = (w, h) => {
|
|
147
|
-
w ??= window.innerWidth;
|
|
148
|
-
h ??= window.innerHeight;
|
|
149
|
-
$.defaultWidth = c.w = w = Math.ceil(w);
|
|
150
|
-
$.defaultHeight = c.h = h = Math.ceil(h);
|
|
151
|
-
c.hw = w / 2;
|
|
152
|
-
c.hh = h / 2;
|
|
153
|
-
|
|
154
|
-
// changes the actual size of the canvas
|
|
155
|
-
c.width = Math.ceil(w * $._pixelDensity);
|
|
156
|
-
c.height = Math.ceil(h * $._pixelDensity);
|
|
157
|
-
|
|
158
|
-
if (!$._da) {
|
|
159
|
-
q.width = w;
|
|
160
|
-
q.height = h;
|
|
161
|
-
} else $.flexibleCanvas($._dau);
|
|
162
|
-
|
|
163
|
-
if ($.displayMode && !c.displayMode) $.displayMode();
|
|
164
|
-
else $._adjustDisplay();
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
$._setImageSize = (w, h) => {
|
|
168
|
-
q.width = c.w = w;
|
|
169
|
-
q.height = c.h = h;
|
|
170
|
-
c.hw = w / 2;
|
|
171
|
-
c.hh = h / 2;
|
|
172
|
-
|
|
173
|
-
// changes the actual size of the canvas
|
|
174
|
-
c.width = Math.ceil(w * $._pixelDensity);
|
|
175
|
-
c.height = Math.ceil(h * $._pixelDensity);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
$.defaultImageScale = (scale) => {
|
|
179
|
-
if (!scale) return $._defaultImageScale;
|
|
180
|
-
return ($._defaultImageScale = scale);
|
|
181
|
-
};
|
|
182
|
-
$.defaultImageScale(0.5);
|
|
183
|
-
|
|
184
|
-
if ($._scope == 'image') return;
|
|
185
|
-
|
|
186
|
-
if (c && $._scope != 'graphics') {
|
|
187
|
-
c.parent = (el) => {
|
|
188
|
-
if (c.parentElement) c.parentElement.removeChild(c);
|
|
189
|
-
|
|
190
|
-
if (typeof el == 'string') el = document.getElementById(el);
|
|
191
|
-
el.append(c);
|
|
192
|
-
|
|
193
|
-
function parentResized() {
|
|
194
|
-
if ($.frameCount > 1) {
|
|
195
|
-
$._didResize = true;
|
|
196
|
-
$._adjustDisplay();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if (typeof ResizeObserver == 'function') {
|
|
200
|
-
if ($._ro) $._ro.disconnect();
|
|
201
|
-
$._ro = new ResizeObserver(parentResized);
|
|
202
|
-
$._ro.observe(el);
|
|
203
|
-
} else if (!$.frameCount) {
|
|
204
|
-
window.addEventListener('resize', parentResized);
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
function addCanvas() {
|
|
209
|
-
let el = $._parent;
|
|
210
|
-
el ??= document.getElementsByTagName('main')[0];
|
|
211
|
-
if (!el) {
|
|
212
|
-
el = document.createElement('main');
|
|
213
|
-
document.body.append(el);
|
|
214
|
-
}
|
|
215
|
-
c.parent(el);
|
|
216
|
-
}
|
|
217
|
-
if (document.body) addCanvas();
|
|
218
|
-
else document.addEventListener('DOMContentLoaded', addCanvas);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
$.resizeCanvas = (w, h) => {
|
|
222
|
-
if (!$.ctx) return $.createCanvas(w, h);
|
|
223
|
-
if (w == c.w && h == c.h) return;
|
|
224
|
-
|
|
225
|
-
$._resizeCanvas(w, h);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
if (c && !Q5._createServerCanvas) {
|
|
229
|
-
c.resize = $.resizeCanvas;
|
|
230
|
-
c.save = $.saveCanvas = $.save;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
$.pixelDensity = (v) => {
|
|
234
|
-
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
235
|
-
$._pixelDensity = v;
|
|
236
|
-
$._setCanvasSize(c.w, c.h);
|
|
237
|
-
return v;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
$.flexibleCanvas = (unit = 400) => {
|
|
241
|
-
if (unit) {
|
|
242
|
-
$._da = c.width / (unit * $._pixelDensity);
|
|
243
|
-
q.width = $._dau = unit;
|
|
244
|
-
q.height = (c.h / c.w) * unit;
|
|
245
|
-
} else $._da = 0;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
$._styleNames = [
|
|
249
|
-
'_fill',
|
|
250
|
-
'_stroke',
|
|
251
|
-
'_strokeWeight',
|
|
252
|
-
'_doStroke',
|
|
253
|
-
'_doFill',
|
|
254
|
-
'_strokeSet',
|
|
255
|
-
'_fillSet',
|
|
256
|
-
'_shadow',
|
|
257
|
-
'_doShadow',
|
|
258
|
-
'_shadowOffsetX',
|
|
259
|
-
'_shadowOffsetY',
|
|
260
|
-
'_shadowBlur',
|
|
261
|
-
'_tint',
|
|
262
|
-
'_imageMode',
|
|
263
|
-
'_rectMode',
|
|
264
|
-
'_ellipseMode',
|
|
265
|
-
'_textSize',
|
|
266
|
-
'_textAlign',
|
|
267
|
-
'_textBaseline'
|
|
268
|
-
];
|
|
269
|
-
$._styles = [];
|
|
270
|
-
|
|
271
|
-
$.pushStyles = () => {
|
|
272
|
-
let styles = {};
|
|
273
|
-
for (let s of $._styleNames) styles[s] = $[s];
|
|
274
|
-
$._styles.push(styles);
|
|
275
|
-
};
|
|
276
|
-
$.popStyles = () => {
|
|
277
|
-
let styles = $._styles.pop();
|
|
278
|
-
for (let s of $._styleNames) $[s] = styles[s];
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
if (window && $._scope != 'graphics') {
|
|
282
|
-
window.addEventListener('resize', () => {
|
|
283
|
-
$._didResize = true;
|
|
284
|
-
q.windowWidth = window.innerWidth;
|
|
285
|
-
q.windowHeight = window.innerHeight;
|
|
286
|
-
q.deviceOrientation = window.screen?.orientation?.type;
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
Q5.CENTER = 'center';
|
|
292
|
-
Q5.LEFT = 'left';
|
|
293
|
-
Q5.RIGHT = 'right';
|
|
294
|
-
Q5.TOP = 'top';
|
|
295
|
-
Q5.BOTTOM = 'bottom';
|
|
296
|
-
|
|
297
|
-
Q5.BASELINE = 'alphabetic';
|
|
298
|
-
Q5.MIDDLE = 'middle';
|
|
299
|
-
|
|
300
|
-
Q5.NORMAL = 'normal';
|
|
301
|
-
Q5.ITALIC = 'italic';
|
|
302
|
-
Q5.BOLD = 'bold';
|
|
303
|
-
Q5.BOLDITALIC = 'italic bold';
|
|
304
|
-
|
|
305
|
-
Q5.ROUND = 'round';
|
|
306
|
-
Q5.SQUARE = 'butt';
|
|
307
|
-
Q5.PROJECT = 'square';
|
|
308
|
-
Q5.MITER = 'miter';
|
|
309
|
-
Q5.BEVEL = 'bevel';
|
|
310
|
-
|
|
311
|
-
Q5.CHORD_OPEN = 0;
|
|
312
|
-
Q5.PIE_OPEN = 1;
|
|
313
|
-
Q5.PIE = 2;
|
|
314
|
-
Q5.CHORD = 3;
|
|
315
|
-
|
|
316
|
-
Q5.RADIUS = 'radius';
|
|
317
|
-
Q5.CORNER = 'corner';
|
|
318
|
-
Q5.CORNERS = 'corners';
|
|
319
|
-
|
|
320
|
-
Q5.OPEN = 0;
|
|
321
|
-
Q5.CLOSE = 1;
|
|
322
|
-
|
|
323
|
-
Q5.LANDSCAPE = 'landscape';
|
|
324
|
-
Q5.PORTRAIT = 'portrait';
|
|
325
|
-
|
|
326
|
-
Q5.BLEND = 'source-over';
|
|
327
|
-
Q5.REMOVE = 'destination-out';
|
|
328
|
-
Q5.ADD = 'lighter';
|
|
329
|
-
Q5.DARKEST = 'darken';
|
|
330
|
-
Q5.LIGHTEST = 'lighten';
|
|
331
|
-
Q5.DIFFERENCE = 'difference';
|
|
332
|
-
Q5.SUBTRACT = 'subtract';
|
|
333
|
-
Q5.EXCLUSION = 'exclusion';
|
|
334
|
-
Q5.MULTIPLY = 'multiply';
|
|
335
|
-
Q5.SCREEN = 'screen';
|
|
336
|
-
Q5.REPLACE = 'copy';
|
|
337
|
-
Q5.OVERLAY = 'overlay';
|
|
338
|
-
Q5.HARD_LIGHT = 'hard-light';
|
|
339
|
-
Q5.SOFT_LIGHT = 'soft-light';
|
|
340
|
-
Q5.DODGE = 'color-dodge';
|
|
341
|
-
Q5.BURN = 'color-burn';
|
|
342
|
-
|
|
343
|
-
Q5.THRESHOLD = 1;
|
|
344
|
-
Q5.GRAY = 2;
|
|
345
|
-
Q5.OPAQUE = 3;
|
|
346
|
-
Q5.INVERT = 4;
|
|
347
|
-
Q5.POSTERIZE = 5;
|
|
348
|
-
Q5.DILATE = 6;
|
|
349
|
-
Q5.ERODE = 7;
|
|
350
|
-
Q5.BLUR = 8;
|
|
351
|
-
Q5.SEPIA = 9;
|
|
352
|
-
Q5.BRIGHTNESS = 10;
|
|
353
|
-
Q5.SATURATION = 11;
|
|
354
|
-
Q5.CONTRAST = 12;
|
|
355
|
-
Q5.HUE_ROTATE = 13;
|
|
356
|
-
|
|
357
|
-
Q5.P2D = '2d';
|
|
358
|
-
Q5.WEBGL = 'webgl';
|
|
359
|
-
|
|
360
|
-
Q5.canvasOptions = {
|
|
361
|
-
alpha: false,
|
|
362
|
-
colorSpace: 'display-p3'
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
|
|
366
|
-
Q5.canvasOptions.colorSpace = 'srgb';
|
|
367
|
-
} else Q5.supportsHDR = true;
|