"""B07 도면 불러오기 — 호 · bulge 폴리선 · 거울 블록 · 겹 블록 견본 DXF(84-2) · 내보내기 시계방향 호(84-3). 견본은 ezdxf 로 그 자리에서 만들어 끌어다 놓기 길(`drawing_file_to_entities`)로 읽는다. """ from __future__ import annotations import io import math import ezdxf from ezdxf.enums import TextEntityAlignment from B07_DesignDetail import B07_DesignDetail_Engine_Frame_Export as export from B07_DesignDetail.B07_DesignDetail_Engine_Frame_Import import drawing_file_to_entities def _read(doc: ezdxf.document.Drawing) -> list[dict]: stream = io.StringIO() doc.write(stream) entities, _layers = drawing_file_to_entities("견본.dxf", stream.getvalue().encode("utf-8")) return entities def _points(polyline: dict) -> list[tuple[float, float]]: children = polyline["children"] points = [ (c["shapeData"]["startPoint"]["x"], c["shapeData"]["startPoint"]["y"]) for c in children ] last = children[-1]["shapeData"]["endPoint"] return [*points, (last["x"], last["y"])] def _on_circle(points, cx: float, cy: float, r: float) -> bool: return all(abs(math.hypot(x - cx, y - cy) - r) < 0.21 for x, y in points) def test_호는_1사분면_작은_호(): doc = ezdxf.new() doc.modelspace().add_arc((0, 0), 10, 0, 90) (arc,) = _read(doc) points = _points(arc) assert _on_circle(points, 0, 0, 10) assert points[0] == (10.0, 0.0) and math.isclose(points[-1][0], 0, abs_tol=1e-9) assert all(x >= -1e-9 and y >= -1e-9 for x, y in points) # 큰 호(270°)로 뒤집히지 않음 def test_bulge_폴리선은_호로_펴짐(): doc = ezdxf.new() space = doc.modelspace() # 반원(bulge 1 · 반시계 → 아래로) · 작은 호(bulge −0.2 · 시계 → 위로 1) space.add_lwpolyline([(0, 0, 0, 0, 1), (10, 0)], format="xyseb") space.add_lwpolyline([(0, 20, 0, 0, -0.2), (10, 20)], format="xyseb") half, small = _read(doc) half_points = _points(half) assert len(half_points) > 3 and _on_circle(half_points, 5, 0, 5) assert min(y for _x, y in half_points) < -4.9 small_points = _points(small) assert len(small_points) > 3 assert math.isclose(max(y for _x, y in small_points), 21.0, abs_tol=0.01) # 처짐 = 0.2 × 10 / 2 assert min(y for _x, y in small_points) >= 20 - 1e-9 def test_거울_블록은_WCS_자리(): doc = ezdxf.new() block = doc.blocks.new("거울") block.add_lwpolyline([(0, 0, 0, 0, -0.2), (10, 0)], format="xyseb") block.add_arc((20, 0), 2, 0, 90) block.add_text("가", dxfattribs={"height": 2.5}).set_placement((2, 3)) doc.modelspace().add_blockref("거울", (100, 0), dxfattribs={"xscale": -1}) by_type: dict[str, list[dict]] = {} for entity in _read(doc): by_type.setdefault(entity["type"], []).append(entity) bulge, arc = by_type["PolyLine"] bulge_points = _points(bulge) assert ( min(x for x, _y in bulge_points) >= 90 - 1e-9 and max(x for x, _y in bulge_points) <= 100 + 1e-9 ) assert math.isclose(max(y for _x, y in bulge_points), 1.0, abs_tol=0.01) # 위로 그대로 arc_points = _points(arc) assert _on_circle(arc_points, 80, 0, 2) assert all(x <= 80 + 1e-9 and y >= -1e-9 for x, y in arc_points) # 2사분면으로 대칭 (text,) = by_type["Text"] assert text["shapeData"]["basePoint"] == {"x": 98.0, "y": 3.0} def test_겹_블록도_풀림(): doc = ezdxf.new() doc.blocks.new("속").add_line((0, 0), (1, 0)) doc.blocks.new("겉").add_blockref("속", (5, 0)) doc.modelspace().add_blockref("겉", (10, 0)) (line,) = _read(doc) assert line["type"] == "Line" assert line["shapeData"] == { "startPoint": {"x": 15.0, "y": 0.0}, "endPoint": {"x": 16.0, "y": 0.0}, } def test_TEXT_정렬점_자리(): doc = ezdxf.new() space = doc.modelspace() space.add_text("가운데", dxfattribs={"height": 2}).set_placement( (50, 5), align=TextEntityAlignment.MIDDLE_CENTER ) space.add_text("오른", dxfattribs={"height": 2}).set_placement( (80, 5), align=TextEntityAlignment.BOTTOM_RIGHT ) center, right = _read(doc) assert center["shapeData"]["basePoint"] == {"x": 50.0, "y": 5.0} assert center["shapeData"]["options"]["textAlign"] == "center" assert right["shapeData"]["basePoint"] == {"x": 80.0, "y": 5.0} assert right["shapeData"]["options"]["textAlign"] == "right" def test_내보내기_시계방향_작은_호(tmp_path): arc = { "type": "Arc", "layerId": "기본", "shapeData": { "center": {"x": 0, "y": 0}, "radius": 1, "startAngle": math.pi / 2, "endAngle": 0, "counterClockwise": False, }, } raw, skipped = export.drawing_to_dxf({"entities": [arc], "layers": []}) assert skipped == {} path = tmp_path / "out.dxf" path.write_bytes(raw) (out,) = ezdxf.readfile(path).modelspace().query("ARC") span = (out.dxf.end_angle - out.dxf.start_angle) % 360 assert math.isclose(span, 90) and math.isclose(out.dxf.start_angle % 360, 0, abs_tol=1e-9) def test_작은_원_호는_뭉개지지_않음(): """m 단위 도면(99-3 중력식사방댐) — 반지름 0.071 원 · 0.36 호 · bulge 0.4 폭 반원이 점 · 직선이 되지 않음.""" doc = ezdxf.new() space = doc.modelspace() space.add_circle((0, 0), 0.071) space.add_arc((5, 0), 0.361, 10, 96) space.add_lwpolyline([(10, 0, 0, 0, 1), (10.4, 0)], format="xyseb") circle, arc, half = _read(doc) circle_points = _points(circle) assert len(circle_points) >= 33 and circle_points[0] == circle_points[-1] assert _on_circle(circle_points, 0, 0, 0.071) assert max(x for x, _y in circle_points) > 0.07 and min(x for x, _y in circle_points) < -0.07 arc_points = _points(arc) assert len(arc_points) >= 9 for x, y in arc_points: # 10°~96° 안 · 큰 호로 뒤집히지 않음 assert abs(math.hypot(x - 5, y) - 0.361) < 0.003 assert 10 - 0.01 <= math.degrees(math.atan2(y, x - 5)) <= 96 + 0.01 half_points = _points(half) assert len(half_points) >= 9 and min(y for _x, y in half_points) < -0.19