@vectojs/core 0.2.7 → 0.2.8
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/dist/{chunk-SSOP3F5F.mjs → chunk-FNXOS7IA.mjs} +161 -15
- package/dist/{chunk-YVN3PJRC.mjs → chunk-G6GQO7W2.mjs} +1 -0
- package/dist/{chunk-GTQRCY6W.js → chunk-XB3ZY5MF.js} +78 -1
- package/dist/{chunk-BWLNEJJW.js → chunk-XLZDOFSB.js} +175 -29
- package/dist/{chunk-DWMQYHNC.js → chunk-XRWZS2DM.js} +9 -8
- package/dist/{chunk-HVJHXIKW.mjs → chunk-YBW7ECZP.mjs} +78 -1
- package/dist/components/TextEntity.d.ts +11 -0
- package/dist/index.js +148 -115
- package/dist/index.mjs +55 -22
- package/dist/layout/LayoutEngine.d.ts +24 -0
- package/dist/layout.js +2 -2
- package/dist/layout.mjs +1 -1
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Scene.d.ts +10 -2
- package/package.json +1 -1
|
@@ -32,6 +32,13 @@ function computeLineSegments(top, bottom, maxWidth, exclusions) {
|
|
|
32
32
|
}
|
|
33
33
|
var LayoutEngine = class {
|
|
34
34
|
maxWidth;
|
|
35
|
+
/**
|
|
36
|
+
* Horizontal alignment. `'justify'` stretches inter-word spaces (or, for
|
|
37
|
+
* space-less CJK lines, inter-character gaps) so wrapped lines end flush at
|
|
38
|
+
* `maxWidth`; the last line of each paragraph stays ragged. Only applies to
|
|
39
|
+
* the object layout path without exclusion shapes.
|
|
40
|
+
*/
|
|
41
|
+
textAlign = "left";
|
|
35
42
|
maxHeight;
|
|
36
43
|
preserveLeadingSpaces = false;
|
|
37
44
|
wordSegmenter;
|
|
@@ -50,6 +57,22 @@ var LayoutEngine = class {
|
|
|
50
57
|
richParagraphCache = /* @__PURE__ */ new Map();
|
|
51
58
|
lastAtlas = null;
|
|
52
59
|
measurer;
|
|
60
|
+
_hyphenate = null;
|
|
61
|
+
/**
|
|
62
|
+
* Optional hyphenator: given a word, return its break parts (e.g.
|
|
63
|
+
* `['hyphen', 'ation']`). Used at wrap time when a word doesn't fit; a
|
|
64
|
+
* visible '-' is drawn at the chosen break. Soft hyphens (U+00AD) in the
|
|
65
|
+
* source work without any hyphenator. Setting this clears the prepared
|
|
66
|
+
* caches (break opportunities are baked in during prepare()).
|
|
67
|
+
*/
|
|
68
|
+
get hyphenate() {
|
|
69
|
+
return this._hyphenate;
|
|
70
|
+
}
|
|
71
|
+
set hyphenate(fn) {
|
|
72
|
+
this._hyphenate = fn;
|
|
73
|
+
this.paragraphCache.clear();
|
|
74
|
+
this.richParagraphCache.clear();
|
|
75
|
+
}
|
|
53
76
|
constructor(maxWidth, maxHeight, measurer) {
|
|
54
77
|
this.maxWidth = maxWidth;
|
|
55
78
|
this.maxHeight = maxHeight;
|
|
@@ -157,7 +180,13 @@ var LayoutEngine = class {
|
|
|
157
180
|
const word = segment.segment;
|
|
158
181
|
const glyphs = [];
|
|
159
182
|
let width = 0;
|
|
183
|
+
let breakPoints;
|
|
160
184
|
for (const char of this.getGraphemes(word)) {
|
|
185
|
+
if (char === "\xAD") {
|
|
186
|
+
(breakPoints ??= []).push(glyphs.length);
|
|
187
|
+
shapedCharIdx += char.length;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
161
190
|
const visualStart = shapedCharIdx;
|
|
162
191
|
const visualEnd = shapedCharIdx + char.length;
|
|
163
192
|
const rawStart = indexMap[visualStart];
|
|
@@ -182,11 +211,23 @@ var LayoutEngine = class {
|
|
|
182
211
|
width += w;
|
|
183
212
|
shapedCharIdx += char.length;
|
|
184
213
|
}
|
|
214
|
+
if (!breakPoints && this._hyphenate && segment.isWordLike && glyphs.length > 3) {
|
|
215
|
+
const parts = this._hyphenate(word);
|
|
216
|
+
if (parts.length > 1) {
|
|
217
|
+
breakPoints = [];
|
|
218
|
+
let count = 0;
|
|
219
|
+
for (let pi = 0; pi < parts.length - 1; pi++) {
|
|
220
|
+
for (const _g of this.getGraphemes(parts[pi])) count++;
|
|
221
|
+
breakPoints.push(count);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
185
225
|
words.push({
|
|
186
226
|
glyphs,
|
|
187
227
|
width,
|
|
188
228
|
isWordLike: segment.isWordLike,
|
|
189
|
-
isWhitespace: word.trim().length === 0
|
|
229
|
+
isWhitespace: word.trim().length === 0,
|
|
230
|
+
breakPoints
|
|
190
231
|
});
|
|
191
232
|
}
|
|
192
233
|
const prepared = {
|
|
@@ -200,7 +241,12 @@ var LayoutEngine = class {
|
|
|
200
241
|
paragraphs.push(prepared);
|
|
201
242
|
offset += paragraph.length + 1;
|
|
202
243
|
}
|
|
203
|
-
return {
|
|
244
|
+
return {
|
|
245
|
+
paragraphs,
|
|
246
|
+
fontSize,
|
|
247
|
+
fallbackToCanvas: fallbackToCanvas || void 0,
|
|
248
|
+
hyphenWidth: this.glyphWidth(this.glyphKeyFor("-", fontAtlas), fontAtlas, fontSize)
|
|
249
|
+
};
|
|
204
250
|
}
|
|
205
251
|
/**
|
|
206
252
|
* **Cold pass for rich text.** Like {@link prepare}, but takes an array of
|
|
@@ -274,7 +320,13 @@ var LayoutEngine = class {
|
|
|
274
320
|
const word = segment.segment;
|
|
275
321
|
const glyphs = [];
|
|
276
322
|
let width = 0;
|
|
323
|
+
let breakPoints;
|
|
277
324
|
for (const char of this.getGraphemes(word)) {
|
|
325
|
+
if (char === "\xAD") {
|
|
326
|
+
(breakPoints ??= []).push(glyphs.length);
|
|
327
|
+
shapedCharIdx += char.length;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
278
330
|
const visualStart = shapedCharIdx;
|
|
279
331
|
const visualEnd = shapedCharIdx + char.length;
|
|
280
332
|
const rawStart = indexMap[visualStart];
|
|
@@ -302,11 +354,23 @@ var LayoutEngine = class {
|
|
|
302
354
|
width += w;
|
|
303
355
|
shapedCharIdx += char.length;
|
|
304
356
|
}
|
|
357
|
+
if (!breakPoints && this._hyphenate && segment.isWordLike && glyphs.length > 3) {
|
|
358
|
+
const parts = this._hyphenate(word);
|
|
359
|
+
if (parts.length > 1) {
|
|
360
|
+
breakPoints = [];
|
|
361
|
+
let count = 0;
|
|
362
|
+
for (let pi = 0; pi < parts.length - 1; pi++) {
|
|
363
|
+
for (const _g of this.getGraphemes(parts[pi])) count++;
|
|
364
|
+
breakPoints.push(count);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
305
368
|
words.push({
|
|
306
369
|
glyphs,
|
|
307
370
|
width,
|
|
308
371
|
isWordLike: segment.isWordLike,
|
|
309
|
-
isWhitespace: word.trim().length === 0
|
|
372
|
+
isWhitespace: word.trim().length === 0,
|
|
373
|
+
breakPoints
|
|
310
374
|
});
|
|
311
375
|
}
|
|
312
376
|
const prepared = {
|
|
@@ -350,7 +414,7 @@ var LayoutEngine = class {
|
|
|
350
414
|
let si = 0;
|
|
351
415
|
let currentLineNodes = [];
|
|
352
416
|
let paragraphBaseLevel = 0;
|
|
353
|
-
const commitLine = () => {
|
|
417
|
+
const commitLine = (justifyTo) => {
|
|
354
418
|
if (currentLineNodes.length === 0) return;
|
|
355
419
|
const runs = [];
|
|
356
420
|
let currentRun = [];
|
|
@@ -375,6 +439,37 @@ var LayoutEngine = class {
|
|
|
375
439
|
node.isRTL = node.level % 2 === 1;
|
|
376
440
|
x += node.width;
|
|
377
441
|
}
|
|
442
|
+
if (justifyTo !== void 0 && runs.length === 1) {
|
|
443
|
+
let lastContent = run.length - 1;
|
|
444
|
+
while (lastContent >= 0 && run[lastContent].char.trim() === "") lastContent--;
|
|
445
|
+
if (lastContent > 0) {
|
|
446
|
+
const contentEnd = run[lastContent].x + run[lastContent].width;
|
|
447
|
+
const slack = justifyTo - contentEnd;
|
|
448
|
+
if (slack > 0 && slack <= (justifyTo - runStartX) * 0.5) {
|
|
449
|
+
const spaceIdx = [];
|
|
450
|
+
for (let k = 1; k < lastContent; k++) {
|
|
451
|
+
if (run[k].char.trim() === "") spaceIdx.push(k);
|
|
452
|
+
}
|
|
453
|
+
if (spaceIdx.length > 0) {
|
|
454
|
+
const extra = slack / spaceIdx.length;
|
|
455
|
+
let shift = 0;
|
|
456
|
+
let nextSpace = 0;
|
|
457
|
+
for (let k = 0; k <= lastContent; k++) {
|
|
458
|
+
run[k].x += shift;
|
|
459
|
+
if (nextSpace < spaceIdx.length && k === spaceIdx[nextSpace]) {
|
|
460
|
+
run[k].width += extra;
|
|
461
|
+
shift += extra;
|
|
462
|
+
nextSpace++;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
} else {
|
|
466
|
+
const extra = slack / lastContent;
|
|
467
|
+
for (let k = 1; k <= lastContent; k++) run[k].x += extra * k;
|
|
468
|
+
}
|
|
469
|
+
if (justifyTo > maxLineWidth) maxLineWidth = justifyTo;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
378
473
|
for (const node of run) {
|
|
379
474
|
layoutNodes.push(node);
|
|
380
475
|
}
|
|
@@ -394,6 +489,8 @@ var LayoutEngine = class {
|
|
|
394
489
|
}
|
|
395
490
|
return false;
|
|
396
491
|
};
|
|
492
|
+
const justifyTarget = this.textAlign === "justify" && !hasEx ? this.maxWidth : void 0;
|
|
493
|
+
const hyphenWidth = prepared.hyphenWidth ?? fontSize * 0.3;
|
|
397
494
|
for (const paragraph of prepared.paragraphs) {
|
|
398
495
|
if (paragraph.isEmpty) {
|
|
399
496
|
commitLine();
|
|
@@ -411,16 +508,65 @@ var LayoutEngine = class {
|
|
|
411
508
|
}
|
|
412
509
|
const lineHeight = pMax * 1.5;
|
|
413
510
|
if (!startLine(lineHeight)) break;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
511
|
+
const wordQueue = paragraph.words.slice();
|
|
512
|
+
for (let qi = 0; qi < wordQueue.length; qi++) {
|
|
513
|
+
const word = wordQueue[qi];
|
|
514
|
+
if (currentX + word.width > segs[si].x1) {
|
|
515
|
+
if (!hasEx && word.breakPoints && word.breakPoints.length > 0) {
|
|
516
|
+
const avail = segs[si].x1 - currentX;
|
|
517
|
+
let chosen = -1;
|
|
518
|
+
let prefixWidth = 0;
|
|
519
|
+
let acc = 0;
|
|
520
|
+
let bpIdx = 0;
|
|
521
|
+
for (let g = 0; g < word.glyphs.length && bpIdx < word.breakPoints.length; g++) {
|
|
522
|
+
acc += word.glyphs[g].width;
|
|
523
|
+
if (g + 1 === word.breakPoints[bpIdx]) {
|
|
524
|
+
if (acc + hyphenWidth <= avail) {
|
|
525
|
+
chosen = word.breakPoints[bpIdx];
|
|
526
|
+
prefixWidth = acc;
|
|
527
|
+
}
|
|
528
|
+
bpIdx++;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (chosen > 0) {
|
|
532
|
+
const anchorGlyph = word.glyphs[chosen - 1];
|
|
533
|
+
const prefix = {
|
|
534
|
+
glyphs: [
|
|
535
|
+
...word.glyphs.slice(0, chosen),
|
|
536
|
+
{
|
|
537
|
+
char: "-",
|
|
538
|
+
width: hyphenWidth,
|
|
539
|
+
level: anchorGlyph.level,
|
|
540
|
+
sourceIndex: anchorGlyph.sourceIndex,
|
|
541
|
+
sourceLength: 0
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
width: prefixWidth + hyphenWidth,
|
|
545
|
+
isWordLike: true,
|
|
546
|
+
isWhitespace: false
|
|
547
|
+
};
|
|
548
|
+
const rest = {
|
|
549
|
+
glyphs: word.glyphs.slice(chosen),
|
|
550
|
+
width: word.width - prefixWidth,
|
|
551
|
+
isWordLike: true,
|
|
552
|
+
isWhitespace: false,
|
|
553
|
+
breakPoints: word.breakPoints.filter((bp) => bp > chosen).map((bp) => bp - chosen)
|
|
554
|
+
};
|
|
555
|
+
wordQueue.splice(qi, 1, prefix, rest);
|
|
556
|
+
qi--;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (currentX > segs[si].x0) {
|
|
561
|
+
if (word.isWordLike === false && word.isWhitespace) continue;
|
|
562
|
+
if (si < segs.length - 1) {
|
|
563
|
+
si++;
|
|
564
|
+
currentX = segs[si].x0;
|
|
565
|
+
} else {
|
|
566
|
+
commitLine(justifyTarget);
|
|
567
|
+
currentY += lineHeight;
|
|
568
|
+
if (!startLine(lineHeight)) break;
|
|
569
|
+
}
|
|
424
570
|
}
|
|
425
571
|
}
|
|
426
572
|
for (const glyph of word.glyphs) {
|
|
@@ -433,7 +579,7 @@ var LayoutEngine = class {
|
|
|
433
579
|
si++;
|
|
434
580
|
currentX = segs[si].x0;
|
|
435
581
|
} else {
|
|
436
|
-
commitLine();
|
|
582
|
+
commitLine(justifyTarget);
|
|
437
583
|
currentY += lineHeight;
|
|
438
584
|
if (!startLine(lineHeight)) break;
|
|
439
585
|
}
|
|
@@ -825,6 +825,19 @@ void main() {
|
|
|
825
825
|
vec4 t = texture(u_tex, v_uv);
|
|
826
826
|
outColor = vec4(t.rgb * v_tint.rgb, t.a * v_tint.a);
|
|
827
827
|
}`;
|
|
828
|
+
var CIRCLE_QUAD_FRAG = `#version 300 es
|
|
829
|
+
precision mediump float;
|
|
830
|
+
in vec2 v_uv;
|
|
831
|
+
in vec4 v_tint;
|
|
832
|
+
out vec4 outColor;
|
|
833
|
+
void main() {
|
|
834
|
+
vec2 c = v_uv - 0.5;
|
|
835
|
+
float d = length(c);
|
|
836
|
+
float aa = fwidth(d);
|
|
837
|
+
float alpha = 1.0 - smoothstep(0.5 - aa, 0.5, d);
|
|
838
|
+
if (alpha <= 0.0) discard;
|
|
839
|
+
outColor = vec4(v_tint.rgb, v_tint.a * alpha);
|
|
840
|
+
}`;
|
|
828
841
|
var MSDF_FRAG = `#version 300 es
|
|
829
842
|
precision mediump float;
|
|
830
843
|
uniform sampler2D u_tex;
|
|
@@ -891,7 +904,9 @@ function createWebGLPointRenderer(canvas) {
|
|
|
891
904
|
const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
|
|
892
905
|
const spriteProgram = link(gl, SPRITE_VERT, SPRITE_FRAG);
|
|
893
906
|
const msdfProgram = link(gl, SPRITE_VERT, MSDF_FRAG);
|
|
894
|
-
|
|
907
|
+
const circleQuadProgram = link(gl, SPRITE_VERT, CIRCLE_QUAD_FRAG);
|
|
908
|
+
if (!pointProgram || !rectProgram || !spriteProgram || !msdfProgram || !circleQuadProgram)
|
|
909
|
+
return null;
|
|
895
910
|
gl.enable(gl.BLEND);
|
|
896
911
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
897
912
|
const pAPos = gl.getAttribLocation(pointProgram, "a_pos");
|
|
@@ -951,6 +966,20 @@ function createWebGLPointRenderer(canvas) {
|
|
|
951
966
|
gl.vertexAttribPointer(gAUv, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 8);
|
|
952
967
|
gl.enableVertexAttribArray(gATint);
|
|
953
968
|
gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
|
|
969
|
+
const cAPos = gl.getAttribLocation(circleQuadProgram, "a_pos");
|
|
970
|
+
const cAUv = gl.getAttribLocation(circleQuadProgram, "a_uv");
|
|
971
|
+
const cATint = gl.getAttribLocation(circleQuadProgram, "a_tint");
|
|
972
|
+
const cURes = gl.getUniformLocation(circleQuadProgram, "u_resolution");
|
|
973
|
+
const circleQuadBuffer = gl.createBuffer();
|
|
974
|
+
const circleQuadVAO = gl.createVertexArray();
|
|
975
|
+
gl.bindVertexArray(circleQuadVAO);
|
|
976
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
|
|
977
|
+
gl.enableVertexAttribArray(cAPos);
|
|
978
|
+
gl.vertexAttribPointer(cAPos, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 0);
|
|
979
|
+
gl.enableVertexAttribArray(cAUv);
|
|
980
|
+
gl.vertexAttribPointer(cAUv, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 8);
|
|
981
|
+
gl.enableVertexAttribArray(cATint);
|
|
982
|
+
gl.vertexAttribPointer(cATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
|
|
954
983
|
gl.bindVertexArray(null);
|
|
955
984
|
let texture = null;
|
|
956
985
|
let textureSource = null;
|
|
@@ -965,10 +994,16 @@ function createWebGLPointRenderer(canvas) {
|
|
|
965
994
|
let spriteCount = 0;
|
|
966
995
|
let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 1024);
|
|
967
996
|
let glyphCount = 0;
|
|
997
|
+
let circleQuadData = new Float32Array(
|
|
998
|
+
FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 64
|
|
999
|
+
);
|
|
1000
|
+
let circleQuadCount = 0;
|
|
968
1001
|
let logicalW = 0;
|
|
969
1002
|
let logicalH = 0;
|
|
970
1003
|
let dpr = 1;
|
|
971
1004
|
let destroyed = false;
|
|
1005
|
+
const pointSizeRange = typeof gl.getParameter === "function" ? gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) : null;
|
|
1006
|
+
const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
|
|
972
1007
|
const drawGlyphs = () => {
|
|
973
1008
|
if (glyphCount === 0 || !msdfTexture) return;
|
|
974
1009
|
const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
@@ -1001,6 +1036,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1001
1036
|
rectCount = 0;
|
|
1002
1037
|
spriteCount = 0;
|
|
1003
1038
|
glyphCount = 0;
|
|
1039
|
+
circleQuadCount = 0;
|
|
1004
1040
|
gl.clearColor(0, 0, 0, 0);
|
|
1005
1041
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
1006
1042
|
},
|
|
@@ -1108,6 +1144,35 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1108
1144
|
glyphCount++;
|
|
1109
1145
|
},
|
|
1110
1146
|
addCircle(x, y, radius, color, alpha = 1) {
|
|
1147
|
+
const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
|
|
1148
|
+
if (needsQuad) {
|
|
1149
|
+
const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
|
|
1150
|
+
circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
|
|
1151
|
+
const [qr, qg, qb, qa] = parseColorToRGBA(color);
|
|
1152
|
+
const al = qa * alpha;
|
|
1153
|
+
const quad = [
|
|
1154
|
+
[x - radius, y - radius, 0, 0],
|
|
1155
|
+
[x + radius, y - radius, 1, 0],
|
|
1156
|
+
[x + radius, y + radius, 1, 1],
|
|
1157
|
+
[x - radius, y + radius, 0, 1]
|
|
1158
|
+
];
|
|
1159
|
+
const order = [0, 1, 2, 0, 2, 3];
|
|
1160
|
+
let o2 = circleQuadCount * stride;
|
|
1161
|
+
for (const i of order) {
|
|
1162
|
+
const [vx, vy, vu, vv] = quad[i];
|
|
1163
|
+
circleQuadData[o2] = vx;
|
|
1164
|
+
circleQuadData[o2 + 1] = vy;
|
|
1165
|
+
circleQuadData[o2 + 2] = vu;
|
|
1166
|
+
circleQuadData[o2 + 3] = vv;
|
|
1167
|
+
circleQuadData[o2 + 4] = qr;
|
|
1168
|
+
circleQuadData[o2 + 5] = qg;
|
|
1169
|
+
circleQuadData[o2 + 6] = qb;
|
|
1170
|
+
circleQuadData[o2 + 7] = al;
|
|
1171
|
+
o2 += FLOATS_PER_SPRITE_VERT;
|
|
1172
|
+
}
|
|
1173
|
+
circleQuadCount++;
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1111
1176
|
pointData = grow(pointData, (pointCount + 1) * FLOATS_PER_POINT);
|
|
1112
1177
|
const [r, g, b, a] = parseColorToRGBA(color);
|
|
1113
1178
|
const o = pointCount * FLOATS_PER_POINT;
|
|
@@ -1171,6 +1236,15 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1171
1236
|
gl.uniform1f(pUDpr, dpr);
|
|
1172
1237
|
gl.drawArrays(gl.POINTS, 0, pointCount);
|
|
1173
1238
|
}
|
|
1239
|
+
if (circleQuadCount > 0) {
|
|
1240
|
+
const floats = circleQuadCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
1241
|
+
gl.useProgram(circleQuadProgram);
|
|
1242
|
+
gl.bindVertexArray(circleQuadVAO);
|
|
1243
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
|
|
1244
|
+
gl.bufferData(gl.ARRAY_BUFFER, circleQuadData.subarray(0, floats), gl.DYNAMIC_DRAW);
|
|
1245
|
+
gl.uniform2f(cURes, logicalW, logicalH);
|
|
1246
|
+
gl.drawArrays(gl.TRIANGLES, 0, circleQuadCount * VERTS_PER_SPRITE);
|
|
1247
|
+
}
|
|
1174
1248
|
if (spriteCount > 0 && texture) {
|
|
1175
1249
|
const floats = spriteCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
1176
1250
|
gl.useProgram(spriteProgram);
|
|
@@ -1193,14 +1267,17 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1193
1267
|
gl.deleteBuffer(rectBuffer);
|
|
1194
1268
|
gl.deleteBuffer(spriteBuffer);
|
|
1195
1269
|
gl.deleteBuffer(glyphBuffer);
|
|
1270
|
+
gl.deleteBuffer(circleQuadBuffer);
|
|
1196
1271
|
gl.deleteVertexArray(pointVAO);
|
|
1197
1272
|
gl.deleteVertexArray(rectVAO);
|
|
1198
1273
|
gl.deleteVertexArray(spriteVAO);
|
|
1199
1274
|
gl.deleteVertexArray(glyphVAO);
|
|
1275
|
+
gl.deleteVertexArray(circleQuadVAO);
|
|
1200
1276
|
gl.deleteProgram(pointProgram);
|
|
1201
1277
|
gl.deleteProgram(rectProgram);
|
|
1202
1278
|
gl.deleteProgram(spriteProgram);
|
|
1203
1279
|
gl.deleteProgram(msdfProgram);
|
|
1280
|
+
gl.deleteProgram(circleQuadProgram);
|
|
1204
1281
|
if (texture) gl.deleteTexture(texture);
|
|
1205
1282
|
if (msdfTexture) gl.deleteTexture(msdfTexture);
|
|
1206
1283
|
}
|