From 540812861dafb24603262b8917ba428a0ac3e2bc Mon Sep 17 00:00:00 2001 From: yscha Date: Wed, 10 Dec 2025 00:40:53 +0900 Subject: [PATCH] sortedBaseLines --- src/util/skeleton-utils.js | 105 ++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index f9a9360d..a7bcb46f 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -754,6 +754,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { const sortedCurrentRoofLines = sortCurrentRoofLines(alignedCurrentRoofLines); const sortedRoofLines = sortCurrentRoofLines(roofLines); const sortedWallBaseLines = sortCurrentRoofLines(wall.baseLines); + const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, wallLines); //wall.lines 는 기본 벽 라인 @@ -772,8 +773,8 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { const roofLine = roofLines[index]; const currentRoofLine = currentRoofLines[index]; - const moveLine = wall.baseLines[index] - const wallBaseLine = wall.baseLines[index] + const moveLine = sortedBaseLines[index] + const wallBaseLine = sortedBaseLines[index] //roofline 외곽선 설정 @@ -861,14 +862,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { //두 포인트가 변경된 라인인 if (fullyMoved ) { //반시계방향향 - console.log("moveFully:::::::::::::", wallBaseLine, newPStart, newPEnd) - console.log("moveFully:::::::::::::", roofLine.direction) + const mLine = getSelectLinePosition(wall, wallBaseLine) if (getOrientation(roofLine) === 'vertical') { if (['left', 'right'].includes(mLine.position)) { - if(wallLine.x1 === wallBaseLine.x1) { + if(Math.abs(wallLine.x1 - wallBaseLine.x1) < 0.1 || Math.abs(wallLine.x2 - wallBaseLine.x2) < 0.1) { return false } const positionType = @@ -876,7 +876,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { (mLine.position === 'right' && wallLine.x1 > wallBaseLine.x1) ? 'in' : 'out'; const condition = `${mLine.position}_${positionType}`; - let isStartEnd = findInteriorPoint(wallBaseLine, wall.baseLines) + let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) let sPoint, ePoint; if(condition === 'left_in') { isIn = true @@ -1014,6 +1014,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { } } + findPoints.push({ y: newPStart.y, x: newPEnd.x, position: 'left_out_end' }); } }else if(condition === 'right_in') { if (isStartEnd.start ) { @@ -1165,7 +1166,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { } else if (getOrientation(roofLine) === 'horizontal') { //red if (['top', 'bottom'].includes(mLine.position)) { - if(Math.abs(wallLine.y1 - wallBaseLine.y1) < 0.1) { + if(Math.abs(wallLine.y1 - wallBaseLine.y1) < 0.1 || Math.abs(wallLine.y2 - wallBaseLine.y2) < 0.1) { return false } const positionType = @@ -1174,7 +1175,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { ? 'in' : 'out'; const condition = `${mLine.position}_${positionType}`; - let isStartEnd = findInteriorPoint(wallBaseLine, wall.baseLines) + let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) let sPoint, ePoint; @@ -1401,15 +1402,15 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() newPEnd.x = aStartX newPStart.x = Big(roofLine.x1).plus(eLineX).toNumber() - let idx = (0 > index - 1)?roofLines.length:index - const newLine = roofLines[idx-1]; + let idx = (roofLines.length < index + 1)?0:index + const newLine = roofLines[idx + 1]; if(Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { if(inLine){ if(inLine.y2 < inLine.y1 ) { getAddLine({ x: bStartX, y: wallLine.y1 }, { x: inLine.x2, y: inLine.y2 }, 'pink') }else{ - getAddLine({ x: inLine.x2, y: inLine.y2 }, { x: bStartX, y: wallLine.y1 }, 'pink') + getAddLine({ x: inLine.x1, y: inLine.y1 }, { x: bStartX, y: wallLine.y1 }, 'pink') } } getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x2, y: wallLine.y2 }, 'magenta') @@ -3136,7 +3137,7 @@ function updateAndAddLine(innerLines, targetPoint) { isUpdatingStart = true; } }else if(targetPoint.position === "top_out_end"){ - if(foundLine.y2 > foundLine.y1){ + if(foundLine.y2 >= foundLine.y1){ isUpdatingStart = true; } }else if(targetPoint.position === "bottom_out_start"){ @@ -3317,4 +3318,82 @@ function findInteriorPoint(line, polygonLines) { start: startIsValley, end: endIsValley }; -} \ No newline at end of file +} + +/** + * baseLines의 순서를 wallLines의 순서와 일치시킵니다. + * 각 wallLine에 대해 가장 가깝고 평행한 baseLine을 찾아 정렬된 배열을 반환합니다. + * + * @param {Array} baseLines - 정렬할 원본 baseLine 배열 + * @param {Array} wallLines - 기준이 되는 wallLine 배열 + * @returns {Array} wallLines 순서에 맞춰 정렬된 baseLines + */ +export const sortBaseLinesByWallLines = (baseLines, wallLines) => { + if (!baseLines || !wallLines || baseLines.length === 0 || wallLines.length === 0) { + return baseLines; + } + + const sortedBaseLines = []; + const usedIndices = new Set(); // 이미 매칭된 baseLine 인덱스를 추적 + + wallLines.forEach((wallLine) => { + let bestMatchIndex = -1; + let minDistance = Infinity; + + // wallLine의 중점 계산 + const wMidX = (wallLine.x1 + wallLine.x2) / 2; + const wMidY = (wallLine.y1 + wallLine.y2) / 2; + + // wallLine의 방향 벡터 (평행 확인용) + const wDx = wallLine.x2 - wallLine.x1; + const wDy = wallLine.y2 - wallLine.y1; + const wLen = Math.hypot(wDx, wDy); + + baseLines.forEach((baseLine, index) => { + // 이미 매칭된 라인은 건너뜀 (1:1 매칭) + if (usedIndices.has(index)) return; + + // baseLine의 중점 계산 + const bMidX = (baseLine.x1 + baseLine.x2) / 2; + const bMidY = (baseLine.y1 + baseLine.y2) / 2; + + // 두 라인의 중점 사이 거리 계산 + const dist = Math.hypot(wMidX - bMidX, wMidY - bMidY); + + // 평행 여부 확인 (내적 사용) + const bDx = baseLine.x2 - baseLine.x1; + const bDy = baseLine.y2 - baseLine.y1; + const bLen = Math.hypot(bDx, bDy); + + if (wLen > 0 && bLen > 0) { + // 단위 벡터 내적값 (-1 ~ 1) + const dot = (wDx * bDx + wDy * bDy) / (wLen * bLen); + + // 내적의 절대값이 1에 가까우면 평행 (약 10도 오차 허용) + if (Math.abs(Math.abs(dot) - 1) < 0.1) { + if (dist < minDistance) { + minDistance = dist; + bestMatchIndex = index; + } + } + } + }); + + if (bestMatchIndex !== -1) { + sortedBaseLines.push(baseLines[bestMatchIndex]); + usedIndices.add(bestMatchIndex); + } else { + // 매칭되는 라인을 찾지 못한 경우, 아직 사용되지 않은 첫 번째 라인을 할당 (Fallback) + const unusedIndex = baseLines.findIndex((_, idx) => !usedIndices.has(idx)); + if (unusedIndex !== -1) { + sortedBaseLines.push(baseLines[unusedIndex]); + usedIndices.add(unusedIndex); + } else { + // 더 이상 남은 라인이 없으면 null 또는 기존 라인 중 하나(에러 방지) + sortedBaseLines.push(baseLines[0]); + } + } + }); + + return sortedBaseLines; +}; \ No newline at end of file