Visore <tmf-viewer> di TieVision-Station con no-layers su ogni immagine di TMF (#126)
Rotella senza Ctrl = zoom sul punto del mouse su tutte le immagini. image_viewer.js e vision_overlay.js copiati byte per byte da TieVision-Station (lavoro/daitarn-nolayers b1af911); attributo no-layers su ogni <tmf-viewer> (task_editor 3, task_execute 1, calibrazione_pagina 4). Test JS e pagina demo allineati. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019A9CSrh9rfM5WwqKtg8BSE
This commit is contained in:
@@ -361,5 +361,93 @@ const SHAPE_RE = /<(polygon|line|circle|path|polyline) /g;
|
||||
check('svg: coordinata stringa -> forma saltata', strNum === '');
|
||||
}
|
||||
|
||||
// --- #12: livelli e quote CAD ---
|
||||
{
|
||||
const VO = VisionOverlay;
|
||||
check('layerOf: layer esplicito vince sul ruolo',
|
||||
VO.layerOf({ role: 'found', layer: 'quote' }) === 'quote' && VO.layerOf({ role: 'roi', layer: 'costruzioni' }) === 'costruzioni');
|
||||
check('layerOf: senza campo found = costruzioni, altro = ricerca',
|
||||
VO.layerOf({ role: 'found' }) === 'costruzioni' && VO.layerOf({ role: 'roi' }) === 'ricerca' &&
|
||||
VO.layerOf({ role: 'reference' }) === 'ricerca' && VO.layerOf({}) === 'ricerca' && VO.layerOf(null) === 'ricerca');
|
||||
check('layerOf: layer ignoto -> secondo il ruolo', VO.layerOf({ role: 'found', layer: 'boh' }) === 'costruzioni');
|
||||
|
||||
const lin = { tipo: 'lineare', p1: [0, 0], p2: [100, 0], testo: null };
|
||||
const rag = { tipo: 'raggio', centro: [50, 50], r: 10, dir_deg: 90, testo: null };
|
||||
const ang = { tipo: 'angolo', centro: [50, 50], r: 20, start_deg: 0, extent_deg: 90, testo: null };
|
||||
// default
|
||||
let p = VO.quotaPunto(lin);
|
||||
check('quotaPunto lineare default = punto medio', near(p.x, 50) && near(p.y, 0), JSON.stringify(p));
|
||||
p = VO.quotaPunto(rag);
|
||||
check('quotaPunto raggio default = centro + 2r lungo dir (90 gradi = verso l\'alto a video)', near(p.x, 50) && near(p.y, 30), JSON.stringify(p));
|
||||
p = VO.quotaPunto(ang);
|
||||
{ // u a 45 gradi = (cos45, -sin45); T = centro + 1.5r u
|
||||
const e = 30 * Math.SQRT1_2;
|
||||
check('quotaPunto angolo default = centro + 1.5r a meta arco', near(p.x, 50 + e) && near(p.y, 50 - e), JSON.stringify(p));
|
||||
}
|
||||
// con testo: lineare p1->p2 lungo +x, n = (0, 1): dv positivo = verso il basso a video
|
||||
p = VO.quotaPunto({ tipo: 'lineare', p1: [0, 0], p2: [100, 0], testo: [10, 20] });
|
||||
check('quotaPunto lineare con testo [du,dv]', near(p.x, 60) && near(p.y, 20), JSON.stringify(p));
|
||||
// inverse
|
||||
[lin, rag, ang].forEach((q) => {
|
||||
[[3, -7], [-12.5, 40], [0, 0]].forEach((t) => {
|
||||
const q2 = Object.assign({}, q, { testo: t });
|
||||
const pt = VO.quotaPunto(q2);
|
||||
const back = VO.quotaTesto(q2, pt.x, pt.y);
|
||||
check('quotaTesto inverso di quotaPunto (' + q.tipo + ' ' + t + ')', near(back[0], t[0]) && near(back[1], t[1]), JSON.stringify(back));
|
||||
});
|
||||
// dal punto di default si torna a testo di default
|
||||
const d = VO.quotaPunto(q), back = VO.quotaTesto(q, d.x, d.y), p2 = VO.quotaPunto(Object.assign({}, q, { testo: back }));
|
||||
check('quotaTesto dal punto di default riproduce il default (' + q.tipo + ')', near(p2.x, d.x) && near(p2.y, d.y));
|
||||
});
|
||||
check('quota malformata: quotaPunto/quotaTesto null, quotaSvg stringa vuota',
|
||||
VO.quotaPunto(null) === null && VO.quotaPunto({ tipo: 'boh' }) === null &&
|
||||
VO.quotaPunto({ tipo: 'lineare', p1: [0, 'a'], p2: [1, 1] }) === null &&
|
||||
VO.quotaTesto({ tipo: 'raggio' }, 1, 1) === null &&
|
||||
VO.quotaSvg({ tipo: 'angolo', centro: [0, 0] }) === '' && VO.quotaSvg(undefined) === '');
|
||||
check('quotaPunto: testo malformato = default', near(VO.quotaPunto({ tipo: 'lineare', p1: [0, 0], p2: [10, 0], testo: ['x', 1] }).x, 5));
|
||||
|
||||
// quotaSvg lineare: linee di estensione solo con dv != 0
|
||||
const conDv = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [200, 0], testo: [0, 30] }, { zoom: 1, color: '#16a34a' });
|
||||
const senzaDv = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [200, 0], testo: [0, 0] }, { zoom: 1, color: '#16a34a' });
|
||||
check('quotaSvg lineare dv=30: 2 estensioni + 1 linea di misura, 2 frecce', count(conDv, /<line /g) === 3 && count(conDv, /<polygon /g) === 2, conDv);
|
||||
check('quotaSvg lineare dv=0: nessuna estensione (solo linea di misura), 2 frecce', count(senzaDv, /<line /g) === 1 && count(senzaDv, /<polygon /g) === 2, senzaDv);
|
||||
check('quotaSvg: tratto non-scaling 1.5 e colore passato', /stroke-width="1.5" vector-effect="non-scaling-stroke"/.test(conDv) && conDv.includes('#16a34a'));
|
||||
// frecce dentro/fuori: lineare lunga 200 a zoom 1 -> la punta sta in A (0, 30): primo vertice del poligono
|
||||
{ // dentro: punta in A, coda verso +u (x crescente) -> il secondo vertice ha x > 0? no: dentro la freccia punta verso fuori, coda verso l'interno (x>0)
|
||||
const m = conDv.match(/<polygon points="([^"]+)"/)[1].split(' ').map((s) => s.split(',').map(Number));
|
||||
check('freccia dentro: punta in A, coda verso l\'interno della misura', near(m[0][0], 0) && near(m[0][1], 30) && m[1][0] > 0, m.join('|'));
|
||||
const corta = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [10, 0], testo: [0, 30] }, { zoom: 1 });
|
||||
const mc = corta.match(/<polygon points="([^"]+)"/)[1].split(' ').map((s) => s.split(',').map(Number));
|
||||
check('freccia fuori (misura corta): punta in A, coda fuori (x < 0)', near(mc[0][0], 0) && mc[1][0] < 0, mc.join('|'));
|
||||
const zz = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [200, 0], testo: [0, 30] }, { zoom: 4 });
|
||||
const mz = zz.match(/<polygon points="([^"]+)"/)[1].split(' ').map((s) => s.split(',').map(Number));
|
||||
check('freccia: lunghezza fissa a video (9 px / zoom)', near(mz[1][0], 9 / 4) && count(zz, /<line /g) === 3, mz.join('|'));
|
||||
}
|
||||
// T oltre B: la linea di misura si allunga
|
||||
const oltre = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [100, 0], testo: [150, 30] }, { zoom: 1 });
|
||||
check('quotaSvg lineare: scritta oltre B allunga la linea di misura fino a lei', oltre.includes('x2="200"'), oltre);
|
||||
// raggio
|
||||
const rFuori = VO.quotaSvg({ tipo: 'raggio', centro: [50, 50], r: 10, dir_deg: 0, testo: [30, 0] }, { zoom: 1 });
|
||||
const rDentro = VO.quotaSvg({ tipo: 'raggio', centro: [50, 50], r: 10, dir_deg: 0, testo: [5, 0] }, { zoom: 1 });
|
||||
check('quotaSvg raggio: crocetta + linea + freccia (fuori)', count(rFuori, /<line /g) === 3 && count(rFuori, /<polygon /g) === 1, rFuori);
|
||||
{ // fuori: P = (60, 50), freccia verso il centro: coda a x > 60
|
||||
const m = rFuori.match(/<polygon points="([^"]+)"/)[1].split(' ').map((s) => s.split(',').map(Number));
|
||||
check('raggio fuori: punta in P sul cerchio, coda verso fuori', near(m[0][0], 60) && near(m[0][1], 50) && m[1][0] > 60, m.join('|'));
|
||||
const d = rDentro.match(/<polygon points="([^"]+)"/)[1].split(' ').map((s) => s.split(',').map(Number));
|
||||
check('raggio dentro: punta in P, coda verso il centro (x < 60)', near(d[0][0], 60) && d[1][0] < 60, d.join('|'));
|
||||
}
|
||||
// angolo
|
||||
const a1 = VO.quotaSvg(ang, { zoom: 1 });
|
||||
check('quotaSvg angolo: 2 estensioni radiali + arco + 2 frecce', count(a1, /<line /g) === 2 && count(a1, /<path /g) === 1 && count(a1, /<polygon /g) === 2, a1);
|
||||
// markup senza stringhe dei dati
|
||||
const evil = '<script>alert(1)</script>"onload="x';
|
||||
const s1 = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [100, 0], testo: [0, 20], nome: evil, extra: evil }, { zoom: 1, color: evil });
|
||||
const s2 = VO.quotaSvg({ tipo: 'lineare', p1: [0, 0], p2: [100, 0], testo: [0, evil] }, { zoom: 1 });
|
||||
check('quotaSvg: nessuna stringa dei dati nel markup, colore non in lista -> rosso',
|
||||
!/script|onload|alert/.test(s1) && s1.includes('#ef4444') && !/script|onload|alert/.test(s2), s1);
|
||||
check('quotaSvg: colori ammessi (esiti)', ['#16a34a', '#d97706', '#dc2626'].every((c) => VO.quotaSvg(lin, { color: c }).includes(c)));
|
||||
check('svg() invariato: uscita di un segmento', VO.svg([{ type: 'segment', role: 'found', row1: 0, col1: 0, row2: 1, col2: 1 }]).startsWith('<line x1="0"'));
|
||||
}
|
||||
|
||||
console.log('\n' + (total - failures) + '/' + total + ' controlli ok');
|
||||
process.exit(failures ? 1 : 0);
|
||||
|
||||
Reference in New Issue
Block a user