하나의 빠르고 더러운 방법은 재귀 구형 세분을 사용합니다 . 지구 표면의 삼각 측량으로 시작하여 각 삼각형을 정점에서 가장 긴 변의 중간으로 재귀 적으로 나눕니다. (사실 삼각형을 두 개의 동일한 직경 부분 또는 같은 면적의 부분으로 나눕니다.하지만 약간의 계산이 필요하기 때문에 측면을 정확히 반으로 나눕니다. 이로 인해 다양한 삼각형의 크기가 약간 달라 지지만 이 응용 프로그램에는 중요하지 않은 것 같습니다.)
물론이 세분화를 임의의 점이있는 삼각형을 빠르게 식별 할 수있는 데이터 구조로 유지합니다. 재귀 호출을 기반으로하는 이진 트리는 훌륭하게 작동합니다. 삼각형이 분할 될 때마다 트리는 해당 삼각형의 노드에서 분할됩니다. 분할 평면과 관련된 데이터가 유지되므로 임의의 점이있는 평면의 어느 쪽을 신속하게 결정할 수 있습니다. 트리를 왼쪽 또는 오른쪽으로 이동할지 여부가 결정됩니다.
(평면 분할을 말했습니까? 그렇습니다. 지구 표면을 구체로 모델링하고 지구 중심 (x, y, z) 좌표를 사용하는 경우 대부분의 계산은 3 차원으로 이루어지며 삼각형의 변이 원점을 통해 평면 과 구의 교차점을 계산하면 빠르고 쉽게 계산할 수 있습니다.)
나는 구체의 한 옥탄트에 대한 절차를 보여줌으로써 설명 할 것이다. 다른 7 개의 옥타 트도 같은 방식으로 처리됩니다. 그러한 낙타는 90-90-90 삼각형입니다. 내 그래픽에서는 같은 모서리에 걸쳐 유클리드 삼각형을 그릴 것입니다. 작아 질 때까지 잘 보이지 않지만 쉽고 빠르게 그릴 수 있습니다. 다음은 유족에 해당하는 유클리드 삼각형입니다. 그것은 절차의 시작입니다.
모든 변의 길이가 같으므로 한 변이 "가장 긴"것으로 임의로 선택되고 세분됩니다.
새로운 삼각형 각각에 대해 이것을 반복하십시오.
n 단계 후에 2 ^ n 삼각형이 생깁니다. 10 단계 후의 상황은 다음과 같습니다. 8 각이 1024 개 (구체적으로는 전체 8192 개)로 표시됩니다.
더 설명하기 위해, 나는이 octant 내에서 임의의 점을 생성하고 삼각형의 가장 긴 변이 0.05 라디안 미만에 도달 할 때까지 세분화 트리를 여행했습니다. (카테 시안) 삼각형은 프로브 포인트가 빨간색으로 표시됩니다.
덧붙여 말하면, 한 지점의 위도를 대략 1도까지 좁히려면 약 1/60 라디안이므로 (1/60) ^ 2 / (Pi / 2) = 1/6000 정도입니다. 총 표면. 각 세분은 삼각형 크기의 약 절반이므로, 옥탄트의 약 13 ~ 14 세분이 트릭을 수행합니다. 아래에서 볼 수 있듯이 많은 계산이 아니므로 트리를 전혀 저장하지 않고 하위 분류를 즉시 수행하는 것이 효율적입니다. 처음에는 점이 어느 옥탄트에 있는지, 즉 3 자리 이진수로 기록 될 수있는 세 좌표의 부호에 의해 결정되며 각 단계에서 점이 있는지 여부를 기억하려고합니다. 삼각형의 왼쪽 (0) 또는 오른쪽 (1)에 다른 14 자리 이진수를 제공합니다. 이 코드를 사용하여 임의의 점을 그룹화 할 수 있습니다.
(일반적으로 두 개의 코드가 실제 이진수로 가까우면 해당 점이 가까워 지지만 점이 여전히 가까울 수 있으며 현저하게 다른 코드가있을 수 있습니다. 예를 들어, 1 미터 간격으로 Equator와 분리 된 두 점을 고려하십시오. 이진수 앞에 다른 옥탄트가 있기 때문에 고정 된 공간 분할에서는 피할 수 없습니다.)
Mathematica 8 을 사용 하여 이것을 구현했습니다. 선호하는 프로그래밍 환경에서 구현하기 위해 그대로 또는 의사 코드로 사용할 수 있습니다.
평면 0-ab 점 p 의 어느 쪽이 위치하는지 확인 합니다.
side[p_, {a_, b_}] := If[Det[{p, a, b}] >= 0, left, right];
점 p를 기준으로 삼각형 abc를 세분화합니다.
refine[p_, {a_, b_, c_}] := Block[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
If[side[p, {x, m}] === right, {y, m, x}, {x, m, z}]
]
마지막 그림은 octant를 표시하고 그 위에 다음 목록을 다각형 세트로 렌더링하여 그려졌습니다.
p = Normalize@RandomReal[NormalDistribution[0, 1], 3] (* Random point *)
{a, b, c} = IdentityMatrix[3] . DiagonalMatrix[Sign[p]] // N (* First octant *)
NestWhileList[refine[p, #] &, {a, b, c}, Norm[#[[1]] - #[[2]]] >= 0.05 &, 1, 16]
NestWhileList
refine
조건이 관련되어 있거나 (삼각형이 클 때) 또는 최대 작업 횟수에 도달 할 때까지 (16) 작업을 반복해서 적용합니다 .
8 인의 완전한 삼각 분할을 나타 내기 위해, 나는 첫 번째 8 인간부터 시작하여 10 번 정련을 반복했습니다. 이것은 약간의 수정으로 시작됩니다 refine
.
split[{a_, b_, c_}] := Module[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
{{y, m, x}, {x, m, z}}
]
차이점은 주어진 점이있는 것이 아니라 입력 삼각형의 두 반쪽 을 split
반환 한다는 것입니다. 전체 삼각 분할은 다음을 반복하여 얻습니다.
triangles = NestList[Flatten[split /@ #, 1] &, {IdentityMatrix[3] // N}, 10];
확인하기 위해 모든 삼각형의 크기를 측정하고 범위를 보았습니다. (이 "크기"는 각 삼각형과 구의 중심이 차지하는 피라미드 모양의 도형에 비례합니다. 이와 같은 작은 삼각형의 경우이 크기는 기본적으로 구형 영역에 비례합니다.)
Through[{Min, Max}[Map[Round[Det[#], 0.00001] &, triangles[[10]] // N, {1}]]]
{0.00523, 0.00739}
따라서 크기는 평균보다 약 25 % 증가 또는 감소합니다. 이는 점을 그룹화하는 대략 균일 한 방법을 달성하는 데 합리적입니다.
이 코드를 스캔 할 때 삼각법 이 없다는 것을 알 수 있습니다. 구면 좌표와 직교 좌표 간을 앞뒤로 변환하는 데 필요한 유일한 곳입니다. 이 코드는 또한 지구 표면을지도에 투영하지 않으므로 수반 왜곡을 피할 수 있습니다. 그렇지 않으면 평균화 ( Mean
), 피타고라스 정리 ( Norm
) 및 3 x 3 결정자 ( Det
) 만 사용하여 모든 작업을 수행합니다. ( RotateLeft
및 Flatten
삼각형과 같은 가장 긴면을 검색하는 것과 같은 간단한 목록 조작 명령 도 있습니다.)