Lorsque la courbe est composée de segments de ligne, tous les points intérieurs de ces segments sont des points d'inflexion, ce qui n'est pas intéressant. Au lieu de cela, la courbe doit être considérée comme étant approximée par les sommets de ces segments. En splining une courbe deux fois différentiable par morceaux à travers ces segments, nous pouvons alors calculer la courbure. Un point d'inflexion proprement dit est alors un endroit où la courbure est nulle.
Dans l'exemple, il y a des étirements longs où la courbure est presque nulle. Cela suggère que les points indiqués devraient se rapprocher des extrémités de ces tronçons de régions à faible courbure.
Un algorithme efficace va donc spliner les sommets, calculer la courbure le long d'un ensemble dense de points intermédiaires, identifier les plages de courbure proche de zéro (en utilisant une estimation raisonnable de ce que signifie être "proche") et marquer les extrémités de ces plages .
Voici un R
code de travail pour illustrer ces idées. Commençons par une chaîne de lignes exprimée comme une séquence de coordonnées:
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 les coordonnées x et y séparément pour obtenir un paramétrage de la courbe. (Le paramètre sera appelé time
.)
n <- dim(xy)[1]
fx <- splinefun(1:n, xy[,1], method="natural")
fy <- splinefun(1:n, xy[,2], method="natural")
Interpoler les splines pour le traçage et le calcul:
time <- seq(1,n,length.out=511)
uv <- sapply(time, function(t) c(fx(t), fy(t)))
Nous avons besoin d'une fonction pour calculer la courbure d'une courbe paramétrée. Il doit estimer les première et deuxième dérivées de la spline. Avec de nombreuses splines (telles que des splines cubiques), il s'agit d'un calcul algébrique facile. R
fournit automatiquement les trois premiers dérivés. (Dans d'autres environnements, on peut vouloir calculer les dérivées numériquement.)
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
Je propose d' estimer un seuil de courbure nulle en termes d'étendue de la courbe. C'est au moins un bon point de départ; il doit être ajusté en fonction de la tortuosité de la courbe (c'est-à-dire augmentée pour les courbes plus longues). Il sera ensuite utilisé pour colorer les parcelles en fonction de la courbure.
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
Maintenant que les sommets ont été cannelés et la courbure calculée, il ne reste plus qu'à trouver les points d'inflexion . Pour les montrer, nous pouvons tracer les sommets, tracer la spline et y marquer les points d'inflexion.
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)
Les points ouverts sont les sommets d'origine xy
et les points noirs sont les points d'inflexion identifiés automatiquement avec cet algorithme. Étant donné que la courbure ne peut pas être calculée de manière fiable aux extrémités de la courbe, ces points ne sont pas spécialement marqués.