From 881fcb91f691081d9cc0d1f8133cc3a5b7c274a3 Mon Sep 17 00:00:00 2001 From: ysCha Date: Fri, 7 Nov 2025 18:34:16 +0900 Subject: [PATCH] sk --- src/components/fabric/QPolygon.js | 4 +- src/util/skeleton-utils.js | 270 +++++++++++++++--------------- 2 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 8186b449..3ca095a8 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -336,8 +336,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { if (types.every((type) => type === LINE_TYPE.WALLLINE.EAVES)) { // 용마루 -- straight-skeleton console.log('용마루 지붕') - drawRidgeRoof(this.id, this.canvas, textMode) - //drawSkeletonRidgeRoof(this.id, this.canvas, textMode); + //drawRidgeRoof(this.id, this.canvas, textMode) + drawSkeletonRidgeRoof(this.id, this.canvas, textMode); } else if (isGableRoof(types)) { // A형, B형 박공 지붕 console.log('패턴 지붕') diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 95c5cb7b..06208ddd 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -231,19 +231,19 @@ const movingLineFromSkeleton = (roofId, canvas) => { newPoints.forEach((point, index) => { if(position === 'bottom'){ if (moveDirection === 'in') { - if(isSamePoint(roof.basePoints[index], originalStartPoint)) { - point.y = Big(point.y).minus(absMove).toNumber(); - } - if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + if(isSamePoint(roof.basePoints[index], originalStartPoint) || isSamePoint(roof.basePoints[index], originalEndPoint)) { point.y = Big(point.y).minus(absMove).toNumber(); } + // if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + // point.y = Big(point.y).minus(absMove).toNumber(); + // } }else if (moveDirection === 'out'){ - if(isSamePoint(roof.basePoints[index], originalStartPoint)) { - point.y = Big(point.y).plus(absMove).toNumber(); - } - if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + if(isSamePoint(roof.basePoints[index], originalStartPoint) || isSamePoint(roof.basePoints[index], originalEndPoint)) { point.y = Big(point.y).plus(absMove).toNumber(); } + // if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + // point.y = Big(point.y).plus(absMove).toNumber(); + // } } }else if (position === 'top'){ @@ -265,19 +265,19 @@ const movingLineFromSkeleton = (roofId, canvas) => { }else if(position === 'left'){ if(moveDirection === 'in'){ - if(isSamePoint(roof.basePoints[index], originalStartPoint)) { - point.x = Big(point.x).plus(absMove).toNumber(); - } - if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + if(isSamePoint(roof.basePoints[index], originalStartPoint) || isSamePoint(roof.basePoints[index], originalEndPoint)) { point.x = Big(point.x).plus(absMove).toNumber(); } + // if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + // point.x = Big(point.x).plus(absMove).toNumber(); + // } }else if(moveDirection === 'out'){ - if(isSamePoint(roof.basePoints[index], originalStartPoint)) { - point.x = Big(point.x).minus(absMove).toNumber(); - } - if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + if(isSamePoint(roof.basePoints[index], originalStartPoint) || isSamePoint(roof.basePoints[index], originalEndPoint)) { point.x = Big(point.x).minus(absMove).toNumber(); } + // if (isSamePoint(roof.basePoints[index], originalEndPoint)) { + // point.x = Big(point.x).minus(absMove).toNumber(); + // } } }else if(position === 'right'){ @@ -426,7 +426,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { */ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { if (!skeleton?.Edges) return [] - let roof = canvas?.getObjects().find((object) => object.id === roofId) + const roof = canvas?.getObjects().find((object) => object.id === roofId) + const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId) let skeletonLines = [] const processedInnerEdges = new Set() @@ -535,140 +536,69 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { }); //skeleton 라인에서 처마선은 삭제 - if(innerLine.lineName !== 'outerLine'){ - canvas.add(innerLine); - innerLine.bringToFront(); - existingLines.add(lineKey); // 추가된 라인을 추적 - }else{ + if(innerLine.lineName === 'outerLine'){ - const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - const wallLines = wall.baseLines - // 현재 지점과 다음 지점을 비교하기 위한 변수 - let changedLine = roof.moveSelectLine; - const roofLines = []; - - - if (!wall.lines || !wall.baseLines) { - return wall.baseLines || wall.lines || []; - } - - // 길이가 다른 경우 baseLines 반환 - if (wall.lines.length !== wall.baseLines.length) { - return wall.baseLines; - } - - for (let i = 0; i < wall.baseLines.length; i++) { - const baseLine = wall.baseLines[i]; - const line = wall.lines[i]; - - if (!line || - ((!isSamePoint(baseLine.startPoint, line.startPoint)) && // 시작점이 다르고 - (!isSamePoint(baseLine.endPoint, line.endPoint)))) { // 끝점도 다른 경우 + let idx = 0; + //const orgWallLine = wall.lines + //console.log("outLine:::::", p1, p2, line) + for(const orgLine of roof.lines){ + console.log("a::::",isSamePoint(p1,orgLine.startPoint), p1, orgLine.startPoint) + console.log("b::::",isSamePoint(p2,orgLine.endPoint), p2, orgLine.endPoint) + console.log("c::::",isSamePoint(p2,orgLine.startPoint), p2, orgLine.startPoint) + console.log("d::::",isSamePoint(p1,orgLine.endPoint), p1, orgLine.endPoint) + let isSampe1 = false + let isSampe2 = false + const orgOuterLine = orgLine + if(isSamePoint(p1,orgLine.startPoint) && isSamePoint(p2,orgLine.endPoint)){ + isSampe1 = true; + break; } - } + orgOuterLine.startPoint = orgLine.endPoint + orgOuterLine.endPoint = orgLine.startPoint - const startClosest = findClosestRoofLine(p1, roof.lines); - const endClosest = findClosestRoofLine(p2, roof.lines); - - - const { point, closest, selectPoint, otherPoint } = - startClosest.distance > endClosest.distance - ? { - point : p2, - closest : endClosest, - otherPoint: p1 - } - : { - point : p1, - closest : startClosest, - otherPoint: p2 - }; - -// Log the relevant information - console.log("Point:", point); - console.log("Closest intersection:", closest); - console.log("moveSelectLinePoint:", selectPoint); - let isTarget = false; - for(const roofLine of roof.lines){ - if( isSamePoint(p1, roofLine.startPoint) || - isSamePoint(p2, roofLine.endPoint) || - isSamePoint(p1, roofLine.endPoint) || - isSamePoint(p2, roofLine.startPoint) ) { - isTarget = true ; - break + if(isSamePoint(p1,orgLine.endPoint) && isSamePoint(p2,orgLine.startPoint)){ + isSampe2 = true + return; } - } - if (isTarget) { - console.warn("matching line found in roof.lines"); - return; // 또는 적절한 오류 처리 - } - const innerLine2 = new QLine([p1.x, p1.y, p2.x, p2.y], { - parentId: roof.id, - fontSize: roof.fontSize, - stroke: 'red', - strokeWidth: lineStyle.width, - name: (line.attributes.isOuterEdge)?'eaves': attributes.type, - attributes: attributes, - direction: direction, - isBaseLine: line.attributes.isOuterEdge, - lineName: (line.attributes.isOuterEdge)?'addLine': attributes.type, - selectable:(!line.attributes.isOuterEdge), - roofId: roofId, - }); + const innerLine2 = new QLine([orgOuterLine.startPoint.x, orgOuterLine.startPoint.y, orgOuterLine.endPoint.x, orgOuterLine.endPoint.y], { + parentId : roof.id, + fontSize : roof.fontSize, + stroke : 'yellow', + strokeWidth: lineStyle.width, + name : (line.attributes.isOuterEdge) ? 'eaves' : attributes.type, + attributes : attributes, + direction : direction, + isBaseLine : line.attributes.isOuterEdge, + lineName : (line.attributes.isOuterEdge) ? 'addLine' : attributes.type, + selectable : (!line.attributes.isOuterEdge), + roofId : roofId, + }); - canvas.add(innerLine2); - //existingLines.add(lineKey); // 추가된 라인을 추적 - /* - //라인추가(까지 못할때때) 외벽선에서 추가 - const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) - const wallLines = wall.baseLines - // 현재 지점과 다음 지점을 비교하기 위한 변수 - let changedLine = roof.moveSelectLine; - const roofLines = []; + //orgOuterLine.setCoords() + //canvas.renderAll() + canvas.add(innerLine2); + } - if (!wall.lines || !wall.baseLines) { - return wall.baseLines || wall.lines || []; - } - // 길이가 다른 경우 baseLines 반환 - if (wall.lines.length !== wall.baseLines.length) { - return wall.baseLines; + for(const orgWallLine of wall.lines){ + // console.log("orgWallLine::::;",orgWallLine.startPoint, orgWallLine.endPoint); + // if(isSamePoint(orgWallLine.startPoint, p1) || + // isSamePoint(orgWallLine.endPoint, p2) || + // isSamePoint(orgWallLine.endPoint, p1) || + // isSamePoint(orgWallLine.startPoint, p2)){ + // idx = orgWallLine.idx + // break + // } } - //그려지는 처마라인이 처마 && 포인터모두가 wall.baseLine에 들어가 있는 경우 - const checkPoint = {x1:line.x1, y1:line.y1, x2:line.x2, y2:line.y2} - if(line.attributes.type === 'hip' && !checkPointInPolygon(checkPoint, wall)) { - const startClosest = findClosestRoofLine(p1, roof.lines); - const endClosest = findClosestRoofLine(p2, roof.lines); - console.log("Lindd::::",line) - const { point, closest, selectPoint, otherPoint } = - startClosest.distance > endClosest.distance - ? { - point : p2, - closest : endClosest, - //selectPoint : changedLine.endPoint, - otherPoint: p1 - } - : { - point : p1, - closest : startClosest, - //selectPoint : changedLine.startPoint, - otherPoint: p2 - }; - -// Log the relevant information - console.log("Point:", point); - console.log("Closest intersection:", closest); - console.log("moveSelectLinePoint:", selectPoint); - } -*/ + //초기외곽라인? const coordinateText = new fabric.Text(`(${Math.round(p1.x)}, ${Math.round(p1.y)})`, { left: p1.x + 5, // 좌표점에서 약간 오른쪽으로 이동 top: p1.y - 20, // 좌표점에서 약간 위로 이동 @@ -685,6 +615,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { }) canvas?.add(coordinateText) + + }else{ + canvas.add(innerLine); + innerLine.bringToFront(); + existingLines.add(lineKey); // 추가된 라인을 추적 } innerLines.push(innerLine) canvas.renderAll(); @@ -2569,4 +2504,69 @@ function hasIntersectionWithOtherLines(point, skeletonLines, currentLine, tolera // 1개 이상의 다른 라인과 연결되어 있으면 교점으로 간주 return connectionCount >= 1; +} + +function findClosestRoofLine(point, roofLines) { + let closestLine = null; + let minDistance = Infinity; + let roofLineIndex = 0; + let interPoint = null; + + roofLines.forEach((roofLine, index) => { + const lineP1 = roofLine.startPoint; + const lineP2 = roofLine.endPoint; + + // 점에서 선분까지의 최단 거리 계산 + const distance = pointToLineDistance(point, lineP1, lineP2); + + // 점에서 수직으로 내린 교점 계산 + const intersection = getProjectionPoint(point, { + x1: lineP1.x, + y1: lineP1.y, + x2: lineP2.x, + y2: lineP2.y + }); + + if (distance < minDistance) { + minDistance = distance < 0.1 ? 0 : distance; //거리가 0.1보다 작으면 0으로 처리 + closestLine = roofLine; + roofLineIndex = index + interPoint = intersection; + } + }); + + return { line: closestLine, distance: minDistance, index: roofLineIndex, intersectionPoint: interPoint }; +} + +// 점에서 선분까지의 최단 거리를 계산하는 도우미 함수 +function pointToLineDistance(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); } \ No newline at end of file