A table's tabletop, chairs, footprint and handle derive from its number of places, chairs numbered clockwise from the top, in drawing centimetres. The seat a pointer designates is read from the same chairs the drawing places, through one implementation, so what is drawn and where a drop lands cannot disagree. No table rotates. Checked: 1681 node tests. --- FR --- [ADD] géométrie : les tables, leurs chaises, la désignation d'un siège Le plateau, les chaises, l'emprise et la poignée d'une table dérivent de son nombre de places, chaises numérotées depuis le haut dans le sens horaire, en centimètres de dessin. Le siège que désigne un pointeur se lit sur les chaises mêmes que le dessin pose, par une seule implémentation : ce qui est dessiné et l'endroit où tombe un dépôt ne peuvent diverger. Aucune table ne tourne. Vérifié : 1681 épreuves node. Assisted-by: Claude Opus 5.5
75 lines
3.2 KiB
JavaScript
75 lines
3.2 KiB
JavaScript
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||
|
||
// La désignation d'un siège (§ 7.3, § 14.10) : ce que contient un point de
|
||
// dessin, lu sur les géométries de tables.js, celles mêmes que le dessin pose.
|
||
// Le dépôt d'un glissement ne cherche pas sa cible dans le DOM : il convertit
|
||
// le point en coordonnées de dessin et le passe à designer.
|
||
|
||
/** Taille d'écran minimale d'une cible de siège, en px (§ 7.3). */
|
||
export const CIBLE_MIN_PX = 24;
|
||
|
||
// Vrai quand le point est dans le disque (ronde) ou le carré aux côtés
|
||
// parallèles aux axes (carrée) de demi-taille donnée, centré sur la table,
|
||
// bord compris.
|
||
function dans(point, { centre, forme }, demiTaille) {
|
||
const dx = point.x - centre.x;
|
||
const dy = point.y - centre.y;
|
||
return forme === 'ronde' ? Math.hypot(dx, dy) <= demiTaille : Math.max(Math.abs(dx), Math.abs(dy)) <= demiTaille;
|
||
}
|
||
|
||
/**
|
||
* Régime des cibles d'une table au zoom k : 'sieges' quand k × pas ≥
|
||
* CIBLE_MIN_PX, 'table' sinon. Le seuil se décide table par table, sur le
|
||
* pas de chacune.
|
||
*
|
||
* @param {number} k px/cm
|
||
* @param {import('./tables.js').GeometrieTable} geometrie
|
||
* @returns {'sieges'|'table'}
|
||
*/
|
||
export function regimeCibles(k, geometrie) {
|
||
return k * geometrie.pas >= CIBLE_MIN_PX ? 'sieges' : 'table';
|
||
}
|
||
|
||
/**
|
||
* Ce que désigne un point de dessin au zoom k.
|
||
*
|
||
* Les tables s'examinent de la dernière reçue à la première, l'ordre inverse
|
||
* de la peinture : la première dont la surface peinte contient le point
|
||
* décide. En régime table, la surface est l'emprise — disque d'une ronde,
|
||
* carré d'une carrée — et la table est désignée. En régime sièges, elle est
|
||
* le plateau et les chaises : un point dans une chaise désigne son siège ;
|
||
* un point sur le plateau ne désigne rien, et cache les chaises des tables
|
||
* peintes dessous ; entre les chaises, le point passe aux tables de dessous.
|
||
* Deux chaises d'une même table ne se recouvrent pas.
|
||
*
|
||
* @param {{x: number, y: number}} point cm
|
||
* @param {import('./tables.js').GeometrieTable[]} geometries dans l'ordre du dessin
|
||
* @param {number} k px/cm
|
||
* @returns {{table: number, siege: number}|{table: number}|null}
|
||
*/
|
||
export function designer(point, geometries, k) {
|
||
for (let i = geometries.length - 1; i >= 0; i -= 1) {
|
||
const geometrie = geometries[i];
|
||
if (regimeCibles(k, geometrie) === 'table') {
|
||
if (dans(point, geometrie, geometrie.rayonEmprise)) return { table: geometrie.id };
|
||
continue;
|
||
}
|
||
const chaise = geometrie.chaises.find((c) => Math.hypot(point.x - c.x, point.y - c.y) <= c.rayon);
|
||
if (chaise !== undefined) return { table: geometrie.id, siege: chaise.siege };
|
||
if (dans(point, geometrie, geometrie.demiTaille)) return null;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Vrai quand le point n'est dans l'emprise — disque ou carré — d'aucune
|
||
* table : le sol, où un dépôt retire la personne du tour.
|
||
*
|
||
* @param {{x: number, y: number}} point cm
|
||
* @param {import('./tables.js').GeometrieTable[]} geometries
|
||
* @returns {boolean}
|
||
*/
|
||
export function surLeSol(point, geometries) {
|
||
return !geometries.some((geometrie) => dans(point, geometrie, geometrie.rayonEmprise));
|
||
}
|