Önce biraz analiz yapalım.
Çokgenin içinde olduğunu varsayalım P olasılık yoğunluğu orantılı fonksiyondur p(x,y). O halde orantısallık sabiti, integralin tersidir. p çokgenin üzerinde,
μ0,0(P)=∬Pp(x,y)dxdy.
Ağırlık merkezi çokgenin ilk moment olarak hesaplanan ortalama koordinatlar, noktasıdır. Birincisi
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
Atalet tensör kökenli onun ağırlık merkezi koymak için poligon çeviri sonrası hesaplanan ikinci anları simetrik dizisi olarak temsil edilebilir: yani, matrisini ikinci merkezi momentler
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
nerede (k,l) dan aralığı (2,0) için (1,1) için (0,2). Tensörün kendisi - diğer adıyla kovaryans matrisi -
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
Bir PCA I(P)verir ana eksenleri arasındaP: bunlar özdeğerlerine göre ölçeklendirilmiş birim özvektörlerdir.
Sonra, hesaplamaların nasıl yapılacağını ele alalım. Çünkü çokgen, yönlendirilmiş sınırını tanımlayan bir köşe sırası dizisi olarak sunulur∂P, çağırmak doğaldır
Green Teoremi: ∬Pdω=∮∂Pω
nerede ω=M(x,y)dx+N(x,y)dy bir mahallede tanımlanan tek P ve dω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Örneğin, dω=xkyldxdyve sabit ( yani , muntazam) yoğunlukp, (muayene ile) gibi birçok çözümden birini seçebiliriz ω(x,y)=−1l+1xkyl+1dx.
Bunun noktası, kontur integralinin, köşe dizisi tarafından belirlenen çizgi parçalarını izlemesidir. Köşeden herhangi bir çizgi parçasıu tepe noktasına v gerçek bir değişken tarafından parametrelendirilebilir t şeklinde
t→u+tw
where w∝v−u is the unit normal direction from u to v. The values of t therefore range from 0 to |v−u|. Under this parameterization x and y are linear functions of t and dx and dy are linear functions of dt. Thus the integrand of the contour integral over each edge becomes a polynomial function of t, which is easily evaluated for small k and l.
Implementing this analysis is as straightforward as coding its components. At the lowest level we will need a function to integrate a polynomial one-form over a line segment. Higher level functions will aggregate these to compute the raw and central moments to obtain the barycenter and inertial tensor, and finally we can operate on that tensor to find the principal axes (which are its scaled eigenvectors). The R
code below performs this work. It makes no pretensions of efficiency: it is intended only to illustrate the practical application of the foregoing analysis. Each function is straightforward and the naming conventions parallel those of the analysis.
Included in the code is a procedure to generate valid closed, simply connected, non-self-intersecting polygons (by randomly deforming points along a circle and including the starting vertex as its final point in order to create a closed loop). Following this are a few statements to plot the polygon, display its vertices, adjoin the barycenter, and plot the principal axes in red (largest) and blue (smallest), creating a polygon-centric positively-oriented coordinate system.
#
# 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")