Vai al contenuto
CADCAMFUN
Documentazione

Documentazione

Visualizzazione

Gli strumenti di disegno su tela e il rendering di Three.js condividono la geometria principale mantenendo esplicito lo stato di modifica e riproduzione.

Tradotto automaticamente dall'inglese. Vedi l'originale in inglese

Componenti della vista

apps/desktop/src/viewport/index.ts esportazioni Editor2D, View3D, i tipi di oggetti di scena, gli identificatori degli strumenti disponibili e solidsFromProfiles. Entrambi i componenti riempiono il loro genitore: forniscono un contenitore con larghezza e altezza diverse da zero. Le entità CAD principali e i programmi CAM costituiscono il modello dati condiviso.

L'editor 2D esegue il rendering su Canvas 2D e mantiene la propria cronologia di selezione, fotocamera e comandi. Il visualizzatore 3D utilizza Three.js, controlli di orbita e trasformazione, processi mesh asincroni e riproduzione del programma. Il rendering di un percorso o di un solido è indipendente dal salvataggio o dalla verifica di un'operazione di produzione.

Contratto di integrazione

Editor2DProps richiede drawing, onChange, e selectedTool. Reinserire i disegni emessi nell'oggetto di scena. Il componente riconosce i propri oggetti documento emessi; la sostituzione del documento esternamente carica il nuovo disegno e ripristina lo stato dell'editor.

Editor 2D controllato
import { createSignal } from "solid-js"
import { Editor2D, type ToolId } from "./viewport/index.ts"

const [drawing, setDrawing] = createSignal(initialDrawing)
const [tool, setTool] = createSignal<ToolId>("select")

<Editor2D drawing={drawing()} onChange={setDrawing} selectedTool={tool()} />

La selezione esclude le entità bloccate e le entità su livelli bloccati. Le entità invisibili e i livelli nascosti sono esclusi dall'indice spaziale renderizzato. Le nuove entità ricevono identificatori univoci del documento e vengono assegnate a un livello visibile e sbloccato quando ne è disponibile uno.

Strumenti e gesti di disegno

Il registro degli strumenti dispone di impostazioni per selezione, linea, rettangolo, cerchio, ellisse, polilinea, arco, testo, quota, spostamento, rotazione, scala, specchio, offset, ritaglio e raccordo. AVAILABLE_TOOLS deriva da quelle fabbriche registrate. startTool riconosce inoltre chamfer attraverso la fabbrica degli strumenti di raccordo, ma lo smusso non è pubblicizzato in quella matrice. I consumatori dovrebbero utilizzare il registro vero e proprio per decidere quali strumenti abilitare.

  • Zoom con rotella o pizzico; Trascina spazio, trascina il centro e trascina la panoramica verso destra.
  • F si adatta al disegno indicizzato. Elimina o Backspace rimuove la selezione sbloccata.
  • Cmd/Ctrl+Z annulla; Cmd/Ctrl+Shift+Z o Cmd/Ctrl+Y rifa.
  • Invio conferma una polilinea. L'Escape viene inviato allo strumento attivo per annullare il gesto corrente.
  • Con il testo attivo, digitare i caratteri o Backspace prima del posizionamento; il testo iniziale è Text.

Le posizioni del puntatore vengono convertite in coordinate mondiali prima dell'invio dello strumento. Lo snap combina l'indice spaziale, i candidati geometrici e una griglia dipendente dalla fotocamera; la tolleranza del colpo di selezione corrisponde a otto pixel dello schermo. Le macchine utensili producono comandi e anteprime anziché scrivere file o chiamare l'API.

3Rendering e riproduzione D

View3D accetta disegni, solidi, estrusione di profili opzionale, a cam.Program, dimensioni grezze e definizioni di utensili. Supporta le preimpostazioni prospettica, superiore, inferiore, anteriore, posteriore, sinistra, destra, isometrica e a quattro viste. Le modalità di visualizzazione sono ombreggiata, ombreggiata con bordi, wireframe e raggi X. Illuminazione, proprietà dei materiali, sezioni di ritaglio, griglia, assi e origine sono configurabili tramite oggetti di scena.

Controlla la riproduzione con playing, playbackTime in pochi secondi, playbackRate, e onPlaybackTimeChange. La riproduzione espande i cicli di foratura, tassella il movimento circolare/elicoidale, preserva il tempo di sosta e disegna distintamente i movimenti rapidi. Mappe degli utensili e geometria predefinita opzionale della taglierina, del gambo e del supporto; la geometria presunta è visivamente distinta.

I callback di selezione riportano un indice solido. I controlli di trasformazione emettono posizione, rotazione e ridimensionamento onTransformCommit; i chiamanti decidono come mantenere tale trasformazione. Le sovrapposizioni del percorso utensile possono mostrare punti ed etichette, filtri di tipo punto, centri dell'arco, raggi dell'arco e trasparenza del grezzo.

La riproduzione è una rappresentazione visiva

Il visualizzatore anima il programma fornito e disegna stock e strumenti. Non esegue asportazione di materiale. I controlli di collisione e di inviluppo macchina appartengono ai moduli di verifica CAM; la visione di un programma non costituisce approvazione della macchina.

Coordinate e unità

Le coordinate del disegno principale e le dimensioni del grezzo utilizzano i millimetri. Gli angoli di disegno e i componenti di rotazione di Three.js utilizzano i radianti; non applicare tale convenzione ai valori dell'asse rotante CAM o agli input di funzionalità che utilizzano esplicitamente i gradi. viewport/units.ts contiene aiutanti di conversione per millimetri e pollici visualizzati. Converti ai limiti di input/output in modo che lo snap, la geometria e il movimento del percorso utensile mantengano un sistema di unità coerente.

La 2fotocamera D mappa le coordinate del mondo su pixel CSS; le dimensioni della tela di supporto incorporano il rapporto pixel del dispositivo. Il ridimensionamento aggiorna la fotocamera e il buffer di backup ResizeObserver. La scala della fotocamera determina la spaziatura della griglia e le tolleranze dei colpi dello spazio sullo schermo.

La visualizzazione in pollici viene convertita esattamente con 25.4 mm per pollice e le lunghezze predefinite sono 3 decimali (apps/desktop/src/viewport/units.ts). Nell'editor 2D, ogni passo della ruota viene ingrandito di un fattore 1.15, la scala è compresa tra 0.001 e 100,000 e l'adattamento alla vista lascia un margine del 10% (editor2d/camera.ts).

Risorse e ciclo di vita

Il componente 2D annulla i fotogrammi di animazione in coda e disconnette il suo osservatore di ridimensionamento durante lo smontaggio. Il componente 3D rilascia listener di ridimensionamento e visibilità, controlli di orbita e trasformazione, meshworker, pianificazione delle animazioni, geometrie, materiali, sprite, mesh di strumenti e il renderer WebGL.

Un frame scheduler riunisce gli invalidamenti e richiede ulteriori frame mentre lo smorzamento o la riproduzione della telecamera ne hanno bisogno. I cambiamenti di visibilità sospendono il lavoro. Questa è una politica di pianificazione, non un budget di frame rate garantito per ogni modello e dispositivo.

Mappa dei moduli

  • editor2d/editor2d.tsx: componente controllato, invio del puntatore, fotocamera, selezione, cronologia e pittura su tela.
  • editor2d/tools/: macchine a stati gestuali e costruzione di comandi.
  • editor2d/camera.ts, render.ts, e snap-pipeline.ts: trasformazioni di coordinate, rendering di entità, selezione e orchestrazione degli snap.
  • spatial/grid.ts: disegno indice spaziale.
  • view3d/view3d.tsx: composizione della scena, controlli della fotocamera, selezione, trasformazioni, stock e strumenti.
  • view3d/playback.ts, toolpath-overlays.ts, e modeling.ts: sequenza temporale del movimento, dati di sovrapposizione e conversione da profilo a solido.
  • view3d/mesh-jobs.ts e mesh-worker.ts: costruzione della geometria dello sfondo e proprietà del lavoro.

Disponibilità degli strumenti

ToolId descrive il vocabolario più ampio degli strumenti; AVAILABLE_TOOLS descrive il sottoinsieme registrato. Per gli strumenti interattivi, utilizzare startTool(id, lookup, context, text). La ricerca di selezione interroga le entità candidate; il contesto fornisce la selezione corrente e l'accesso alle entità; il testo fornisce il contenuto dell'annotazione.

Ritorna una fabbrica scomparsa undefined. Gestiscilo in modo esplicito anziché presentare uno strumento disabilitato o non implementato come modalità di modifica attiva. Gli strumenti che modificano la geometria esistente richiedono la selezione corrente e il contesto dell'entità; invocare una factory senza tali dipendenze non è sufficiente per una modifica utile.

Meshing e verifica

Per i solidi con un conteggio stimato dei vertici superiore a 50,000 e il supporto del browser Worker, il visualizzatore accoda la costruzione della mesh in un modulo di lavoro. I solidi più piccoli si intrecciano in modo sincrono. La coda mesh gestisce il completamento, i lavori obsoleti, gli errori dei lavoratori e la pulizia. La soglia è una stima utilizzata per la pianificazione, non un limite documentato per le dimensioni del modello.

Controlli logici del viewport, dalla radice del repository
bun test apps/desktop/src/viewport
bun run --filter @cadcamfun/desktop typecheck

I test logici riguardano gli assistenti di rendering, i gesti degli strumenti, l'indicizzazione spaziale, la riproduzione e le sovrapposizioni. Il comportamento del browser/WebGL dipende anche dall'ambiente grafico; ispezionare l'effettiva interazione della finestra quando si modificano i controlli della telecamera, il ciclo di vita della scena o il rendering.

Esempio di riproduzione controllata

Un genitore può possedere i controlli di trasporto senza duplicare la sequenza temporale del movimento dello spettatore. Questo componente accetta un programma CAM già costruito e controlla il tempo, lo stato di riproduzione, la velocità e la preimpostazione del viewport. I suoi percorsi di importazione presuppongono un file direttamente sotto apps/desktop/src.

Anteprima del programma con controlli di trasporto esterni
import { createSignal } from "solid-js"
import { cam } from "@cadcamfun/core"
import { View3D } from "./viewport/index.ts"

export function ProgramPreview(props: { program: cam.Program }) {
  const [seconds, setSeconds] = createSignal(0)
  const [playing, setPlaying] = createSignal(false)
  return <div style={{ height: "600px" }}>
    <button onClick={() => setPlaying(value => !value)}>Play / pause</button>
    <button onClick={() => { setSeconds(0); setPlaying(true) }}>Restart</button>
    <View3D
      program={props.program}
      preset="isometric"
      playing={playing()}
      playbackTime={seconds()}
      playbackRate={1}
      onPlaybackTimeChange={setSeconds}
      stock={{ origin: { x: 0, y: 0, z: -10 }, size: { x: 100, y: 80, z: 10 } }}
    />
  </div>
}

Il generatore di riproduzione inizia dall'origine, espande i cicli in mosse e calcola le durate dalla lunghezza del viaggio e dall'avanzamento. La sua velocità rapida predefinita è 5,000 mm/min e la tolleranza predefinita per la tassellatura dell'arco è 0.1 mm. Si tratta di ipotesi di anteprima, non dell'accelerazione della macchina misurata o dei tempi del controller. I comandi di pausa mantengono i secondi dichiarati; i commenti e le voci di solo orientamento non aggiungono la corsa lineare.

I colori degli strumenti vengono assegnati dalla prima occorrenza di ciascun identificatore di strumento e mantenuti nei suoi percorsi. Fornire una mappa strumenti/strumento predefinito per la geometria dell'assieme; un percorso colorato da solo non stabilisce le dimensioni della taglierina o del supporto utilizzati dalla verifica.

Modifica di ricette e limiti attuali dello strumento

OperazioneGesto e precondizioniComportamento attuale
OffsetSeleziona un'entità supportata, quindi trascina; Shift sceglie invece l'entità sotto il cursore.Linee/polilinee/rettangoli producono un risultato polilinea. Le implementazioni di cerchi e archi traducono il centro mantenendo il raggio; questo strumento non è un'operazione generale di offset concentrico per le curve.
Taglia/estendeSelezionare prima le entità sorgente e limite, attivare Taglia e fare clic sulla destinazione. Alt seleziona la modalità di estensione.L'implementazione ricerca le intersezioni nelle altre entità selezionate. Cambia linee e polilinee; i tipi di target non supportati o le intersezioni mancanti non producono alcuna modifica.
Filetto/smussoScegli due linee che si intersecano. Shift passa allo smusso.Lo strumento condiviso utilizza un raggio iniziale di 5 mm e sostituisce la coppia con linee tagliate e una polilinea di collegamento. Non si tratta di un raccordo a bordo pieno arbitrario.
Trasformare un solidoFornire onTransformCommit, seleziona una mesh, quindi attiva trasla/ruota/scala.Il callback riporta l'indice solido e i valori di trasformazione. La selezione e la trasformazione dei gizmo sono disabilitati nella modalità a quattro visualizzazioni.

Questi dettagli provengono dalle implementazioni dei gesti piuttosto che dalle etichette delle tavolozze. Esamina l'anteprima prima di confermare e utilizza Annulla se il risultato differisce dalla geometria desiderata. Uno strumento che non restituisce alcun comando può significare che i suoi prerequisiti geometrici non sono stati soddisfatti, non che l'editor ha perso l'evento del puntatore.

Errori di rendering e mesh

Per impostazione predefinita, la coda mesh in background ammette al massimo 128 lavori in sospeso, viene eseguito uno alla volta e ha un timeout predefinito di 15 secondi per lavoro. Il ripristino di una scena termina il lavoratore e cancella il lavoro in coda; le risposte obsolete vengono ignorate. Le risposte dell'operatore non valide, gli errori di decodifica dei messaggi, i timeout e gli errori di capacità della coda segnalano errori attraverso la richiamata della coda.

SintomoAzione
Tela vuota senza geometriaAssegna al genitore una dimensione diversa da zero, controlla la visibilità dell'entità/livello e adatta la vista.
Non è possibile selezionare nullaControlla i blocchi di entità/livello in 2D; controlla la modalità a quattro visualizzazioni e trasforma lo stato in 3D.
Timeout mesh o coda pienaRiduci la complessità della scena o il numero di solidi inviati simultaneamente e controlla l'errore dell'operatore. Un batch non riuscito viene cancellato anziché installato parzialmente come lavoro completato.
Il tempo di riproduzione sembra sbagliatoControlla i feed del programma, gli spostamenti di pausa, le ipotesi di velocità rapida e se un programma modificato reimposta il tempo principale.
WebGL non è disponibileUtilizza un ambiente grafico supportato e controlla lo stato di rendering non disponibile del componente; i test logici non possono dimostrare la disponibilità della GPU.

I test rilevanti includono editor2d/tools/offset.test.ts, annotation-transform.test.ts, spatial/grid.test.ts, view3d/playback.test.ts, e toolpath-overlays.test.ts. L'implementazione della coda mesh è view3d/mesh-jobs.ts.

Espositori in dettaglio

L'editor lavora sempre in millimetri. Viene convertito solo ciò che l'utente legge e digita, utilizzando i fattori esatti riportati di seguito. L'unità propria di un disegno memorizzato rimane mm o inch; le unità extra sono solo scelte di visualizzazione.

UnitàEtichettamm per unitàDecimali predefinitiUnità di programma codice G
mmmm13mm
umµm0.0011mm
cmcm104mm
mm10006mm
inchpollice25.44pollice
thoutu0.02542pollice
Conversione e analisi delle lunghezze (apps/desktop/src/viewport/units.ts)
import { formatLength, parseLength, formatAngle, programUnitsOf } from "./units"

formatLength(25.4, "inch")     // "1.0000 inch"
formatLength(12.5)             // "12.500 mm"
formatLength(0.0254, "thou")   // "1.00 thou"
parseLength("2 in", "inch")    // 50.8  (a trailing unit word is ignored)
parseLength("abc")             // undefined
formatAngle(Math.PI / 2)       // "90.0°"
programUnitsOf("thou")         // "inch"
parseLength rimuove un suffisso alfabetico finale (incluso µ e °) e interpreta il numero nell'unità di visualizzazione corrente. Il suffisso non viene utilizzato per scegliere un'unità, quindi digitarlo 2 mm mentre la visualizzazione è in pollici restituisce 50.8 mm. Restituzioni di input vuote, non numeriche e infinite undefinede in questo caso gli editor dovrebbero mantenere il valore precedente.

2D fotocamera e griglia

mondo (mm, +Y su)schermo (px, +Y giù) centro+X+Y (larghezza/2, altezza/2)+x+y →
La fotocamera dell'editor 2D memorizza il punto del mondo al centro dell'area visibile e una scala in pixel per millimetro. worldToScreen capovolge Y in modo che il mondo +Y punti verso l'alto sullo schermo.
Costante/funzioneValoreComportamento
ZOOM_STEP1.15Fattore per passo ruota; zoomAt mantiene fisso il punto del mondo sotto il cursore.
MIN_SCALE / MAX_SCALE0.001 / 100,000 px per mmLa scala è limitata a questo intervallo.
FIT_MARGIN0.1Frazione dell'area visibile mantenuta come margine da cameraForBounds.
createCamera()centro (0, 0), scala 1, 800 × 600Fotocamera iniziale prima del primo ridimensionamento.
gridStep(scale, major = 10, minPixels = 8)1-2-5 scalaIl passaggio più piccolo che mantiene i punti della griglia ad almeno 8 px di distanza.
Mappatura tra mondo e schermo
const camera = createCamera({ x: 0, y: 0 }, 2, 800, 600)   // 2 px per mm
worldToScreen(camera, { x: 10, y: 10 })   // { x: 420, y: 280 }
screenToWorld(camera, { x: 420, y: 280 }) // { x: 10, y: 10 }
gridStep(2)    // 5   -> 5 mm apart = 10 px
gridStep(0.5)  // 20  -> 20 mm apart = 10 px

Il rendering seleziona le entità rispetto a visibleBounds(camera). Lo snap evita inoltre la scansione dell'intero disegno: una griglia spaziale uniforme restituisce entità i cui limiti rientrano nella tolleranza di snap più un margine candidato di 2 mm e solo quelle vengono passate al nucleo findSnap. In questo modo il puntatore si muove velocemente nei disegni con molte entità.

Riferimento allo snap

I tipi di snap vengono provati in ordine di priorità. L'ordine predefinito corrisponde all'editor precedente: endpoint, midpoint, center, intersection, perpendicular, tangent, nearest, e quadrant. I punti finali sono i vertici dell'entità di origine, non la sua tassellatura.

CollocamentoSenso
enabledInterruttore principale; false disabilita tutti gli snap.
typesTipi di snap abilitati, prima la priorità più alta.
toleranceRaggio di scatto in millimetri; non dipende dallo zoom.
gridEnabled, gridSpacingEsegui lo snap alla griglia quando non è applicabile lo snap all'oggetto.
referencePointIl punto da cui si trae. perpendicular e tangent non trovare nulla senza di essa.

La pipeline restituisce lo snap, gli ID delle entità sotto il cursore (prima la più vicina, utilizzata dallo strumento di selezione) e il numero di candidati considerati.

Come viene cronometrata la riproduzione del percorso utensile

buildPlayback(program, rapidRate = 5000, tolerance = 0.1) converte un programma CAM in segmenti temporizzati. La durata di ogni mossa è la sua lunghezza divisa per la sua alimentazione (o per la velocità rapida per le rapide), moltiplicata per 60, ovvero i secondi. Le abitazioni aggiungono il proprio tempo senza muoversi. Commenti, spostamenti di orientamento e cicli fissi non vengono disegnati come segmenti. Gli archi sono divisi in accordi la cui sagitta rimane all'interno tolerance (tra 2 e 65, 536 corde per arco) e Z è interpolato lungo l'arco per le eliche.
Cerca e colora un'anteprima
const playback = buildPlayback(program)          // rapids at 5000 mm/min
playback.duration                                // total seconds
positionAt(playback, 12.5)                       // tool position at t = 12.5 s
toolAt(playback, 12.5)                           // toolId cutting at that time
toolColors(program)                              // Map toolId -> colour, first use order

// A 10 mm feed move at 600 mm/min lasts 10 / 600 × 60 = 1 s.
// The same 10 mm as a rapid lasts 10 / 5000 × 60 = 0.12 s.
Anteprima della riproduzione Trascina per orbitare · scorri per ingrandire · trascina con il tasto destro per eseguire la panoramica
Lo stesso programma tascabile della pagina CAM, riprodotto nel primo colore dello strumento (388bdf22). Sul desktop, ogni strumento aggiuntivo ottiene il colore successivo nella tavolozza: 5597316c879e, 9f15, #e#f#, #facc# e così via.
segmentIndexAt trova il segmento in riproduzione mediante la ricerca binaria, quindi la ricerca è economica anche per programmi lunghi. La ricerca oltre la fine restituisce la posizione finale e toolAt quindi segnala l'ultimo strumento. Il tempo di riproduzione è una stima visiva basata sui feed programmati; ignora l'accelerazione della macchina, quindi una macchina reale impiega più tempo.

Pratiche di visualizzazione

  • Converti al limite: archivia e calcola in millimetri e radianti e formatta solo per la visualizzazione formatLength e formatAngle.
  • Non riutilizzare mai una lunghezza analizzata dopo parseLength ritorna undefined; mantieni il valore precedente e mostra un suggerimento di convalida.
  • Quando si aggiunge uno strumento di disegno, registrarlo nella tabella della fabbrica degli strumenti in modo che la barra degli strumenti, le scorciatoie e i test lo indirizzino con lo stesso ID.
  • Mantieni le query dei candidati istantanee spaziali. Passare ogni entità a findSnap su ogni movimento del puntatore sconfigge l'indice.
  • Tratta la durata della riproduzione come un'anteprima, non come una citazione del tempo di ciclo.

Hit testing su larga scala: la griglia spaziale

L'editor 2D ha come target 60 fps con 10,000 entità, quindi gli hit testing e l'abbattimento non scansionano mai ogni entità per evento puntatore. viewport/spatial/grid.ts mantiene una griglia uniforme: ogni elemento è registrato in ogni cella toccata dal riquadro di delimitazione e una query restituisce tutti gli elementi i cui limiti intersecano il riquadro di query, in ordine di inserimento. Il risultato è esatto, mai un campione.

CollocamentoValorePerché
Dimensione cella predefinita50 unità mondiali (mm)Adatto a un disegno di circa 1 m di diametro
Dimensione minima della cella1e-6Protegge da dimensioni pari a zero o negative, che potrebbero ripetersi in loop per sempre
Limite di celle per articolo4,096Gli elementi più grandi (una lunga linea sul foglio) vengono tracciati lateralmente invece che per cella
MetodoComportamento
build(items)Ricostruisci da zero
upsert(id, item)Inserisci o sostituisci un elemento con l'id
remove(id)Rimuovere; gli ID sconosciuti vengono ignorati
query(bounds)Id i cui limiti intersecano la scatola
casella di interrogazioneentità ABlunga fila

Vengono visitate le celle toccate dalla casella di query tratteggiata; A e la linea lunga vengono restituiti perché i loro limiti la intersecano, B no.

3Coda di meshing D

I solidi vengono meshati in un Web Worker in modo che il thread dell'interfaccia utente non si blocchi mai. MeshWorkerQueue in view3d/mesh-jobs.ts esegue un lavoro alla volta:

RegolaValoreMessaggio in caso di fallimento
Capacità (in coda + in esecuzione)128La coda mesh 3D è piena. Ridurre la complessità della scena.
Timeout per lavoro15 s3Mesh D scaduto. Ridurre la complessità della scena.
Errore del lavoratore—Il messaggio del lavoratore o "Il lavoratore mesh si è arrestato in modo imprevisto".
Risposta indecifrabile—Il meshworker non è riuscito a decodificare la sua risposta.
Pessima geometriale posizioni devono essere Float32Array, indici Uint32ArrayIl lavoratore mesh ha restituito una geometria non valida.

Qualsiasi errore termina il lavoratore e cancella l'intera coda, quindi il lavoro sulla scena stantio non arriva mai dopo una modifica più recente; l'invio successivo avvia un nuovo lavoratore. Le risposte corrispondono all'ID lavoro, quindi una risposta tardiva da parte di un lavoratore licenziato viene ignorata.

2Implementazioni dello strumento D

FileUtensili
tools/two-point.tslinea, rettangolo, cerchio per diametro, ellisse
tools/chained.tspolilinea, arco di tre punti
tools/fillet-chamfer.tsarrotondare o smussare l'angolo tra due linee
tools/offset.tssfalsare una polilinea, un rettangolo, un cerchio o un arco di una distanza
tools/trim.tstagliare ed estendere
tools/select.tsfare clic, inserire nella casella e aggiungere/rimuovere la selezione
tools/annotation-transform.tsspostare e trasformare le annotazioni

Ogni strumento è una piccola macchina statale onPointerDown, onPointerMove e onPointerUp, creato da una fabbrica in tools/index.ts, uno per voce della tavolozza. Le posizioni del puntatore passano attraverso snap-pipeline.ts prima che uno strumento li veda.