main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12

Merged
eomsangdon merged 585 commits from main_laptop_1 into main 2026-09-08 17:26:30 +09:00
Showing only changes of commit 0f5b4e4df7 - Show all commits
@@ -323,20 +323,55 @@ function circlePoints(center: XY, radius: number): XY[] {
return points;
}
/**
* `rect` → 닫힌 네 모서리 점열(회전 transform 적용).
*
* 돌쌓기 돌·돌망태 칸(`B06_Section_UI_Cross_Wall_Hatch.cell()`)은 `rect` 하나에
* `rotate(벽기울기 cx cy)` 를 걸어 그린다. 수확기가 `rect` 를 안 보던 때는 형태 해칭
* 80개 중 **49개(돌·칸 전부)가 CAD 에 안 실렸다**(2026-09-03 실측). 모서리 반경(`rx`)은
* 무시한다 — 도면에서는 각진 칸으로 충분하다.
*/
function rectPoints(element: SVGElement): XY[] {
const x = attr(element, "x");
const y = attr(element, "y");
const width = attr(element, "width");
const height = attr(element, "height");
const corners: XY[] = [
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height],
[x, y],
];
const rotate = /rotate\(\s*(-?[\d.]+)[\s,]+(-?[\d.]+)[\s,]+(-?[\d.]+)\s*\)/.exec(
element.getAttribute("transform") ?? "",
);
if (!rotate) return corners.map(([cx, cy]) => flip(cx, cy));
const angle = (Number(rotate[1]) * Math.PI) / 180;
const [ox, oy] = [Number(rotate[2]), Number(rotate[3])];
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return corners.map(([cx, cy]) => {
const dx = cx - ox;
const dy = cy - oy;
return flip(ox + dx * cos - dy * sin, oy + dx * sin + dy * cos);
});
}
/** 오프스크린 SVG에 그려진 도형을 CAD 엔티티로 옮긴다 (블록 테두리 안으로 자른다). */
function harvest(root: SVGElement, style: Style, frame: number[]): Entity[] {
const [fx0, , fx1] = frame;
const entities: Entity[] = [];
const segments: XY[][] = [];
const inside = (px: number): boolean => px >= fx0 && px <= fx1;
const nodes = root.querySelectorAll<SVGElement>("polygon, polyline, line, circle, text");
const nodes = root.querySelectorAll<SVGElement>("polygon, polyline, line, circle, rect, text");
for (const element of Array.from(nodes)) {
if (element.closest(SKIP_SELECTOR)) continue;
const tag = element.tagName.toLowerCase();
// 클립 그룹 안의 해칭은 경계로 **잘라서** 싣는다 — CAD 에는 클립이 없다.
const clip = clipPolygonOf(element);
if (tag === "polygon" || tag === "polyline") {
const points = parsePoints(element);
if (tag === "polygon" || tag === "polyline" || tag === "rect") {
const points = tag === "rect" ? rectPoints(element) : parsePoints(element);
if (tag === "polygon" && points.length > 2) points.push(points[0]);
const runs = clip ? clipPointsToPolygon(points, clip) : [points];
for (const run of runs) {