한양과학기술고등학교

에듀테크 프로젝트 대시보드

보로노이 다이어그램

보로노이 다이어그램

평면에 흩어진 점들. 나머지 모든 위치를 "가장 가까운 점"에게 나눠 주면 평면이 조각난다.
한 줄 요약 — 시드(site) 점들이 주어졌을 때, 각 시드가 "자기가 제일 가까운 영역(셀)"을 가진다. 이 셀들의 모임이 보로노이 다이어그램. 편의점 상권, 게임 맵 분할, 가장 가까운 병원 찾기, 메시 생성까지 — "최근접"이 필요한 곳이면 어디든 등장한다.

1. 직접 찍어 보기

캔버스를 클릭해 시드를 추가하자. 클릭할 때마다 모든 픽셀이 "누가 제일 가까운가"를 다시 계산해 색을 칠한다. 거리 재는 방식(유클리드 / 맨해튼)을 바꾸면 경계 모양이 확 달라진다.

클릭 = 시드 추가 · 시드 위 클릭 = 삭제 · 드래그 = 이동

시드 0개 · 픽셀 판정 0회 · 렌더 0 ms

관찰 포인트 — ① 두 시드 사이의 경계는 항상 수직 이등분선이다. ② 세 경계가 만나는 꼭짓점(보로노이 정점)은 세 시드에서 같은 거리에 있다 → 그 점을 중심으로 세 시드를 지나는 원을 그릴 수 있다. ③ 맨해튼 거리로 바꾸면 경계가 45°·수평·수직 선분으로만 꺾인다.

2. 경계는 왜 수직 이등분선인가

시드 A와 B만 있다고 하자. 어떤 점 P가 셀 A에 속하려면 dist(P, A) < dist(P, B). 등호가 성립하는 점들 dist(P, A) = dist(P, B)은 정확히 A와 B를 잇는 선분의 수직 이등분선이다. 그 선을 기준으로 A쪽 반평면 전체가 셀 A가 된다.

시드가 여러 개면, 시드 A의 셀은 "A와 다른 모든 시드 사이의 수직 이등분선"이 만드는 반평면들을 전부 교집합한 것이다. 반평면의 교집합이므로 각 보로노이 셀은 항상 볼록 다각형 (경계 셀은 무한히 뻗는 볼록 영역)이다.

3. 만드는 방법 세 가지

방법아이디어시간복잡도
브루트포스 (이 데모)모든 픽셀마다 가장 가까운 시드를 선형 탐색O(픽셀 수 × n)
포춘의 스윕라인수평선을 위→아래로 쓸며 포물선(beach line) 관리, 이벤트 처리O(n log n)
델로네의 쌍대델로네 삼각분할을 만든 뒤 각 삼각형의 외심을 연결O(n log n)

화면에 색을 칠하는 용도라면 브루트포스(또는 GPU에서 "제프리 콘" 기법)로 충분하다. 하지만 셀의 실제 다각형 좌표가 필요하면 포춘 알고리즘이나 델로네 쌍대를 쓴다.

# 브루트포스: 픽셀 하나가 어느 셀인지
def owner(px, py, sites):
    best, bi = float('inf'), -1
    for i, (sx, sy) in enumerate(sites):
        d = (px - sx)**2 + (py - sy)**2   # sqrt 불필요: 대소만 비교
        if d < best:
            best, bi = d, i
    return bi

4. 델로네 삼각분할 — 보로노이의 뒷면

위 데모에서 "델로네 삼각분할"을 켜면 시드들을 삼각형으로 잇는 그물이 나타난다. 보로노이 다이어그램과 델로네 삼각분할은 같은 정보의 앞뒷면(쌍대, dual)이다.

델로네 삼각분할의 핵심 성질: 어떤 삼각형의 외접원 안에도 다른 점이 들어가지 않는다 (empty circumcircle). 덕분에 지나치게 가늘고 긴 삼각형이 잘 안 생겨서, 지형 메시·유한요소 해석의 기본 삼각화로 쓰인다.

5. 어디에 쓰나

맛보기 도전 — 시드를 규칙적인 격자로 찍으면 셀이 정사각형이 된다. 그럼 벌집처럼 정육각형 셀을 만들려면 시드를 어떻게 배치해야 할까? (힌트: 삼각 격자) 데모에서 직접 시도해 보자.