- 図形の重心を求める方法
- 図形の重心を求めるプログラム
こんにちは、Youta(@youta_blog)です。
今回は、図形の重心を求めるお話です。
あなたは学生時代、次のような問題を見たことはありますか?

横の長さ×縦の長さが2a×2bの均質な長方形板ABCDがある。
板から\triangle{AOB}部分を切り抜いた。
残りの図形の重心Gの座標(x_G,\ y_G)を求めよ。
解法を忘れてしまったり、そもそもどう解けばいいかわからない…という方が多いのではないでしょうか。
でもご安心ください!
記事を読めば、この問題の対処法を学べます。
合わせて、コンピューターに自動で解かせるためのプログラムもご紹介します。
- 密度が一様
- 自己交差がない
Contents [hide]
図形の重心を求める方法
始めに冒頭の問題の答えを示します。

(x_G,\ y_G) = (\frac{2}{9}a,\ 0)

まあx軸上だよね〜。
求め方知らんけど。

丁寧に解説しますね。
手順は簡単。4ステップで求められます。
図形を複数の三角形に分ける
重心を求めたい図形をバラバラにして、三角形を作り出します。
基準点から左回り(反時計回り)に点を結んで三角形を作るのがポイントです。
基準点はどこでも構いません。

ちょっとよくわかんないわ〜。

2つの場合で解説しましょう。
- Oが基準: 図形内を三角形で分ける場合
- Aが基準: 図形外に三角形ができる場合

点Oから反時計回りに順に頂点を結び、三角形を作ります。
O \rightarrow B \rightarrow Cと辿り、\triangle{OBC}ができます。

O \rightarrow C \rightarrow Dと辿り、\triangle{OCD}ができます。

O \rightarrow D \rightarrow Aと辿り、\triangle{ODA}ができます。


点Aから反時計回りに順に頂点を結び、三角形を作ります。
A \rightarrow O \rightarrow Bと辿り、\triangle{AOB}ができます。


\triangle{AOB}って図形の外じゃん!

反時計回りに頂点を繋いでいるのであれば、全く問題ありません。
最初は戸惑うかも知れません。
ですが、最後は点Oが基準点の場合と同様の結果となるのでご安心ください。
A \rightarrow B \rightarrow Cと辿り、\triangle{ABC}ができます。

A \rightarrow C \rightarrow Dと辿り、\triangle{ACD}ができます。

各三角形の面積を求める
分割された各三角形の面積を求めます。
三角形の頂点から面積を求めるには、次の公式を利用します。
ですが、重心を求めるには符号付き(+ or -)の面積を利用します。

面積がマイナスってどゆこと?
例えば、頂点をA \rightarrow B \rightarrow Cの順に辿る場合。
面積は、経路が反時計回りなら正とし、時計回りなら負とします。

経路がどちら回りかは、外積\overrightarrow{AB} \times \overrightarrow{AC}のz成分の符号で判定します。

先程と同様、次の2つの場合で面積の求め方を見ていきましょう。
- Oが基準: 面積がすべて正の場合
- Aが基準: 面積が負になる場合

\triangle{OBC}・\triangle{OCD}・\triangle{ODA}の面積を求めます。
どの三角形も経路が反時計回りなので、面積が正ですね。
冗長になるため、\triangle{OBC}の場合の計算手順だけをお載せします。

下記の公式を利用して、外積を求めます。
\overrightarrow{OB} \times \overrightarrow{OC} = (x,\ y,\ z)とする。
\overrightarrow{OB} = (-a,\ -b,\ 0), \overrightarrow{OC} = (a,\ -b,\ 0)より、
x = -b \cdot 0\ – 0 \cdot (-b) = 0
y = 0 \cdot a\ – (-a) \cdot 0 = 0
z = (-a) \cdot (-b)\ – (-b) \cdot a = 2ab
よって、\overrightarrow{OB} \times \overrightarrow{OC} = (0,\ 0,\ 2ab)
また、\overrightarrow{OB} \times \overrightarrow{OC}のz成分は正である。
|\overrightarrow{OB} \times \overrightarrow{OC}| = 2abであるから、
\triangle{OBC}の面積 = \frac{1}{2}|\overrightarrow{OB} \times \overrightarrow{OC}| = ab
他の三角形の面積も同じ手順で求められます。
- \triangle{OBC}: ab
- \triangle{OCD}: ab
- \triangle{ODA}: ab
つまり、残った板の面積は3abです。

\triangle{AOB}・\triangle{ABC}・\triangle{ACD}の面積を求めます。
\triangle{AOB}は経路が時計回りなので、その面積を負として扱います。

せっかくなので\triangle{AOB}の面積だけ一緒に求めましょう。
A = (-a,\ b,\ 0),
O = (0,\ 0,\ 0),
B = (-a,\ -b,\ 0)より、
\overrightarrow{AO} \times \overrightarrow{AB} = (x,\ y,\ z)とする。
x = -b \cdot 0\ – 0 \cdot (-2b) = 0
y = 0 \cdot 0\ – a \cdot 0 = 0
z = a \cdot (-2b)\ – (-b) \cdot 0 = -2ab
よって、\overrightarrow{OB} \times \overrightarrow{OC} = (0,\ 0,\ -2ab)
また、\overrightarrow{AO} \times \overrightarrow{AB}のz成分は負である。
|\overrightarrow{OB} \times \overrightarrow{OC}| = 2abであるから、
\triangle{OBC}の面積 = \frac{1}{2}|\overrightarrow{OB} \times \overrightarrow{OC}| = ab
符号を考えると、\triangle{OBC}の面積 = -ab
他の三角形は経路が反時計回りなので、先程と同様に計算できます。
- \triangle{AOB}: -ab
- \triangle{ABC}: 2ab
- \triangle{ACD}: 2ab

マイナスの面積で本当にいいの?

はい。むしろ負の面積があるので、辻褄が合うんですよ。
残りの板の面積は3abでしたね。
試しに\triangle{AOB}・\triangle{ABC}・\triangle{ACD}の面積を全部足してみましょう。
3abピッタリです。
\triangle{ABC}の余分な面積を、\triangle{AOB}の負の面積で相殺したんですね。


へーよくできてるね。
各三角形の重心を求める
分割した各三角形の重心を求めます。
三角形の各頂点の座標から、その重心座標を求める公式です。
頂点の座標さえ分かれば、重心は機械的に計算できます。
3つの座標の平均なのですから。
基準点がOの場合。

- \triangle{OBC}: (0,\ -\frac{2}{3}b)
- \triangle{OCD}: (\frac{2}{3}a,\ 0)
- \triangle{ODA}: (0,\ \frac{2}{3}b)
基準点がAの場合。

- \triangle{AOB}: (-\frac{2}{3}a,\ 0)
- \triangle{ABC}: (-\frac{a}{3},\ -\frac{b}{3})
- \triangle{ACD}: (\frac{a}{3},\ \frac{b}{3})
各三角形の質量を求める
板の密度を\rho・厚みをhとして、各三角形の質量を計算します。
質量 = 密度 \cdot 体積であり、
体積 = 面積 \cdot 厚みです。
よって、質量 = 密度 \cdot 面積 \cdot 厚みです。
- Oが基準: 質量がすべて正の場合
- Aが基準: 質量が負になる場合
\triangle{OBC}・\triangle{OCD}・\triangle{ODA}の質量を求めます。
質量 = 密度 \cdot 面積 \cdot 厚み
この式にパラメータを代入します。
- \triangle{OBC}: \rho ab h
- \triangle{OCD}: \rho ab h
- \triangle{ODA}: \rho ab h
どの三角形にも同じ強さの重力が働くイメージです。

\triangle{AOB}・\triangle{ABC}・\triangle{ACD}の質量を求めます。
質量 = 密度 \cdot 面積 \cdot 厚み
この式にパラメータを代入します。
- \triangle{AOB}: -\rho ab h
- \triangle{ABC}: 2\rho ab h
- \triangle{ACD}: 2\rho ab h

負の質量っておかしくない?

現実にはありえませんが、計算上では全く問題ありません。
\triangle{AOB}だけ上向きに重力がかかる感じです。

図形全体の重心を求める
準備が整ったので、全体の重心を求めます。
重心を求める公式を利用します。
最後も2つの場合に分けて説明します。
- Oが基準: 質点に下に力がかかる場合
- Aが基準: 質点に上に力がかかる場合

各三角形の重心の位置に、質点が存在すると考えます。
では、全体の重心x_G, y_Gを計算しましょう。
座標平面上の3点
(0,\ -\frac{2}{3}b), (\frac{2}{3}a,\ 0), (0,\ \frac{2}{3}b)
に、質量が\rho abhの質点がある。
この質点系の重心の座標(x_G,\ y_G)は、
(x_G,\ y_G) = (\frac{2}{9}a,\ 0)

負の質量がありますが、構いません。
Oが基準点の時と同様の手順です。
では、全体の重心x_G, y_Gを計算しましょう。
座標平面上の3点
(0,\ -\frac{2}{3}a), (-\frac{a}{3},\ -\frac{b}{3}), (\frac{a}{3},\ \frac{b}{3})
に質点がある。
各質点はそれぞれ-\rho abh, 2\rho abh, 2\rho abhの質量を持つ。
よって、この質点系の重心の座標(x_G,\ y_G)は、
(x_G,\ y_G) = (\frac{2}{9}a,\ 0)

点Oが基準のときと一緒だね!
図形の重心を求めるプログラム
実装例
def triangulate(coords):
# 座標のリストを平坦化
coords = flatten(coords)
# 座標の数を計算
coord_len = len(coords) // 2
# 三角形の数を計算
triangle_num = coord_len - 2
# 三角形の座標を格納する空のリストを初期化
result = []
# 三角形の座標を作成するための繰り返し処理
for i in range(triangle_num):
j = (i + 1) * 2
# 各三角形の座標を抽出
triangle_coords = [
[coords[0], coords[1]],
[coords[j + 0], coords[j + 1],
[coords[j + 2], coords[j + 3],
]
result.append(triangle_coords)
# 三角形の座標のリストを返却
return result
def flatten(sequence):
result = []
for item in sequence:
if isinstance(item, (list, tuple, range, dict, set, frozenset)):
result.extend(flatten(item))
else:
result.append(item)
return result
triangulate
関数は、三角形の座標を生成します。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
coords | リスト・タプル | 必須 | 複数の座標 座標は座標平面内に限る。 |
triangulate
関数の引数下記の形式に対応しています。
coords_a = [x1, y1, x2, y2, …, xn, yn] # 一次元リスト
coords_b = (x1, y1, x2, y2, …, xn, yn) # 一次元タプル
coords_c = [[x1, y1], [x2, y2], …, [xn, yn]] # ネストされたリスト(タプル)
triangulate(coords_◯) # OK
データ型 | 説明 |
---|---|
リスト | 三角形の頂点の座標 |
triangulate
関数の戻り値形式に関しては、下記をご覧ください。
coords = [x1, y1, x2, y2, …, xn, yn]
result = triangulate(coords)
print(result)
# [[[x1, y1], [x2, y2], [x3, y3]], [[x1, y1], [x3, y3], [x4, y4]], …, [[x1, y1], [xn-1, yn-1], [xn, yn]]]
numpyの配列に変換すると分かりやすいでしょう。
import numpy as np
print(np.array(result))
# [[[x1 y1]
# [x2 y2]
# [x3 y3]]
#
# [[x1 y1]
# [x3 y3]
# [x4 y4]]
#
# [[x1 y1]
# [x4 y4]
# [x5 y5]]
#
# …
#
# [[x1 y1]
# [xn-1 yn-1]
# [xn yn]]]
リスト・タプルを平坦化するflatten
関数は、下記をご参照ください。
【Python】リストの平坦化(flatten)処理を徹底解説!
import numpy as np
def get_triangle_area(point_a, point_b, point_c):
# ベクトル演算を行うため、点をNumPy配列に変換
point_a = np.array(point_a)
point_b = np.array(point_b)
point_c = np.array(point_c)
# ベクトルABおよびACを計算
vector_ab = point_b - point_a
vector_ac = point_c - point_a
# ベクトルABおよびACの外積を計算
cross_product = np.cross(vector_ab, vector_ac)
# 外積がz成分のみの場合、符号付き面積を利用
if cross_product.size == 1:
parallelogram_area = cross_product
# 外積が3次元配列の場合、外積の大きさを計算
else:
parallelogram_area = np.linalg.norm(cross_product)
# 三角形の面積(平行四辺形の面積の半分)を返却
return parallelogram_area / 2
get_triangle_area
関数は、三角形の面積を求めます。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
point_a | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
point_b | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
point_c | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
get_triangle_area
関数の引数座標平面上の点の際は要素を2つ、座標空間上の点の際は要素を3つ含ませます。
要素の数は3つとも統一させてください。
データ型 | 説明 |
---|---|
浮動小数点 | 引数point_a 〜point_c で囲まれる三角形の面積 |
get_triangle_area
関数の戻り値引数point_◯
の次元数に応じて、下記の違いがあります。
- 二次元: 符号付き面積
- 三次元: 面積
import numpy as np
def get_triangle_centroid(point_a, point_b, point_c):
# numpy.array型に変換
point_a = np.array(point_a)
point_b = np.array(point_b)
point_c = np.array(point_c)
# 重心を計算
centroid = (point_a + point_b + point_c) / 3
# リストに変換して返す
return centroid.tolist()
get_triangle_centroid
関数は、三角形の重心を求めます。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
point_a | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
point_b | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
point_c | リスト・タプル | 必須 | 三角形の頂点の座標 座標は座標平面・座標空間に対応。 |
get_triangle_centroid
関数の引数座標平面上の点の際は要素を2つ、座標空間上の点の際は要素を3つ含ませます。
要素の数は3つとも統一させてください。
データ型 | 説明 |
---|---|
リスト | 引数の座標で囲まれる三角形の重心の座標 次元は引数に一致する。 |
get_triangle_centroid
関数の戻り値
import numpy as np
def get_centroid(coords):
# 座標を三角形に分割
triangles = triangulate(coords)
# 各三角形の重心・符号付き面積を計算
centroids = np.array([get_triangle_centroid(*triangle) for triangle in triangles])
signed_areas = np.array([get_triangle_area(*triangle) for triangle in triangles])
# 図形の総面積を計算
area = np.sum(signed_areas)
# 符号付き面積を考慮した重心を計算
weighted_centroids = centroids * signed_areas[:, None]
# 図形全体の重心を計算
x = np.sum(weighted_centroids[:, 0]) / area
y = np.sum(weighted_centroids[:, 1]) / area
# 重心の座標を返す
return [x, y]
get_centroid
関数は、引数の座標を頂点とする図形の重心を求めます。
変数名 | データ型 | 必須 / 任意 | 説明 |
---|---|---|---|
coords | リスト・タプル | 必須 | 図形の頂点の座標 座標は座標平面内に限る。 |
get_ceontroid
関数の引数下記の形式に対応しています。
coords_a = [x1, y1, x2, y2, …, xn, yn] # 一次元リスト
coords_b = (x1, y1, x2, y2, …, xn, yn) # 一次元タプル
coords_c = [[x1, y1], [x2, y2], …, [xn, yn]] # ネストされたリスト(タプル)
get_centroid(coords_◯) # OK
データ型 | 説明 |
---|---|
リスト | 引数の座標で囲まれる図形の重心の座標 |
get_centroid
関数の戻り値テスト

このプログラムは、頂点さえ分かれば、どんな多角形の重心でも求められます。
その威力を存分に感じてください。

正六角形の重心を取れるか試します。
get_centroid
関数の下に、下記のテストコードを書いて実行します。
a = (1, 0)
b = (1/2, np.sqrt(3)/2)
c = (-1/2, np.sqrt(3)/2)
d = (-1, 0)
e = (-1/2, -np.sqrt(3)/2)
f = (1/2, -np.sqrt(3)/2)
coords = (a, b, c, d, e, f)
answer = get_centroid(coords)
print(answer)
実行結果です。
print(answer)
# [0.0, 0.0]
重心は原点ですね。

六芒星の重心を取れるか試します。
get_centroid
関数の下に、下記のテストコードを書いて実行します。
a = (1, 0)
b = (np.sqrt(3)/4, 1/4),
c = (1/2, np.sqrt(3)/2),
d = (0, 1/2),
e = (-1/2, np.sqrt(3)/2),
f = (-np.sqrt(3)/4, 1/4),
g = (-1, 0),
h = (-np.sqrt(3)/4, -1/4),
i = (-1/2, -np.sqrt(3)/2),
j = (0, -1/2),
k = (1/2, -np.sqrt(3)/2),
l = (np.sqrt(3)/4, -1/4),
coords = (a, b, c, d, e, f, g, h, i, j, k, l)
answer = get_centroid(coords)
print(answer)
実行結果です。
print(answer)
# [-9.251858538542972e-18, 0.0]

やった!バグったね!

嬉しそうですね。
出力結果を確認してみましょう。
[-9.251858538542972e-18, 0.0]
のx座標ですが、後ろにe-18
とありますね。
これは、eの左の数字に1^{-18}を掛けるのを意味します。
よって、-9.251858538542972e-18
= -0.000000000000000009251858538542972
(0が18個)です。
わずかな誤差なので、ほぼ0と見て良いのではないでしょうか。
したがって、重心はやはり原点です。

な〜んだ、つまんないの〜。
図形の重心を求めるプログラムの活用例
図形をマウスドラッグで回転させるプログラムです。
PythonのGUIライブラリであるTkinterで作りました。
複雑そうですが、仕組みはシンプルです。
クリックした瞬間、図形の頂点から重心の座標を計算します。
その重心周りに図形を回転させるだけです。
Tkinterで図形を回転させるプログラムは、下記をご覧ください。
>> 【Tkinter】図形を回転させる!自動で回転・ドラッグで回転
まとめ
今回は、図形の重心を求める方法・プログラムをご紹介しました。
自己交差のない図形であれば、次の手順で重心の位置がわかるのでしたね。
最後までお付き合いいただき、ありがとうございました。