figureone 1.8.0 → 1.9.1
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/figureone.min.js +1 -1
- package/index.js +274 -71
- package/llms-full.txt +37 -0
- package/llms.txt +10 -0
- package/package.json +1 -1
- package/types/js/figure/DrawingObjects/GLObject/GLObject.d.ts +13 -0
- package/types/js/figure/webgl/webgl.d.ts +10 -1
package/index.js
CHANGED
|
@@ -7174,6 +7174,11 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7174
7174
|
// is precomputed so the draw loop allocates nothing). A mask with an empty
|
|
7175
7175
|
// src is a transparent placeholder - an index-preserving no-op slot.
|
|
7176
7176
|
|
|
7177
|
+
// The webgl texture id this object currently holds a base-texture reference to
|
|
7178
|
+
// (acquired by initTexture, or adopted from a shared atlas via
|
|
7179
|
+
// setBaseTextureRef). resetTextureBuffer releases exactly this id, so the
|
|
7180
|
+
// release can never be unbalanced with the acquire. null = no reference held.
|
|
7181
|
+
|
|
7177
7182
|
function GLObject(webgl) {
|
|
7178
7183
|
var _this;
|
|
7179
7184
|
var vertexShader = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
@@ -7203,6 +7208,7 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7203
7208
|
_this.numVertices = 0;
|
|
7204
7209
|
_this.uniforms = {};
|
|
7205
7210
|
_this.texture = null;
|
|
7211
|
+
_this.acquiredBaseTextureId = null;
|
|
7206
7212
|
_this.maskTextures = [];
|
|
7207
7213
|
// this.selectorProgramIndex = this.webgl.getProgram(selectorVertexShader, selectorFragShader);
|
|
7208
7214
|
_this.initProgram();
|
|
@@ -7463,6 +7469,10 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7463
7469
|
// gl.STATIC_DRAW,
|
|
7464
7470
|
// );
|
|
7465
7471
|
webgl.addTexture(id, data || src, loadColor, repeat, this.executeOnLoad.bind(this), force);
|
|
7472
|
+
// addTexture took/holds a reference to this id; record it so
|
|
7473
|
+
// resetTextureBuffer releases exactly what was acquired. (For a forced
|
|
7474
|
+
// update the id is unchanged, so this is idempotent.)
|
|
7475
|
+
this.acquiredBaseTextureId = id;
|
|
7466
7476
|
this.state = webgl.textures[texture.id].state;
|
|
7467
7477
|
|
|
7468
7478
|
// Register the optional mask textures (textureMap color mode). Each loads
|
|
@@ -7527,6 +7537,30 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7527
7537
|
// }
|
|
7528
7538
|
}
|
|
7529
7539
|
|
|
7540
|
+
/**
|
|
7541
|
+
* Adopt a base-texture reference to an already-registered texture (e.g. a
|
|
7542
|
+
* shared font atlas uploaded by the Atlas, whose id this object renders with
|
|
7543
|
+
* but does not upload itself).
|
|
7544
|
+
*
|
|
7545
|
+
* Releases any previously-held base reference first (so a font/atlas change
|
|
7546
|
+
* rebalances correctly) and is idempotent when the id is unchanged. If the
|
|
7547
|
+
* texture isn't registered yet, no reference is taken and acquiredBaseTextureId
|
|
7548
|
+
* is cleared, so the later resetTextureBuffer release stays balanced (it only
|
|
7549
|
+
* releases a reference that was actually acquired).
|
|
7550
|
+
*/
|
|
7551
|
+
}, {
|
|
7552
|
+
key: "setBaseTextureRef",
|
|
7553
|
+
value: function setBaseTextureRef(id) {
|
|
7554
|
+
if (this.acquiredBaseTextureId === id) {
|
|
7555
|
+
return;
|
|
7556
|
+
}
|
|
7557
|
+
var webgl = this.webgl;
|
|
7558
|
+
if (this.acquiredBaseTextureId != null) {
|
|
7559
|
+
webgl.deleteTexture(this.acquiredBaseTextureId);
|
|
7560
|
+
}
|
|
7561
|
+
this.acquiredBaseTextureId = webgl.acquireTexture(id) ? id : null;
|
|
7562
|
+
}
|
|
7563
|
+
|
|
7530
7564
|
// A texture map is a texture coords point that lines up with the texture
|
|
7531
7565
|
// vertex point. So, if the vertex shape is rectangular, centered at the
|
|
7532
7566
|
// origin and wants to incorporate the entire texture, then the map would
|
|
@@ -7707,14 +7741,15 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7707
7741
|
maskTextures = this.maskTextures,
|
|
7708
7742
|
webgl = this.webgl,
|
|
7709
7743
|
gl = this.gl;
|
|
7710
|
-
if (
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7744
|
+
if (deleteTexture && this.acquiredBaseTextureId != null) {
|
|
7745
|
+
// Release exactly the base reference this object acquired, so a failed
|
|
7746
|
+
// acquire (e.g. an atlas not registered yet) can never over-release.
|
|
7747
|
+
webgl.deleteTexture(this.acquiredBaseTextureId);
|
|
7748
|
+
this.acquiredBaseTextureId = null;
|
|
7749
|
+
}
|
|
7750
|
+
if (texture && texture.buffer != null) {
|
|
7751
|
+
gl.deleteBuffer(texture.buffer);
|
|
7752
|
+
texture.buffer = null;
|
|
7718
7753
|
}
|
|
7719
7754
|
if (deleteTexture) {
|
|
7720
7755
|
maskTextures.forEach(function (maskTexture) {
|
|
@@ -7994,6 +8029,9 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
7994
8029
|
gl.uniform1f(locations.u_z, this.z);
|
|
7995
8030
|
gl.uniform4f(locations.u_color, color[0], color[1], color[2], color[3]);
|
|
7996
8031
|
var texture = this.texture;
|
|
8032
|
+
// Set when a textured object's base texture can't be bound, so we skip the
|
|
8033
|
+
// draw rather than sampling a stale/wrong texture (see below).
|
|
8034
|
+
var skipDraw = false;
|
|
7997
8035
|
if (texture != null && targetTexture === false) {
|
|
7998
8036
|
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
|
|
7999
8037
|
var texSize = 2; // 2 components per iteration
|
|
@@ -8007,27 +8045,49 @@ var GLObject = /*#__PURE__*/function (_DrawingObject) {
|
|
|
8007
8045
|
gl.enableVertexAttribArray(locations.a_texcoord);
|
|
8008
8046
|
gl.bindBuffer(gl.ARRAY_BUFFER, texture.buffer);
|
|
8009
8047
|
gl.vertexAttribPointer(locations.a_texcoord, texSize, texType, texNormalize, texStride, texOffset);
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
//
|
|
8015
|
-
//
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8048
|
+
// Assign content texture units for this draw from the shared pool. Unit 0
|
|
8049
|
+
// is reserved for the target/selector framebuffer texture, so content
|
|
8050
|
+
// starts at unit 1: the base texture, then each mask texture in turn.
|
|
8051
|
+
// bindTextureToUnit only issues a bindTexture when the unit isn't already
|
|
8052
|
+
// pointing at that texture, so runs of draws sharing a texture (e.g. text
|
|
8053
|
+
// sharing a font atlas) issue no bind calls.
|
|
8054
|
+
var textureUnit = 1;
|
|
8055
|
+
if (webglInstance.bindTextureToUnit(texture.id, textureUnit)) {
|
|
8056
|
+
gl.uniform1i(locations.u_use_texture, 1);
|
|
8057
|
+
gl.uniform1i(locations.u_texture, textureUnit);
|
|
8058
|
+
textureUnit += 1;
|
|
8059
|
+
|
|
8060
|
+
// Bind the mask textures (textureMap color mode), each to its own
|
|
8061
|
+
// texture unit and precomputed u_mask{i} sampler. They reuse the
|
|
8062
|
+
// a_texcoord / v_texcoord bound above, so no extra attribute is needed.
|
|
8063
|
+
// Only assign the sampler if the bind succeeds — otherwise the uniform
|
|
8064
|
+
// would default to sampler 0 (the reserved target texture) and recolor
|
|
8065
|
+
// with garbage. Uses an indexed loop with precomputed uniform names to
|
|
8066
|
+
// avoid per-frame allocation.
|
|
8067
|
+
var maskTextures = this.maskTextures;
|
|
8068
|
+
for (var i = 0; i < maskTextures.length; i += 1) {
|
|
8069
|
+
var maskTexture = maskTextures[i];
|
|
8070
|
+
var maskLocation = locations[maskTexture.uniformName];
|
|
8071
|
+
if (maskLocation != null && webglInstance.bindTextureToUnit(maskTexture.id, textureUnit)) {
|
|
8072
|
+
gl.uniform1i(maskLocation, textureUnit);
|
|
8073
|
+
textureUnit += 1;
|
|
8074
|
+
}
|
|
8025
8075
|
}
|
|
8076
|
+
} else {
|
|
8077
|
+
// The base texture isn't available (e.g. a shared texture was deleted by
|
|
8078
|
+
// another element's cleanup while this one still references it). The
|
|
8079
|
+
// composed texture shaders sample u_texture unconditionally, so there is
|
|
8080
|
+
// no safe in-shader fallback — skip this object's draw this frame rather
|
|
8081
|
+
// than sampling whatever stale texture occupies the unit.
|
|
8082
|
+
gl.uniform1i(locations.u_use_texture, 0);
|
|
8083
|
+
skipDraw = true;
|
|
8026
8084
|
}
|
|
8027
8085
|
} else {
|
|
8028
8086
|
gl.uniform1i(locations.u_use_texture, 0);
|
|
8029
8087
|
}
|
|
8030
|
-
|
|
8088
|
+
if (!skipDraw) {
|
|
8089
|
+
gl.drawArrays(this.glPrimitive, 0, numDrawVertices);
|
|
8090
|
+
}
|
|
8031
8091
|
if (texture) {
|
|
8032
8092
|
gl.disableVertexAttribArray(locations.a_texcoord);
|
|
8033
8093
|
}
|
|
@@ -52888,11 +52948,20 @@ var FigureElementPrimitiveGLText = /*#__PURE__*/function (_FigureElementPrimiti)
|
|
|
52888
52948
|
scene: scene,
|
|
52889
52949
|
font: this.font
|
|
52890
52950
|
});
|
|
52951
|
+
var textureID = this.atlas.font.getTextureID();
|
|
52891
52952
|
if (this.drawingObject.texture == null) {
|
|
52892
|
-
this.drawingObject.addTexture(
|
|
52953
|
+
this.drawingObject.addTexture(textureID);
|
|
52893
52954
|
} else {
|
|
52894
|
-
this.drawingObject.texture.id =
|
|
52895
|
-
}
|
|
52955
|
+
this.drawingObject.texture.id = textureID;
|
|
52956
|
+
}
|
|
52957
|
+
// Take a webgl reference to the shared atlas texture so it survives other
|
|
52958
|
+
// elements' cleanup. The Atlas registers/uploads the texture; this element
|
|
52959
|
+
// only adopts its id (GLObject.addTexture above doesn't touch webgl), so
|
|
52960
|
+
// without this its resetTextureBuffer release would be unbalanced and the
|
|
52961
|
+
// first element's cleanup would free the atlas for the others.
|
|
52962
|
+
// setBaseTextureRef releases any previously-held id (createAtlas re-runs on
|
|
52963
|
+
// font changes) and is idempotent when the id is unchanged.
|
|
52964
|
+
this.drawingObject.setBaseTextureRef(textureID);
|
|
52896
52965
|
// console.log(this.atlas)
|
|
52897
52966
|
this.setText(this.text);
|
|
52898
52967
|
this.atlasNotificationsID = this.atlas.notifications.add('updated', this.loaded.bind(this));
|
|
@@ -66580,13 +66649,31 @@ function composeFragShader() {
|
|
|
66580
66649
|
// alpha controls how strongly that region is recolored (0 = leave the base
|
|
66581
66650
|
// color unchanged). With one mask this is exactly four mixes and one extra
|
|
66582
66651
|
// texture fetch; each additional mask adds one fetch and four mixes.
|
|
66652
|
+
//
|
|
66653
|
+
// Masks are uploaded with premultiplied alpha, so a region only carries full
|
|
66654
|
+
// weight where the mask pixel is opaque. Author each mask pixel as one of:
|
|
66655
|
+
// keep base : [0, 0, 0, 0] (transparent)
|
|
66656
|
+
// region 0 : [1, 0, 0, 1] -> u_tint0 (r channel)
|
|
66657
|
+
// region 1 : [0, 1, 0, 1] -> u_tint1 (g channel)
|
|
66658
|
+
// region 2 : [0, 0, 1, 1] -> u_tint2 (b channel)
|
|
66659
|
+
// region 3 : [0, 0, 0, 1] -> u_tint3 (a channel, opaque black)
|
|
66660
|
+
// With every painted region opaque, the alpha channel is 1 across all of them,
|
|
66661
|
+
// so it can't act as an independent region on its own. The a-channel mix below
|
|
66662
|
+
// gates on (1 - r - g - b) so region 3 is exactly the opaque-black pixels - the
|
|
66663
|
+
// ones not already claimed by an r/g/b region. Regions 0-2 support partial
|
|
66664
|
+
// weight by lowering their channel value (e.g. [0.5, 0, 0, 1]); region 3 is
|
|
66665
|
+
// full-weight only, with slight antialiasing softness at its borders.
|
|
66583
66666
|
src += ' vec4 base = texture2D(u_texture, v_texcoord);\n';
|
|
66584
66667
|
src += ' vec3 col = base.rgb;\n';
|
|
66585
66668
|
for (var _m = 0; _m < numMasks; _m += 1) {
|
|
66586
66669
|
src += " vec4 mask".concat(_m, " = texture2D(u_mask").concat(_m, ", v_texcoord);\n");
|
|
66587
66670
|
for (var c = 0; c < CHANNELS_PER_MASK; c += 1) {
|
|
66588
66671
|
var _t = _m * CHANNELS_PER_MASK + c;
|
|
66589
|
-
|
|
66672
|
+
if (channels[c] === 'a') {
|
|
66673
|
+
src += " col = mix(col, u_tint".concat(_t, ".rgb, mask").concat(_m, ".a * (1.0 - clamp(mask").concat(_m, ".r + mask").concat(_m, ".g + mask").concat(_m, ".b, 0.0, 1.0)) * u_tint").concat(_t, ".a);\n");
|
|
66674
|
+
} else {
|
|
66675
|
+
src += " col = mix(col, u_tint".concat(_t, ".rgb, mask").concat(_m, ".").concat(channels[c], " * u_tint").concat(_t, ".a);\n");
|
|
66676
|
+
}
|
|
66590
66677
|
}
|
|
66591
66678
|
}
|
|
66592
66679
|
src += ' gl_FragColor = vec4(col, base.a * u_color.a);\n';
|
|
@@ -67165,6 +67252,20 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67165
67252
|
value:
|
|
67166
67253
|
// locations: Object;
|
|
67167
67254
|
|
|
67255
|
+
// Content texture units are a small shared pool reused across draws. Unit 0 is
|
|
67256
|
+
// reserved for the target/selector framebuffer texture, so content starts at
|
|
67257
|
+
// unit 1. boundUnits[u] tracks which texture id is currently bound to GL unit
|
|
67258
|
+
// u, so bindTextureToUnit can skip redundant binds (bind-on-change).
|
|
67259
|
+
|
|
67260
|
+
// Monotonic source for texture handles (never reused, so deleting a texture
|
|
67261
|
+
// can never cause a later texture to collide with a live one).
|
|
67262
|
+
|
|
67263
|
+
// gl.MAX_TEXTURE_IMAGE_UNITS (the fragment-shader sampler limit), queried once
|
|
67264
|
+
// for a diagnostic warning. Matches the per-object mask guard in
|
|
67265
|
+
// FigurePrimitives.gl().
|
|
67266
|
+
|
|
67267
|
+
// Set once the unit-budget warning has fired, so it isn't logged every frame.
|
|
67268
|
+
|
|
67168
67269
|
/*
|
|
67169
67270
|
Add, or update a texture. If the texture already exists, then do nothing.
|
|
67170
67271
|
A texture is referenced with a unique id, and defined by either a url (string), Image or html canvas element.
|
|
@@ -67180,45 +67281,52 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67180
67281
|
var onLoad = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
|
|
67181
67282
|
var force = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
|
67182
67283
|
/*
|
|
67183
|
-
|
|
67184
|
-
|
|
67185
|
-
|
|
67284
|
+
A texture id can be shared by multiple owners (e.g. several GLText elements
|
|
67285
|
+
using the same font atlas, plus the Atlas itself). Each addTexture call
|
|
67286
|
+
that isn't a forced content update acquires one reference; deleteTexture
|
|
67287
|
+
releases one, and the GL texture is freed only when the last owner releases
|
|
67288
|
+
it. This lets one element's cleanup() drop its reference without pulling a
|
|
67289
|
+
still-shared texture out from under the survivors.
|
|
67290
|
+
If the texture already exists and is still loading, the onLoad callback is
|
|
67291
|
+
added to the list to be called once the texture loads.
|
|
67186
67292
|
*/
|
|
67187
67293
|
if (!force && this.textures[id] != null) {
|
|
67188
|
-
|
|
67189
|
-
|
|
67190
|
-
|
|
67191
|
-
|
|
67294
|
+
// Another owner (or the same owner re-acquiring) references an existing
|
|
67295
|
+
// texture: take a reference and (re)register the load callback.
|
|
67296
|
+
var existingTexture = this.textures[id];
|
|
67297
|
+
existingTexture.refCount += 1;
|
|
67298
|
+
if (onLoad != null) {
|
|
67299
|
+
existingTexture.onLoad.push(onLoad);
|
|
67300
|
+
}
|
|
67301
|
+
if (existingTexture.state === 'loaded') {
|
|
67192
67302
|
this.onLoad(id);
|
|
67193
|
-
return this.textures[id].index;
|
|
67194
67303
|
}
|
|
67195
|
-
|
|
67304
|
+
return existingTexture.handle;
|
|
67305
|
+
}
|
|
67306
|
+
var gl = this.gl;
|
|
67307
|
+
if (this.textures[id] != null) {
|
|
67308
|
+
// Forced content update of an existing texture: keep its identity AND
|
|
67309
|
+
// reference count so other owners survive the update. setTextureData
|
|
67310
|
+
// (below) frees and replaces the old glTexture, so the entry — including
|
|
67311
|
+
// its glTexture pointer — is preserved here rather than recreated.
|
|
67312
|
+
// Append (don't replace) onLoad so pending callbacks other owners
|
|
67313
|
+
// registered while the texture was still loading aren't silently dropped.
|
|
67314
|
+
this.textures[id].state = 'loading';
|
|
67196
67315
|
if (onLoad != null) {
|
|
67197
67316
|
this.textures[id].onLoad.push(onLoad);
|
|
67198
67317
|
}
|
|
67199
|
-
return this.textures[id].index;
|
|
67200
|
-
// removed by dead control flow
|
|
67201
|
-
|
|
67202
|
-
}
|
|
67203
|
-
var index = 0;
|
|
67204
|
-
if (this.textures[id] != null) {
|
|
67205
|
-
index = this.textures[id].index;
|
|
67206
67318
|
} else {
|
|
67207
|
-
|
|
67319
|
+
// Brand new texture: the first owner holds the only reference.
|
|
67320
|
+
this.textures[id] = {
|
|
67321
|
+
id: id,
|
|
67322
|
+
state: 'loading',
|
|
67323
|
+
onLoad: onLoad != null ? [onLoad] : [],
|
|
67324
|
+
handle: this.nextTextureHandle,
|
|
67325
|
+
refCount: 1
|
|
67326
|
+
};
|
|
67327
|
+
this.nextTextureHandle += 1;
|
|
67208
67328
|
}
|
|
67209
|
-
// If a texture already exists, then unload it
|
|
67210
|
-
this.deleteTexture(id);
|
|
67211
|
-
var gl = this.gl;
|
|
67212
|
-
this.textures[id] = {
|
|
67213
|
-
id: id,
|
|
67214
|
-
state: 'loading',
|
|
67215
|
-
onLoad: [],
|
|
67216
|
-
index: index
|
|
67217
|
-
};
|
|
67218
67329
|
var texture = this.textures[id];
|
|
67219
|
-
if (onLoad != null) {
|
|
67220
|
-
texture.onLoad.push(onLoad);
|
|
67221
|
-
}
|
|
67222
67330
|
// If the data is a url string, then load the data into an image
|
|
67223
67331
|
if (typeof data === 'string') {
|
|
67224
67332
|
this.setTextureData(id, loadColor);
|
|
@@ -67237,7 +67345,7 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67237
67345
|
this.onLoad(id);
|
|
67238
67346
|
texture.state = 'loaded';
|
|
67239
67347
|
}
|
|
67240
|
-
return this.textures[id].
|
|
67348
|
+
return this.textures[id].handle;
|
|
67241
67349
|
}
|
|
67242
67350
|
}, {
|
|
67243
67351
|
key: "getAtlas",
|
|
@@ -67273,6 +67381,27 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67273
67381
|
throw errors[0];
|
|
67274
67382
|
}
|
|
67275
67383
|
}
|
|
67384
|
+
|
|
67385
|
+
// Take an additional reference to an already-registered texture, without
|
|
67386
|
+
// re-uploading it or touching its load callbacks. Used when an element adopts
|
|
67387
|
+
// a texture another owner created and uploaded (e.g. a GLText element sharing
|
|
67388
|
+
// a font atlas registered by the Atlas). Returns false if the texture isn't
|
|
67389
|
+
// registered yet, in which case no reference was taken. Release with
|
|
67390
|
+
// deleteTexture.
|
|
67391
|
+
}, {
|
|
67392
|
+
key: "acquireTexture",
|
|
67393
|
+
value: function acquireTexture(id) {
|
|
67394
|
+
var texture = this.textures[id];
|
|
67395
|
+
if (texture == null) {
|
|
67396
|
+
return false;
|
|
67397
|
+
}
|
|
67398
|
+
texture.refCount += 1;
|
|
67399
|
+
return true;
|
|
67400
|
+
}
|
|
67401
|
+
|
|
67402
|
+
// Release one reference to a texture. The GL texture is freed only once the
|
|
67403
|
+
// last owner releases it, so one element's cleanup can't delete a texture
|
|
67404
|
+
// another element still shares (see addTexture for the rationale).
|
|
67276
67405
|
}, {
|
|
67277
67406
|
key: "deleteTexture",
|
|
67278
67407
|
value: function deleteTexture(id) {
|
|
@@ -67280,13 +67409,27 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67280
67409
|
if (texture == null) {
|
|
67281
67410
|
return;
|
|
67282
67411
|
}
|
|
67283
|
-
|
|
67412
|
+
texture.refCount -= 1;
|
|
67413
|
+
if (texture.refCount <= 0) {
|
|
67414
|
+
this.freeTexture(id);
|
|
67415
|
+
}
|
|
67416
|
+
}
|
|
67284
67417
|
|
|
67285
|
-
|
|
67418
|
+
// Unconditionally free a texture regardless of reference count. Used for
|
|
67419
|
+
// teardown (cleanup), where every owner is going away at once.
|
|
67420
|
+
}, {
|
|
67421
|
+
key: "freeTexture",
|
|
67422
|
+
value: function freeTexture(id) {
|
|
67423
|
+
var texture = this.textures[id];
|
|
67424
|
+
if (texture == null) {
|
|
67425
|
+
return;
|
|
67426
|
+
}
|
|
67427
|
+
var gl = this.gl;
|
|
67428
|
+
// gl.deleteTexture unbinds it from any unit it was bound to, so we just drop
|
|
67429
|
+
// the stale cache entries.
|
|
67286
67430
|
if (texture.glTexture != null) {
|
|
67287
|
-
gl.activeTexture(gl.TEXTURE0 + texture.index);
|
|
67288
|
-
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
67289
67431
|
gl.deleteTexture(texture.glTexture);
|
|
67432
|
+
this.clearBoundUnits(id);
|
|
67290
67433
|
}
|
|
67291
67434
|
this.cancel(id);
|
|
67292
67435
|
delete this.textures[id];
|
|
@@ -67298,6 +67441,53 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67298
67441
|
Object.keys(this.textures).forEach(function (id) {
|
|
67299
67442
|
_this3.textures[id].glTexture = null;
|
|
67300
67443
|
});
|
|
67444
|
+
// The new context starts with nothing bound, so the bind cache is stale.
|
|
67445
|
+
this.boundUnits = [];
|
|
67446
|
+
}
|
|
67447
|
+
|
|
67448
|
+
// Drop any working-unit cache entries that point at this texture id, so a
|
|
67449
|
+
// future draw re-binds rather than trusting a stale/deleted glTexture.
|
|
67450
|
+
}, {
|
|
67451
|
+
key: "clearBoundUnits",
|
|
67452
|
+
value: function clearBoundUnits(id) {
|
|
67453
|
+
for (var u = 0; u < this.boundUnits.length; u += 1) {
|
|
67454
|
+
if (this.boundUnits[u] === id) {
|
|
67455
|
+
this.boundUnits[u] = null;
|
|
67456
|
+
}
|
|
67457
|
+
}
|
|
67458
|
+
}
|
|
67459
|
+
|
|
67460
|
+
/*
|
|
67461
|
+
Bind a registered texture to a content texture unit for the current draw.
|
|
67462
|
+
Content units are a small shared pool (unit 0 is reserved for the
|
|
67463
|
+
target/selector framebuffer texture). bindTexture is only issued when the
|
|
67464
|
+
unit is not already pointing at this texture, so runs of draws that share a
|
|
67465
|
+
texture (e.g. text sharing a font atlas) issue zero bind calls.
|
|
67466
|
+
*/
|
|
67467
|
+
}, {
|
|
67468
|
+
key: "bindTextureToUnit",
|
|
67469
|
+
value: function bindTextureToUnit(id, unit) {
|
|
67470
|
+
var texture = this.textures[id];
|
|
67471
|
+
if (texture == null || texture.glTexture == null) {
|
|
67472
|
+
return false;
|
|
67473
|
+
}
|
|
67474
|
+
if (unit >= this.maxTextureUnits) {
|
|
67475
|
+
// Out of unit budget: warn once (not every frame) and don't issue an
|
|
67476
|
+
// out-of-range bind. The caller treats false as "not bound".
|
|
67477
|
+
if (!this.warnedUnitOverflow) {
|
|
67478
|
+
(0,_tools_tools__WEBPACK_IMPORTED_MODULE_2__.Console)("FigureOne WebGL warning: texture unit ".concat(unit, " exceeds this device's ") + "MAX_TEXTURE_IMAGE_UNITS (".concat(this.maxTextureUnits, "). Reduce the ") + 'number of simultaneous textures/masks.');
|
|
67479
|
+
this.warnedUnitOverflow = true;
|
|
67480
|
+
}
|
|
67481
|
+
return false;
|
|
67482
|
+
}
|
|
67483
|
+
if (this.boundUnits[unit] === id) {
|
|
67484
|
+
return true;
|
|
67485
|
+
}
|
|
67486
|
+
var gl = this.gl;
|
|
67487
|
+
gl.activeTexture(gl.TEXTURE0 + unit);
|
|
67488
|
+
gl.bindTexture(gl.TEXTURE_2D, texture.glTexture);
|
|
67489
|
+
this.boundUnits[unit] = id;
|
|
67490
|
+
return true;
|
|
67301
67491
|
}
|
|
67302
67492
|
}, {
|
|
67303
67493
|
key: "cleanup",
|
|
@@ -67311,9 +67501,10 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67311
67501
|
}
|
|
67312
67502
|
});
|
|
67313
67503
|
this.programs = [];
|
|
67314
|
-
//
|
|
67504
|
+
// Free all textures outright, ignoring reference counts — every owner is
|
|
67505
|
+
// going away in this teardown.
|
|
67315
67506
|
Object.keys(this.textures).forEach(function (id) {
|
|
67316
|
-
_this4.
|
|
67507
|
+
_this4.freeTexture(id);
|
|
67317
67508
|
});
|
|
67318
67509
|
this.textures = {};
|
|
67319
67510
|
// Clean up atlases
|
|
@@ -67334,20 +67525,27 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67334
67525
|
return (value & value - 1) === 0;
|
|
67335
67526
|
}
|
|
67336
67527
|
var texture = this.textures[id];
|
|
67337
|
-
var index = texture.index;
|
|
67338
67528
|
var gl = this.gl;
|
|
67529
|
+
// Upload happens on the first content unit as scratch. Any cache entry for
|
|
67530
|
+
// this id refers to the old glTexture we're about to replace, so invalidate
|
|
67531
|
+
// them all before binding the new one.
|
|
67532
|
+
var uploadUnit = 1;
|
|
67533
|
+
this.clearBoundUnits(id);
|
|
67339
67534
|
|
|
67340
67535
|
// If texture exists, then delete it
|
|
67341
67536
|
if (texture.glTexture != null) {
|
|
67342
|
-
gl.activeTexture(gl.TEXTURE0 +
|
|
67537
|
+
gl.activeTexture(gl.TEXTURE0 + uploadUnit);
|
|
67343
67538
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
67344
67539
|
gl.deleteTexture(texture.glTexture);
|
|
67345
67540
|
}
|
|
67346
67541
|
// Create a texture
|
|
67347
67542
|
var glTexture = gl.createTexture();
|
|
67348
67543
|
texture.glTexture = glTexture;
|
|
67349
|
-
gl.activeTexture(gl.TEXTURE0 +
|
|
67544
|
+
gl.activeTexture(gl.TEXTURE0 + uploadUnit);
|
|
67350
67545
|
gl.bindTexture(gl.TEXTURE_2D, glTexture);
|
|
67546
|
+
// The new glTexture is now bound to the upload unit; record it so a draw
|
|
67547
|
+
// that needs it on this unit can skip a redundant bind.
|
|
67548
|
+
this.boundUnits[uploadUnit] = id;
|
|
67351
67549
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
|
|
67352
67550
|
|
|
67353
67551
|
// If image is a color, then create s ingle pixel image of that color
|
|
@@ -67411,7 +67609,7 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67411
67609
|
value: function onLoad(id) {
|
|
67412
67610
|
var _this5 = this;
|
|
67413
67611
|
this.textures[id].onLoad.forEach(function (f) {
|
|
67414
|
-
return _this5.fnMap.exec(f, true, _this5.textures[id].
|
|
67612
|
+
return _this5.fnMap.exec(f, true, _this5.textures[id].handle);
|
|
67415
67613
|
});
|
|
67416
67614
|
this.textures[id].onLoad = [];
|
|
67417
67615
|
}
|
|
@@ -67420,7 +67618,7 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67420
67618
|
value: function cancel(id) {
|
|
67421
67619
|
var _this6 = this;
|
|
67422
67620
|
this.textures[id].onLoad.forEach(function (f) {
|
|
67423
|
-
return _this6.fnMap.exec(f, false, _this6.textures[id].
|
|
67621
|
+
return _this6.fnMap.exec(f, false, _this6.textures[id].handle);
|
|
67424
67622
|
});
|
|
67425
67623
|
this.textures[id].onLoad = [];
|
|
67426
67624
|
}
|
|
@@ -67485,6 +67683,11 @@ var WebGLInstance = /*#__PURE__*/function () {
|
|
|
67485
67683
|
value: function init(gl) {
|
|
67486
67684
|
this.gl = gl;
|
|
67487
67685
|
this.textures = {};
|
|
67686
|
+
this.boundUnits = [];
|
|
67687
|
+
this.nextTextureHandle = 1;
|
|
67688
|
+
this.warnedUnitOverflow = false;
|
|
67689
|
+
var maxUnits = gl.getParameter ? gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) : 0;
|
|
67690
|
+
this.maxTextureUnits = maxUnits || 8;
|
|
67488
67691
|
this.programs = [];
|
|
67489
67692
|
this.targetTexture = null;
|
|
67490
67693
|
this.lastUsedProgram = null;
|
|
@@ -82098,8 +82301,8 @@ var tools = {
|
|
|
82098
82301
|
*/
|
|
82099
82302
|
|
|
82100
82303
|
var Fig = {
|
|
82101
|
-
version: "1.
|
|
82102
|
-
gitHash: "
|
|
82304
|
+
version: "1.9.1",
|
|
82305
|
+
gitHash: "0adca1678",
|
|
82103
82306
|
tools: tools,
|
|
82104
82307
|
Figure: _js_figure_Figure__WEBPACK_IMPORTED_MODULE_5__["default"],
|
|
82105
82308
|
Recorder: _js_figure_Recorder_Recorder__WEBPACK_IMPORTED_MODULE_7__.Recorder,
|
package/llms-full.txt
CHANGED
|
@@ -115,6 +115,8 @@ Path to figure element(s) within a collection. Supports multiple formats:
|
|
|
115
115
|
| loadColor | TypeColor | `[0, 0, 1, 0.5]` | Color while loading |
|
|
116
116
|
| onLoad | () => void | | Callback after load |
|
|
117
117
|
|
|
118
|
+
A `texture` on the `gl` primitive can be recolored by region with one or more masks — see OBJ_TextureMask under OBJ_GenericGL.
|
|
119
|
+
|
|
118
120
|
### OBJ_FigurePrimitive (base for all primitives)
|
|
119
121
|
|
|
120
122
|
| Property | Type | Default | Description |
|
|
@@ -365,12 +367,47 @@ Extends OBJ_Generic (without drawType).
|
|
|
365
367
|
| attributes | OBJ_GLAttribute[] | | Shader attributes |
|
|
366
368
|
| uniforms | OBJ_GLUniform[] | | Shader uniforms |
|
|
367
369
|
| texture | OBJ_Texture | | Texture |
|
|
370
|
+
| mask | OBJ_TextureMask | | Single mask to recolor regions of `texture` |
|
|
371
|
+
| masks | OBJ_TextureMask[] | | Multiple masks (each adds 4 recolorable regions) |
|
|
372
|
+
| tints | (TypeColor \| null)[] | | Recolor per region; mask `m` uses `tints[4m]`…`tints[4m+3]` for its r,g,b,a channels (`null` = leave region unchanged) |
|
|
368
373
|
| dimension | `2` \| `3` | `2` | Coordinate dimensions |
|
|
369
374
|
| light | `'directional'` \| `'point'` \| null | null | Lighting |
|
|
370
375
|
| vertices | number[] \| object | | Vertex data |
|
|
371
376
|
| colors | number[] \| object | | Per-vertex colors |
|
|
372
377
|
| normals | number[] \| object | | Normal vectors |
|
|
373
378
|
|
|
379
|
+
A texture can be recolored by region using one or more masks. A mask shares the base texture's coordinates (same dimensions, aligned to the base image); each of its r, g, b, a channels marks a region. Mask `m` is recolored by `tints[4m]` (r), `tints[4m + 1]` (g), `tints[4m + 2]` (b), `tints[4m + 3]` (a); unmasked pixels keep the base texture's color. A single mask costs one extra texture fetch and four mixes; each additional mask adds one fetch and four mixes.
|
|
380
|
+
|
|
381
|
+
### OBJ_TextureMask
|
|
382
|
+
|
|
383
|
+
| Property | Type | Default | Description |
|
|
384
|
+
|---|---|---|---|
|
|
385
|
+
| src | string | | URL of the mask image |
|
|
386
|
+
| loadColor | TypeColor | `[0, 0, 0, 0]` | Color shown while the mask loads (transparent = nothing recolored until loaded) |
|
|
387
|
+
|
|
388
|
+
```js
|
|
389
|
+
// Recolor three regions of a texture with one mask. The mask image's red,
|
|
390
|
+
// green and blue channels mark the regions tinted by tints 0, 1 and 2.
|
|
391
|
+
figure.add({
|
|
392
|
+
make: 'gl',
|
|
393
|
+
vertices: [-0.8, -0.8, 0.8, -0.8, -0.8, 0.8, 0.8, -0.8, 0.8, 0.8, -0.8, 0.8],
|
|
394
|
+
texture: { src: './image.png', coords: [0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1] },
|
|
395
|
+
mask: { src: './mask.png' },
|
|
396
|
+
tints: [[1, 0, 0, 1], [0, 0.6, 0, 1], [0, 0, 1, 1]],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Two masks: mask 0 uses tints 0-3, mask 1 uses tints 4-7.
|
|
400
|
+
figure.add({
|
|
401
|
+
make: 'gl',
|
|
402
|
+
// ...vertices, texture...
|
|
403
|
+
masks: [{ src: './mask.png' }, { src: './mask1.png' }],
|
|
404
|
+
tints: [
|
|
405
|
+
[1, 0, 0, 1], [0, 0.6, 0, 1], [0, 0, 1, 1], null, // mask 0
|
|
406
|
+
[0.6, 0, 0.8, 1], // mask 1
|
|
407
|
+
],
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
374
411
|
### OBJ_Morph (`make: 'morph'`)
|
|
375
412
|
|
|
376
413
|
Morphable shape with multiple point arrays that can be animated between.
|
package/llms.txt
CHANGED
|
@@ -97,6 +97,16 @@ figure.add({
|
|
|
97
97
|
make: 'rectangle', width: 1.8, height: 1.2,
|
|
98
98
|
texture: { src: 'image.jpg', mapTo: [-0.9, -0.6, 1.8, 1.2] },
|
|
99
99
|
});
|
|
100
|
+
|
|
101
|
+
// Recolor regions of a texture with masks (gl primitive). Each mask's r,g,b,a
|
|
102
|
+
// channels mark regions; mask m is tinted by tints[4m]..tints[4m+3].
|
|
103
|
+
figure.add({
|
|
104
|
+
make: 'gl',
|
|
105
|
+
vertices: [-0.8, -0.8, 0.8, -0.8, -0.8, 0.8, 0.8, -0.8, 0.8, 0.8, -0.8, 0.8],
|
|
106
|
+
texture: { src: 'image.png', coords: [0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1] },
|
|
107
|
+
masks: [{ src: 'mask.png' }, { src: 'mask1.png' }],
|
|
108
|
+
tints: [[1, 0, 0, 1], [0, 0.6, 0, 1], [0, 0, 1, 1], null, [0.6, 0, 0.8, 1]],
|
|
109
|
+
});
|
|
100
110
|
```
|
|
101
111
|
|
|
102
112
|
### Element Properties
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "figureone",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Draw, animate and interact with shapes, text, plots and equations in Javascript. Create interactive slide shows, and interactive videos.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -76,6 +76,7 @@ declare class GLObject extends DrawingObject {
|
|
|
76
76
|
fragmentShader: TypeFragmentShader;
|
|
77
77
|
selectorVertexShader: TypeVertexShader;
|
|
78
78
|
selectorFragmentShader: TypeFragmentShader;
|
|
79
|
+
acquiredBaseTextureId: string | null;
|
|
79
80
|
constructor(webgl: WebGLInstance, vertexShader?: TypeVertexShader, fragmentShader?: TypeFragmentShader, selectorVertexShader?: TypeVertexShader, selectorFragShader?: TypeFragmentShader);
|
|
80
81
|
init(webgl: WebGLInstance): void;
|
|
81
82
|
initProgram(): void;
|
|
@@ -105,6 +106,18 @@ declare class GLObject extends DrawingObject {
|
|
|
105
106
|
addMaskTexture(location?: string, loadColor?: TypeColor): void;
|
|
106
107
|
updateTexture(data: HTMLImageElement): void;
|
|
107
108
|
initTexture(force?: boolean): void;
|
|
109
|
+
/**
|
|
110
|
+
* Adopt a base-texture reference to an already-registered texture (e.g. a
|
|
111
|
+
* shared font atlas uploaded by the Atlas, whose id this object renders with
|
|
112
|
+
* but does not upload itself).
|
|
113
|
+
*
|
|
114
|
+
* Releases any previously-held base reference first (so a font/atlas change
|
|
115
|
+
* rebalances correctly) and is idempotent when the id is unchanged. If the
|
|
116
|
+
* texture isn't registered yet, no reference is taken and acquiredBaseTextureId
|
|
117
|
+
* is cleared, so the later resetTextureBuffer release stays balanced (it only
|
|
118
|
+
* releases a reference that was actually acquired).
|
|
119
|
+
*/
|
|
120
|
+
setBaseTextureRef(id: string): void;
|
|
108
121
|
createTextureMap(xMinGL?: number, xMaxGL?: number, yMinGL?: number, yMaxGL?: number, xMinTex?: number, xMaxTex?: number, yMinTex?: number, yMaxTex?: number): void;
|
|
109
122
|
addVertices(vertices: Array<number>, dimension?: 2 | 3, usage?: TypeGLBufferUsage): void;
|
|
110
123
|
addVertices3(vertices: Array<number>, usage?: TypeGLBufferUsage): void;
|