Давайте спочатку зробимо деякий аналіз.
Припустимо, у полігоні його щільність ймовірності пропорційна функції Тоді константа пропорційності - обернена інтеграл над багатокутником,Pp(x,y).p
μ0,0(P)=∬Pp(x,y)dxdy.
Баріцентр багатокутника є точкою середніх координат, обчислених як їх перших моментів. Перший - це
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
Тензор інерції може бути представлена в вигляді симетричній матриці других моментів , обчислених після перекладу багатокутника поставити свій баріцентр на початку координат: тобто матриця центральних моментів другого
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
де варіюється від до до Тензор в тому : ака ковариационная матриця - це(k,l)(2,0)(1,1)(0,2).
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
РС з дає головну вісь з ці поодинокі власні вектори , масштабовані їх власних значень.I(P)P:
Далі давайте розберемося, як робити розрахунки. Оскільки багатокутник представлений у вигляді послідовності вершин, що описують його орієнтовану границю природно викликати∂P,
Теорема Гріна: де - одноформатна форма, визначена в сусідстві з і∬Pdω=∮∂Pω
ω=M(x,y)dx+N(x,y)dyPdω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Наприклад, з та постійною ( тобто рівномірною) щільністю ми можемо (за допомогою перевірки) вибрати одну з багатьох рішення, такі якdω=xkyldxdyp,ω(x,y)=−1l+1xkyl+1dx.
Сенс цього полягає в тому, що контурний інтеграл слідує за відрізками ліній, визначених послідовністю вершин. Будь-який відрізок рядка від вершини до вершини може бути параметризований реальною змінною у виглядіuvt
t→u+tw
де є одиничним нормальним напрямком від доЗначення тому варіюються від до При цій параметризації і є лінійними функціями і а є лінійними функціями Таким чином, підінтегральна інтеграла контуру над кожним ребром стає поліноміальна функція від яке легко обчислюються при малих іw∝v−uuv.t0|v−u|.xytdxdydt.t,kl.
Реалізація цього аналізу настільки ж проста, як і кодування його компонентів. На найнижчому рівні нам знадобиться функція інтегрувати одночлен многочлена над відрізком лінії. Функції вищого рівня об'єднають їх для обчислення сирих та центральних моментів для отримання барицентра та інерційного тензору, і, нарешті, ми можемо діяти на цьому тензорі, щоб знайти основні осі (які є його масштабованими власними векторами). R
Нижче код виконує цю роботу. Це не робить претензій на ефективність: він призначений лише для ілюстрації практичного застосування попереднього аналізу. Кожна функція є прямолінійною, а конвенції про іменування паралельно виконуючи аналіз.
В код включена процедура генерації дійсних замкнених, просто з'єднаних, непересічних багатокутників (випадковим чином деформуючи точки вздовж кола і включаючи стартову вершину як свою кінцеву точку для створення замкнутого циклу). Слідом за цим є кілька тверджень про побудову полігона, відображення його вершин, примикання до барицентра та побудова основних головних осей червоним (найбільшим) та синім (найменшим), створюючи позитивно орієнтовану позитивно орієнтовану систему координат.
#
# 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")