From 3cbfbbd0b313e0def6c41e926ea9799077da00ea Mon Sep 17 00:00:00 2001 From: ysCha Date: Thu, 4 Dec 2025 19:10:02 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A1=9C=EC=A7=81=EB=B3=80=EA=B2=BD1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/skeleton-utils.js | 607 ++++++++++++++++++++++++++++--------- 1 file changed, 469 insertions(+), 138 deletions(-) diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index df920a88..02e4ab54 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -453,7 +453,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => { left: line.x1 + 5, top: line.y1 - 20, fontSize: 10, - fill: 'green', + fill: 'magenta', fontFamily: 'Arial', selectable: false, hasControls: false, @@ -813,12 +813,12 @@ if((roof.moveUpDown??0 > 0) ) { //현재 roof는 무조건 시계방향 - const getAddLine = (p1, p2, stroke = '#1083E3') => { + const getAddLine = (p1, p2, stroke = '') => { movedLines.push({ index, p1, p2 }) // Usage: let mergeLines = mergeMovedLines(movedLines); - console.log("mergeLines:::::::", mergeLines); + //console.log("mergeLines:::::::", mergeLines); const line = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId : roof.id, fontSize : roof.fontSize, @@ -855,6 +855,7 @@ if((roof.moveUpDown??0 > 0) ) { if (fullyMoved ) { //반시계방향향 console.log("moveFully:::::::::::::", wallBaseLine, newPStart, newPEnd) + console.log("moveFully:::::::::::::", roofLine.direction) const mLine = getSelectLinePosition(wall, wallBaseLine) if (getOrientation(roofLine) === 'vertical') { @@ -864,26 +865,68 @@ if((roof.moveUpDown??0 > 0) ) { (mLine.position === 'right' && wallLine.x1 > wallBaseLine.x1) ? 'in' : 'out'; const condition = `${mLine.position}_${positionType}`; - + let isStartEnd = findInteriorPoint(wallBaseLine, sortedWallBaseLines) + let sPoint, ePoint; switch (condition) { case 'left_in': isIn = true - newPEnd.y = wallBaseLine.y2; - getAddLine({ x: newPEnd.x, y: wallBaseLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 }) - findPoints.push({ x: wallBaseLine.x2, y: wallBaseLine.y2 }); - break; + + + if (isStartEnd.start ) { + const moveDist = Big(wallBaseLine.x1).minus(wallLine.x1).abs().toNumber() + sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; + newPEnd.y = wallBaseLine.y2; + newPStart.x = wallBaseLine.x2 + getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue') + findPoints.push({ x: sPoint.x, y: sPoint.y }); + const newPointX = Big(roofLine.x1).plus(moveDist).toNumber() + getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange') + + }else if(isStartEnd.end) { + newPStart.y = roofLine.y1; + newPStart.x = roofLine.x1; + + const moveDist = Big(wallBaseLine.x2).minus(wallLine.x2).abs().toNumber() + ePoint = {x: wallBaseLine.x2, y: wallBaseLine.y2}; + newPEnd.y = wallBaseLine.y2 + + findPoints.push({ x: ePoint.x, y: ePoint.y }); + const newPointX = Big(roofLine.x1).plus(moveDist).toNumber() + + + const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() + const pLineY = Big(roofLine.y2).minus(0).abs().toNumber() + + getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue') + getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange') + if(Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { + getAddLine({ x: roofLine.x1, y: pLineY }, { x: newPointX, y: pLineY }, 'green') + getAddLine({ x: newPointX, y: pLineY }, { x: ePoint.x, y: ePoint.y }, 'pink') + } + + + + } + + + break; case 'left_out': - const moveX = Math.abs(wallBaseLine.x1 - wallLine.x1) - const aStartY = Math.abs(newPEnd.y + moveX) - const bStartY = Math.abs(wallLine.y2 + moveX) - findPoints.push({ x: newPEnd.x, y: aStartY }); - const inLine = findLineContainingPoint(innerLines, { x: newPEnd.x, y: aStartY }) - // console.log("startLines:::::::", inLine); - getAddLine({ x: wallLine.x1, y: roofLine.y2 }, { x: wallLine.x1, y: bStartY }) - getAddLine({ x: wallLine.x2, y: bStartY }, { x: inLine.x2, y: inLine.y2 }, 'pink') - const eLineY = Math.abs(bStartY - wallLine.y2) - newPStart.y += eLineY - newPEnd.y = aStartY + if(isStartEnd.start){ + const moveX = Math.abs(wallBaseLine.x1 - wallLine.x1) + const aStartY = Math.abs(newPEnd.y + moveX) + const bStartY = Math.abs(wallLine.y2 + moveX) + findPoints.push({ x: newPEnd.x, y: aStartY }); + const inLine = findLineContainingPoint(innerLines, { x: newPEnd.x, y: aStartY }) + // console.log("startLines:::::::", inLine); + getAddLine({ x: wallLine.x1, y: roofLine.y2 }, { x: wallLine.x1, y: bStartY }) + getAddLine({ x: wallLine.x2, y: bStartY }, { x: inLine.x2, y: inLine.y2 }, 'pink') + const eLineY = Math.abs(bStartY - wallLine.y2) + newPStart.y += eLineY + newPEnd.y = aStartY + + }else if(isStartEnd.end){ + + } break; case 'right_in': newPEnd.y = wallBaseLine.y2; @@ -972,17 +1015,34 @@ if((roof.moveUpDown??0 > 0) ) { const condition = `${mLine.position}_${positionType}`; let isStartEnd = findInteriorPoint(wallBaseLine, sortedWallBaseLines) + let sPoint, ePoint; + switch (condition) { case 'top_in': console.log("findInteriorPoint result:::::::", isStartEnd); - let sPoint, ePoint; + if (isStartEnd.start ) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; newPStart.x = wallBaseLine.x1; - getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }) + + + const newPointY = Big(roofLine.y2).plus(moveDist).toNumber() + + const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber() + const pLineX = Big(roofLine.x1).minus(0).abs().toNumber() + getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue') findPoints.push({ x: sPoint.x, y: sPoint.y }); + + if(Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { + getAddLine({ x: pLineX, y: roofLine.y2 }, { x: pLineX, y: newPointY }, 'green') + getAddLine({ x: pLineX, y: newPointY }, { x: sPoint.x, y: sPoint.y }, 'pink') + } + + + //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') } if(isStartEnd.end){ @@ -994,7 +1054,6 @@ if((roof.moveUpDown??0 > 0) ) { break; case 'top_out': - console.log("findInteriorPoint result:::::::", isStartEnd); const moveY = Math.abs(wallLine.y1 - wallBaseLine.y1) @@ -1015,9 +1074,46 @@ if((roof.moveUpDown??0 > 0) ) { break; case 'bottom_in': + if (isStartEnd.start ) { + const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() + sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; + newPStart.x = wallBaseLine.x1; + getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }) + findPoints.push({ x: sPoint.x, y: sPoint.y }); + const newPointY = Big(roofLine.y2).minus(moveDist).abs().toNumber() + //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: roofLine.x2, y: newPointY }, 'orange') + } + + if(isStartEnd.end){ + const moveDist = Big(wallLine.y2).minus(wallBaseLine.y2).abs().toNumber() + sPoint = {x: wallBaseLine.x1, y: wallBaseLine.y1}; + newPStart.x = wallBaseLine.x1; + getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }) + findPoints.push({ x: sPoint.x, y: sPoint.y }); + const newPointY = Big(roofLine.y1).minus(moveDist).abs().toNumber() + //getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: roofLine.x1, y: newPointY }, 'orange') + + } + break; case 'bottom_out': - + // console.log("findInteriorPoint result:::::::", isStartEnd); + // + // const moveY = Math.abs(wallLine.y1 - wallBaseLine.y1) + // const dist = Math.abs(roofLine.y1 - wallLine.y1) + // const aStartX = Math.abs(newPStart.x + moveY) + // const bStartX = Math.abs(wallLine.x1 + moveY) + // + // //newPStart.x += moveX + // //wallLine.x1 += moveX + // findPoints.push({ x: aStartX, y: newPEnd.y }); + // const inLine = findLineContainingPoint(innerLines, { x: aStartX, y: newPEnd.y }) + // console.log("startLines:::::::", inLine); + // getAddLine({ x: bStartX, y: wallLine.y1 }, { x: roofLine.x1, y: wallLine.y1 }) + // getAddLine({ x: bStartX, y: wallLine.y2 }, { x: inLine.x2, y: inLine.y2 }, 'pink') + // const eLineX = Math.abs(bStartX - wallLine.x1) + // newPEnd.x += eLineX + // newPStart.x = aStartX break; } } @@ -1095,72 +1191,115 @@ if((roof.moveUpDown??0 > 0) ) { } getAddLine(newPStart, newPEnd, 'red') - } -/* - } else if (movedStart ) { //end 변경경 + + } /* else if (movedStart ) { //end 변경경 - if (getOrientation(roofLine) === 'vertical') { //green 수직 - + if (getOrientation(roofLine) === 'vertical') { //brown 수직 const mLine = getSelectLinePosition(wall, wallBaseLine) + if (['left', 'right'].includes(mLine.position)) { + const positionType = + (mLine.position === 'left' && wallLine.x1 <= wallBaseLine.x1) || + (mLine.position === 'right' && wallLine.x1 >= wallBaseLine.x1) + ? 'in' : 'out'; + + const condition = `${mLine.position}_${positionType}`; + let isStartEnd = findInteriorPoint(wallBaseLine, sortedWallBaseLines) + let sPoint, ePoint; + let isCross = false + if (Math.abs(currentRoofLine?.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine?.x1 - roofLine.x2) < 0.1) { + isCross = true; + } + + + switch (condition) { + case 'left_in': + + if(isStartEnd.start){ + + }else if(isStartEnd.end){ + newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } + newPEnd ={ x: roofLine.x2, y: roofLine.y2 } + }else { + newPStart = { x: roofLine.x1, y: roofLine.y1 } + newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } + + } + break; + + + case 'left_out': + + break; + case 'right_in': + newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } + newPEnd ={ x: roofLine.x2, y: roofLine.y2 } + //대각선 라인을 보조라인으로 그린다. + if(isCross){ + getAddLine({ x: wallBaseLine.x1, y: wallBaseLine.y1 }, { x: roofLine.x1, y: currentRoofLine?.y1 }, 'purple') + } + + + break; + case 'right_out': + + break; + } + } + if(mLine.position === 'left') isIn = true if(mLine.position === 'right') isOut = true - let isCross = false - if (Math.abs(currentRoofLine?.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine?.x1 - roofLine.x2) < 0.1) { - isCross = true; - } - if(newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 < wallBaseLine.y2 && wallBaseLine.y2 < newPEnd.y){//in bottom left - if(isIn){ - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } - } + // if(isIn){ + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } + // } }else if(newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 < wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y){ //하단 오른쪽v - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } }else if(newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPStart.y && newPStart.y <= wallBaseLine.y1) { //top right - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } - newPEnd ={ x: roofLine.x2, y: roofLine.y2 } - - //대각선 라인을 보조라인으로 그린다. - if(isCross){ - getAddLine({ x: wallBaseLine.x1, y: wallBaseLine.y1 }, { x: roofLine.x1, y: currentRoofLine?.y1 }, 'yellow') - } + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine?.y1 : wallBaseLine.y1 } + // newPEnd ={ x: roofLine.x2, y: roofLine.y2 } + // + // //대각선 라인을 보조라인으로 그린다. + // if(isCross){ + // getAddLine({ x: wallBaseLine.x1, y: wallBaseLine.y1 }, { x: roofLine.x1, y: currentRoofLine?.y1 }, 'purple') + // } }else if(newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPEnd.y && newPEnd.y <= wallBaseLine.y2) {//상단 오르쪽 - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } }else if(wallBaseLine.y1 <= newPStart.y && newPStart.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPEnd.y) { // out bottom left //bottom left - if(isIn){ - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } - newPEnd = { x: roofLine.x2, y: roofLine.y2 } - - //대각선 라인을 보조라인으로 그린다. - if(isCross){ - getAddLine( { x: wallBaseLine.x1, y: wallBaseLine.y1 }, { x: roofLine.x1, y: currentRoofLine.y1 }, 'yellow') - } - }else if(isOut){ - - } + // if(isIn){ + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } + // newPEnd = { x: roofLine.x2, y: roofLine.y2 } + // + // //대각선 라인을 보조라인으로 그린다. + // if(isCross){ + // getAddLine( { x: wallBaseLine.x1, y: wallBaseLine.y1 }, { x: roofLine.x1, y: currentRoofLine.y1 }, 'purple') + // } + // }else if(isOut){ + // + // } }else if (wallBaseLine.y2 <= newPEnd.y && newPEnd.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y) { //하단 왼쪽 - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } - newPEnd ={ x: roofLine.x2, y: roofLine.y2 } + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } + // newPEnd ={ x: roofLine.x2, y: roofLine.y2 } } - } else if (getOrientation(roofLine) === 'horizontal') { //green 수평 + } else if (getOrientation(roofLine) === 'horizontal') { //magenta 수평 const mLine = getSelectLinePosition(wall, wallBaseLine) @@ -1193,7 +1332,7 @@ if((roof.moveUpDown??0 > 0) ) { //대각선 라인을 보조라인으로 그린다. if(isCross){ - getAddLine({ x: currentRoofLine.x1, y: roofLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 }, 'yellow') + getAddLine({ x: currentRoofLine.x1, y: roofLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 }, 'purple') } }else if(isOut){ @@ -1213,7 +1352,7 @@ if((roof.moveUpDown??0 > 0) ) { //대각선 라인을 보조라인으로 그린다. if(isCross){ - getAddLine({ x: wallBaseLine.x1, y: wallBaseLine.y1 },{ x: currentRoofLine.x1, y: roofLine.y1 }, 'yellow') + getAddLine({ x: wallBaseLine.x1, y: wallBaseLine.y1 },{ x: currentRoofLine.x1, y: roofLine.y1 }, 'purple') } }else if (wallBaseLine.x2 <= newPEnd.x && newPEnd.x <= wallBaseLine.x1 && wallBaseLine.x1 <= newPStart.x) { //right / top @@ -1229,7 +1368,7 @@ if((roof.moveUpDown??0 > 0) ) { //newPEnd = { x: (isCross) ? currentRoofLine.x1 : origin.x1, y: roofLine.y1 } //수직라인 접점까지지 } - getAddLine(newPStart, newPEnd, 'green') + getAddLine(newPStart, newPEnd, 'magenta') //movedLines.push({ index, newPStart, newPEnd }) //console.log("moveStart:::::::::::::", origin, newPStart, newPEnd) @@ -1239,68 +1378,115 @@ if((roof.moveUpDown??0 > 0) ) { if (getOrientation(roofLine) === 'vertical') { //수직 오렌지 - const mLine = getSelectLinePosition(wall, wallBaseLine) - if(mLine.position === 'left') isIn = true - if(mLine.position === 'right') isOut = true - let isCross = false - if (Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1) { - isCross = true; + const mLine = getSelectLinePosition(wall, wallBaseLine) + if (['left', 'right'].includes(mLine.position)) { + const positionType = + (mLine.position === 'left' && wallLine.x1 <= wallBaseLine.x1) || + (mLine.position === 'right' && wallLine.x1 >= wallBaseLine.x1) + ? 'in' : 'out'; + + const condition = `${mLine.position}_${positionType}`; + let isStartEnd = findInteriorPoint(wallBaseLine, sortedWallBaseLines) + let sPoint, ePoint; + let isCross = false + if (Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1) { + isCross = true; + } + + switch (condition) { + case 'left_in': + + + newPStart = { x: roofLine.x1, y: roofLine.y1 } + + newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + + //대각선 라인을 보조라인으로 그린다. + if (isCross) { + getAddLine({ x: roofLine.x2, y: currentRoofLine.y2 }, { + x: wallBaseLine.x2, + y: wallBaseLine.y2 + }, 'purple') + } + + break; + + + case 'left_out': + + break; + case 'right_in': + newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + newPEnd = { x: roofLine.x2, y: roofLine.y2 } + + break; + case 'right_out': + + break; + } } + + // const mLine = getSelectLinePosition(wall, wallBaseLine) + // if(mLine.position === 'left') isIn = true + // if(mLine.position === 'right') isOut = true + + + if(newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 < wallBaseLine.y2 && wallBaseLine.y2 < newPEnd.y){//out top left - if(isIn) { - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } - newPEnd = { x: roofLine.x2, y: roofLine.y2 } - }else if(isOut) { - - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: wallLine.y2 } - - } + // if(isIn) { + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + // newPEnd = { x: roofLine.x2, y: roofLine.y2 } + // }else if(isOut) { + // + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: wallLine.y2 } + // + // } }else if(newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 < wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y){ //top /right - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } - newPEnd = { x: roofLine.x2, y: roofLine.y2 } + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + // newPEnd = { x: roofLine.x2, y: roofLine.y2 } }else if(newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPStart.y && newPStart.y <= wallBaseLine.y1) { //top / left - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } - newPEnd ={ x: roofLine.x2, y: roofLine.y2 } + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + // newPEnd ={ x: roofLine.x2, y: roofLine.y2 } }else if(newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPEnd.y && newPEnd.y <= wallBaseLine.y2) {//in top left/ - if(isIn) { - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } - - //대각선 라인을 보조라인으로 그린다. - if (isCross) { - getAddLine({ x: roofLine.x2, y: currentRoofLine.y2 }, { - x: wallBaseLine.x2, - y: wallBaseLine.y2 - }, 'yellow') - } - } + // if(isIn) { + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + // + // //대각선 라인을 보조라인으로 그린다. + // if (isCross) { + // getAddLine({ x: roofLine.x2, y: currentRoofLine.y2 }, { + // x: wallBaseLine.x2, + // y: wallBaseLine.y2 + // }, 'purple') + // } + // } }else if(wallBaseLine.y1 <= newPStart.y && newPStart.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPEnd.y) { //하단 오른쪽v - newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } - newPEnd = { x: roofLine.x2, y: roofLine.y2 } + // newPStart = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : wallBaseLine.y1 } + // newPEnd = { x: roofLine.x2, y: roofLine.y2 } }else if (wallBaseLine.y2 <= newPEnd.y && newPEnd.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y) { //bottom right - newPStart = { x: roofLine.x1, y: roofLine.y1 } - newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } - - //대각선 라인을 보조라인으로 그린다. - if(isCross){ - getAddLine({ x: roofLine.x2, y: currentRoofLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 }, 'yellow') - } + // newPStart = { x: roofLine.x1, y: roofLine.y1 } + // newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : wallBaseLine.y2 } + // + // //대각선 라인을 보조라인으로 그린다. + // if(isCross){ + // getAddLine({ x: roofLine.x2, y: currentRoofLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 }, 'purple') + // } } @@ -1340,7 +1526,7 @@ if((roof.moveUpDown??0 > 0) ) { //대각선 라인을 보조라인으로 그린다. if(isCross){ - getAddLine({ x: currentRoofLine.x2, y: roofLine.y2 }, { x:wallBaseLine.x2 , y: wallBaseLine.y2 }, 'yellow') + getAddLine({ x: currentRoofLine.x2, y: roofLine.y2 }, { x:wallBaseLine.x2 , y: wallBaseLine.y2 }, 'purple') } @@ -1356,7 +1542,7 @@ if((roof.moveUpDown??0 > 0) ) { newPEnd = { y: roofLine.y2, x: (isCross) ? currentRoofLine.x2 : wallBaseLine.x2 } //대각선 라인을 보조라인으로 그린다. if(isCross){ - getAddLine({ x: currentRoofLine.x2, y: roofLine.y2 }, { x:wallBaseLine.x2 , y: wallBaseLine.y2 }, 'yellow') + getAddLine({ x: currentRoofLine.x2, y: roofLine.y2 }, { x:wallBaseLine.x2 , y: wallBaseLine.y2 }, 'purple') } }else if(isOut){ // newPEnd = { x: roofLine.x2, y: roofLine.y2 } @@ -4256,42 +4442,99 @@ function PointBasedOnBaseLength(x1, y1, x2, y2) { * @param {Array} polygonLines - 폴리곤을 구성하는 선분들의 배열 * @returns {Object} - { start: boolean, end: boolean } 시작점과 끝점의 내부 여부 */ -function findInteriorPoint(line, polygonLines) { - const { x1, y1, x2, y2 } = line; +// function findInteriorPoint(line, polygonLines) { +// const { x1, y1, x2, y2 } = line; +// +// // 선분의 방향 벡터 +// const dx = x2 - x1; +// const dy = y2 - y1; +// +// // 수직 벡터 (왼쪽으로 90도 회전) +// const perpX = -dy; +// const perpY = dx; +// +// // 정규화 +// const length = Math.sqrt(perpX * perpX + perpY * perpY); +// const nx = perpX / length; +// const ny = perpY / length; +// +// // 오프셋 계산 (선분 길이의 1% 또는 최소 0.1) +// const lineLength = Math.sqrt(dx*dx + dy*dy); +// const offset = Math.max(0.1, lineLength * 0.01); +// +// // 시작점에서 수직 방향으로 약간 떨어진 점 +// const testPoint = { +// x: x1 + nx * offset, +// y: y1 + ny * offset +// }; +// +// // 반대 방향 점 추가 확인 (라인이 중앙에 있는 경우를 위해) +// const testPoint2 = { +// x: x1 - nx * offset, +// y: y1 - ny * offset +// }; +// +// // 이 점이 폴리곤 내부에 있는지 확인 +// const isInside = isPointInPolygon(testPoint, polygonLines); +// const isInside2 = isPointInPolygon(testPoint2, polygonLines); +// +// // 라인이 폴리곤 내부에 완전히 포함된 경우 (중앙) -> 양쪽 다 true +// if (isInside && isInside2) { +// return { +// start: true, +// end: true +// }; +// } +// +// // 시작점이 내부를 향하는지 여부 +// return { +// start: isInside, +// // 끝점 방향은 시작점과 반대 +// end: !isInside +// }; +// } - // 선분의 방향 벡터 - const dx = x2 - x1; - const dy = y2 - y1; - - // 수직 벡터 (왼쪽으로 90도 회전) - const perpX = -dy; - const perpY = dx; - - // 정규화 - const length = Math.sqrt(perpX * perpX + perpY * perpY); - const nx = perpX / length; - const ny = perpY / length; - - // 오프셋 계산 (선분 길이의 1% 또는 최소 0.1) - const lineLength = Math.sqrt(dx*dx + dy*dy); - const offset = Math.max(0.1, lineLength * 0.01); - - // 시작점에서 수직 방향으로 약간 떨어진 점 - const testPoint = { - x: x1 + nx * offset, - y: y1 + ny * offset +/** + * 레이저(Ray)가 폴리곤의 다른 선분과 교차하는지 확인하는 헬퍼 함수 + * @param {Object} origin - 레이저 시작점 {x, y} + * @param {Object} dir - 레이저 방향 벡터 {x, y} (정규화됨) + * @param {Array} lines - 검사할 선분들 + * @param {Object} excludeLine - 자기 자신 라인 (제외) + */ +function checkRayIntersection(origin, dir, lines, excludeLine) { + // 레이저를 아주 멀리까지 쏘아봅니다 (도면 크기에 따라 조절, 충분히 큰 값) + const rayLength = 100000; + const rayEnd = { + x: origin.x + dir.x * rayLength, + y: origin.y + dir.y * rayLength }; - // 이 점이 폴리곤 내부에 있는지 확인 - const isInside = isPointInPolygon(testPoint, polygonLines); + for (const line of lines) { + // 자기 자신은 검사 제외 + if (line === excludeLine) continue; + // 좌표 기반 비교로도 자기 자신 체크 (객체 참조가 다를 수 있으므로) + if (Math.abs(line.x1 - excludeLine.x1) < 0.1 && Math.abs(line.y1 - excludeLine.y1) < 0.1 && + Math.abs(line.x2 - excludeLine.x2) < 0.1 && Math.abs(line.y2 - excludeLine.y2) < 0.1) continue; - // 시작점이 내부를 향하는지 여부 - return { - start: isInside, - // 끝점 방향은 시작점과 반대 - end: !isInside - }; + const lineStart = { x: line.x1, y: line.y1 }; + const lineEnd = { x: line.x2, y: line.y2 }; + + // 선분 교차 검사 + const intersect = getLineIntersection(origin, rayEnd, lineStart, lineEnd); + + if (intersect) { + // 교차점이 시작점(origin)과 너무 가까우면(코너 등) 무시하고, + // 확실히 전방에 있는 경우만 인정 + const dist = Math.hypot(intersect.x - origin.x, intersect.y - origin.y); + if (dist > 1.0) { // 1.0 정도의 여유를 둠 + return true; // 부딪힘! (빨간 라인 후보) + } + } + } + return false; // 허공! (파란 라인 후보) } + + // 점이 선분의 어느 쪽에 있는지 확인 function isPointInDirection(line, point) { const {x1, y1, x2, y2} = line; @@ -4378,4 +4621,92 @@ function isPointOnLineSegment2(point, lineStart, lineEnd, tolerance = 0.1) { } return isOnSegment; +} + +/** + * 세 점(p1 -> p2 -> p3)의 방향성을 계산합니다. (2D 외적) + * 반시계 방향(CCW)으로 그려진 폴리곤(Y축 Down) 기준: + * - 결과 > 0 : 오른쪽 턴 (Right Turn) -> 골짜기 (Valley/Reflex Vertex) + * - 결과 < 0 : 왼쪽 턴 (Left Turn) -> 외곽 모서리 (Convex Vertex) + * - 결과 = 0 : 직선 + */ +function getTurnDirection(p1, p2, p3) { + // 벡터 a: p1 -> p2 + // 벡터 b: p2 -> p3 + const val = (p2.x - p1.x) * (p3.y - p2.y) - (p2.y - p1.y) * (p3.x - p2.x); + return val; +} + +/** + * 현재 점(point)을 기준으로 연결된 이전 라인과 다음 라인을 찾아 골짜기 여부 판단 + */ +function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { + const tolerance = 0.1; + + // 1. 연결된 '다른' 라인을 찾습니다. + // isStartVertex가 true면 : 이 점으로 '들어오는' 라인(Previous Line)을 찾아야 함 + // isStartVertex가 false면 : 이 점에서 '나가는' 라인(Next Line)을 찾아야 함 + + let neighborLine = null; + + if (isStartVertex) { + // targetPoint가 Start이므로, 어떤 라인의 End가 targetPoint와 같아야 함 (Previous Line) + neighborLine = allLines.find(l => + l !== connectedLine && + isSamePoint(l.endPoint || {x:l.x2, y:l.y2}, targetPoint, tolerance) + ); + } else { + // targetPoint가 End이므로, 어떤 라인의 Start가 targetPoint와 같아야 함 (Next Line) + neighborLine = allLines.find(l => + l !== connectedLine && + isSamePoint(l.startPoint || {x:l.x1, y:l.y1}, targetPoint, tolerance) + ); + } + + // 연결된 라인을 못 찾았거나 끊겨있으면 판단 불가 (일단 false) + if (!neighborLine) return false; + + // 2. 세 점을 구성하여 회전 방향(Turn) 계산 + // 순서: PrevLine.Start -> [TargetVertex] -> NextLine.End + let p1, p2, p3; + + if (isStartVertex) { + // neighbor(Prev) -> connected(Current) + p1 = neighborLine.startPoint || {x: neighborLine.x1, y: neighborLine.y1}; + p2 = targetPoint; // 접점 + p3 = connectedLine.endPoint || {x: connectedLine.x2, y: connectedLine.y2}; + } else { + // connected(Current) -> neighbor(Next) + p1 = connectedLine.startPoint || {x: connectedLine.x1, y: connectedLine.y1}; + p2 = targetPoint; // 접점 + p3 = neighborLine.endPoint || {x: neighborLine.x2, y: neighborLine.y2}; + } + + // 3. 외적 계산 (Y축이 아래로 증가하는 캔버스 좌표계 + CCW 진행 기준) + // 값이 양수(+)면 오른쪽 턴 = 골짜기 + const crossProduct = getTurnDirection(p1, p2, p3); + + return crossProduct > 0; +} + +function findInteriorPoint(line, polygonLines) { + const { x1, y1, x2, y2 } = line; + + // line 객체 포맷 통일 + const currentLine = { + ...line, + startPoint: { x: x1, y: y1 }, + endPoint: { x: x2, y: y2 } + }; + + // 1. 시작점이 골짜기인지 확인 (들어오는 라인과 나가는 라인의 각도) + const startIsValley = isValleyVertex(currentLine.startPoint, currentLine, polygonLines, true); + + // 2. 끝점이 골짜기인지 확인 + const endIsValley = isValleyVertex(currentLine.endPoint, currentLine, polygonLines, false); + + return { + start: startIsValley, + end: endIsValley + }; } \ No newline at end of file