Пример
three 0.185.1: Prove BufferGeometry.attributes behavior regarding InterleavedBuffer sharing on clone, matrix transformation isolation, and write-only needsUpdate semantics in three 0.185.1.
Проверенный пример — npm three 0.185.1: Prove BufferGeometry.attributes behavior regarding InterleavedBuffer sharing on clone, matrix transformation…
sha256:ccf77a5f7c1d7339622c21cac780775cb81b2a9d16de462b90eb25a4a88382df
Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла.
Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди.
MIT-0
Свидетельства выполнения
Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.
- Основа свидетельства
- Подписанный контракт пройден
- Квитанции проверки
- 3
- Ключи подписи, собравшие его
- 3
Заявленная среда
node linux x64 node node npm
Среды запусков проверки
| Окружение | Контракт | Этапы | Запуск |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
| node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5… |
2026-09-07 |
Кейс
HOW- Цель
- Prove BufferGeometry.attributes behavior regarding InterleavedBuffer sharing on clone, matrix transformation isolation, and write-only needsUpdate semantics in three 0.185.1.
- Пакеты
- Символы
-
- BufferGeometry.attributes
- Окружение
- node
- Создан
- 2026-08-18T00:48:17Z
Контракт
- BufferGeometry.clone preserves shared InterleavedBuffer instances across attributes while standalone InterleavedBufferAttribute.clone de-interleaves into standard BufferAttribute, and BufferAttribute.needsUpdate is a write-only setter that increments version rather than exposing a readable boolean property.
- BufferGeometry.applyMatrix4 selectively transforms position and normal buffer attributes and increments their versions while leaving other attributes untouched.
- BufferGeometry.computeVertexNormals generates a normal BufferAttribute on the geometry without requiring manual setAttribute invocation.
- Direct dictionary assignment to BufferGeometry.attributes is reflected identically across query helpers, serialization, and cloning.
Файлы
- NOTES.md
- csx.json
- package-lock.json
- package.json
- test/contract.mjs
Исходный код
# three BufferGeometry.attributes behavior
## Prior Solution and Pinned Release
An initial search of known solutions returned sample `sha256:7af2379b90650905a89e1d9f8bc695ab98b906c41fb16742d1f4e118f4e5bc97`, which verified basic `setAttribute` and `deleteAttribute` interactions along with unrelated `BufferAttribute.getX`, `Object3D.traverse`, and `Texture.source` semantics.
This contract pins `three@0.185.1` exactly and verifies deeper memory layout, transformation isolation, and lifecycle behaviors of `BufferGeometry.attributes`.
## Observed Behaviors and Traps
1. **InterleavedBuffer sharing on clone vs standalone de-interleaving**:
Calling `.clone()` directly on an `InterleavedBufferAttribute` de-interleaves the data into a non-interleaved `BufferAttribute`. However, when multiple `InterleavedBufferAttribute`s sharing a single `InterleavedBuffer` are assigned to `BufferGeometry.attributes`, calling `geometry.clone()` retains the shared `InterleavedBuffer` across the cloned attributes.
2. **Write-only `needsUpdate` setter**:
`BufferAttribute.prototype.needsUpdate` is implemented solely as a setter that increments the internal `version` integer (`this.version++`). Reading `attr.needsUpdate` evaluates to `undefined`. Code expecting a boolean flag check (`if (attr.needsUpdate) ...`) fails silently.
3. **Selective transformation via `applyMatrix4`**:
Calling `geometry.applyMatrix4(matrix)` specifically transforms `attributes.position` and `attributes.normal` (incrementing their `version`), leaving all other attributes in `geometry.attributes` (such as `uv` or custom attributes) untouched.
4. **Dynamic normal generation**:
`geometry.computeVertexNormals()` automatically allocates and assigns a new `Float32BufferAttribute` to `geometry.attributes.normal` (or resets an existing matching buffer in place) using `attributes.position`.
5. **Direct dictionary reflections**:
`BufferGeometry.attributes` is a standard JavaScript dictionary. Direct property assignments and deletions (`geometry.attributes.custom = attr`, `delete geometry.attributes.custom`) reflect consistently across `getAttribute()`, `hasAttribute()`, `toJSON()`, and `clone()`.
{"case":{"believed":"Cloning an InterleavedBufferAttribute preserves its interleaved layout and BufferAttribute.needsUpdate is a readable boolean flag.","caseId":"case:sha256:432a86b9f80253955e369aa128b9b84e6e91b35d07ba35359405c3375ed96aec","contract":["BufferGeometry.clone preserves shared InterleavedBuffer instances across attributes while standalone InterleavedBufferAttribute.clone de-interleaves into standard BufferAttribute, and BufferAttribute.needsUpdate is a write-only setter that increments version rather than exposing a readable boolean property.","BufferGeometry.applyMatrix4 selectively transforms position and normal buffer attributes and increments their versions while leaving other attributes untouched.","BufferGeometry.computeVertexNormals generates a normal BufferAttribute on the geometry without requiring manual setAttribute invocation.","Direct dictionary assignment to BufferGeometry.attributes is reflected identically across query helpers, serialization, and cloning."],"goal":"Prove BufferGeometry.attributes behavior regarding InterleavedBuffer sharing on clone, matrix transformation isolation, and write-only needsUpdate semantics in three 0.185.1.","kind":"HOW","packages":["pkg:npm/three@0.185.1"],"schemaVersion":1,"symbols":["BufferGeometry.attributes"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/three@0.185.1"],"schemaVersion":1,"symbols":["BufferGeometry.attributes"],"verifierAdapter":"node-typescript@1"}
{
"name": "npm-three-buffergeometry-attributes",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "npm-three-buffergeometry-attributes",
"version": "1.0.0",
"dependencies": {
"three": "0.185.1"
}
},
"node_modules/three": {
"version": "0.185.1",
"resolved": "https://registry.npmjs.org/three/-/three-0.185.1.tgz",
"integrity": "sha512-5aojFCXKwnjBRZvUnt3WFfEcvUJgkN5LlijRFN95hMy8WVkG4I0QNcJE+OuWvuJ0bOdStrbfXn0pkd6/QyiAlg==",
"license": "MIT"
}
}
}
{
"name": "npm-three-buffergeometry-attributes",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "node test/contract.mjs"
},
"dependencies": {
"three": "0.185.1"
}
}
import assert from 'node:assert/strict';
import {
BufferAttribute,
BufferGeometry,
InterleavedBuffer,
InterleavedBufferAttribute,
Matrix4,
} from 'three';
// 1. BufferGeometry.clone preserves shared InterleavedBuffer instances across attributes while standalone InterleavedBufferAttribute.clone de-interleaves into standard BufferAttribute, and BufferAttribute.needsUpdate is a write-only setter that increments version rather than exposing a readable boolean property.
{
const bufferData = new Float32Array([
// pos (xyz), uv (uv)
0, 0, 0, 0.0, 0.0,
1, 0, 0, 1.0, 0.0,
0, 1, 0, 0.0, 1.0,
]);
const interleaved = new InterleavedBuffer(bufferData, 5);
const positionAttr = new InterleavedBufferAttribute(interleaved, 3, 0);
const uvAttr = new InterleavedBufferAttribute(interleaved, 2, 3);
// Standalone attr clone de-interleaves into a regular BufferAttribute
const standaloneClone = positionAttr.clone();
assert.equal(standaloneClone instanceof InterleavedBufferAttribute, false);
assert.equal(standaloneClone instanceof BufferAttribute, true);
assert.equal(standaloneClone.itemSize, 3);
assert.equal(standaloneClone.count, 3);
assert.equal(standaloneClone.array.length, 9);
// When attached to BufferGeometry.attributes, geometry.clone() retains InterleavedBuffer sharing
const geometry = new BufferGeometry();
geometry.setAttribute('position', positionAttr);
geometry.setAttribute('uv', uvAttr);
assert.strictEqual(geometry.attributes.position, positionAttr);
assert.strictEqual(geometry.attributes.uv, uvAttr);
assert.strictEqual(geometry.attributes.position.data, geometry.attributes.uv.data);
const clonedGeometry = geometry.clone();
assert.ok(clonedGeometry.attributes.position instanceof InterleavedBufferAttribute);
assert.ok(clonedGeometry.attributes.uv instanceof InterleavedBufferAttribute);
assert.notStrictEqual(clonedGeometry.attributes.position.data, geometry.attributes.position.data);
assert.strictEqual(clonedGeometry.attributes.position.data, clonedGeometry.attributes.uv.data);
// needsUpdate is write-only: it increments version but yields undefined on read
const regularAttr = new BufferAttribute(new Float32Array([1, 2, 3]), 3);
assert.equal(regularAttr.version, 0);
assert.equal(regularAttr.needsUpdate, undefined);
regularAttr.needsUpdate = true;
assert.equal(regularAttr.version, 1);
assert.equal(regularAttr.needsUpdate, undefined);
}
// 2. BufferGeometry.applyMatrix4 selectively transforms position and normal buffer attributes and increments their versions while leaving other attributes untouched.
{
const geometry = new BufferGeometry();
const positions = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1]);
const uvs = new Float32Array([0.5, 0.25, 0.5, 0.75, 0.25, 0.5]);
const custom = new Float32Array([10, 20, 30]);
geometry.setAttribute('position', new BufferAttribute(positions, 3));
geometry.setAttribute('normal', new BufferAttribute(normals, 3));
geometry.setAttribute('uv', new BufferAttribute(uvs, 2));
geometry.attributes.custom = new BufferAttribute(custom, 1);
const mat = new Matrix4().makeScale(2, 2, 2).setPosition(0, 5, 0);
geometry.applyMatrix4(mat);
// Position is scaled and translated, version is incremented
assert.equal(geometry.attributes.position.getX(0), 2);
assert.equal(geometry.attributes.position.getY(0), 5);
assert.equal(geometry.attributes.position.getZ(0), 0);
assert.equal(geometry.attributes.position.version, 1);
// UV and custom attributes in geometry.attributes are not touched (version remains 0)
assert.equal(geometry.attributes.uv.getX(0), 0.5);
assert.equal(geometry.attributes.uv.getY(0), 0.25);
assert.equal(geometry.attributes.uv.version, 0);
assert.equal(geometry.attributes.custom.getX(0), 10);
assert.equal(geometry.attributes.custom.version, 0);
}
// 3. BufferGeometry.computeVertexNormals generates a normal BufferAttribute on the geometry without requiring manual setAttribute invocation.
{
const geometry = new BufferGeometry();
geometry.setAttribute('position', new BufferAttribute(new Float32Array([
0, 0, 0,
1, 0, 0,
0, 1, 0,
]), 3));
assert.equal(geometry.attributes.normal, undefined);
geometry.computeVertexNormals();
assert.ok(geometry.attributes.normal instanceof BufferAttribute);
assert.equal(geometry.attributes.normal.count, 3);
assert.equal(geometry.attributes.normal.itemSize, 3);
assert.equal(geometry.attributes.normal.getX(0), 0);
assert.equal(geometry.attributes.normal.getY(0), 0);
assert.equal(geometry.attributes.normal.getZ(0), 1);
}
// 4. Direct dictionary assignment to BufferGeometry.attributes is reflected identically across query helpers, serialization, and cloning.
{
const geometry = new BufferGeometry();
const attr = new BufferAttribute(new Float32Array([1, 2, 3]), 3);
geometry.attributes.velocity = attr;
assert.strictEqual(geometry.getAttribute('velocity'), attr);
assert.equal(geometry.hasAttribute('velocity'), true);
const json = geometry.toJSON();
assert.ok(json.data.attributes.velocity !== undefined);
assert.deepEqual(json.data.attributes.velocity.array, [1, 2, 3]);
delete geometry.attributes.velocity;
assert.equal(geometry.hasAttribute('velocity'), false);
assert.strictEqual(geometry.getAttribute('velocity'), undefined);
}