Funzioni e serie dati
Le serie di func3d nascono da due registri JavaScript:
SERIES_FUNCS (funzioni analitiche, file js/local/func3d/series-funcs.js)
e SERIES_DATA (dataset numerici, file js/local/func3d/series-data.js).
Un modulo ponte (series-bridge.js) registra ogni dataset come "funzione"
campionabile tramite un metodo di interpolazione, così
superfici analitiche e superfici da dati condividono la stessa pipeline di disegno.
Funzioni analitiche implementate
| Funzione | Note | Parametri |
|---|---|---|
| Sinc | sin(r)/r, il classico "cappello" | — |
| cos(r)/r | oscillante con singolarità smorzata | — |
| Paraboloide ellittico | quadrica | a, b |
| Gaussiana | campana | σ |
| Sella iperbolica quadratica | paraboloide iperbolico | a, b |
| Cono | |r| | — |
| Piano | ax + by + c | a, b, c |
| Sella lineare | k·x·y | k |
| Rosenbrock | banana function, banco di prova per l'ottimizzazione | a, b |
| Ackley | moltissimi minimi locali, test per ottimizzatori globali | a, b, c |
| Bessel J0 | J0(s·r), modi radiali | s |
| Ripple 2D | interferenza di onde in x e y | fx, fy |
| Onda | sinusoide 2D | a, b |
| Gobbe | massimi e minimi periodici | a, b, c |
| Piramide | superficie a spigoli | — |
| Onda smorzata | sinusoide con inviluppo | kx, ky |
| Rastrigin | multimodale con griglia regolare di minimi (test di ottimizzazione) | A |
| Funzione di Franke | il banco di prova classico dell'interpolazione 2D: due colline, un crinale e una buca | — |
| Himmelblau | quattro minimi globali identici (test di ottimizzazione) | — |
| Membrana circolare (modi) | onda stazionaria di un tamburo Jn(s·r)·cos(nθ); la fase la anima | n, s, fase |
| Müller‑Brown (PES) | superficie di energia potenziale test per i cammini di reazione | Vmax |
| Potenziale di dipolo | V = 1/r₊ − 1/r₋ ammorbidito (colormap divergente) | separazione, ε |
Le superfici chiuse (z a due valori: la pagina disegna automaticamente i rami superiore e inferiore):
| Funzione | Parametri |
|---|---|
| Sommità tronca | H, R |
| Iperboloide | a |
Ellissoide e toroide stavano in questo elenco e adesso sono parametrici (qui sotto). Cucire due rami z(x,y) lungo l'equatore lascia una fessura dove la derivata diverge: alla giunzione i triangoli dei due rami non combaciano e la superficie appare lacerata. In forma parametrica il problema non esiste, perché la maglia è una sola.
Le superfici parametriche (u,v) → (x,y,z): oggetti non esprimibili come z(x,y), disegnati in proporzioni reali con scala uniforme; il colore si può basare sulla quota z oppure sui parametri u/v (select Base colore nella card):
| Funzione | Note | Parametri |
|---|---|---|
| Armoniche sferiche |Ylm| | r(θ,φ) = |Ylm|: le forme degli orbitali (u=θ, v=φ) | l, m, scala |
| Orbitale ibrido spⁿ | r = |ψ|² con ψ = (s + √λ·pz)/√(1+λ): il lobo asimmetrico degli ibridi (λ=1,2,3) | λ, scala |
| Nastro di Möbius | nastro con mezzo giro di torsione | R, w |
| Toro annodato (p,q) | tubo attorno al nodo torico (p,q) | p, q, R, r, a |
| Ellissoide | x = a·sinθcosφ, y = b·sinθsinφ, z = c·cosθ (u = θ, v = φ): poli ed equatore puliti | a, b, c |
| Toroide | x = (R + r·cos v)·cos u, y = (R + r·cos v)·sin u, z = r·sin v | R, r |
| Bottiglia di Klein | immersione "a otto": superficie chiusa non orientabile | R |
| Catenoide ↔ Elicoide | la famiglia isometrica delle superfici minime, con morphing continuo t=0→1 | t, c |
| Superficie di rivoluzione | profilo r(v) = R + A·sin(k·v) ruotato attorno all'asse z | R, A, k |
Una voce parametrica dichiara parametric: true, i range dei parametri nei
defaults (xMin/xMax = u, yMin/yMax = v), il numero di campioni
nU/nV e una build(params) che restituisce
(u, v) => [x, y, z]. Per questi oggetti non esistono isolinee, sezione 2D
e zScale (non c'è una z(x,y)); l'export CSV fornisce i punti 3D della griglia.
Le curve parametriche t → (x,y,z): una curva non è una superficie sottile, può passare più volte sopra lo stesso punto del piano, e questo un campo di quota non lo sa fare. Il colore segue il parametro t, quindi si legge anche il verso di percorrenza; lo spessore è in pixel reali (fat lines) e i punti non finiti spezzano la linea invece di chiuderla con un segmento inventato:
| Curva | Note | Parametri |
|---|---|---|
| Elica cilindrica | x = R·cos t, y = R·sin t, z = c·t; il passo è 2πc | R, c |
| Nodo trifoglio | il nodo non banale più semplice: una curva chiusa che non si scioglie senza tagliarla | a |
| Traiettoria di Lorenz | integrazione Runge-Kutta 4 con σ=10, β=8/3 e ρ regolabile; t è il tempo | ρ, passo |
Nella card della serie i campi diventano t iniziale, t finale, Campioni e Spessore linea: modo superficie/wireframe, zScale, isolinee, sezione e classi non compaiono, perché su una curva non vogliono dire nulla. Se in scena non c'è nessuna superficie, i tick degli assi seguono gli estremi della curva.
Questi comandi agiscono in posto: muovere un parametro (il raggio dell'elica, il ρ di Lorenz) ricampiona la curva dentro il gruppo esistente, con la stessa normalizzazione — durante il gesto la scala non balla, e trascinare lo slider diventa un'animazione fluida. Un ricostruire completo (qualsiasi altro comando) rinormalizza sui dati nuovi.
Il piano immagine non è una funzione: è un rettangolo texturizzato su uno dei tre piani coordinati (xy, xz, yz), spostabile lungo la propria normale e ridimensionabile. Serve a mettere una foto, una mappa, una scansione o un fotogramma accanto ai dati — non sopra, come fa il drappeggio. La posizione è espressa, a scelta, in frazioni del riquadro degli assi (−1…1, utile per uno sfondo) oppure in coordinate dati: un rettangolo x0…x1, y0…y1 alla quota z, nelle unità della prima superficie a griglia in scena — il caso dell'ortofoto sul DEM, dove l'immagine deve stare dove stanno i metri. In coordinate dati il rettangolo può non essere quadrato e viene piazzato a fine costruzione, quando la mappatura della superficie di riferimento esiste; senza superficie di riferimento le coordinate si leggono come unità del riquadro. Le immagini disponibili sono quelle del registro (le stesse del drappeggio, vedi sotto).
Dataset di esempio
| Dataset | Tipo | Contenuto |
|---|---|---|
| Ozono | griglia 11×11 (bilineare) | campo di concentrazione con scala z ridotta |
| Tempo | griglia temporale, 10 frame 21×21 | superficie che fluttua nel tempo |
| Sbuffo avvettato | nube temporale [x, y, z, c] | nube che si muove, si allarga e si diluisce |
| Nube rotante | nube temporale [x, y, z, c] | rotazione rigida con ricircolo |
| Demo nearest / IDW / bilineare / RBF | punti sparsi o griglia | demo dei primi metodi di interpolazione (gli 11 attuali sono tutti selezionabili dalla card) |
| Demo points | punti | disegno dei soli punti, senza superficie |
| Nucleo del calore 2D | griglia temporale, 16 frame 41×41 | diffusione pura: gaussiana che si allarga e si abbassa |
| Lorenz (ensemble) | nube temporale, 110 frame × 50 punti | ensemble sull'attrattore di Lorenz, colore = velocità; con le traiettorie si vede la divergenza caotica |
| Vortice di Rankine + pozzo | campo vettoriale piano 15×15 | nucleo in rotazione rigida, esterno potenziale, pozzo radiale |
| Dipolo magnetico (B) | campo vettoriale 3D | linee di campo di un dipolo con momento lungo z (softening) |
| Vulcano Maunga Whau (DEM) | griglia reale 87×61, passo 10 m | il classico dataset "volcano" di R: quote 94–195 m |
| Vulcano — campione sparso | 260 punti estratti dal DEM (estrazione fissa) | da interpolare (natural, krige, rst…) e confrontare col DEM completo con "Differenza (A − B)" |
Aggiungere una funzione analitica (su file)
Una voce di SERIES_FUNCS dichiara etichetta, parametri (con default e
limiti per la UI) e la fabbrica build(params) che restituisce la
funzione campionabile:
nome_funzione: {
label: 'La mia funzione',
params: [
{ name: 'k', label: 'k', type: 'number', step: 0.1, min: 0, max: 10, default: 1.0 }
],
defaults: { xMin: -8, xMax: 8, yMin: -8, yMax: 8, step: 0.3, zScale: 1.0 },
build: (p) => (x, y) => Math.sin(p.k * x) * Math.cos(p.k * y)
}
Per una superficie chiusa si aggiunge closed: true e la build riceve in
params._branch il ramo (+1 superiore, −1 inferiore).
Aggiungere un dataset (su file)
Una voce di SERIES_DATA porta i dati e il metodo con cui campionarli.
Punti sparsi in data (righe [x, y, z]) oppure griglie/frame in
values:
mio_dataset: {
label: 'Il mio dataset',
defaults: {
xMin: -3, xMax: 3, yMin: -3, yMax: 3, step: 0.15, zScale: 1.0,
mode: 'surface', colormap: { mode: 'preset', name: 'viridis' }
},
method: { name: 'idw', params: { power: 2 } }, // nearest | idw | bilinear | bicubic | lanczos | natural | rbf | rst | bspline | krige | points
data: [
[-2, -2, 1.0], [-2, 0, 0.3], [0, 0, 1.0], [2, 2, 0.1]
],
enabled: false, // true = compare tra le serie all'apertura
onLoad: false // true = viene anche plottato subito
}
Varianti:
- griglia regolare:
values= matrice di z + estremi del dominio (metodobilinear); - serie temporale:
values= array di frame (matrici per le griglie, liste di punti per le nubi) con passo temporale nei defaults; - nube di punti:
kind: 'cloud', righe [x, y, z, c] o [x, y, z, c1, c2] (c = colore, c2 = dimensione), con bounds e range c fissi dichiarati nel dataset; - campo vettoriale:
kind: 'vector', righe [x, y, z, u, v, w].
Aggiungere una curva parametrica o un piano immagine (su file)
Una curva dichiara isCurve: true, l'intervallo del parametro nei
defaults (xMin/xMax = t), il numero di campioni nU e una
build(params) che restituisce (t) => [x, y, z]:
elica: {
label: 'Elica cilindrica',
isCurve: true,
nU: 800,
params: [
{ name: 'R', label: 'R (raggio)', type: 'number', step: 0.1, min: 0.1, max: 20, default: 1.0 },
{ name: 'c', label: 'c (passo/2π)', type: 'number', step: 0.05, min: -5, max: 5, default: 0.25 }
],
defaults: { xMin: 0, xMax: 8 * Math.PI, wireWidth: 3, colormap: { mode: 'preset', name: 'turbo' } },
build: (p) => (t) => [p.R * Math.cos(t), p.R * Math.sin(t), p.c * t]
}
Se la curva non ha forma chiusa (una traiettoria integrata, per esempio) si integra
una volta sola dentro build e la funzione restituita legge il
risultato per interpolazione: rifare l'integrazione a ogni campione costerebbe O(n²)
e darebbe la stessa curva. La traiettoria di Lorenz è scritta così.
Un piano immagine dichiara isImagePlane: true e i tre parametri
piano (xy|xz|yz), posizione e copertura;
l'immagine si scegle nella card fra quelle del registro. La build non viene
usata (resta un segnaposto).
Le colonne in più (dimensioni extra)
Un dato di ricerca ha di solito più di tre colonne: x, y, quota, e poi la concentrazione, la data del rilievo, l'errore di misura, il numero di campione. Le colonne non assegnate a un ruolo nell'import non vengono più buttate: restano accanto ai dati, allineate riga per riga, e compaiono nella select Colora per della card.
// come si presentano nel dataset (import CSV o payload sul database)
{
id: 'imp_1', label: 'campagna 2026',
data: [ [x, y, z], ... ], // geometria: la quota
extra: {
names: ['concentrazione', 'errore'],
values: [ [12.4, 0.3], ... ] // una riga per punto, nello stesso ordine
}
}
Scegliendo una di queste grandezze la superficie resta quella della quota e il colore racconta l'altra cosa. Il campo viene interpolato sulla stessa griglia e con lo stesso metodo (vedi Interpolazione): se colore e forma venissero da griglie diverse non parlerebbero degli stessi punti. Dove il campo non è definito il vertice resta grigio, che è diverso dal prendere un colore della scala — quello direbbe un valore che non c'è.
Immagini utente e layer salvati (profilo graph_editor)
Gli utenti col profilo graph_editor (o admin) trovano nel pannello
Importa/Esporta due blocchi in più: Le mie immagini — upload personale
di immagini da drappeggio (ridimensionate lato client, salvate fuori dal docroot
in upload/graph/<utente>/ con quota per utente; nel select delle
card compaiono accanto a quelle di sistema) — e Layer salvati — salvataggio su
DB della serie selezionata (ricombinabile
in altri grafici) o dello snapshot intero del grafico, con nome.
Immagini e layer nascono privati. Accanto a ogni elemento proprio c'è il
controllo di condivisione, con la stessa semantica del resto della piattaforma:
interruttore spento = privato; acceso senza profili = pubblico; acceso con
profili scelti = visibile solo a quelli. Gli elementi condivisi da altri compaiono
negli elenchi con l'etichetta condiviso e sono in sola lettura. Le tabelle di
gestione dei grafici vivono nello schema graph del DB boot.
L'amministratore fa eccezione: vede tutti i contenuti di tutti gli utenti, compresi quelli privati (identificati dal nome del proprietario), e li può rinominare, ricondividere, sovrascrivere ed eliminare — dalla pagina stessa oppure, con la vista d'insieme di tutti gli utenti, dalla console gest → database → Dati func3d.
Importare da CSV (runtime)
Il pannello Importa/Esporta di func3d costruisce dataset temporanei a partire da CSV/TSV o dagli appunti, con mappatura guidata delle colonne (x, y, z, c, c2, t, gruppo, u, v, w). Sono dataset di sessione: per renderli permanenti si esporta il payload e lo si salva come dataset amministrato dalla console.
Keywords: funzioni, serie dati, dataset, registro, payload, grafici 3D