@liquidcars/atlas 0.1.25 → 0.1.27
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 -6
- package/demo/index.html +2 -2
- package/dist/liquidcars-atlas.js +124 -14
- 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; los SVG también pueden proporcionarse inline. Para planos con
|
|
184
|
+
imagen, `imageFit` admite `cover`, `contain` y `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,14 +222,17 @@ 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.27.tgz
|
|
217
226
|
```
|
|
218
227
|
|
|
219
228
|
## Estado
|
|
220
229
|
|
|
221
|
-
`0.1.
|
|
230
|
+
`0.1.27` añade carga automática de imágenes SVG remotas, SVG mediante data URI
|
|
231
|
+
y SVG inline, manteniendo las mismas opciones de cara y ajuste que las imágenes
|
|
232
|
+
raster. `0.1.26` mantiene el centro como anclaje implícito de toda relación y utiliza
|
|
222
233
|
los puertos de geometrías externas únicamente cuando el modelo los solicita.
|
|
223
|
-
También evita recorrer los materiales de geometrías compuestas en cada frame
|
|
234
|
+
También evita recorrer los materiales de geometrías compuestas en cada frame,
|
|
235
|
+
refuerza la liberación de recursos y expone `getRenderStats()`.
|
|
224
236
|
`0.1.24` incorporó geometry packs independientes, namespaces, parámetros
|
|
225
237
|
personalizados, paletas locales, anchors e imágenes/iconos declarativos. La
|
|
226
238
|
versión `0.1.23` añadió desplazamiento lateral de cámara mediante ratón, teclado
|
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.27"></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.27</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.27 | MIT */
|
|
2
2
|
|
|
3
3
|
// src/three.core.js
|
|
4
4
|
/**
|
|
@@ -29642,18 +29642,90 @@ 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
|
+
let objectUrl = null;
|
|
29654
|
+
const abortController = new AbortController();
|
|
29655
|
+
const releaseObjectUrl = () => {
|
|
29656
|
+
if (!objectUrl) return;
|
|
29657
|
+
URL.revokeObjectURL(objectUrl);
|
|
29658
|
+
objectUrl = null;
|
|
29659
|
+
};
|
|
29660
|
+
const removeListeners = () => {
|
|
29661
|
+
bitmap.removeEventListener("load", onLoad);
|
|
29662
|
+
bitmap.removeEventListener("error", onError);
|
|
29663
|
+
};
|
|
29664
|
+
const onLoad = () => {
|
|
29665
|
+
removeListeners();
|
|
29666
|
+
releaseObjectUrl();
|
|
29667
|
+
if (cancelled || !material.map) return;
|
|
29668
|
+
const fit = part.imageFit || part.fit || "stretch";
|
|
29669
|
+
const imageAspect = bitmap.naturalWidth / Math.max(1, bitmap.naturalHeight);
|
|
29670
|
+
const baseWidth = Math.abs(Number(part.size?.[0])) || 1;
|
|
29671
|
+
const baseHeight = Math.abs(Number(part.size?.[1])) || 1;
|
|
29672
|
+
const faceAspect = baseWidth / baseHeight;
|
|
29673
|
+
if (fit === "cover") {
|
|
29674
|
+
if (imageAspect > faceAspect) {
|
|
29675
|
+
material.map.repeat.set(faceAspect / imageAspect, 1);
|
|
29676
|
+
material.map.offset.set((1 - material.map.repeat.x) / 2, 0);
|
|
29677
|
+
} else {
|
|
29678
|
+
material.map.repeat.set(1, imageAspect / faceAspect);
|
|
29679
|
+
material.map.offset.set(0, (1 - material.map.repeat.y) / 2);
|
|
29680
|
+
}
|
|
29681
|
+
} else if (fit === "contain" && mesh) {
|
|
29682
|
+
if (imageAspect > faceAspect) mesh.scale.y = baseWidth / imageAspect;
|
|
29683
|
+
else mesh.scale.x = baseHeight * imageAspect;
|
|
29684
|
+
}
|
|
29652
29685
|
material.map.needsUpdate = true;
|
|
29653
29686
|
invalidate?.();
|
|
29654
|
-
}
|
|
29655
|
-
|
|
29656
|
-
|
|
29687
|
+
};
|
|
29688
|
+
const onError = () => {
|
|
29689
|
+
removeListeners();
|
|
29690
|
+
releaseObjectUrl();
|
|
29691
|
+
if (!cancelled) invalidate?.();
|
|
29692
|
+
};
|
|
29693
|
+
material.userData.atlasImageCleanup = () => {
|
|
29694
|
+
cancelled = true;
|
|
29695
|
+
abortController.abort();
|
|
29696
|
+
removeListeners();
|
|
29697
|
+
releaseObjectUrl();
|
|
29698
|
+
bitmap.src = "";
|
|
29699
|
+
};
|
|
29700
|
+
bitmap.addEventListener("load", onLoad);
|
|
29701
|
+
bitmap.addEventListener("error", onError);
|
|
29702
|
+
const assignImageSource = async () => {
|
|
29703
|
+
const inlineSvg = /^\s*<svg(?:\s|>)/i.test(image);
|
|
29704
|
+
if (inlineSvg) {
|
|
29705
|
+
objectUrl = URL.createObjectURL(new Blob([image], { type: "image/svg+xml" }));
|
|
29706
|
+
bitmap.src = objectUrl;
|
|
29707
|
+
return;
|
|
29708
|
+
}
|
|
29709
|
+
if (/^(?:data|blob):/i.test(image)) {
|
|
29710
|
+
bitmap.src = image;
|
|
29711
|
+
return;
|
|
29712
|
+
}
|
|
29713
|
+
try {
|
|
29714
|
+
const response = await fetch(image, { mode: "cors", signal: abortController.signal });
|
|
29715
|
+
if (!response.ok) throw new Error(`Image request failed with ${response.status}`);
|
|
29716
|
+
let blob = await response.blob();
|
|
29717
|
+
const contentType = response.headers.get("content-type") || blob.type;
|
|
29718
|
+
const looksLikeSvg = /image\/svg\+xml/i.test(contentType) || /\.svg(?:[?#]|$)/i.test(image);
|
|
29719
|
+
if (looksLikeSvg && blob.type !== "image/svg+xml") blob = new Blob([await blob.text()], { type: "image/svg+xml" });
|
|
29720
|
+
if (cancelled) return;
|
|
29721
|
+
objectUrl = URL.createObjectURL(blob);
|
|
29722
|
+
bitmap.src = objectUrl;
|
|
29723
|
+
} catch (error) {
|
|
29724
|
+
if (cancelled || error?.name === "AbortError") return;
|
|
29725
|
+
bitmap.src = image;
|
|
29726
|
+
}
|
|
29727
|
+
};
|
|
29728
|
+
assignImageSource();
|
|
29657
29729
|
material.map.colorSpace = SRGBColorSpace;
|
|
29658
29730
|
material.needsUpdate = true;
|
|
29659
29731
|
} else if (icon != null && icon !== "") {
|
|
@@ -29661,7 +29733,7 @@ function createGeometryContent(definition, entity, modelPalette, invalidate) {
|
|
|
29661
29733
|
material.transparent = true;
|
|
29662
29734
|
material.needsUpdate = true;
|
|
29663
29735
|
}
|
|
29664
|
-
|
|
29736
|
+
mesh = new Mesh(geometry, material);
|
|
29665
29737
|
mesh.position.fromArray(Array.isArray(part.position) ? part.position : [0, 0, 0]);
|
|
29666
29738
|
mesh.scale.fromArray(Array.isArray(part.size) ? part.size : [1, 1, 1]);
|
|
29667
29739
|
if (Array.isArray(part.rotation)) mesh.rotation.set(...part.rotation.map(Number));
|
|
@@ -29820,6 +29892,7 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29820
29892
|
}
|
|
29821
29893
|
connectedCallback() {
|
|
29822
29894
|
globalThis.addEventListener?.("lc-geometry-registry-change", this._geometryRegistryListener);
|
|
29895
|
+
this.resizeObserver.observe(this.canvas);
|
|
29823
29896
|
if (this.hasAttribute("height")) this.style.setProperty("--lc-height", this.getAttribute("height"));
|
|
29824
29897
|
this._loadInitial();
|
|
29825
29898
|
}
|
|
@@ -29827,7 +29900,13 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29827
29900
|
globalThis.removeEventListener?.("lc-geometry-registry-change", this._geometryRegistryListener);
|
|
29828
29901
|
this.resizeObserver.disconnect();
|
|
29829
29902
|
if (this.animationFrame) cancelAnimationFrame(this.animationFrame);
|
|
29830
|
-
|
|
29903
|
+
this._clearObjects();
|
|
29904
|
+
if (this.renderer) {
|
|
29905
|
+
this.renderer.dispose();
|
|
29906
|
+
this.renderer.forceContextLoss?.();
|
|
29907
|
+
this.renderer = null;
|
|
29908
|
+
}
|
|
29909
|
+
this.ready = false;
|
|
29831
29910
|
this.dispatchEvent(new CustomEvent("lc-disconnect"));
|
|
29832
29911
|
}
|
|
29833
29912
|
attributeChangedCallback(name, previous, value) {
|
|
@@ -29861,6 +29940,28 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29861
29940
|
const relation = this.selectedRelation;
|
|
29862
29941
|
return relation ? { kind: "relation", id: relation.id || null, index: relation.index, relation } : null;
|
|
29863
29942
|
}
|
|
29943
|
+
getRenderStats() {
|
|
29944
|
+
const info = this.renderer?.info;
|
|
29945
|
+
return {
|
|
29946
|
+
ready: this.ready,
|
|
29947
|
+
nodes: this.nodes.size,
|
|
29948
|
+
relations: this.relations.length,
|
|
29949
|
+
relationObjects: this.relationGroup.children.length,
|
|
29950
|
+
animatedRelationObjects: this.relationGroup.children.filter((object) => object.userData?.start).length,
|
|
29951
|
+
memory: {
|
|
29952
|
+
geometries: info?.memory?.geometries || 0,
|
|
29953
|
+
textures: info?.memory?.textures || 0,
|
|
29954
|
+
programs: info?.programs?.length || 0
|
|
29955
|
+
},
|
|
29956
|
+
render: {
|
|
29957
|
+
frame: info?.render?.frame || 0,
|
|
29958
|
+
calls: info?.render?.calls || 0,
|
|
29959
|
+
triangles: info?.render?.triangles || 0,
|
|
29960
|
+
lines: info?.render?.lines || 0,
|
|
29961
|
+
points: info?.render?.points || 0
|
|
29962
|
+
}
|
|
29963
|
+
};
|
|
29964
|
+
}
|
|
29864
29965
|
get clickBehavior() {
|
|
29865
29966
|
return this.selectionMode;
|
|
29866
29967
|
}
|
|
@@ -30240,17 +30341,26 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30240
30341
|
return parent ? this._worldPosition(parent, trail).add(position) : position;
|
|
30241
30342
|
}
|
|
30242
30343
|
_clearObjects() {
|
|
30344
|
+
const geometries = /* @__PURE__ */ new Set();
|
|
30345
|
+
const materials = /* @__PURE__ */ new Set();
|
|
30346
|
+
const textures2 = /* @__PURE__ */ new Set();
|
|
30243
30347
|
for (const node of this.nodes.values()) {
|
|
30244
30348
|
node.group?.traverse((object) => {
|
|
30245
30349
|
if (!object.isMesh && !object.isSprite && !object.isLineSegments) return;
|
|
30246
|
-
object.geometry
|
|
30247
|
-
|
|
30248
|
-
|
|
30350
|
+
if (object.geometry && !geometries.has(object.geometry)) {
|
|
30351
|
+
geometries.add(object.geometry);
|
|
30352
|
+
object.geometry.dispose();
|
|
30353
|
+
}
|
|
30354
|
+
const objectMaterials = Array.isArray(object.material) ? object.material : [object.material];
|
|
30355
|
+
for (const material of objectMaterials.filter(Boolean)) {
|
|
30356
|
+
if (materials.has(material)) continue;
|
|
30357
|
+
materials.add(material);
|
|
30358
|
+
material.userData?.atlasImageCleanup?.();
|
|
30359
|
+
if (material.map && !textures2.has(material.map)) {
|
|
30360
|
+
textures2.add(material.map);
|
|
30361
|
+
material.map.dispose();
|
|
30362
|
+
}
|
|
30249
30363
|
material.dispose();
|
|
30250
|
-
});
|
|
30251
|
-
else {
|
|
30252
|
-
object.material?.map?.dispose();
|
|
30253
|
-
object.material?.dispose();
|
|
30254
30364
|
}
|
|
30255
30365
|
});
|
|
30256
30366
|
this.scene.remove(node.group);
|
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.27"></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.27</p>
|
|
23
23
|
</header>
|
|
24
24
|
<main>
|
|
25
25
|
<liquidcars-atlas id="atlas" height="720" relation-mode="selected" shell-labels="on"></liquidcars-atlas>
|