@liquidcars/atlas 0.1.22 → 0.1.23
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 +6 -2
- package/dist/liquidcars-atlas.js +65 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ atlas.setRelationMode('all'); // 'all' | 'selected' | 'none'
|
|
|
55
55
|
atlas.setToolbarVisible(true); // también se puede usar toolbar="on" | "off"
|
|
56
56
|
atlas.zoomIn();
|
|
57
57
|
atlas.zoomOut();
|
|
58
|
+
atlas.panBy(-1.5, 0); // desplazamiento horizontal/vertical en el plano de cámara
|
|
59
|
+
atlas.panTo(0, 0, 0); // centra la cámara en un punto del mundo
|
|
58
60
|
atlas.fit();
|
|
59
61
|
atlas.frontView();
|
|
60
62
|
atlas.setShellLabels(true);
|
|
@@ -127,7 +129,7 @@ Geometrías incluidas en esta primera versión: `box`, `cylinder`, `sphere`, `co
|
|
|
127
129
|
|
|
128
130
|
La paleta puede cambiarse después de cargar el modelo mediante `atlas.setPalette({ nombre: color })` o asignando `atlas.palette = { ... }`. El cambio actualiza las piezas, aristas y relaciones sin reconstruir el modelo.
|
|
129
131
|
|
|
130
|
-
Las acciones de navegación y exploración
|
|
132
|
+
Las acciones de navegación y exploración también están disponibles para la página anfitriona: `zoomIn()`, `zoomOut()`, `panBy()`, `panTo()`, `fit()`, `frontView()`, `setShellLabels()`, `toggleShellLabels()`, `openAllContainers()` y `closeAllContainers()`. El usuario puede desplazar la vista con el botón derecho, con `Shift` + arrastre o mediante dos dedos en una pantalla táctil. Así se puede construir una barra de controles propia sin depender de la toolbar interna.
|
|
131
133
|
|
|
132
134
|
La escena también se puede editar en caliente. `updateModel(patch)` reemplaza
|
|
133
135
|
las secciones indicadas; `addEntity`, `updateEntity` y `removeEntity` gestionan
|
|
@@ -154,7 +156,9 @@ npm install ./liquidcars-atlas-0.1.22.tgz
|
|
|
154
156
|
|
|
155
157
|
## Estado
|
|
156
158
|
|
|
157
|
-
`0.1.
|
|
159
|
+
`0.1.23` añade desplazamiento lateral de cámara mediante ratón, teclado
|
|
160
|
+
modificador y gestos táctiles, además de la API pública `panBy()` y `panTo()`.
|
|
161
|
+
Se conserva la selección contextual de relaciones introducida en `0.1.22`: el usuario puede
|
|
158
162
|
seleccionar un tubo directamente, el componente lo realza, muestra sus extremos
|
|
159
163
|
y modo en el panel y emite el mismo evento `lc-select` con `kind: 'relation'`.
|
|
160
164
|
La API pública añade `selectRelation()`, `selectedRelation` y `selection`, y la
|
package/dist/liquidcars-atlas.js
CHANGED
|
@@ -29371,7 +29371,7 @@ var CSS = [
|
|
|
29371
29371
|
":host{display:block;position:relative;height:var(--lc-height,680px);min-height:420px;color:var(--lc-panel-title,#e9f0f5);font:14px Inter,system-ui,-apple-system,sans-serif;background:var(--lc-bg,#0a111a);border:1px solid #294252;border-radius:12px;overflow:hidden;contain:content}",
|
|
29372
29372
|
"*{box-sizing:border-box}.atlas{display:grid;grid-template-columns:minmax(0,1fr) 300px;height:100%}.atlas[data-panel=hidden]{grid-template-columns:1fr}.viewport{position:relative;min-width:0;overflow:hidden;background:radial-gradient(ellipse at 50% 45%,var(--lc-bg-glow,#1c2b39),var(--lc-bg-fade,#0a111a) 82%)}canvas{display:block;width:100%;height:100%;cursor:grab;touch-action:none}.toolbar{position:absolute;z-index:10;bottom:28px;left:50%;transform:translateX(-50%);display:flex;gap:3px;padding:6px;background:var(--lc-toolbar-bg,#15232fe8);border:1px solid var(--lc-toolbar-border,#3a5062);border-radius:8px;white-space:nowrap}.toolbar[data-hidden=true],.toolbar[hidden],.panel[data-hidden=true],.panel[hidden],.crumbs[data-hidden=true],.crumbs[hidden],.hint[data-hidden=true],.hint[hidden]{display:none!important}.toolbar button{height:30px;padding:0 9px;border:0;border-radius:5px;background:none;color:var(--lc-toolbar-text,#dbe7ec);cursor:pointer}.toolbar button:hover,.toolbar button.active{background:var(--lc-toolbar-active,#1f3d4b);color:var(--lc-toolbar-active-text,#c5f2e9)}.toolbar i{display:block;width:1px;margin:2px 3px;background:#38515f}.hint{position:absolute;z-index:9;top:8px;bottom:auto;left:0;right:0;text-align:center;padding:2px 8px;font-size:11px;line-height:16px;color:var(--lc-hint,#8da6b8);pointer-events:none}.panel{overflow:auto;padding:24px 20px;background:var(--lc-panel-bg,linear-gradient(140deg,#182633,#0e1925 65%));border-left:1px solid var(--lc-panel-border,#2a3a48)}.panel h2{font-size:26px;line-height:1.12;font-weight:470;margin:12px 0;color:var(--lc-panel-title,#e9f0f5)}.panel h3{font-size:11px;letter-spacing:1.4px;color:#91aabd;margin:24px 0 10px}.panel p{font-size:14px;line-height:1.55;color:var(--lc-panel-text,#b5c6d2)}.panel .eyebrow{font-size:11px;letter-spacing:1.7px;color:#6ad7c0}.panel .sub{font-size:13px;color:#8fa8bd;margin-bottom:16px}.panel button{display:block;width:100%;padding:10px 0;text-align:left;background:none;border:0;border-bottom:1px solid var(--lc-panel-border,#2a3a48);color:#d9e7eb;cursor:pointer}.panel button:hover{color:#6ad7c0}.panel .relation{padding:10px 0;border-bottom:1px solid var(--lc-panel-border,#2a3a48)}.panel .relation strong{display:block;font-weight:550}.panel .relation small{display:block;color:#86a6b9;font-size:12px;margin-top:4px}.panel .back{color:#9db7c8;font-size:13px}.panel .crumbs{display:flex;flex-wrap:wrap;gap:5px;align-items:center;margin:0 0 12px}.panel .crumbs button{display:inline;width:auto;padding:3px 7px;border:0;border-radius:4px;background:var(--lc-crumb-bg,#142733);color:var(--lc-crumb-text,#91b6c7);font-size:12px}.panel .crumbs button.current{background:var(--lc-crumb-active,#1c4a4d);color:var(--lc-crumb-active-text,#c5f2e9)}.panel .note{border-top:1px solid var(--lc-panel-border,#2a3a48);padding-top:16px;margin-top:22px;font-size:12px;color:#7692a6}.error{position:absolute;inset:20%;padding:18px;background:#172b3b;border:1px solid #7896a9;line-height:1.5;z-index:4}@media(max-width:900px){.toolbar{gap:0}.toolbar button{padding:0 6px;font-size:12px}}@media(max-width:760px){:host{height:680px}.atlas{grid-template-columns:1fr;grid-template-rows:minmax(420px,1fr) auto}.panel{max-height:250px;border-left:0;border-top:1px solid var(--lc-panel-border,#2a3a48);padding:18px}.toolbar{bottom:24px}.hint{top:8px;bottom:auto;display:block}}"
|
|
29373
29373
|
].join("");
|
|
29374
|
-
var TEMPLATE = '<div class="atlas"><section class="viewport"><canvas tabindex="0" aria-label="Modelo 3D interactivo"></canvas><div class="toolbar"><button data-action="minus" aria-label="Alejar">\u2212</button><button data-action="plus" aria-label="Acercar">+</button><i></i><button data-action="fit">\u2316 Encuadrar</button><button data-action="front">\u25B1 Frontal</button><button data-action="labels" class="active" aria-pressed="true">\u25A7 Etiquetas</button><i></i><button data-action="relations-all" aria-pressed="false">Todas</button><button data-action="relations-selected" class="active" aria-pressed="true">Selecci\xF3n</button><button data-action="relations-none" aria-pressed="false">Ocultar</button></div><div class="hint">
|
|
29374
|
+
var TEMPLATE = '<div class="atlas"><section class="viewport"><canvas tabindex="0" aria-label="Modelo 3D interactivo"></canvas><div class="toolbar"><button data-action="minus" aria-label="Alejar">\u2212</button><button data-action="plus" aria-label="Acercar">+</button><i></i><button data-action="fit">\u2316 Encuadrar</button><button data-action="front">\u25B1 Frontal</button><button data-action="labels" class="active" aria-pressed="true">\u25A7 Etiquetas</button><i></i><button data-action="relations-all" aria-pressed="false">Todas</button><button data-action="relations-selected" class="active" aria-pressed="true">Selecci\xF3n</button><button data-action="relations-none" aria-pressed="false">Ocultar</button></div><div class="hint">Gira: arrastra \xB7 Pan: \u21E7+arrastra / 2 dedos \xB7 Zoom: rueda o pellizco \xB7 Selecciona: clic</div></section><aside class="panel" aria-live="polite"></aside></div>';
|
|
29375
29375
|
function escapeHtml(value) {
|
|
29376
29376
|
return String(value ?? "").replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
29377
29377
|
}
|
|
@@ -29608,6 +29608,7 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29608
29608
|
this.focus = new Vector3();
|
|
29609
29609
|
this.targetFocus = new Vector3();
|
|
29610
29610
|
this.drag = null;
|
|
29611
|
+
this.pointers = /* @__PURE__ */ new Map();
|
|
29611
29612
|
this.relationGroup = new Group();
|
|
29612
29613
|
this.scene.add(this.relationGroup);
|
|
29613
29614
|
this.relations = [];
|
|
@@ -29830,6 +29831,27 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
29830
29831
|
this.invalidate();
|
|
29831
29832
|
return this;
|
|
29832
29833
|
}
|
|
29834
|
+
panBy(horizontal = 0, vertical = 0) {
|
|
29835
|
+
const direction = this.camera.getWorldDirection(new Vector3());
|
|
29836
|
+
const right = new Vector3().crossVectors(direction, this.camera.up).normalize();
|
|
29837
|
+
const up = new Vector3().crossVectors(right, direction).normalize();
|
|
29838
|
+
this.targetFocus.addScaledVector(right, Number(horizontal) || 0);
|
|
29839
|
+
this.targetFocus.addScaledVector(up, Number(vertical) || 0);
|
|
29840
|
+
this.invalidate();
|
|
29841
|
+
return this;
|
|
29842
|
+
}
|
|
29843
|
+
panTo(x = 0, y = 0, z = 0) {
|
|
29844
|
+
const value = Array.isArray(x) ? x : x && typeof x === "object" ? [x.x, x.y, x.z] : [x, y, z];
|
|
29845
|
+
this.targetFocus.set(Number(value[0]) || 0, Number(value[1]) || 0, Number(value[2]) || 0);
|
|
29846
|
+
this.invalidate();
|
|
29847
|
+
return this;
|
|
29848
|
+
}
|
|
29849
|
+
_panByPixels(dx, dy) {
|
|
29850
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
29851
|
+
const distance = Math.max(1, this.camera.position.distanceTo(this.focus));
|
|
29852
|
+
const worldPerPixel = 2 * distance * Math.tan(MathUtils.degToRad(this.camera.fov) / 2) / Math.max(1, rect.height);
|
|
29853
|
+
return this.panBy(-dx * worldPerPixel, dy * worldPerPixel);
|
|
29854
|
+
}
|
|
29833
29855
|
fit() {
|
|
29834
29856
|
const bounds = new Box3();
|
|
29835
29857
|
for (const node of this.nodes.values()) {
|
|
@@ -30463,28 +30485,60 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30463
30485
|
if (action === "relations-none") this.setRelationMode("none");
|
|
30464
30486
|
if (action) this.invalidate();
|
|
30465
30487
|
});
|
|
30488
|
+
this.canvas.addEventListener("contextmenu", (event) => event.preventDefault());
|
|
30466
30489
|
this.canvas.addEventListener("pointerdown", (event) => {
|
|
30467
|
-
if (event.
|
|
30468
|
-
this.
|
|
30490
|
+
if (event.pointerType !== "touch" && ![0, 1, 2].includes(event.button)) return;
|
|
30491
|
+
this.pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
30492
|
+
const mode = event.pointerType === "touch" ? "rotate" : event.shiftKey || event.button !== 0 ? "pan" : "rotate";
|
|
30493
|
+
this.drag = { pointerId: event.pointerId, pointerType: event.pointerType, button: event.button, mode, x: event.clientX, y: event.clientY, moved: false };
|
|
30494
|
+
if (event.pointerType === "touch" && this.pointers.size === 2) {
|
|
30495
|
+
const [a, b] = [...this.pointers.values()];
|
|
30496
|
+
this.drag = { mode: "touch-pan", x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, distance: Math.hypot(a.x - b.x, a.y - b.y), moved: true };
|
|
30497
|
+
}
|
|
30469
30498
|
this.canvas.setPointerCapture(event.pointerId);
|
|
30470
30499
|
this.canvas.style.cursor = "grabbing";
|
|
30471
30500
|
});
|
|
30472
30501
|
this.canvas.addEventListener("pointermove", (event) => {
|
|
30473
|
-
if (!this.drag) return;
|
|
30502
|
+
if (!this.drag || !this.pointers.has(event.pointerId)) return;
|
|
30503
|
+
this.pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
30504
|
+
if (event.pointerType === "touch" && this.pointers.size >= 2) {
|
|
30505
|
+
const [a, b] = [...this.pointers.values()];
|
|
30506
|
+
const x = (a.x + b.x) / 2;
|
|
30507
|
+
const y = (a.y + b.y) / 2;
|
|
30508
|
+
const distance = Math.max(1, Math.hypot(a.x - b.x, a.y - b.y));
|
|
30509
|
+
if (this.drag.mode !== "touch-pan") this.drag = { mode: "touch-pan", x, y, distance, moved: true };
|
|
30510
|
+
else {
|
|
30511
|
+
this._panByPixels(x - this.drag.x, y - this.drag.y);
|
|
30512
|
+
this.targetZoom = Math.min(2.8, Math.max(0.55, this.targetZoom * distance / Math.max(1, this.drag.distance)));
|
|
30513
|
+
Object.assign(this.drag, { x, y, distance, moved: true });
|
|
30514
|
+
}
|
|
30515
|
+
this.invalidate();
|
|
30516
|
+
return;
|
|
30517
|
+
}
|
|
30518
|
+
if (this.drag.pointerId !== event.pointerId) return;
|
|
30474
30519
|
const dx = event.clientX - this.drag.x;
|
|
30475
30520
|
const dy = event.clientY - this.drag.y;
|
|
30476
30521
|
if (Math.abs(dx) + Math.abs(dy) > 3) this.drag.moved = true;
|
|
30477
30522
|
this.drag.x = event.clientX;
|
|
30478
30523
|
this.drag.y = event.clientY;
|
|
30479
|
-
this.
|
|
30480
|
-
|
|
30524
|
+
if (this.drag.mode === "pan" || event.shiftKey) this._panByPixels(dx, dy);
|
|
30525
|
+
else {
|
|
30526
|
+
this.yaw -= dx * 5e-3;
|
|
30527
|
+
this.pitch = Math.max(-1.56, Math.min(1.56, this.pitch + dy * 5e-3));
|
|
30528
|
+
}
|
|
30481
30529
|
this.invalidate();
|
|
30482
30530
|
});
|
|
30483
|
-
|
|
30531
|
+
const endPointer = (event, cancelled = false) => {
|
|
30484
30532
|
const drag = this.drag;
|
|
30533
|
+
this.pointers.delete(event.pointerId);
|
|
30534
|
+
if (this.pointers.size) {
|
|
30535
|
+
const [pointerId, point] = this.pointers.entries().next().value;
|
|
30536
|
+
this.drag = { pointerId, pointerType: "touch", button: 0, mode: "rotate", x: point.x, y: point.y, moved: true };
|
|
30537
|
+
return;
|
|
30538
|
+
}
|
|
30485
30539
|
this.drag = null;
|
|
30486
30540
|
this.canvas.style.cursor = "grab";
|
|
30487
|
-
if (drag && !drag.moved) {
|
|
30541
|
+
if (!cancelled && drag && !drag.moved && drag.button === 0) {
|
|
30488
30542
|
const picked = this._pick(event);
|
|
30489
30543
|
if (picked?.kind === "entity") {
|
|
30490
30544
|
const node = picked.node;
|
|
@@ -30498,7 +30552,9 @@ var LiquidCarsAtlas = class extends HTMLElement {
|
|
|
30498
30552
|
}
|
|
30499
30553
|
}
|
|
30500
30554
|
this.invalidate();
|
|
30501
|
-
}
|
|
30555
|
+
};
|
|
30556
|
+
this.canvas.addEventListener("pointerup", (event) => endPointer(event));
|
|
30557
|
+
this.canvas.addEventListener("pointercancel", (event) => endPointer(event, true));
|
|
30502
30558
|
this.canvas.addEventListener("wheel", (event) => {
|
|
30503
30559
|
event.preventDefault();
|
|
30504
30560
|
this.targetZoom = Math.min(2.8, Math.max(0.55, this.targetZoom * Math.exp(-event.deltaY * 1e-3)));
|