Quando la curva è composta da segmenti di linea, tutti i punti interni di quei segmenti sono punti di flesso, il che non è interessante. Invece, la curva dovrebbe essere considerata come approssimata dai vertici di quei segmenti. Dividendo una curva due volte differenziabile a tratti attraverso quei segmenti, possiamo quindi calcolare la curvatura. Un punto di flesso, a rigor di termini, è quindi un luogo in cui la curvatura è zero.
Nell'esempio ci sono tratti allungati in cui la curvatura è quasi zero. Ciò suggerisce che i punti indicati dovrebbero approssimare le estremità di tali tratti di regioni a bassa curvatura.
Un algoritmo efficace splineerà quindi i vertici, calcolerà la curvatura lungo un denso insieme di punti intermedi, identificherà intervalli di curvatura quasi zero (usando una stima ragionevole di cosa significhi essere "vicino") e segnerà i punti finali di tali intervalli .
Ecco un R
codice funzionante per illustrare queste idee. Cominciamo con una stringa di linea espressa come una sequenza di coordinate:
xy <- matrix(c(5,20, 3,18, 2,19, 1.5,16, 5.5,9, 4.5,8, 3.5,12, 2.5,11, 3.5,3,
2,3, 2,6, 0,6, 2.5,-4, 4,-5, 6.5,-2, 7.5,-2.5, 7.7,-3.5, 6.5,-8), ncol=2, byrow=TRUE)
Spline le x ed y coordinate separatamente per ottenere una parametrizzazione della curva. (Il parametro verrà chiamato time
.)
n <- dim(xy)[1]
fx <- splinefun(1:n, xy[,1], method="natural")
fy <- splinefun(1:n, xy[,2], method="natural")
Interpolare le spline per la stampa e il calcolo:
time <- seq(1,n,length.out=511)
uv <- sapply(time, function(t) c(fx(t), fy(t)))
Abbiamo bisogno di una funzione per calcolare la curvatura di una curva parametrizzata. Deve stimare il primo e il secondo derivato della spline. Con molte spline (come spline cubiche) questo è un calcolo algebrico facile. R
fornisce automaticamente i primi tre derivati. (In altri ambienti, si potrebbe voler calcolare numericamente le derivate.)
curvature <- function(t, fx, fy) {
# t is an argument to spline functions fx and fy.
xp <- fx(t,1); yp <- fy(t,1) # First derivatives
xpp <- fx(t,2); ypp <- fy(t,2) # Second derivatives
v <- sqrt(xp^2 + yp^2) # Speed
(xp*ypp - yp*xpp) / v^3 # (Signed) curvature
# (Left turns have positive curvature; right turns, negative.)
}
kappa <- abs(curvature(time, fx, fy)) # Absolute curvature of the data
Propongo di stimare una soglia per curvatura zero in termini di estensione della curva. Questo almeno è un buon punto di partenza; dovrebbe essere regolato in base alla tortuosità della curva (ovvero aumentata per curve più lunghe). Questo verrà successivamente utilizzato per colorare i grafici in base alla curvatura.
curvature.zero <- 2*pi / max(range(xy[,1]), range(xy[,2])) # A small threshold
i.col <- 1 + floor(127 * curvature.zero/(curvature.zero + kappa))
palette(terrain.colors(max(i.col))) # Colors
Ora che i vertici sono stati scanalati e la curvatura calcolata, resta solo da trovare i punti di flesso . Per mostrarli possiamo tracciare i vertici, tracciare la spline e segnare i punti di flesso su di essa.
plot(xy, asp=1, xlab="x",ylab="y", type="n")
tmp <- sapply(2:length(kappa), function(i) lines(rbind(uv[,i-1],uv[,i]), lwd=2, col=i.col[i]))
points(t(sapply(time[diff(kappa < curvature.zero/2) != 0],
function(t) c(fx(t), fy(t)))), pch=19, col="Black")
points(xy)
I punti aperti sono i vertici originali xy
e i punti neri sono i punti di flesso identificati automaticamente con questo algoritmo. Poiché la curvatura non può essere calcolata in modo affidabile ai punti finali della curva, tali punti non sono contrassegnati in modo speciale.