Mapping Fase 2: malla deformable (superficies curvas) + mascaras
- Superficies con rejilla n x n (hasta 6x6): arrastra cualquier punto para curvar, no solo las esquinas. Render perspectiva-correcto por celda (homografia cuadrado->celda). - Mascaras: cuadrilateros negros que tapan zonas / recortan derrames de luz. - Editable desde la previsualizacion del panel (raton/tactil) y el escenario: arrastrar puntos, +/- malla para subdividir, borrar por lista. - set_mapping acepta "masks"; persiste en data/mapping.json (ignorado en git). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8dd58bb293
commit
7c0be6b666
4 changed files with 460 additions and 334 deletions
|
|
@ -417,25 +417,58 @@ function renderProjectM() {
|
|||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Projection mapping: previsualizacion arrastrable + lista
|
||||
// Projection mapping (Fase 2): previsualizacion con malla + mascaras
|
||||
// ==========================================================================
|
||||
let mapSurfaces = []; // copia local (para editar sin parpadeos)
|
||||
let mapSel = null; // indice de superficie seleccionada
|
||||
let mapDrag = null; // { idx, corner|null, sx, sy, orig }
|
||||
let mapSurfaces = []; // superficies (malla) — copia local para editar
|
||||
let mapMasks = []; // mascaras (cuadrilateros negros)
|
||||
let mapSel = null; // { type:'surface'|'mask', id }
|
||||
let mapDrag = null; // { type, id, pt|null, sx, sy, orig }
|
||||
let mapEmitTimer = null;
|
||||
const MAP_H = 15; // radio del tirador (px)
|
||||
const MAP_H = 13; // radio del tirador (px)
|
||||
const MAP_MAXN = 6;
|
||||
const sidx = (c, r, n) => r * (n + 1) + c;
|
||||
const cl01 = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);
|
||||
|
||||
const cloneSurfaces = (arr) =>
|
||||
(arr || []).map((s) => ({ id: s.id, corners: s.corners.map((p) => [p[0], p[1]]) }));
|
||||
|
||||
// interpolacion bilineal dentro del cuadrilatero (pts = TL,TR,BR,BL)
|
||||
function bilerp(pts, u, v) {
|
||||
const tx = pts[0][0] + (pts[1][0] - pts[0][0]) * u;
|
||||
const ty = pts[0][1] + (pts[1][1] - pts[0][1]) * u;
|
||||
const bx = pts[3][0] + (pts[2][0] - pts[3][0]) * u;
|
||||
const by = pts[3][1] + (pts[2][1] - pts[3][1]) * u;
|
||||
return [tx + (bx - tx) * v, ty + (by - ty) * v];
|
||||
function mapMigrate(s) {
|
||||
if (Array.isArray(s.points) && s.n)
|
||||
return { id: s.id, n: s.n, points: s.points.map((p) => [p[0], p[1]]) };
|
||||
if (Array.isArray(s.corners)) {
|
||||
const c = s.corners;
|
||||
return { id: s.id, n: 1, points: [[c[0][0], c[0][1]], [c[1][0], c[1][1]],
|
||||
[c[3][0], c[3][1]], [c[2][0], c[2][1]]] }; // TL,TR,BL,BR
|
||||
}
|
||||
return { id: s.id, n: 1, points: [[0.3, 0.3], [0.7, 0.3], [0.3, 0.7], [0.7, 0.7]] };
|
||||
}
|
||||
function mapSample(s, u, v) {
|
||||
const n = s.n;
|
||||
let ci = Math.min(n - 1, Math.floor(u * n)); if (ci < 0) ci = 0;
|
||||
let cj = Math.min(n - 1, Math.floor(v * n)); if (cj < 0) cj = 0;
|
||||
const lu = u * n - ci, lv = v * n - cj;
|
||||
const P = (c, r) => s.points[sidx(c, r, n)];
|
||||
const tl = P(ci, cj), tr = P(ci + 1, cj), bl = P(ci, cj + 1), br = P(ci + 1, cj + 1);
|
||||
const ax = tl[0] + (tr[0] - tl[0]) * lu, ay = tl[1] + (tr[1] - tl[1]) * lu;
|
||||
const bx = bl[0] + (br[0] - bl[0]) * lu, by = bl[1] + (br[1] - bl[1]) * lu;
|
||||
return [ax + (bx - ax) * lv, ay + (by - ay) * lv];
|
||||
}
|
||||
function mapResample(s, newN) {
|
||||
const pts = [];
|
||||
for (let r = 0; r <= newN; r++)
|
||||
for (let c = 0; c <= newN; c++) pts.push(mapSample(s, c / newN, r / newN));
|
||||
return { id: s.id, n: newN, points: pts };
|
||||
}
|
||||
function mapPointInQuad(x, y, c) {
|
||||
let inside = false;
|
||||
for (let i = 0, j = 3; i < 4; j = i++) {
|
||||
const xi = c[i][0], yi = c[i][1], xj = c[j][0], yj = c[j][1];
|
||||
if (((yi > y) !== (yj > y)) && (x < ((xj - xi) * (y - yi)) / ((yj - yi) || 1e-9) + xi)) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
function mapOutline(s) {
|
||||
const n = s.n;
|
||||
return [s.points[sidx(0, 0, n)], s.points[sidx(n, 0, n)], s.points[sidx(n, n, n)], s.points[sidx(0, n, n)]];
|
||||
}
|
||||
const mapSelSurf = () => (mapSel && mapSel.type === "surface") ? mapSurfaces.find((s) => s.id === mapSel.id) : null;
|
||||
|
||||
function drawMapPreview() {
|
||||
const cv = $("#map-preview");
|
||||
|
|
@ -451,47 +484,46 @@ function drawMapPreview() {
|
|||
ctx.strokeStyle = "#333"; ctx.lineWidth = 1; ctx.strokeRect(0.5, 0.5, w - 1, h - 1);
|
||||
ctx.font = "600 13px system-ui, sans-serif";
|
||||
ctx.textAlign = "center"; ctx.textBaseline = "middle";
|
||||
|
||||
if (!mapSurfaces.length) {
|
||||
if (!mapSurfaces.length && !mapMasks.length) {
|
||||
ctx.fillStyle = "rgba(255,255,255,0.4)";
|
||||
ctx.fillText("Pulsa '+ Anadir superficie'", w / 2, h / 2);
|
||||
ctx.fillText("Pulsa '+ Superficie'", w / 2, h / 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// superficies (malla n x n)
|
||||
mapSurfaces.forEach((s, i) => {
|
||||
const pts = s.corners.map(([x, y]) => [x * w, y * h]);
|
||||
const sel = i === mapSel;
|
||||
// rejilla interna: hace visible la deformacion
|
||||
ctx.strokeStyle = sel ? "rgba(0,229,255,0.4)" : "rgba(180,255,0,0.22)";
|
||||
const n = s.n;
|
||||
const sel = mapSel && mapSel.type === "surface" && mapSel.id === s.id;
|
||||
const col = sel ? "#00e5ff" : "#b4ff00";
|
||||
const P = (c, r) => { const p = s.points[sidx(c, r, n)]; return [p[0] * w, p[1] * h]; };
|
||||
ctx.strokeStyle = sel ? "rgba(0,229,255,0.45)" : "rgba(180,255,0,0.3)";
|
||||
ctx.lineWidth = 1;
|
||||
const N = 4;
|
||||
for (let g = 0; g <= N; g++) {
|
||||
const t = g / N;
|
||||
ctx.beginPath();
|
||||
for (let k = 0; k <= N; k++) { const p = bilerp(pts, k / N, t); k ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]); }
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
for (let k = 0; k <= N; k++) { const p = bilerp(pts, t, k / N); k ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]); }
|
||||
ctx.stroke();
|
||||
for (let r = 0; r <= n; r++) { ctx.beginPath(); for (let c = 0; c <= n; c++) { const p = P(c, r); c ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]); } ctx.stroke(); }
|
||||
for (let c = 0; c <= n; c++) { ctx.beginPath(); for (let r = 0; r <= n; r++) { const p = P(c, r); r ? ctx.lineTo(p[0], p[1]) : ctx.moveTo(p[0], p[1]); } ctx.stroke(); }
|
||||
const cc = P((n / 2) | 0, (n / 2) | 0);
|
||||
ctx.fillStyle = col; ctx.fillText(String(i + 1), cc[0], cc[1]);
|
||||
for (let r = 0; r <= n; r++) for (let c = 0; c <= n; c++) {
|
||||
const p = P(c, r);
|
||||
ctx.beginPath(); ctx.arc(p[0], p[1], MAP_H, 0, Math.PI * 2);
|
||||
ctx.fillStyle = sel ? "#00e5ff" : "#fff"; ctx.fill();
|
||||
ctx.lineWidth = 2; ctx.strokeStyle = "#000"; ctx.stroke();
|
||||
}
|
||||
// contorno + relleno
|
||||
});
|
||||
// mascaras (rojo)
|
||||
mapMasks.forEach((m, i) => {
|
||||
const sel = mapSel && mapSel.type === "mask" && mapSel.id === m.id;
|
||||
const pts = m.corners.map(([x, y]) => [x * w, y * h]);
|
||||
ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]);
|
||||
for (let k = 1; k < 4; k++) ctx.lineTo(pts[k][0], pts[k][1]);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = sel ? "rgba(0,229,255,0.08)" : "rgba(180,255,0,0.05)";
|
||||
ctx.fill();
|
||||
ctx.lineWidth = sel ? 2.5 : 1.5;
|
||||
ctx.strokeStyle = sel ? "#00e5ff" : "#b4ff00";
|
||||
ctx.stroke();
|
||||
// numero
|
||||
ctx.fillStyle = "rgba(255,60,60,0.3)"; ctx.fill();
|
||||
ctx.lineWidth = sel ? 3 : 2; ctx.strokeStyle = sel ? "#ff2d2d" : "#ff6b6b";
|
||||
ctx.setLineDash([6, 4]); ctx.stroke(); ctx.setLineDash([]);
|
||||
const cx = (pts[0][0] + pts[1][0] + pts[2][0] + pts[3][0]) / 4;
|
||||
const cy = (pts[0][1] + pts[1][1] + pts[2][1] + pts[3][1]) / 4;
|
||||
ctx.fillStyle = sel ? "#00e5ff" : "rgba(180,255,0,0.9)";
|
||||
ctx.fillText(String(i + 1), cx, cy);
|
||||
// tiradores
|
||||
ctx.fillStyle = "#ff6b6b"; ctx.fillText("M" + (i + 1), cx, cy);
|
||||
pts.forEach(([px, py]) => {
|
||||
ctx.beginPath(); ctx.arc(px, py, MAP_H * 0.7, 0, Math.PI * 2);
|
||||
ctx.fillStyle = sel ? "#00e5ff" : "#fff"; ctx.fill();
|
||||
ctx.beginPath(); ctx.arc(px, py, MAP_H, 0, Math.PI * 2);
|
||||
ctx.fillStyle = "#ff6b6b"; ctx.fill();
|
||||
ctx.lineWidth = 2; ctx.strokeStyle = "#000"; ctx.stroke();
|
||||
});
|
||||
});
|
||||
|
|
@ -501,51 +533,54 @@ function mapLocalPt(ev) {
|
|||
const r = $("#map-preview").getBoundingClientRect();
|
||||
return { x: (ev.clientX - r.left) / r.width, y: (ev.clientY - r.top) / r.height, w: r.width, h: r.height };
|
||||
}
|
||||
function mapPointInQuad(x, y, c) {
|
||||
let inside = false;
|
||||
for (let i = 0, j = 3; i < 4; j = i++) {
|
||||
const xi = c[i][0], yi = c[i][1], xj = c[j][0], yj = c[j][1];
|
||||
if (((yi > y) !== (yj > y)) && (x < ((xj - xi) * (y - yi)) / ((yj - yi) || 1e-9) + xi)) inside = !inside;
|
||||
function mapPick(p) {
|
||||
const rx = (MAP_H + 8) / p.w, ry = (MAP_H + 8) / p.h;
|
||||
for (let i = mapSurfaces.length - 1; i >= 0; i--) {
|
||||
const s = mapSurfaces[i];
|
||||
for (let k = 0; k < s.points.length; k++) {
|
||||
const dx = (s.points[k][0] - p.x) / rx, dy = (s.points[k][1] - p.y) / ry;
|
||||
if (dx * dx + dy * dy <= 1) return { type: "surface", id: s.id, pt: k };
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
for (let i = mapMasks.length - 1; i >= 0; i--) {
|
||||
const c = mapMasks[i].corners;
|
||||
for (let k = 0; k < 4; k++) {
|
||||
const dx = (c[k][0] - p.x) / rx, dy = (c[k][1] - p.y) / ry;
|
||||
if (dx * dx + dy * dy <= 1) return { type: "mask", id: mapMasks[i].id, pt: k };
|
||||
}
|
||||
}
|
||||
for (let i = mapMasks.length - 1; i >= 0; i--)
|
||||
if (mapPointInQuad(p.x, p.y, mapMasks[i].corners)) return { type: "mask", id: mapMasks[i].id, pt: null };
|
||||
for (let i = mapSurfaces.length - 1; i >= 0; i--)
|
||||
if (mapPointInQuad(p.x, p.y, mapOutline(mapSurfaces[i]))) return { type: "surface", id: mapSurfaces[i].id, pt: null };
|
||||
return null;
|
||||
}
|
||||
function onMapDown(ev) {
|
||||
ev.preventDefault();
|
||||
const p = mapLocalPt(ev);
|
||||
const rx = (MAP_H + 8) / p.w, ry = (MAP_H + 8) / p.h;
|
||||
for (let i = mapSurfaces.length - 1; i >= 0; i--) {
|
||||
const c = mapSurfaces[i].corners;
|
||||
for (let k = 0; k < 4; k++) {
|
||||
const dx = (c[k][0] - p.x) / rx, dy = (c[k][1] - p.y) / ry;
|
||||
if (dx * dx + dy * dy <= 1) {
|
||||
mapSel = i; mapDrag = { idx: i, corner: k, sx: p.x, sy: p.y, orig: c.map((q) => q.slice()) };
|
||||
drawMapPreview(); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = mapSurfaces.length - 1; i >= 0; i--) {
|
||||
if (mapPointInQuad(p.x, p.y, mapSurfaces[i].corners)) {
|
||||
mapSel = i; mapDrag = { idx: i, corner: null, sx: p.x, sy: p.y, orig: mapSurfaces[i].corners.map((q) => q.slice()) };
|
||||
drawMapPreview(); return;
|
||||
}
|
||||
}
|
||||
mapSel = null; mapDrag = null; drawMapPreview();
|
||||
const hit = mapPick(p);
|
||||
if (!hit) { mapSel = null; mapDrag = null; drawMapPreview(); updateMapSelBar(); return; }
|
||||
mapSel = { type: hit.type, id: hit.id };
|
||||
const arr = hit.type === "surface"
|
||||
? mapSurfaces.find((s) => s.id === hit.id).points
|
||||
: mapMasks.find((m) => m.id === hit.id).corners;
|
||||
mapDrag = { type: hit.type, id: hit.id, pt: hit.pt, sx: p.x, sy: p.y, orig: arr.map((q) => q.slice()) };
|
||||
drawMapPreview(); updateMapSelBar();
|
||||
}
|
||||
function onMapMove(ev) {
|
||||
if (!mapDrag) return;
|
||||
ev.preventDefault();
|
||||
const p = mapLocalPt(ev);
|
||||
const dx = p.x - mapDrag.sx, dy = p.y - mapDrag.sy;
|
||||
const s = mapSurfaces[mapDrag.idx]; if (!s) return;
|
||||
const cl = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);
|
||||
if (mapDrag.corner == null) {
|
||||
for (let k = 0; k < 4; k++) {
|
||||
s.corners[k][0] = cl(mapDrag.orig[k][0] + dx);
|
||||
s.corners[k][1] = cl(mapDrag.orig[k][1] + dy);
|
||||
}
|
||||
const arr = mapDrag.type === "surface"
|
||||
? (mapSurfaces.find((s) => s.id === mapDrag.id) || {}).points
|
||||
: (mapMasks.find((m) => m.id === mapDrag.id) || {}).corners;
|
||||
if (!arr) return;
|
||||
if (mapDrag.pt == null) {
|
||||
for (let k = 0; k < arr.length; k++) { arr[k][0] = cl01(mapDrag.orig[k][0] + dx); arr[k][1] = cl01(mapDrag.orig[k][1] + dy); }
|
||||
} else {
|
||||
s.corners[mapDrag.corner][0] = cl(mapDrag.orig[mapDrag.corner][0] + dx);
|
||||
s.corners[mapDrag.corner][1] = cl(mapDrag.orig[mapDrag.corner][1] + dy);
|
||||
arr[mapDrag.pt][0] = cl01(mapDrag.orig[mapDrag.pt][0] + dx);
|
||||
arr[mapDrag.pt][1] = cl01(mapDrag.orig[mapDrag.pt][1] + dy);
|
||||
}
|
||||
drawMapPreview(); scheduleMapEmit();
|
||||
}
|
||||
|
|
@ -553,49 +588,54 @@ function onMapUp() { if (mapDrag) { emitMapNow(); mapDrag = null; } }
|
|||
function scheduleMapEmit() { if (!mapEmitTimer) mapEmitTimer = setTimeout(() => { mapEmitTimer = null; emitMapNow(); }, 60); }
|
||||
function emitMapNow() {
|
||||
if (mapEmitTimer) { clearTimeout(mapEmitTimer); mapEmitTimer = null; }
|
||||
socket.emit("set_mapping", { surfaces: mapSurfaces });
|
||||
socket.emit("set_mapping", { surfaces: mapSurfaces, masks: mapMasks });
|
||||
}
|
||||
|
||||
// refleja estado, dibuja la previsualizacion y la lista de superficies
|
||||
function updateMapSelBar() {
|
||||
const bar = $("#map-selbar");
|
||||
const s = mapSelSurf();
|
||||
if (!s) { bar.hidden = true; return; }
|
||||
bar.hidden = false;
|
||||
$("#map-sub-val").textContent = s.n + "x" + s.n;
|
||||
}
|
||||
|
||||
// refleja estado, dibuja previsualizacion y listas (superficies + mascaras)
|
||||
function renderMapping() {
|
||||
const m = state.mapping || { enabled: false, edit: false, surfaces: [] };
|
||||
const m = state.mapping || { enabled: false, edit: false, surfaces: [], masks: [] };
|
||||
const en = $("#map-enabled"), ed = $("#map-edit");
|
||||
if (document.activeElement !== en) en.checked = !!m.enabled;
|
||||
if (document.activeElement !== ed) ed.checked = !!m.edit;
|
||||
ed.disabled = !m.enabled;
|
||||
const surfaces = m.surfaces || [];
|
||||
const surfaces = m.surfaces || [], masks = m.masks || [];
|
||||
|
||||
// previsualizacion (no piso la geometria mientras arrastro aqui)
|
||||
if (!mapDrag) mapSurfaces = cloneSurfaces(surfaces);
|
||||
if (mapSel != null && mapSel >= mapSurfaces.length) mapSel = null;
|
||||
if (!mapDrag) {
|
||||
mapSurfaces = surfaces.map(mapMigrate);
|
||||
mapMasks = masks.map((k) => ({ id: k.id, corners: k.corners.map((p) => [p[0], p[1]]) }));
|
||||
if (mapSel) {
|
||||
const ok = mapSel.type === "surface" ? mapSurfaces.some((s) => s.id === mapSel.id) : mapMasks.some((k) => k.id === mapSel.id);
|
||||
if (!ok) mapSel = null;
|
||||
}
|
||||
}
|
||||
drawMapPreview();
|
||||
updateMapSelBar();
|
||||
|
||||
// lista con boton de borrar (se reconstruye solo si cambia)
|
||||
// lista (superficies + mascaras) con borrar
|
||||
const list = $("#map-list");
|
||||
const sig = surfaces.map((s) => s.id).join("|") + "#" + surfaces.length;
|
||||
const sig = surfaces.map((s) => s.id + ":" + (s.n || 1)).join("|") + "##" + masks.map((k) => k.id).join("|");
|
||||
if (list.dataset.sig === sig) return;
|
||||
list.dataset.sig = sig;
|
||||
list.innerHTML = "";
|
||||
if (!surfaces.length) return;
|
||||
surfaces.forEach((s, i) => {
|
||||
const row = document.createElement("div");
|
||||
row.className = "row";
|
||||
const lbl = document.createElement("span");
|
||||
lbl.className = "label";
|
||||
lbl.textContent = "Superficie " + (i + 1);
|
||||
const del = document.createElement("button");
|
||||
del.className = "cmd favdel";
|
||||
del.textContent = "✕";
|
||||
del.title = "Borrar superficie";
|
||||
del.addEventListener("click", () => {
|
||||
const rest = ((state.mapping && state.mapping.surfaces) || [])
|
||||
.filter((x) => x.id !== s.id);
|
||||
socket.emit("set_mapping", { surfaces: rest });
|
||||
});
|
||||
row.appendChild(lbl);
|
||||
row.appendChild(del);
|
||||
list.appendChild(row);
|
||||
});
|
||||
const addRow = (label, onDel) => {
|
||||
const row = document.createElement("div"); row.className = "row";
|
||||
const lbl = document.createElement("span"); lbl.className = "label"; lbl.textContent = label;
|
||||
const del = document.createElement("button"); del.className = "cmd favdel"; del.textContent = "✕";
|
||||
del.addEventListener("click", onDel);
|
||||
row.appendChild(lbl); row.appendChild(del); list.appendChild(row);
|
||||
};
|
||||
surfaces.forEach((s, i) => addRow("Superficie " + (i + 1) + " (" + (s.n || 1) + "x" + (s.n || 1) + ")",
|
||||
() => socket.emit("set_mapping", { surfaces: surfaces.filter((x) => x.id !== s.id) })));
|
||||
masks.forEach((k, i) => addRow("Mascara " + (i + 1),
|
||||
() => socket.emit("set_mapping", { masks: masks.filter((x) => x.id !== k.id) })));
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
|
|
@ -613,15 +653,33 @@ $("#map-add").addEventListener("click", () => {
|
|||
const cur = (state.mapping && state.mapping.surfaces) || [];
|
||||
const s = 0.2, cx = 0.5, cy = 0.5;
|
||||
const nueva = {
|
||||
id: "s" + Date.now() + "_" + Math.floor(Math.random() * 1e4),
|
||||
corners: [[cx - s, cy - s], [cx + s, cy - s], [cx + s, cy + s], [cx - s, cy + s]],
|
||||
id: "s" + Date.now() + "_" + Math.floor(Math.random() * 1e4), n: 1,
|
||||
points: [[cx - s, cy - s], [cx + s, cy - s], [cx - s, cy + s], [cx + s, cy + s]], // TL,TR,BL,BR
|
||||
};
|
||||
socket.emit("set_mapping",
|
||||
{ enabled: true, edit: true, surfaces: cur.concat([nueva]) });
|
||||
socket.emit("set_mapping", { enabled: true, edit: true, surfaces: cur.concat([nueva]) });
|
||||
});
|
||||
$("#map-add-mask").addEventListener("click", () => {
|
||||
const cur = (state.mapping && state.mapping.masks) || [];
|
||||
const s = 0.15, cx = 0.5, cy = 0.5;
|
||||
const nueva = {
|
||||
id: "m" + Date.now() + "_" + Math.floor(Math.random() * 1e4),
|
||||
corners: [[cx - s, cy - s], [cx + s, cy - s], [cx + s, cy + s], [cx - s, cy + s]], // TL,TR,BR,BL
|
||||
};
|
||||
socket.emit("set_mapping", { enabled: true, edit: true, masks: cur.concat([nueva]) });
|
||||
});
|
||||
$("#map-reset").addEventListener("click", () => {
|
||||
if (confirm("Borrar todas las superficies de mapping?"))
|
||||
socket.emit("set_mapping", { surfaces: [] });
|
||||
if (confirm("Borrar todas las superficies y mascaras de mapping?"))
|
||||
socket.emit("set_mapping", { surfaces: [], masks: [] });
|
||||
});
|
||||
$("#map-sub-inc").addEventListener("click", () => {
|
||||
const s = mapSelSurf(); if (!s || s.n >= MAP_MAXN) return;
|
||||
const ns = mapResample(s, s.n + 1);
|
||||
socket.emit("set_mapping", { surfaces: mapSurfaces.map((x) => x.id === s.id ? ns : x) });
|
||||
});
|
||||
$("#map-sub-dec").addEventListener("click", () => {
|
||||
const s = mapSelSurf(); if (!s || s.n <= 1) return;
|
||||
const ns = mapResample(s, s.n - 1);
|
||||
socket.emit("set_mapping", { surfaces: mapSurfaces.map((x) => x.id === s.id ? ns : x) });
|
||||
});
|
||||
// Previsualizacion arrastrable (raton + tactil via pointer events)
|
||||
(function () {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue