diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js index fc4c907b..1a114c93 100644 --- a/src/hooks/common/useCommonUtils.js +++ b/src/hooks/common/useCommonUtils.js @@ -556,6 +556,12 @@ export function useCommonUtils() { if (object.id) { const group = canvas.getObjects().filter((obj) => obj.id === object.id) group.forEach((obj) => canvas?.remove(obj)) + // QLine 보조선은 lengthText 를 this.text(단수)에 저장해 아래 texts(배열) 제거에 안 걸린다. + // lengthText 는 parentId === line.id 로 연결되므로 parentId 매칭으로 함께 제거 (삭제 시 text 잔존 방지). + canvas + .getObjects() + .filter((obj) => obj.parentId === object.id && obj.name === 'lengthText') + .forEach((obj) => canvas?.remove(obj)) } if (object.type === 'group') { diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 40f7e695..065d738a 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1628,9 +1628,10 @@ export const drawGableRoof = (roofId, canvas, textMode) => { drawRoofPlane(backward) }) roof.innerLines.push(...ridgeLines, ...innerLines) - // 처마선 innerLine 이 외곽선을 전담하므로 roof 원본 외곽(stroke)은 숨긴다. - // (박공은 split 공용 함수를 안 거치므로 여기서 직접 처리 — usePolygon.js 의 roof 기본 stroke:'black') + // 처마선 innerLine 이 외곽선/치수를 전담하므로 roof 원본 외곽(stroke) 숨김 + 지붕선 치수(roof lengthText) 제거. + // 외벽선(wall) 치수는 유지. (박공은 split 공용 함수를 안 거치므로 여기서 직접 처리) roof.set({ stroke: 'transparent' }) + removeRoofLengthText(roof, canvas) canvas .getObjects() .filter((obj) => obj.name === 'check') @@ -1722,6 +1723,16 @@ export const drawShedRoof = (roofId, canvas, textMode) => { const maxLine = pitchSizeLines.reduce((prev, current) => (prev.length > current.length ? prev : current), pitchSizeLines[0]) canvas.add(maxLine) + + // [docs §8] 한쪽흐름 외곽 처마선 — 내부 보조선이 없어 각 처마변이 통짜 innerLine 으로 그려진다. + // 경사변(gable)은 평면길이≠실제길이 라서 degreeOf 로 shedDegree 를 주입 → drawHipLine(plane/cos) 으로 보정. + // roof 원본 외곽(stroke) 숨김은 splitRoofLinesToInnerLines 가 담당. + roof.innerLines = roof.innerLines || [] + splitRoofLinesToInnerLines(roof, roof.innerLines, canvas, textMode, { + degreeOf: (parentLine) => (parentLine.attributes?.type === LINE_TYPE.WALLLINE.GABLE ? shedDegree : 0), + }) + // 지붕선 치수(roof lengthText) 제거는 split 내부 removeRoofLengthText 가 담당. 외벽선(wall) 치수는 유지. + canvas.renderAll() } @@ -5445,6 +5456,18 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { canvas.renderAll() } +/** + * 지붕선(roof.lines)의 원본 변 길이 text(lengthText)를 제거한다. + * 지붕선 치수는 그 위에 그려진 보조선(처마선 innerLine)의 치수로 대체되므로 roof.lines 자체 치수는 숨긴다. + * ※ 외벽선(wall) 치수는 유지한다. (변별/박공/용마루/한쪽흐름 공통) + */ +export const removeRoofLengthText = (roof, canvas) => { + canvas + ?.getObjects() + .filter((o) => o.name === 'lengthText' && o.parentId === roof.id) + .forEach((o) => canvas.remove(o)) +} + /** * roof.lines(처마 외곽선)를 innerLine 끝점 기준으로 split 해 편집 가능한 innerLine(hip/roofLine)으로 만들고, * 처마선 innerLine 이 외곽선을 전담하므로 roof 원본 외곽선(stroke)은 숨긴다(transparent). @@ -5454,9 +5477,13 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { * @param {Array} innerLines - 내부선 배열 (split 결과가 여기에 in-place 추가됨) * @param {fabric.Canvas} canvas * @param {string} textMode + * @param {{degreeOf?: (parentLine) => number}} [opts] - degreeOf: 부모 외곽변 → 경사각(도). + * 한쪽흐름 경사변(gable)처럼 평면길이≠실제길이인 변을 통짜로 그릴 때 drawHipLine 으로 실제길이 보정(plane/cos) — docs §8. + * 미전달 시 기존 동작(분할점 인접 pitch 기반). * @returns {Array} innerLines */ -export const splitRoofLinesToInnerLines = (roof, innerLines, canvas, textMode) => { +export const splitRoofLinesToInnerLines = (roof, innerLines, canvas, textMode, opts = {}) => { + const { degreeOf } = opts // 분할점이 처마변 끝점(코너)과 이 거리(px) 이내면 분할에서 제외. // SK 추녀 연장이 처마 코너에 닿을 때 ray cast 부동소수 오차(관측 0.02px)로 코너와 미세하게 // 어긋나면, 기존 almostEqual(1e-10)은 "끝점 아님"으로 오판 → 0 길이 조각 생성. END_EPS 로 흡수. @@ -5521,13 +5548,20 @@ export const splitRoofLinesToInnerLines = (roof, innerLines, canvas, textMode) = innerLines.push(drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, nextDegree, nextDegree)) } } else { - innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode)) + // 분할점 없는 통짜 변. degreeOf 가 주어지면(한쪽흐름) 경사변은 drawHipLine(plane/cos) 으로 실제길이 보정. + const deg = degreeOf ? degreeOf(currentLine) : 0 + if (deg > 0) { + innerLines.push(drawHipLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode, deg, deg)) + } else { + innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode)) + } } }) - // 처마선 innerLine 이 외곽선을 전담하므로 roof 원본 외곽(stroke)은 숨긴다. - // (안 숨기면 처마선 삭제 시 roof 의 기본 stroke:'black'(usePolygon.js) 이 노출됨) + // 처마선 innerLine 이 외곽선/치수를 전담하므로 roof 원본 외곽(stroke) 숨김 + 지붕선 치수(roof lengthText) 제거. + // (안 숨기면 처마선 삭제 시 roof 기본 stroke:'black'(usePolygon.js) 노출. 외벽선 wall 치수는 유지) roof.set({ stroke: 'transparent' }) + removeRoofLengthText(roof, canvas) return innerLines }