diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 6294faaf..a05866f1 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -532,12 +532,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { selectable:(!line.attributes.isOuterEdge), roofId: roofId, }); - + canvas.add(innerLine); + innerLine.bringToFront(); + existingLines.add(lineKey); // 추가된 라인을 추적 //skeleton 라인에서 처마선은 삭제 if(innerLine.lineName !== 'outerLine'){ - canvas.add(innerLine); - innerLine.bringToFront(); - existingLines.add(lineKey); // 추가된 라인을 추적 + // canvas.add(innerLine); + // innerLine.bringToFront(); + // existingLines.add(lineKey); // 추가된 라인을 추적 }else{ const coordinateText = new fabric.Text(`(${Math.round(p1.x)}, ${Math.round(p1.y)})`, { left: p1.x + 5, // 좌표점에서 약간 오른쪽으로 이동 @@ -615,7 +617,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { // 지붕 경계선과 교차 확인 및 클리핑 const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines, roof.moveSelectLine); - //console.log('clipped line', clippedLine.p1, clippedLine.p2); + console.log('clipped line', clippedLine.p1, clippedLine.p2); const isOuterLine = isOuterEdge(clippedLine.p1, clippedLine.p2, [edgeResult.Edge]) addRawLine(roof.id, skeletonLines, clippedLine.p1, clippedLine.p2, 'ridge', 'red', 5, pitch, isOuterLine); // } @@ -1641,7 +1643,7 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { * @param {Array} roofLines - 다각형을 구성하는 선분들 * @returns {boolean} 점이 다각형 내부에 있으면 true */ -function isPointInsidePolygon(point, roofLines) { +function isPointInsidePolygon2(point, roofLines) { let inside = false; const x = point.x; const y = point.y; @@ -1656,11 +1658,78 @@ function isPointInsidePolygon(point, roofLines) { if (((y1 > y) !== (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1)) { inside = !inside; } + } return inside; } +function isPointInsidePolygon(point, roofLines) { + // 1. 먼저 경계선 위에 있는지 확인 (방향 무관) + if (isOnBoundaryDirectionIndependent(point, roofLines)) { + return true; + } + + // 2. 내부/외부 판단 (기존 알고리즘) + let winding = 0; + const x = point.x; + const y = point.y; + + for (let i = 0; i < roofLines.length; i++) { + const line = roofLines[i]; + const x1 = line.x1, y1 = line.y1; + const x2 = line.x2, y2 = line.y2; + + if (y1 <= y) { + if (y2 > y) { + const orientation = (x2 - x1) * (y - y1) - (x - x1) * (y2 - y1); + if (orientation > 0) winding++; + } + } else { + if (y2 <= y) { + const orientation = (x2 - x1) * (y - y1) - (x - x1) * (y2 - y1); + if (orientation < 0) winding--; + } + } + } + + return winding !== 0; +} + +// 방향에 무관한 경계선 검사 +function isOnBoundaryDirectionIndependent(point, roofLines) { + const tolerance = 1e-10; + + for (const line of roofLines) { + if (isPointOnLineSegmentDirectionIndependent(point, line, tolerance)) { + return true; + } + } + return false; +} + +// 핵심: 방향에 무관한 선분 위 점 검사 +function isPointOnLineSegmentDirectionIndependent(point, line, tolerance) { + const x = point.x, y = point.y; + const x1 = line.x1, y1 = line.y1; + const x2 = line.x2, y2 = line.y2; + + // 방향에 무관하게 경계 상자 체크 + const minX = Math.min(x1, x2); + const maxX = Math.max(x1, x2); + const minY = Math.min(y1, y2); + const maxY = Math.max(y1, y2); + + if (x < minX - tolerance || x > maxX + tolerance || + y < minY - tolerance || y > maxY + tolerance) { + return false; + } + + // 외적을 이용한 직선 위 판단 (방향 무관) + const cross = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1); + return Math.abs(cross) < tolerance; +} + /** * 선분 위의 점에 대한 매개변수 t를 계산합니다. * p = p1 + t * (p2 - p1)에서 t 값을 구합니다.