@liquidcars/atlas 0.1.24 → 0.1.26
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 +18 -5
- package/demo/index.html +2 -2
- package/dist/liquidcars-atlas.js +101 -21
- package/index.html +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,9 +180,11 @@ El modelo configura cada instancia sin conocer la implementación del paquete:
|
|
|
180
180
|
Cada definición puede declarar parámetros tipados, una paleta local,
|
|
181
181
|
proporciones canónicas y anchors. Las piezas declarativas admiten primitivas,
|
|
182
182
|
posición, rotación, escala, materiales, iconos de texto o emoji e imágenes por
|
|
183
|
-
URL/data URI.
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
URL/data URI. Para planos con imagen, `imageFit` admite `cover`, `contain` y
|
|
184
|
+
`stretch`. El renderer mantiene Three.js encapsulado para evitar que los packs
|
|
185
|
+
dependan de su versión interna. Las relaciones parten del centro por defecto;
|
|
186
|
+
`fromAnchor` y `toAnchor` quedan como overrides explícitos para casos
|
|
187
|
+
excepcionales.
|
|
186
188
|
|
|
187
189
|
Si un namespace no está registrado, Atlas conserva el modelo renderizable con
|
|
188
190
|
una caja roja de fallback y emite `lc-warning` con el código
|
|
@@ -200,6 +202,13 @@ gestionan relaciones. Cada operación reconstruye la geometría necesaria,
|
|
|
200
202
|
conserva la cámara y la selección cuando sigue siendo válida, y emite
|
|
201
203
|
`lc-model-change` con el tipo de operación y el modelo resultante.
|
|
202
204
|
|
|
205
|
+
`getRenderStats()` devuelve una instantánea de entidades, relaciones, objetos
|
|
206
|
+
animados, draw calls y contadores de geometrías, texturas y programas del
|
|
207
|
+
renderer. Resulta útil para comprobar que una secuencia repetida de mutaciones
|
|
208
|
+
vuelve a una línea base estable. Al reconstruir o desconectar el componente se
|
|
209
|
+
liberan geometrías, materiales, texturas, cargas de imagen pendientes y el
|
|
210
|
+
contexto WebGL.
|
|
211
|
+
|
|
203
212
|
## Distribución
|
|
204
213
|
|
|
205
214
|
El archivo `dist/liquidcars-atlas.js` es un módulo ESM autocontenido: Three.js se incluye dentro del bundle, por lo que el consumidor no necesita instalar otra dependencia para probarlo. El bundle no contiene ningún modelo de negocio ni datos de LiquidCars Atlas; los modelos se suministran aparte mediante `model`, `src` o un `<script type="application/json">`. Para una distribución privada se puede servir ese archivo desde un CDN propio o desde el mismo sitio. Para publicar en npm, después de revisar el API estable:
|
|
@@ -213,12 +222,16 @@ Una vez publicado, los consumidores podrán instalarlo con `npm install @liquidc
|
|
|
213
222
|
Para probar el paquete sin publicarlo, basta con instalar el tarball generado:
|
|
214
223
|
|
|
215
224
|
```bash
|
|
216
|
-
npm install ./liquidcars-atlas-0.1.
|
|
225
|
+
npm install ./liquidcars-atlas-0.1.26.tgz
|
|
217
226
|
```
|
|
218
227
|
|
|
219
228
|
## Estado
|
|
220
229
|
|
|
221
|
-
`0.1.
|
|
230
|
+
`0.1.26` mantiene el centro como anclaje implícito de toda relación y utiliza
|
|
231
|
+
los puertos de geometrías externas únicamente cuando el modelo los solicita.
|
|
232
|
+
También evita recorrer los materiales de geometrías compuestas en cada frame,
|
|
233
|
+
refuerza la liberación de recursos y expone `getRenderStats()`.
|
|
234
|
+
`0.1.24` incorporó geometry packs independientes, namespaces, parámetros
|
|
222
235
|
personalizados, paletas locales, anchors e imágenes/iconos declarativos. La
|
|
223
236
|
versión `0.1.23` añadió desplazamiento lateral de cámara mediante ratón, teclado
|
|
224
237
|
modificador y gestos táctiles, además de la API pública `panBy()` y `panTo()`.
|
package/demo/index.html
CHANGED
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
liquidcars-atlas { display: block; width: 100%; max-width: 1480px; margin: 0 auto; }
|
|
15
15
|
code { color: #85e5d3; }
|
|
16
16
|
</style>
|
|
17
|
-
<script type="module" src="../dist/liquidcars-atlas.js?v=0.1.
|
|
17
|
+
<script type="module" src="../dist/liquidcars-atlas.js?v=0.1.26"></script>
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
20
|
<header>
|
|
21
21
|
<h1>LiquidCars Atlas</h1>
|
|
22
|
-
<p>Prueba de distribución del componente <code><liquidcars-atlas></code> · v0.1.
|
|
22
|
+
<p>Prueba de distribución del componente <code><liquidcars-atlas></code> · v0.1.26</p>
|
|
23
23
|
</header>
|
|
24
24
|
<main>
|
|
25
25
|
<liquidcars-atlas id="atlas" height="720" relation-mode="selected" shell-labels="on"></liquidcars-atlas>
|
package/dist/liquidcars-atlas.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! LiquidCars Atlas 0.1.
|
|
1
|
+
/*! LiquidCars Atlas 0.1.26 | MIT */
|
|
2
2
|
|
|
3
3
|
// src/three.core.js
|
|
4
4
|
/**
|
|
@@ -29642,17 +29642,52 @@ function createGeometryContent(definition, entity, modelPalette, invalidate) {
|
|
|
29642
29642
|
opacity: Number.isFinite(Number(part.opacity)) ? Number(part.opacity) : 1,
|
|
29643
29643
|
side: part.doubleSided ? DoubleSide : FrontSide
|
|
29644
29644
|
});
|
|
29645
|
+
let mesh;
|
|
29645
29646
|
const icon = part.icon;
|
|
29646
29647
|
const image = part.image;
|
|
29647
29648
|
if (typeof image === "string" && image) {
|
|
29648
29649
|
const bitmap = new Image();
|
|
29649
29650
|
bitmap.crossOrigin = "anonymous";
|
|
29650
29651
|
material.map = new Texture(bitmap);
|
|
29651
|
-
|
|
29652
|
+
let cancelled = false;
|
|
29653
|
+
const removeListeners = () => {
|
|
29654
|
+
bitmap.removeEventListener("load", onLoad);
|
|
29655
|
+
bitmap.removeEventListener("error", onError);
|
|
29656
|
+
};
|
|
29657
|
+
const onLoad = () => {
|
|
29658
|
+
removeListeners();
|
|
29659
|
+
if (cancelled || !material.map) return;
|
|
29660
|
+
const fit = part.imageFit || part.fit || "stretch";
|
|
29661
|
+
const imageAspect = bitmap.naturalWidth / Math.max(1, bitmap.naturalHeight);
|
|
29662
|
+
const baseWidth = Math.abs(Number(part.size?.[0])) || 1;
|
|
29663
|
+
const baseHeight = Math.abs(Number(part.size?.[1])) || 1;
|
|
29664
|
+
const faceAspect = baseWidth / baseHeight;
|
|
29665
|
+
if (fit === "cover") {
|
|
29666
|
+
if (imageAspect > faceAspect) {
|
|
29667
|
+
material.map.repeat.set(faceAspect / imageAspect, 1);
|
|
29668
|
+
material.map.offset.set((1 - material.map.repeat.x) / 2, 0);
|
|
29669
|
+
} else {
|
|
29670
|
+
material.map.repeat.set(1, imageAspect / faceAspect);
|
|
29671
|
+
material.map.offset.set(0, (1 - material.map.repeat.y) / 2);
|
|
29672
|
+
}
|
|
29673
|
+
} else if (fit === "contain" && mesh) {
|
|
29674
|
+
if (imageAspect > faceAspect) mesh.scale.y = baseWidth / imageAspect;
|
|
29675
|
+
else mesh.scale.x = baseHeight * imageAspect;
|
|
29676
|
+
}
|
|
29652
29677
|
material.map.needsUpdate = true;
|
|
29653
29678
|
invalidate?.();
|
|
29654
|
-
}
|
|
29655
|
-
|
|
29679
|
+
};
|
|
29680
|
+
const onError = () => {
|
|
29681
|
+
removeListeners();
|
|
29682
|
+
if (!cancelled) invalidate?.();
|
|
29683
|
+
};
|
|
29684
|
+
material.userData.atlasImageCleanup = () => {
|
|
29685
|
+
cancelled = true;
|
|
29686
|
+
removeListeners();
|
|
29687
|
+
bitmap.src = "";
|
|
29688
|
+
};
|
|
29689
|
+
bitmap.addEventListener("load", onLoad);
|
|
29690
|
+
bitmap.addEventListener("error", onError);
|
|
29656
29691
|
bitmap.src = image;
|
|
29657
29692
|
material.map.colorSpace = SRGBColorSpace;
|
|
29658
29693
|
material.needsUpdate = true;
|
|
@@ -29661,7 +29696,7 @@ function createGeometryContent(definition, entity, modelPalette, invalidate) {
|
|
|
29661
29696
|
material.transparent = true;
|
|
29662
29697
|
material.needsUpdate = true;
|
|
29663
29698
|
}
|
|
29664
|
-
|
|
29699
|
+
mesh = new Mesh(geometry, material);
|
|
29665
29700
|
mesh.position.fromArray(Array.isArray(part.position) ? part.position : [0, 0, 0]);
|
|
29666
29701
|
mesh.scale.fromArray(Array.isArray(part.size) ? part.size : [1, 1, 1]);
|
|
29667
29702
|
if (Array.isArray(part.rotation)) mesh.rotation.set(...part.rotation.map(Number));
|
|
@@ -29820,6 +29855,7 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29820
29855
|
}
|
|
29821
29856
|
connectedCallback() {
|
|
29822
29857
|
globalThis.addEventListener?.("lc-geometry-registry-change", this._geometryRegistryListener);
|
|
29858
|
+
this.resizeObserver.observe(this.canvas);
|
|
29823
29859
|
if (this.hasAttribute("height")) this.style.setProperty("--lc-height", this.getAttribute("height"));
|
|
29824
29860
|
this._loadInitial();
|
|
29825
29861
|
}
|
|
@@ -29827,7 +29863,13 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29827
29863
|
globalThis.removeEventListener?.("lc-geometry-registry-change", this._geometryRegistryListener);
|
|
29828
29864
|
this.resizeObserver.disconnect();
|
|
29829
29865
|
if (this.animationFrame) cancelAnimationFrame(this.animationFrame);
|
|
29830
|
-
|
|
29866
|
+
this._clearObjects();
|
|
29867
|
+
if (this.renderer) {
|
|
29868
|
+
this.renderer.dispose();
|
|
29869
|
+
this.renderer.forceContextLoss?.();
|
|
29870
|
+
this.renderer = null;
|
|
29871
|
+
}
|
|
29872
|
+
this.ready = false;
|
|
29831
29873
|
this.dispatchEvent(new CustomEvent("lc-disconnect"));
|
|
29832
29874
|
}
|
|
29833
29875
|
attributeChangedCallback(name, previous, value) {
|
|
@@ -29861,6 +29903,28 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29861
29903
|
const relation = this.selectedRelation;
|
|
29862
29904
|
return relation ? { kind: "relation", id: relation.id || null, index: relation.index, relation } : null;
|
|
29863
29905
|
}
|
|
29906
|
+
getRenderStats() {
|
|
29907
|
+
const info = this.renderer?.info;
|
|
29908
|
+
return {
|
|
29909
|
+
ready: this.ready,
|
|
29910
|
+
nodes: this.nodes.size,
|
|
29911
|
+
relations: this.relations.length,
|
|
29912
|
+
relationObjects: this.relationGroup.children.length,
|
|
29913
|
+
animatedRelationObjects: this.relationGroup.children.filter((object) => object.userData?.start).length,
|
|
29914
|
+
memory: {
|
|
29915
|
+
geometries: info?.memory?.geometries || 0,
|
|
29916
|
+
textures: info?.memory?.textures || 0,
|
|
29917
|
+
programs: info?.programs?.length || 0
|
|
29918
|
+
},
|
|
29919
|
+
render: {
|
|
29920
|
+
frame: info?.render?.frame || 0,
|
|
29921
|
+
calls: info?.render?.calls || 0,
|
|
29922
|
+
triangles: info?.render?.triangles || 0,
|
|
29923
|
+
lines: info?.render?.lines || 0,
|
|
29924
|
+
points: info?.render?.points || 0
|
|
29925
|
+
}
|
|
29926
|
+
};
|
|
29927
|
+
}
|
|
29864
29928
|
get clickBehavior() {
|
|
29865
29929
|
return this.selectionMode;
|
|
29866
29930
|
}
|
|
@@ -30240,17 +30304,26 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30240
30304
|
return parent ? this._worldPosition(parent, trail).add(position) : position;
|
|
30241
30305
|
}
|
|
30242
30306
|
_clearObjects() {
|
|
30307
|
+
const geometries = /* @__PURE__ */ new Set();
|
|
30308
|
+
const materials = /* @__PURE__ */ new Set();
|
|
30309
|
+
const textures2 = /* @__PURE__ */ new Set();
|
|
30243
30310
|
for (const node of this.nodes.values()) {
|
|
30244
30311
|
node.group?.traverse((object) => {
|
|
30245
30312
|
if (!object.isMesh && !object.isSprite && !object.isLineSegments) return;
|
|
30246
|
-
object.geometry
|
|
30247
|
-
|
|
30248
|
-
|
|
30313
|
+
if (object.geometry && !geometries.has(object.geometry)) {
|
|
30314
|
+
geometries.add(object.geometry);
|
|
30315
|
+
object.geometry.dispose();
|
|
30316
|
+
}
|
|
30317
|
+
const objectMaterials = Array.isArray(object.material) ? object.material : [object.material];
|
|
30318
|
+
for (const material of objectMaterials.filter(Boolean)) {
|
|
30319
|
+
if (materials.has(material)) continue;
|
|
30320
|
+
materials.add(material);
|
|
30321
|
+
material.userData?.atlasImageCleanup?.();
|
|
30322
|
+
if (material.map && !textures2.has(material.map)) {
|
|
30323
|
+
textures2.add(material.map);
|
|
30324
|
+
material.map.dispose();
|
|
30325
|
+
}
|
|
30249
30326
|
material.dispose();
|
|
30250
|
-
});
|
|
30251
|
-
else {
|
|
30252
|
-
object.material?.map?.dispose();
|
|
30253
|
-
object.material?.dispose();
|
|
30254
30327
|
}
|
|
30255
30328
|
});
|
|
30256
30329
|
this.scene.remove(node.group);
|
|
@@ -30305,8 +30378,12 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30305
30378
|
try {
|
|
30306
30379
|
const content = createGeometryContent(definition, entity, this.modelData.palette, () => this.invalidate());
|
|
30307
30380
|
content.root.scale.multiply(new Vector3().fromArray(entity.s));
|
|
30381
|
+
node.appearanceMaterials = [];
|
|
30308
30382
|
content.root.traverse((object) => {
|
|
30309
|
-
if (object.isMesh)
|
|
30383
|
+
if (!object.isMesh) return;
|
|
30384
|
+
object.userData.node = node;
|
|
30385
|
+
const materials = Array.isArray(object.material) ? object.material : [object.material];
|
|
30386
|
+
node.appearanceMaterials.push(...materials.filter(Boolean));
|
|
30310
30387
|
});
|
|
30311
30388
|
node.mesh = content.root;
|
|
30312
30389
|
node.colorBindings = content.colorBindings;
|
|
@@ -30370,7 +30447,10 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30370
30447
|
const center = this._worldPosition(node);
|
|
30371
30448
|
const mode = node.anchor || "center";
|
|
30372
30449
|
const requestedAnchor = relation?.[isSource ? "fromAnchor" : "toAnchor"] || relation?.[isSource ? "from_anchor" : "to_anchor"];
|
|
30373
|
-
|
|
30450
|
+
// Geometry packs may advertise available ports, but their mere presence
|
|
30451
|
+
// must not alter the model. The centre remains the default endpoint;
|
|
30452
|
+
// ports are used only when the entity or relation explicitly requests one.
|
|
30453
|
+
if (mode === "port" || requestedAnchor) {
|
|
30374
30454
|
const ports = node.ports || {};
|
|
30375
30455
|
const port = ports[requestedAnchor] || ports[isSource ? "output" : "input"] || ports.default;
|
|
30376
30456
|
if (Array.isArray(port) && port.length >= 3) {
|
|
@@ -30631,12 +30711,12 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30631
30711
|
node.material.depthWrite = !transparent;
|
|
30632
30712
|
node.material.emissive?.set(node === this.selected ? node.material.color : 0);
|
|
30633
30713
|
node.material.emissiveIntensity = node === this.selected ? 0.15 : 0;
|
|
30634
|
-
} else {
|
|
30635
|
-
node.
|
|
30636
|
-
|
|
30637
|
-
|
|
30638
|
-
|
|
30639
|
-
|
|
30714
|
+
} else if (node._appearanceSelected !== (node === this.selected)) {
|
|
30715
|
+
for (const material of node.appearanceMaterials || []) {
|
|
30716
|
+
material.emissive?.set(node === this.selected ? material.color : 0);
|
|
30717
|
+
material.emissiveIntensity = node === this.selected ? 0.15 : 0;
|
|
30718
|
+
}
|
|
30719
|
+
node._appearanceSelected = node === this.selected;
|
|
30640
30720
|
}
|
|
30641
30721
|
if (node.edge?.material) node.edge.material.opacity = node === this.selected ? 1 : 0.65;
|
|
30642
30722
|
const opened = node.type === "shell" && this.open.has(node.id);
|
package/index.html
CHANGED
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
liquidcars-atlas { display: block; width: 100%; max-width: 1480px; margin: 0 auto; }
|
|
15
15
|
code { color: #85e5d3; }
|
|
16
16
|
</style>
|
|
17
|
-
<script type="module" src="./dist/liquidcars-atlas.js?v=0.1.
|
|
17
|
+
<script type="module" src="./dist/liquidcars-atlas.js?v=0.1.26"></script>
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
20
|
<header>
|
|
21
21
|
<h1>LiquidCars Atlas</h1>
|
|
22
|
-
<p>Prueba de distribución del componente <code><liquidcars-atlas></code> · v0.1.
|
|
22
|
+
<p>Prueba de distribución del componente <code><liquidcars-atlas></code> · v0.1.26</p>
|
|
23
23
|
</header>
|
|
24
24
|
<main>
|
|
25
25
|
<liquidcars-atlas id="atlas" height="720" relation-mode="selected" shell-labels="on"></liquidcars-atlas>
|