Lassen Sie uns zuerst eine Analyse durchführen.
Angenommen, innerhalb des Polygons seine Wahrscheinlichkeitsdichte die proportionale Funktion Dann ist die Proportionalitätskonstante die Umkehrung des Integrals von über dem Polygon.Pp(x,y).p
μ0,0(P)=∬Pp(x,y)dxdy.
Der Schwerpunkt des Polygons ist der Punkt der Durchschnittskoordinaten, der als erste Momente berechnet wird. Der erste ist
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
Der Trägheitstensor kann als die symmetrische Anordnung von zweiten Momenten dargestellt werden, die nach der Übersetzung des Polygons berechnet wird, um seinen Schwerpunkt auf den Ursprung zu setzen, dh die Matrix der zentralen zweiten Momente
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
wobei von bis bis reichen Der Tensor selbst - auch bekannt als Kovarianzmatrix - ist(k,l)(2,0)(1,1)(0,2).
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
Eine PCA von ergibt die Hauptachsen von Dies sind die Einheitseigenvektoren, die durch ihre Eigenwerte skaliert werden.I(P)P:
Als nächstes wollen wir herausfinden, wie die Berechnungen durchgeführt werden. Da das Polygon als eine Folge von Eckpunkten dargestellt wird, die seine orientierte Grenze es natürlich, es aufzurufen∂P,
Green's Theorem: wobei ist eine Einform, die in einer Nachbarschaft von und∬Pdω=∮∂Pω
ω=M(x,y)dx+N(x,y)dyPdω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Zum Beispiel können wir mit und konstanter ( dh einheitlicher) Dichte (durch Inspektion) eine der vielen auswählen Lösungen wiedω=xkyldxdyp,ω(x,y)=−1l+1xkyl+1dx.
Der Punkt dabei ist, dass das Konturintegral den Liniensegmenten folgt, die durch die Folge von Eckpunkten bestimmt werden. Jedes Liniensegment von Vertex zu Vertex kann durch eine reelle Variable im Formular parametrisiert werdenuvt
t→u+tw
Dabei ist die Einheitsnormalrichtung von nachDie Werte von reichen daher von bis Unter dieser Parametrisierung sind und lineare Funktionen von und und sind lineare Funktionen von Somit wird der Integrand des Konturintegrals über jeder Kante eine Polynomfunktion von die leicht für kleine und ausgewertet werden kannw∝v−uuv.t0|v−u|.xytdxdydt.t,kl.
Die Implementierung dieser Analyse ist so einfach wie die Codierung ihrer Komponenten. Auf der untersten Ebene benötigen wir eine Funktion, um eine Polynom-Einform über ein Liniensegment zu integrieren. Übergeordnete Funktionen aggregieren diese, um die rohen und zentralen Momente zu berechnen, um den Schwerpunkt und den Trägheitstensor zu erhalten, und schließlich können wir diesen Tensor bearbeiten, um die Hauptachsen (die seine skalierten Eigenvektoren sind) zu finden. Der folgende R
Code führt diese Arbeit aus. Es erhebt keinen Anspruch auf Effizienz: Es soll nur die praktische Anwendung der vorstehenden Analyse veranschaulichen. Jede Funktion ist unkompliziert und die Namenskonventionen entsprechen denen der Analyse.
Der Code enthält eine Prozedur zum Generieren gültiger geschlossener, einfach verbundener, sich nicht selbst schneidender Polygone (durch zufälliges Verformen von Punkten entlang eines Kreises und Einbeziehen des Startscheitelpunkts als Endpunkt, um eine geschlossene Schleife zu erstellen). Im Folgenden finden Sie einige Anweisungen zum Zeichnen des Polygons, Anzeigen seiner Eckpunkte, Angrenzen an das Schwerpunktzentrum und Zeichnen der Hauptachsen in Rot (am größten) und Blau (am kleinsten), wodurch ein polygonzentriertes positiv orientiertes Koordinatensystem erstellt wird.
#
# Integrate a monomial one-form x^k*y^l*dx along the line segment given as an
# origin, unit direction vector, and distance.
#
lintegrate <- function(k, l, origin, normal, distance) {
# Binomial theorem expansion of (u + tw)^k
expand <- function(k, u, w) {
i <- seq_len(k+1)-1
u^i * w^rev(i) * choose(k,i)
}
# Construction of the product of two polynomials times a constant.
omega <- normal[1] * convolve(rev(expand(k, origin[1], normal[1])),
expand(l, origin[2], normal[2]),
type="open")
# Integrate the resulting polynomial from 0 to `distance`.
sum(omega * distance^seq_along(omega) / seq_along(omega))
}
#
# Integrate monomials along a piecewise linear path given as a sequence of
# (x,y) vertices.
#
cintegrate <- function(xy, k, l) {
n <- dim(xy)[1]-1 # Number of edges
sum(sapply(1:n, function(i) {
dv <- xy[i+1,] - xy[i,] # The direction vector
lambda <- sum(dv * dv)
if (isTRUE(all.equal(lambda, 0.0))) {
0.0
} else {
lambda <- sqrt(lambda) # Length of the direction vector
-lintegrate(k, l+1, xy[i,], dv/lambda, lambda) / (l+1)
}
}))
}
#
# Compute moments of inertia.
#
inertia <- function(xy) {
mass <- cintegrate(xy, 0, 0)
barycenter = c(cintegrate(xy, 1, 0), cintegrate(xy, 0, 1)) / mass
uv <- t(t(xy) - barycenter) # Recenter the polygon to obtain central moments
i <- matrix(0.0, 2, 2)
i[1,1] <- cintegrate(uv, 2, 0)
i[1,2] <- i[2,1] <- cintegrate(uv, 1, 1)
i[2,2] <- cintegrate(uv, 0, 2)
list(Mass=mass,
Barycenter=barycenter,
Inertia=i / mass)
}
#
# Find principal axes of an inertial tensor.
#
principal.axes <- function(i.xy) {
obj <- eigen(i.xy)
t(t(obj$vectors) * obj$values)
}
#
# Construct a polygon.
#
circle <- t(sapply(seq(0, 2*pi, length.out=11), function(a) c(cos(a), sin(a))))
set.seed(17)
radii <- (1 + rgamma(dim(circle)[1]-1, 3, 3))
radii <- c(radii, radii[1]) # Closes the loop
xy <- circle * radii
#
# Compute principal axes.
#
i.xy <- inertia(xy)
axes <- principal.axes(i.xy$Inertia)
sign <- sign(det(axes))
#
# Plot barycenter and principal axes.
#
plot(xy, bty="n", xaxt="n", yaxt="n", asp=1, xlab="x", ylab="y",
main="A random polygon\nand its principal axes", cex.main=0.75)
polygon(xy, col="#e0e0e080")
arrows(rep(i.xy$Barycenter[1], 2),
rep(i.xy$Barycenter[2], 2),
-axes[1,] + i.xy$Barycenter[1], # The -signs make the first axis ..
-axes[2,]*sign + i.xy$Barycenter[2],# .. point to the right or down.
length=0.1, angle=15, col=c("#e02020", "#4040c0"), lwd=2)
points(matrix(i.xy$Barycenter, 1, 2), pch=21, bg="#404040")