[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
|
|
// La géométrie d'une table (§ 7.1, § 7.7), en centimètres de dessin, l'axe y
|
|
|
|
|
|
// vers le bas : plateau tiré du nombre de places, chaises numérotées depuis le
|
|
|
|
|
|
// haut dans le sens horaire, emprise, poignée et origine du bloc de noms. Le
|
|
|
|
|
|
// dessin pose les chaises par geometrieTable et la désignation d'un siège
|
|
|
|
|
|
// (designation.js) lit les mêmes chaises : une seule implémentation, que le
|
|
|
|
|
|
// dessin et le dépôt partagent. Aucune table ne tourne : la géométrie ne
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
// porte aucun angle. La grille où se posent les tables des démonstrations et
|
|
|
|
|
|
// celles que l'opérateur crée a un seul pas, ecartsGrille.
|
|
|
|
|
|
import { constante } from './constantes.js';
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @typedef {Object} GeometrieTable en cm
|
|
|
|
|
|
* @property {number} id
|
|
|
|
|
|
* @property {number} numero
|
|
|
|
|
|
* @property {'ronde'|'carree'} forme
|
|
|
|
|
|
* @property {{x: number, y: number}} centre
|
|
|
|
|
|
* @property {number} demiTaille rayon du plateau (ronde) ou demi-côté (carrée)
|
|
|
|
|
|
* @property {number} pas distance entre deux sièges voisins, au bord du plateau
|
|
|
|
|
|
* @property {Array<{siege: number, x: number, y: number, rayon: number}>} chaises sièges 1 à places
|
|
|
|
|
|
* @property {{x: number, y: number}} poignee centre du disque de la poignée
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
* @property {number} rayonCible rayon du disque, centré sur une chaise, où un point désigne
|
|
|
|
|
|
* son siège en régime sièges : max(RAYON_CHAISE, pas / 2)
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
* @property {number} rayonEmprise demiTaille + 2 × RAYON_CHAISE : rayon du disque d'emprise
|
|
|
|
|
|
* (ronde) ou demi-côté du carré d'emprise (carrée)
|
|
|
|
|
|
* @property {{x: number, y: number}} origineListe coin haut gauche du bloc de noms
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/** Diamètre minimal d'une table ronde, côté minimal d'une carrée, en cm :
|
|
|
|
|
|
* réglage par défaut, non norme (§ 7.7). */
|
|
|
|
|
|
export const PLANCHER_DIAMETRE = 70;
|
|
|
|
|
|
/** Longueur de bord par convive, en cm (§ 7.7). */
|
|
|
|
|
|
export const LARGEUR_PAR_PLACE = 60;
|
|
|
|
|
|
/** Rayon d'une chaise, en cm. */
|
|
|
|
|
|
export const RAYON_CHAISE = 22;
|
|
|
|
|
|
/** Rayon du disque de la poignée, en cm. */
|
|
|
|
|
|
export const RAYON_POIGNEE = 15;
|
|
|
|
|
|
/** Distance entre l'emprise et le bord de la poignée, en cm. */
|
|
|
|
|
|
export const ECART_POIGNEE = 8;
|
[ADD] interface: cross-screen rendering checks, frame marks, badge plate
A screen walker reaches every screen of the iteration by gestures; on each,
in both themes, text holds 4.5:1 and marks 3:1 against what is painted
under them, every text passes through the translation tables, the conflict
hue paints only conflict marks, and no state differs by colour alone. Mode
frames now carry their mark, the count badge sits on a plate over the
conflict hatch, the name list clears the focus square, the participant form
is novalidate. Demonstrations space columns so a name list never runs over
a neighbour's chairs. Checked: 2676 node, 489 browser, 60 long tests.
--- FR ---
[ADD] interface : rendu transversal, marques des cadres, socle, pastille
Un parcours atteint par gestes chaque écran de l'itération ; sur chacun,
dans les deux thèmes, le texte tient 4,5:1 et les marques 3:1 sur ce qui
est peint dessous, tout texte passe par les tables de libellés, la teinte
de conflit ne peint que les marques de conflit, et aucun état ne diffère par
la seule couleur. Les cadres de mode portent leur marque, la pastille pose
sur un socle au-dessus de la trame, la liste des noms s'écarte du focus, le
formulaire est novalidate. Les démonstrations espacent leurs colonnes : une
liste ne court plus sur les chaises voisines. Vérifié : 2676, 489, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 14:06:56 -04:00
|
|
|
|
/** Distance entre l'emprise et le bloc de noms, en cm : le carré du focus
|
|
|
|
|
|
* que l'interface pose autour de l'emprise, ou d'une chaise, s'étend à
|
|
|
|
|
|
* 26 cm d'elle, plus la moitié de son trait ; le bloc commence au-delà, et
|
|
|
|
|
|
* aucune marque ne couvre la liste. */
|
|
|
|
|
|
export const ECART_LISTE = 36;
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
/** Écart minimal entre les centres de deux rangées de la grille des tables,
|
|
|
|
|
|
* en cm. */
|
|
|
|
|
|
export const ECART_RANGEES = 300;
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
|
|
|
|
|
|
// Accroissement du rayon d'une ronde pour une place de plus, au-dessus du
|
|
|
|
|
|
// plancher : le périmètre croît de LARGEUR_PAR_PLACE, le rayon de 60 / 2π.
|
|
|
|
|
|
const RAYON_PAR_PLACE = LARGEUR_PAR_PLACE / (2 * Math.PI);
|
|
|
|
|
|
|
|
|
|
|
|
// Les quatre côtés d'une carrée, dans l'ordre où ils reçoivent les places —
|
|
|
|
|
|
// haut, droit, bas, gauche — : le coin où commence le côté dans le sens
|
|
|
|
|
|
// horaire, en demi-côtés depuis le centre, la direction de parcours et la
|
|
|
|
|
|
// normale sortante.
|
|
|
|
|
|
const COTES = [
|
|
|
|
|
|
{ depart: { x: -1, y: -1 }, direction: { x: 1, y: 0 }, normale: { x: 0, y: -1 } },
|
|
|
|
|
|
{ depart: { x: 1, y: -1 }, direction: { x: 0, y: 1 }, normale: { x: 1, y: 0 } },
|
|
|
|
|
|
{ depart: { x: 1, y: 1 }, direction: { x: -1, y: 0 }, normale: { x: 0, y: 1 } },
|
|
|
|
|
|
{ depart: { x: -1, y: 1 }, direction: { x: 0, y: -1 }, normale: { x: -1, y: 0 } },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
// Lève RangeError sur un nombre de places qui n'est pas un entier ≥ 1 : la
|
|
|
|
|
|
// géométrie d'une table se calcule sans valeur de repli.
|
|
|
|
|
|
function exigerPlaces(places) {
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
if (!Number.isInteger(places) || places < 1) {
|
|
|
|
|
|
throw new RangeError(`nombre de places entier ≥ 1 attendu, reçu ${JSON.stringify(places)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
* Diamètre d'une table ronde (§ 7.7) : max(70, places × 60 / π). Le format 1
|
|
|
|
|
|
* ne porte aucun diamètre saisi : la formule dessine toute table, et le pas
|
|
|
|
|
|
* qu'en tire geometrieTable ne connaît qu'elle.
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @param {number} places entier ≥ 1
|
|
|
|
|
|
* @returns {number} cm
|
|
|
|
|
|
*/
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
export function diametre(places) {
|
|
|
|
|
|
exigerPlaces(places);
|
|
|
|
|
|
return Math.max(PLANCHER_DIAMETRE, (places * LARGEUR_PAR_PLACE) / Math.PI);
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Côté d'une table carrée : max(70, ⌈places / 4⌉ × 60) — le côté le plus
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
* garni porte ⌈places / 4⌉ sièges.
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @param {number} places entier ≥ 1
|
|
|
|
|
|
* @returns {number} cm
|
|
|
|
|
|
*/
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
export function cote(places) {
|
|
|
|
|
|
exigerPlaces(places);
|
|
|
|
|
|
return Math.max(PLANCHER_DIAMETRE, Math.ceil(places / 4) * LARGEUR_PAR_PLACE);
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Chaises d'une ronde : sur le cercle de rayon demiTaille + RAYON_CHAISE,
|
|
|
|
|
|
// siège s à l'angle −π/2 + 2π(s − 1)/places, le siège 1 en haut et la
|
|
|
|
|
|
// numérotation dans le sens horaire à l'écran (y vers le bas).
|
|
|
|
|
|
function chaisesRonde(centre, demiTaille, places) {
|
|
|
|
|
|
const rayon = demiTaille + RAYON_CHAISE;
|
|
|
|
|
|
return Array.from({ length: places }, (_, i) => {
|
|
|
|
|
|
const angle = -Math.PI / 2 + (2 * Math.PI * i) / places;
|
|
|
|
|
|
return { siege: i + 1, x: centre.x + rayon * Math.cos(angle), y: centre.y + rayon * Math.sin(angle), rayon: RAYON_CHAISE };
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Nombre de sièges de chaque côté d'une carrée, dans l'ordre de COTES :
|
|
|
|
|
|
// ⌊places / 4⌋ chacun, et une place en surplus à chacun des premiers côtés.
|
|
|
|
|
|
const siegesParCote = (places) => COTES.map((_, i) => Math.floor(places / 4) + (i < places % 4 ? 1 : 0));
|
|
|
|
|
|
|
|
|
|
|
|
// Chaises d'une carrée : sur chaque côté, n sièges espacés de côté / n, le
|
|
|
|
|
|
// premier à côté / 2n du coin de départ, leur centre à RAYON_CHAISE au-delà
|
|
|
|
|
|
// du bord ; le siège 1 est le premier du côté haut.
|
|
|
|
|
|
function chaisesCarree(centre, demiTaille, places) {
|
|
|
|
|
|
const chaises = [];
|
|
|
|
|
|
siegesParCote(places).forEach((nombre, i) => {
|
|
|
|
|
|
const { depart, direction, normale } = COTES[i];
|
|
|
|
|
|
for (let j = 0; j < nombre; j += 1) {
|
|
|
|
|
|
const avance = ((j + 0.5) * 2 * demiTaille) / nombre;
|
|
|
|
|
|
chaises.push({
|
|
|
|
|
|
siege: chaises.length + 1,
|
|
|
|
|
|
x: centre.x + depart.x * demiTaille + direction.x * avance + normale.x * RAYON_CHAISE,
|
|
|
|
|
|
y: centre.y + depart.y * demiTaille + direction.y * avance + normale.y * RAYON_CHAISE,
|
|
|
|
|
|
rayon: RAYON_CHAISE,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return chaises;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Géométrie d'une table, en cm (contrat de données, GeometrieTable).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Le pas d'une ronde est π × diamètre / places, calculé sous la forme
|
|
|
|
|
|
* max(60, 70π / places) qui lui est égale : elle rend 60 exactement dès que
|
|
|
|
|
|
* le périmètre borne la table, là où le produit rend 59,999… pour certains
|
|
|
|
|
|
* nombres de places et ferait basculer le régime des cibles (designation.js)
|
|
|
|
|
|
* sous le seuil. Le pas d'une carrée est son côté divisé par le nombre de
|
|
|
|
|
|
* sièges du côté le plus garni.
|
|
|
|
|
|
*
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
* La cible d'un siège est un disque de rayon max(RAYON_CHAISE, pas / 2)
|
|
|
|
|
|
* centré sur sa chaise : au zoom où le régime des cibles passe aux sièges,
|
|
|
|
|
|
* k × pas ≥ 24 px (designation.js), elle mesure au moins 24 px à l'écran.
|
|
|
|
|
|
* Deux chaises d'une même table sont à un pas au moins l'une de l'autre :
|
|
|
|
|
|
* deux cibles se touchent au plus, sans se recouvrir.
|
|
|
|
|
|
*
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
* La poignée est un disque de rayon RAYON_POIGNEE sur la diagonale à +45°
|
|
|
|
|
|
* (vers le bas à droite), son bord à ECART_POIGNEE de l'emprise : son centre
|
|
|
|
|
|
* est à rayonEmprise + 23 cm du centre d'une ronde, à rayonEmprise × √2 + 23
|
|
|
|
|
|
* cm de celui d'une carrée, au-delà du coin de son emprise.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {{id: number, numero: number, forme: 'ronde'|'carree', position: {x: number, y: number}}} table
|
|
|
|
|
|
* l'enregistrement de charge.tables ; position est le centre
|
|
|
|
|
|
* @param {number} places la capacité courante, entier ≥ 1
|
|
|
|
|
|
* @returns {GeometrieTable}
|
|
|
|
|
|
* @throws {RangeError} pour une forme inconnue ou un nombre de places invalide
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function geometrieTable(table, places) {
|
|
|
|
|
|
const { id, numero, forme } = table;
|
|
|
|
|
|
if (forme !== 'ronde' && forme !== 'carree') {
|
|
|
|
|
|
throw new RangeError(`forme de table inconnue : ${JSON.stringify(forme)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
const centre = { x: table.position.x, y: table.position.y };
|
|
|
|
|
|
const ronde = forme === 'ronde';
|
|
|
|
|
|
const demiTaille = (ronde ? diametre(places) : cote(places)) / 2;
|
|
|
|
|
|
const pas = ronde
|
|
|
|
|
|
? Math.max(LARGEUR_PAR_PLACE, (PLANCHER_DIAMETRE * Math.PI) / places)
|
|
|
|
|
|
: (2 * demiTaille) / Math.ceil(places / 4);
|
|
|
|
|
|
const chaises = ronde ? chaisesRonde(centre, demiTaille, places) : chaisesCarree(centre, demiTaille, places);
|
|
|
|
|
|
const rayonEmprise = demiTaille + 2 * RAYON_CHAISE;
|
|
|
|
|
|
const distancePoignee = (ronde ? rayonEmprise : rayonEmprise * Math.SQRT2) + ECART_POIGNEE + RAYON_POIGNEE;
|
|
|
|
|
|
const decalage = distancePoignee / Math.SQRT2;
|
|
|
|
|
|
return {
|
|
|
|
|
|
id,
|
|
|
|
|
|
numero,
|
|
|
|
|
|
forme,
|
|
|
|
|
|
centre,
|
|
|
|
|
|
demiTaille,
|
|
|
|
|
|
pas,
|
|
|
|
|
|
chaises,
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
rayonCible: Math.max(RAYON_CHAISE, pas / 2),
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
poignee: { x: centre.x + decalage, y: centre.y + decalage },
|
|
|
|
|
|
rayonEmprise,
|
|
|
|
|
|
origineListe: { x: centre.x + rayonEmprise + ECART_LISTE, y: centre.y - demiTaille },
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
* Rectangle englobant de ce que le dessin pose de chaque table et qui se
|
|
|
|
|
|
* pointe : le carré de demi-côté rayonEmprise centré sur elle — il
|
|
|
|
|
|
* circonscrit le disque d'une ronde et égale l'emprise d'une carrée —, le
|
|
|
|
|
|
* carré circonscrit à la cible de chaque siège, qui déborde l'emprise de
|
|
|
|
|
|
* pas / 2 − RAYON_CHAISE, et celui du disque de la poignée, posée au-delà du
|
|
|
|
|
|
* coin d'une carrée. L'ajustement (vue.js) le fait tenir dans le cadre :
|
|
|
|
|
|
* toute cible et toute poignée y restent visibles.
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
*
|
|
|
|
|
|
* @param {GeometrieTable[]} geometries
|
|
|
|
|
|
* @returns {{minX: number, minY: number, maxX: number, maxY: number}|null} null sans table
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function englobant(geometries) {
|
|
|
|
|
|
if (geometries.length === 0) return null;
|
|
|
|
|
|
let minX = Infinity;
|
|
|
|
|
|
let minY = Infinity;
|
|
|
|
|
|
let maxX = -Infinity;
|
|
|
|
|
|
let maxY = -Infinity;
|
[FIX] iteration 3 review: seat targets, gestures, warnings, settings
The wheel no longer zooms during a gesture, so a drop lands under the
ghost. A seat is a target the size of its pitch. A floor drop refuses to
remove an every-tour reservation. A seat reservation the retained plan
does not hold is flagged at the seat, with its swap as remedy. DERIVE names
the person; capacities have an upper bound; Generate waits for a pending
setting; a worker that fails to load shows a refusal; § 5.3 constraints
get their switches. Commands are not re-entrant and a double click does not
confirm. Checked: 2755 node, 550 browser tests, from the index alone.
--- FR ---
[FIX] revue de l'itération 3 : cibles, gestes, avertissements, réglages
La molette ne zoome plus pendant un geste : le dépôt tombe sous le fantôme.
Un siège est une cible de la taille de son pas. Un dépôt sur le sol refuse
de retirer une réservation de tous les tours. Une réservation à un siège
que le retenu ne tient pas est relevée au siège, l'échange en remède. La
dérive nomme la personne ; les capacités ont une borne ; Générer attend un
réglage en cours ; un travailleur qui ne se charge pas donne un refus ; le
§ 5.3 a ses bascules. Les commandes ne sont pas ré-entrantes et un double
clic ne confirme pas. Vérifié : 2755 node, 550 navigateur, index seul.
Assisted-by: Claude Opus 5.5
2026-10-07 17:50:35 -04:00
|
|
|
|
const etendre = ({ x, y }, demi) => {
|
|
|
|
|
|
minX = Math.min(minX, x - demi);
|
|
|
|
|
|
minY = Math.min(minY, y - demi);
|
|
|
|
|
|
maxX = Math.max(maxX, x + demi);
|
|
|
|
|
|
maxY = Math.max(maxY, y + demi);
|
|
|
|
|
|
};
|
|
|
|
|
|
for (const { centre, rayonEmprise, chaises, rayonCible, poignee } of geometries) {
|
|
|
|
|
|
etendre(centre, rayonEmprise);
|
|
|
|
|
|
for (const chaise of chaises) etendre(chaise, rayonCible);
|
|
|
|
|
|
etendre(poignee, RAYON_POIGNEE);
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
}
|
|
|
|
|
|
return { minX, minY, maxX, maxY };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Le pas de la grille des tables, en cm entiers, pour la plus grande emprise
|
|
|
|
|
|
* E des tables données, chacune de sa forme et de son nombre de places :
|
|
|
|
|
|
* entre deux colonnes, ⌈2E + 2 × ECART_LISTE + LARGEUR_BLOC_NOMS_CM⌉ — le bloc
|
|
|
|
|
|
* de noms d'une table, à ECART_LISTE de son emprise et large de
|
|
|
|
|
|
* LARGEUR_BLOC_NOMS_CM, où toute ligne s'abrège, s'achève à ECART_LISTE de
|
|
|
|
|
|
* l'emprise de la colonne suivante : aucune liste ne passe sur une autre
|
|
|
|
|
|
* table de la grille, d'aucune rangée ; entre deux rangées,
|
|
|
|
|
|
* max(ECART_RANGEES, ⌈2E + ECART_LISTE⌉) — deux emprises voisines restent à
|
|
|
|
|
|
* ECART_LISTE l'une de l'autre, la marge du carré de focus.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Array<{forme: 'ronde'|'carree', places: number}>} tables au moins une
|
|
|
|
|
|
* @returns {{colonnes: number, rangees: number}}
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function ecartsGrille(tables) {
|
|
|
|
|
|
const emprise = Math.max(
|
|
|
|
|
|
...tables.map(({ forme, places }) => geometrieTable({ id: 0, numero: 0, forme, position: { x: 0, y: 0 } }, places).rayonEmprise),
|
|
|
|
|
|
);
|
|
|
|
|
|
return {
|
|
|
|
|
|
colonnes: Math.ceil(2 * emprise + 2 * ECART_LISTE + constante('LARGEUR_BLOC_NOMS_CM')),
|
|
|
|
|
|
rangees: Math.max(ECART_RANGEES, Math.ceil(2 * emprise + ECART_LISTE)),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Position d'une table créée : la première case libre, ligne par ligne, d'une
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
* grille à ⌈√(T + 1)⌉ colonnes, T = tables.length, partant de (0, 0), au pas
|
|
|
|
|
|
* d'ecartsGrille des tables posées et de la nouvelle — le pas des
|
|
|
|
|
|
* démonstrations (scripts/engendrer_livrables.js). Une case est prise quand
|
|
|
|
|
|
* une table posée est à moins d'un pas de son centre sur chacun des deux
|
|
|
|
|
|
* axes, bornes exclues. Une table à un pas de colonne ou plus a son emprise
|
|
|
|
|
|
* hors de la bande du bloc de noms de la table nouvelle, et la sienne
|
|
|
|
|
|
* s'achève avant l'emprise nouvelle, à ECART_LISTE au moins, quelle que soit
|
|
|
|
|
|
* la rangée : c'est le calcul d'ecartsGrille, fait sur la plus grande
|
|
|
|
|
|
* emprise des deux. Une table à un pas de rangée ou plus a son emprise à
|
|
|
|
|
|
* ECART_LISTE au moins de l'emprise nouvelle. Ainsi une table déplacée hors
|
|
|
|
|
|
* de la grille, ou une grille dont le pas a crû depuis qu'elle a été posée,
|
|
|
|
|
|
* ne reçoit pas la liste de la table nouvelle et ne lui porte pas la sienne
|
|
|
|
|
|
* à travers une colonne.
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
*
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
* La recherche s'arrête : un intervalle ouvert de largeur 2 × pas contient
|
|
|
|
|
|
* au plus deux multiples du pas, et une table prend donc au plus quatre
|
|
|
|
|
|
* cases ; l'une des 4T + 1 premières cases est libre.
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
*
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
* @param {Array<{position: {x: number, y: number}, forme: 'ronde'|'carree', places: number}>} tables
|
|
|
|
|
|
* les tables posées, chacune à sa capacité courante
|
|
|
|
|
|
* @param {{forme: 'ronde'|'carree', places: number}} nouvelle la table à poser
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
* @returns {{x: number, y: number}}
|
|
|
|
|
|
*/
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
export function caseLibre(tables, nouvelle) {
|
|
|
|
|
|
const { colonnes: pasX, rangees: pasY } = ecartsGrille([...tables, nouvelle]);
|
|
|
|
|
|
const colonnes = Math.ceil(Math.sqrt(tables.length + 1));
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
for (let rang = 0; ; rang += 1) {
|
[FIX] plan: named warning, one places state, highlight, table spacing
The unheld-reservation warning names the person and the table number
instead of their identifiers. The places state is computed once per change
and handed to the side panels, not once per panel. A person can be
highlighted from their menu or from the grid, and the view bar names them.
A created table takes a cell a full pitch away from every table, its own
capacity included, so no name list runs over a neighbour's chairs. The
rendering guards fail on an empty walk and also check focus and selection.
Checked: 2692 node, 496 browser, 60 long tests, from the index alone.
--- FR ---
[FIX] plan : avertissement nommé, état des places unique, évidence, pas
L'avertissement de réservation non honorée nomme la personne et le numéro
de la table plutôt que leurs identifiants. L'état des places se calcule une
fois par changement et passe aux panneaux, non une fois par panneau. Une
personne se met en évidence par son menu ou depuis la grille, et la barre
de vue la nomme. Une table créée prend une case à un pas plein de chaque
table, sa capacité comprise : aucune liste ne court sur les chaises
voisines. Les gardes du rendu échouent sur un parcours vide et éprouvent
aussi le focus et la sélection. Vérifié : 2692 node, 496 navigateur, 60.
Assisted-by: Claude Opus 5.5
2026-10-07 15:05:01 -04:00
|
|
|
|
const candidate = { x: (rang % colonnes) * pasX, y: Math.floor(rang / colonnes) * pasY };
|
|
|
|
|
|
const prise = tables.some(
|
|
|
|
|
|
({ position: { x, y } }) => Math.abs(x - candidate.x) < pasX && Math.abs(y - candidate.y) < pasY,
|
|
|
|
|
|
);
|
[ADD] geometry: tables, their chairs, designating a seat
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
2026-10-07 02:32:47 -04:00
|
|
|
|
if (!prise) return candidate;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Nombre de places sous la poignée : placesInitiales + round(ecartRadial ×
|
|
|
|
|
|
* 2π / 60), jamais sous 2. L'écart se compte depuis le début du geste
|
|
|
|
|
|
* (§ 7.2) ; un demi-pas exact s'arrondit vers le haut (Math.round).
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {number} placesInitiales les places au début du geste
|
|
|
|
|
|
* @param {number} ecartRadial cm, projeté sur l'axe de la poignée, positif en s'éloignant du centre
|
|
|
|
|
|
* @returns {number} entier ≥ 2
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function placesSousPoignee(placesInitiales, ecartRadial) {
|
|
|
|
|
|
return Math.max(2, placesInitiales + Math.round(ecartRadial / RAYON_PAR_PLACE));
|
|
|
|
|
|
}
|