- 원인: 펴기 허용오차 0.2 고정 — m 단위 도면(중력식 사방댐 시트 폭 약 80)에서 반지름 0.071 원이 길이 0 선 1개 · 0.15~0.2 원이 삼각형 · 0.36~0.46 호가 직선 한 마디 - 고침: 원·호·타원·스플라인·bulge 폴리선 모두 ezdxf 경로로 펴고 90° 이하 토막마다 8마디 이상 - 원본 DWG 대조: 원 42 · 호 16 오차 반지름의 0.5% 이하 · 호 16 모두 원래 각 범위 안(큰 호로 뒤집힘 0) · 불러오기 1.45 s · 도형 1,959 - 시험: test_b07_frame_import_shapes 작은 원·호 시험 하나 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
157 lines
6.1 KiB
Python
157 lines
6.1 KiB
Python
"""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
|