"""월드 좌표와 코핑 로컬 좌표를 바꾸는 모듈. Rhino 없이 실행됩니다. 단위는 mm, 각도는 라디안입니다. 월드 좌표 (x, y, z) <-> 로컬 좌표 (s, t, z) s = 길이방향 (코핑 축 방향), t = 폭방향 (s를 반시계로 90도 돌린 방향) z = 월드 높이를 그대로 사용 원점(origin_x, origin_y)과 각도(angle)는 값으로 받으므로 언제든 바꿀 수 있습니다. 기존 프로젝트 rebar_pipeline/rhino/07_ctc_extract.py 의 OX, OY, ANG 방식과 같습니다. """ import math class Frame(object): """코핑 로컬 좌표계. 원점(월드 x, y)과 s축 각도(월드 +X 기준, 반시계)로 정해집니다.""" def __init__(self, origin_x, origin_y, angle): self.origin_x = float(origin_x) self.origin_y = float(origin_y) self.angle = float(angle) self._cos = math.cos(self.angle) self._sin = math.sin(self.angle) def to_local(self, x, y, z): """월드 점 (x, y, z) -> 로컬 점 (s, t, z).""" dx = x - self.origin_x dy = y - self.origin_y return (dx * self._cos + dy * self._sin, -dx * self._sin + dy * self._cos, z) def to_world(self, s, t, z): """로컬 점 (s, t, z) -> 월드 점 (x, y, z).""" return (self.origin_x + s * self._cos - t * self._sin, self.origin_y + s * self._sin + t * self._cos, z) def direction_to_local(self, dx, dy, dz): """월드 방향 벡터 -> 로컬 방향 벡터 (원점 이동 없이 회전만).""" return (dx * self._cos + dy * self._sin, -dx * self._sin + dy * self._cos, dz) def direction_to_world(self, ds, dt, dz): """로컬 방향 벡터 -> 월드 방향 벡터 (원점 이동 없이 회전만).""" return (ds * self._cos - dt * self._sin, ds * self._sin + dt * self._cos, dz) def to_local_many(self, points): """월드 점 목록 -> 로컬 점 목록.""" return [self.to_local(x, y, z) for (x, y, z) in points] def to_world_many(self, points): """로컬 점 목록 -> 월드 점 목록.""" return [self.to_world(s, t, z) for (s, t, z) in points] def from_two_points(a, b): """두 점 a, b (월드 (x, y))로 프레임을 만듭니다. 원점 = 두 점의 가운데, s축 = a에서 b로 향하는 방향. (예: 2주식 교각의 두 기둥 중심) """ dx = b[0] - a[0] dy = b[1] - a[1] if dx == 0 and dy == 0: raise ValueError("두 점이 같아서 방향을 정할 수 없습니다: %r" % (a,)) return Frame((a[0] + b[0]) / 2.0, (a[1] + b[1]) / 2.0, math.atan2(dy, dx)) def convert(point, src, dst): """src 프레임의 로컬 점을 dst 프레임의 로컬 점으로 바꿉니다 (월드를 거쳐서).""" return dst.to_local(*src.to_world(*point))