좌표계 변환 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""좌표계 변환 문서의 주장 검증."""
import math, random
def cross(a, b):
return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0])
def dot(a, b):
return sum(x * y for x, y in zip(a, b))
def mm(A, B):
return [[sum(A[i][k] * B[k][j] for k in range(len(B))) for j in range(len(B[0]))] for i in range(len(A))]
def mv(A, v):
return [sum(A[i][k] * v[k] for k in range(len(v))) for i in range(len(A))]
def frame_matrix(X0, U, V):
N = cross(U, V); ln = math.sqrt(dot(N, N)); N = tuple(x / ln for x in N)
Rinv = [list(U) + [0], list(V) + [0], list(N) + [0], [0, 0, 0, 1]] # 행에 U, V, N
Tinv = [[1, 0, 0, -X0[0]], [0, 1, 0, -X0[1]], [0, 0, 1, -X0[2]], [0, 0, 0, 1]]
return mm(Rinv, Tinv), Rinv, Tinv, N
def main():
X0, U, V = (1, 2, 3), (0, 1, 0), (-1, 0, 0)
W, Rinv, Tinv, N = frame_matrix(X0, U, V)
assert N == (0, 0, 1)
assert mv(W, [1, 3, 3, 1]) == [1, 0, 0, 1] # 예시로 보기
assert mv(W, [1, 2, 3, 1]) == [0, 0, 0, 1] # 새 원점은 (0, 0, 0)
assert mv(W, [0, 2, 5, 1]) == [0, 1, 2, 1] # 카드 C2
# 순서가 중요하다: R⁻¹T⁻¹ ≠ T⁻¹R⁻¹
assert mv(mm(Tinv, Rinv), [1, 3, 3, 1]) != mv(W, [1, 3, 3, 1])
# 좌표계를 +t만큼 옮기는 것 = 점을 −t만큼 옮기는 것
assert mv(frame_matrix((5, 0, 0), (1, 0, 0), (0, 1, 0))[0], [7, 1, 1, 1]) == [2, 1, 1, 1]
# 좌표계를 θ만큼 돌리는 것 = 점을 −θ만큼 돌리는 것
th = 0.5; c, s = math.cos(th), math.sin(th)
W2 = frame_matrix((0, 0, 0), (c, s, 0), (-s, c, 0))[0]
p = [2.0, 1.0, 0.0]
q = mv(W2, p + [1])
r = [c * p[0] + s * p[1], -s * p[0] + c * p[1], 0] # −θ 회전
assert all(abs(a - b) < 1e-12 for a, b in zip(q, r))
# 로컬 좌표 = (U·(X−X0), V·(X−X0), N·(X−X0)), 되돌리면 X0 + aU + bV + cN
random.seed(2)
for _ in range(100):
a = random.random() * 6
U = (math.cos(a), math.sin(a), 0); V = (0, 0, 1)
X0 = tuple(random.uniform(-3, 3) for _ in range(3)); X = [random.uniform(-3, 3) for _ in range(3)]
W, _, _, N = frame_matrix(X0, U, V)
loc = mv(W, X + [1])
back = [X0[i] + loc[0] * U[i] + loc[1] * V[i] + loc[2] * N[i] for i in range(3)]
assert all(abs(x - y) < 1e-9 for x, y in zip(back, X))
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()