From 1a432913fc940e61ec9b5d44dcdf9a1c181feb07 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Thu, 18 Dec 2025 17:59:16 +0900 Subject: [PATCH] =?UTF-8?q?A,B=ED=83=80=EC=9E=85=20=EC=A7=80=EB=B6=95?= =?UTF-8?q?=EC=9E=AC=20=ED=95=A0=EB=8B=B9=20=EC=8B=9C=20=EB=B0=A9=ED=96=A5?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/usePolygon.js | 125 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index fe8fbd78..011b8852 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -851,12 +851,15 @@ export const usePolygon = () => { // innerLines와 polygonLines의 겹침을 확인하고 type 변경 innerLines.forEach((innerLine) => { polygonLines.forEach((polygonLine) => { + if (polygonLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + return + } if (checkLineOverlap(innerLine, polygonLine)) { // innerLine의 type을 polygonLine의 type으로 변경 if (innerLine.attributes && polygonLine.attributes.type) { // innerLine이 polygonLine보다 긴 경우 polygonLine.need를 false로 변경 if (polygonLine.length < innerLine.length) { - if (polygonLine.lineName !== 'eaveHelpLine') { + if (polygonLine.lineName !== 'eaveHelpLine' || polygonLine.lineName !== 'eaveHelpLine') { polygonLine.need = false } } @@ -1014,6 +1017,7 @@ export const usePolygon = () => { canvas.add(line) }) canvas.renderAll()*/ + polygonLines = polygonLines.filter((line) => line.need) polygonLines.forEach((line) => { @@ -1377,7 +1381,6 @@ export const usePolygon = () => { let newRoofs = getSplitRoofsPoints(allLines) newRoofs = newRoofs.filter((roof) => roof.length !== 0) - newRoofs.forEach((roofPoint, index) => { let defense, pitch @@ -1411,6 +1414,124 @@ export const usePolygon = () => { } }) + // representLines가 없다면 A,B타입중 하나임 + if (representLines.length === 0) { + // 1. roofPoint로 폴리곤의 라인들을 생성 + const roofPolygonLines = [] + for (let i = 0; i < roofPoint.length; i++) { + const nextIndex = (i + 1) % roofPoint.length + const startPt = roofPoint[i] + const endPt = roofPoint[nextIndex] + roofPolygonLines.push({ + x1: startPt.x, + y1: startPt.y, + x2: endPt.x, + y2: endPt.y, + startPoint: startPt, + endPoint: endPt, + }) + } + + // 3. 평행 여부 확인 함수 + const checkParallel = (line1, line2) => { + const v1x = line1.x2 - line1.x1 + const v1y = line1.y2 - line1.y1 + const v2x = line2.x2 - line2.x1 + const v2y = line2.y2 - line2.y1 + + const length1 = Math.sqrt(v1x ** 2 + v1y ** 2) + const length2 = Math.sqrt(v2x ** 2 + v2y ** 2) + + if (length1 === 0 || length2 === 0) return false + + const norm1x = v1x / length1 + const norm1y = v1y / length1 + const norm2x = v2x / length2 + const norm2y = v2y / length2 + + const EPSILON = 0.01 + const crossProduct = Math.abs(norm1x * norm2y - norm1y * norm2x) + const dotProduct = norm1x * norm2x + norm1y * norm2y + + return crossProduct < EPSILON || Math.abs(Math.abs(dotProduct) - 1) < EPSILON + } + + // 4. 점에서 라인까지의 거리 계산 함수 + const getDistanceFromPointToLine = (point, lineP1, lineP2) => { + const A = point.x - lineP1.x + const B = point.y - lineP1.y + const C = lineP2.x - lineP1.x + const D = lineP2.y - lineP1.y + + const dot = A * C + B * D + const lenSq = C * C + D * D + let param = -1 + + if (lenSq !== 0) { + param = dot / lenSq + } + + let xx, yy + + if (param < 0) { + xx = lineP1.x + yy = lineP1.y + } else if (param > 1) { + xx = lineP2.x + yy = lineP2.y + } else { + xx = lineP1.x + param * C + yy = lineP1.y + param * D + } + + const dx = point.x - xx + const dy = point.y - yy + + return Math.sqrt(dx * dx + dy * dy) + } + + // 5. 두 평행한 라인 사이의 거리 계산 (한 라인의 중점에서 다른 라인까지의 거리) + const getDistanceBetweenParallelLines = (line1, line2) => { + const midPoint = { + x: (line1.x1 + line1.x2) / 2, + y: (line1.y1 + line1.y2) / 2, + } + return getDistanceFromPointToLine(midPoint, { x: line2.x1, y: line2.y1 }, { x: line2.x2, y: line2.y2 }) + } + + // 6. roofPolygonLines의 모든 라인에서 평행하면서 가장 가까운 EAVES 라인 찾기 + let closestLine = null + let minDistance = Infinity + + roofPolygonLines.forEach((roofLine) => { + ;[...polygonLines, ...innerLines].forEach((line) => { + // EAVES 타입만 필터링 + if (line.attributes?.type !== LINE_TYPE.WALLLINE.EAVES && line.attributes?.type !== LINE_TYPE.WALLLINE.EAVE_HELP_LINE) { + return + } + + const lineObj = { + x1: line.startPoint.x, + y1: line.startPoint.y, + x2: line.endPoint.x, + y2: line.endPoint.y, + } + + if (checkParallel(roofLine, lineObj)) { + const distance = getDistanceBetweenParallelLines(roofLine, lineObj) + if (distance < minDistance && distance > 0) { + minDistance = distance + closestLine = line + } + } + }) + }) + + if (closestLine) { + representLines.push(closestLine) + } + } + // representLines중 가장 긴 line을 찾는다. representLines.forEach((line) => { if (!representLine) {