This commit is contained in:
yscha 2025-12-21 00:03:04 +09:00
parent ad8990b74c
commit 20e820e298
2 changed files with 907 additions and 886 deletions

View File

@ -631,7 +631,7 @@ export function useMovementSetting(id) {
} }
return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값 return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값
})(); })();
if (target.y1 === target.y2) { if (Math.abs(target.y1 - target.y2) < 0.5) {
value = value.neg() value = value.neg()
} }
} else { } else {

View File

@ -494,29 +494,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
} }
}); });
/*
//2. 연결이 끊어진 스켈레톤 선을 찾아 연장합니다.
const { disconnectedLines } = findDisconnectedSkeletonLines(skeletonLines, roof.lines);
if(disconnectedLines.length > 0) {
disconnectedLines.forEach(dLine => {
const { index, extendedLine, p1Connected, p2Connected } = dLine;
const newPoint = extendedLine?.point;
if (!newPoint) return;
// p1이 끊어졌으면 p1을, p2가 끊어졌으면 p2를 연장된 지점으로 업데이트
if (p1Connected) { //p2 연장
skeletonLines[index].p2 = { ...skeletonLines[index].p2, x: newPoint.x, y: newPoint.y };
} else if (p2Connected) {//p1 연장
skeletonLines[index].p1 = { ...skeletonLines[index].p1, x: newPoint.x, y: newPoint.y };
}
});
//2-1 확장된 스켈레톤 선이 연장되다가 서로 만나면 만난점(접점)에서 멈추어야 된다.
trimIntersectingExtendedLines(skeletonLines, disconnectedLines);
}
*/
//2. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때) //2. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때)
@ -549,17 +527,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
{ x: sktLine.p2.x, y: sktLine.p2.y } { x: sktLine.p2.x, y: sktLine.p2.y }
); );
//그림을 그릴때 idx 가 필요함 roof는 왼쪽부터 시작됨 - 그림그리는 순서가 필요함
// roofLines.forEach((roofLine) => {
//
// if (isSameLine(p1.x, p1.y, p2.x, p2.y, roofLine) || isSameLine(p2.x, p2.y, p1.x, p1.y, roofLine)) {
// roofIdx = roofLine.idx;
// console.log("roofIdx::::::", roofIdx)
// return false; // forEach 중단
// }
// });
const skeletonLine = new QLine([p1.x, p1.y, p2.x, p2.y], { const skeletonLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
@ -613,7 +580,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const currentRoofLines = canvas.getObjects().filter((obj) => obj.lineName === 'roofLine' && obj.attributes.roofId === roofId) const currentRoofLines = canvas.getObjects().filter((obj) => obj.lineName === 'roofLine' && obj.attributes.roofId === roofId)
let roofLineRects = canvas.getObjects().filter((obj) => obj.name === 'roofLineRect' && obj.roofId === roofId) let roofLineRects = canvas.getObjects().filter((obj) => obj.name === 'roofLineRect' && obj.roofId === roofId)
roofLineRects.forEach((roofLineRect) => { roofLineRects.forEach((roofLineRect) => {
canvas.remove(roofLineRect) canvas.remove(roofLineRect)
canvas.renderAll() canvas.renderAll()
@ -629,69 +595,64 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
return [...lines].sort((a, b) => { return [...lines].sort((a, b) => {
// Get all coordinates in a consistent order // Get all coordinates in a consistent order
const getCoords = (line) => { const getCoords = (line) => {
const x1 = line.x1 ?? line.get('x1'); const x1 = line.x1 ?? line.get('x1')
const y1 = line.y1 ?? line.get('y1'); const y1 = line.y1 ?? line.get('y1')
const x2 = line.x2 ?? line.get('x2'); const x2 = line.x2 ?? line.get('x2')
const y2 = line.y2 ?? line.get('y2'); const y2 = line.y2 ?? line.get('y2')
// Sort points left-to-right, then top-to-bottom // Sort points left-to-right, then top-to-bottom
return x1 < x2 || (x1 === x2 && y1 < y2) return x1 < x2 || (x1 === x2 && y1 < y2) ? [x1, y1, x2, y2] : [x2, y2, x1, y1]
? [x1, y1, x2, y2] }
: [x2, y2, x1, y1];
};
const aCoords = getCoords(a); const aCoords = getCoords(a)
const bCoords = getCoords(b); const bCoords = getCoords(b)
// Compare each coordinate in order // Compare each coordinate in order
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) { if (Math.abs(aCoords[i] - bCoords[i]) > 0.1) {
return aCoords[i] - bCoords[i]; return aCoords[i] - bCoords[i]
} }
} }
return 0; return 0
}); })
} }
// 각 라인 집합 정렬 // 각 라인 집합 정렬
const sortWallLines = ensureCounterClockwiseLines(wallLines)
const sortWallBaseLines = ensureCounterClockwiseLines(wall.baseLines)
const sortRoofLines = ensureCounterClockwiseLines(roofLines)
// roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정 // roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정
const alignLineDirection = (sourceLines, targetLines) => { const alignLineDirection = (sourceLines, targetLines) => {
return sourceLines.map(sourceLine => { return sourceLines.map((sourceLine) => {
// 가장 가까운 targetLine 찾기 // 가장 가까운 targetLine 찾기
const nearestTarget = targetLines.reduce((nearest, targetLine) => { const nearestTarget = targetLines.reduce((nearest, targetLine) => {
const sourceCenter = { const sourceCenter = {
x: (sourceLine.x1 + sourceLine.x2) / 2, x: (sourceLine.x1 + sourceLine.x2) / 2,
y: (sourceLine.y1 + sourceLine.y2) / 2 y: (sourceLine.y1 + sourceLine.y2) / 2,
}; }
const targetCenter = { const targetCenter = {
x: (targetLine.x1 + targetLine.x2) / 2, x: (targetLine.x1 + targetLine.x2) / 2,
y: (targetLine.y1 + targetLine.y2) / 2 y: (targetLine.y1 + targetLine.y2) / 2,
}; }
const distance = Math.hypot( const distance = Math.hypot(sourceCenter.x - targetCenter.x, sourceCenter.y - targetCenter.y)
sourceCenter.x - targetCenter.x,
sourceCenter.y - targetCenter.y
);
return !nearest || distance < nearest.distance return !nearest || distance < nearest.distance ? { line: targetLine, distance } : nearest
? { line: targetLine, distance } }, null)?.line
: nearest;
}, null)?.line;
if (!nearestTarget) return sourceLine; if (!nearestTarget) return sourceLine
// 방향이 반대인지 확인 (벡터 내적을 사용) // 방향이 반대인지 확인 (벡터 내적을 사용)
const sourceVec = { const sourceVec = {
x: sourceLine.x2 - sourceLine.x1, x: sourceLine.x2 - sourceLine.x1,
y: sourceLine.y2 - sourceLine.y1 y: sourceLine.y2 - sourceLine.y1,
}; }
const targetVec = { const targetVec = {
x: nearestTarget.x2 - nearestTarget.x1, x: nearestTarget.x2 - nearestTarget.x1,
y: nearestTarget.y2 - nearestTarget.y1 y: nearestTarget.y2 - nearestTarget.y1,
}; }
const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y; const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y
// 내적이 음수이면 방향이 반대이므로 뒤집기 // 내적이 음수이면 방향이 반대이므로 뒤집기
if (dotProduct < 0) { if (dotProduct < 0) {
@ -700,81 +661,43 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
x1: sourceLine.x2, x1: sourceLine.x2,
y1: sourceLine.y2, y1: sourceLine.y2,
x2: sourceLine.x1, x2: sourceLine.x1,
y2: sourceLine.y1 y2: sourceLine.y1,
}; }
} }
return sourceLine; return sourceLine
}); })
}; }
console.log("wallBaseLines", wall.baseLines) console.log('wallBaseLines', wall.baseLines)
// const sortedWallLines = sortCurrentRoofLines(wall.lines);
// roofLines의 방향에 맞춰 currentRoofLines 조정 후 정렬
const alignedCurrentRoofLines = alignLineDirection(currentRoofLines, roofLines);
const sortedCurrentRoofLines = sortCurrentRoofLines(alignedCurrentRoofLines)
// const sortedRoofLines = sortCurrentRoofLines(roofLines);
const sortedWallBaseLines = sortCurrentRoofLines(wall.baseLines)
// const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, wallLines);
const sortRoofLines = sortBaseLinesByWallLines(roofLines, wallLines);
// 원본 wallLines를 복사하여 사용
const sortedWallLines = [...wallLines];
const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, sortedWallLines);
const sortedRoofLines = sortBaseLinesByWallLines(roofLines, sortedWallLines);
//wall.lines 는 기본 벽 라인
//wall.baseLine은 움직인라인 //wall.baseLine은 움직인라인
const movedLines = [] let movedLines = []
// 조건에 맞는 라인들만 필터링 // 조건에 맞는 라인들만 필터링
const validWallLines = [...wallLines] const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index)
.sort((a, b) => a.idx - b.idx)
.filter((wallLine, index) => wallLine.idx - 1 === index);
wallLines.length > 3 && wallLines.forEach((wallLine, index) => { console.log('', sortRoofLines, sortWallLines, sortWallBaseLines)
const originalIndex = wallLines.indexOf(wallLine) sortWallLines.length > 3 &&
const roofLine = sortRoofLines[originalIndex] sortWallLines.forEach((wallLine, index) => {
const currentRoofLine = currentRoofLines[originalIndex]
const moveLine = wall.baseLines[originalIndex]
const wallBaseLine = wall.baseLines[originalIndex]
// const roofLine = sortRoofLines[index]; const roofLine = sortRoofLines[index]
const wallBaseLine = sortWallBaseLines[index]
// if (roofLine.attributes.wallLine !== wallLine.id || (roofLine.idx - 1) !== index) {
// console.log("wallLine2::::", wallLine.id)
// console.log('roofLine:::', roofLine.attributes.wallLine)
// console.log("w:::", wallLine.startPoint, wallLine.endPoint)
// console.log("R:::", roofLine.startPoint, roofLine.endPoint)
// console.log("not matching roofLine", roofLine);
// return false
// }//roofLines.find(line => line.attributes.wallLineId === wallLine.attributes.wallId);
// const currentRoofLine = currentRoofLines[index];
// const moveLine = wall.baseLines[index]
// const wallBaseLine = wall.baseLines[index]
//console.log("wallBaseLine", wallBaseLine);
//roofline 외곽선 설정 //roofline 외곽선 설정
const origin = moveLine.attributes?.originPoint
if (!origin) return
console.log('index::::', index) console.log('index::::', index)
console.log("", roofLines, wallLines, wall.baseLines)
console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2) console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2)
// console.log('moveLine:', moveLine.x1, moveLine.y1, moveLine.x2, moveLine.y2)
console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2) console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2)
console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2) console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2)
console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine))
if (isSameLine2(wallBaseLine, wallLine)) {
console.log('isSamePoint result:', isSameLine2(moveLine, wallLine))
if (isSameLine2(moveLine, wallLine)) {
return return
} }
const movedStart = Math.abs(moveLine.x1 - wallLine.x1) > EPSILON || Math.abs(moveLine.y1 - origin.y1) > EPSILON const movedStart = Math.abs(wallBaseLine.x1 - wallLine.x1) > EPSILON || Math.abs(wallBaseLine.y1 - wallLine.y1) > EPSILON
const movedEnd = Math.abs(moveLine.x2 - wallLine.x2) > EPSILON || Math.abs(moveLine.y2 - origin.y2) > EPSILON const movedEnd = Math.abs(wallBaseLine.x2 - wallLine.x2) > EPSILON || Math.abs(wallBaseLine.y2 - wallLine.y2) > EPSILON
const fullyMoved = movedStart && movedEnd const fullyMoved = movedStart && movedEnd
@ -787,8 +710,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const getAddLine = (p1, p2, stroke = '') => { const getAddLine = (p1, p2, stroke = '') => {
movedLines.push({ index, p1, p2 }) 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], { const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
parentId: roof.id, parentId: roof.id,
@ -846,7 +767,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const positionType = isInPosition ? 'in' : 'out' const positionType = isInPosition ? 'in' : 'out'
const condition = `${mLine.position}_${positionType}` const condition = `${mLine.position}_${positionType}`
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
let sPoint, ePoint let sPoint, ePoint
if (condition === 'left_in') { if (condition === 'left_in') {
isIn = true isIn = true
@ -863,8 +784,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber() const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber()
const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber() const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber()
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
let idx = 0 > index - 1 ? roofLines.length : index // let idx = 0 > index - 1 ? sortRoofLines.length : index
const pLineX = roofLines[idx - 1].x1 // const pLineX = sortRoofLines[idx - 1].x1
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineX = sortRoofLines[prevIndex].x1
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue') getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange')
@ -887,8 +812,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const newPointX = Big(roofLine.x1).plus(moveDist).toNumber() const newPointX = Big(roofLine.x1).plus(moveDist).toNumber()
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
const pLineY = Big(roofLine.y2).minus(0).toNumber() const pLineY = Big(roofLine.y2).minus(0).toNumber()
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const pLineX = roofLines[idx + 1].x2 // const pLineX = sortRoofLines[idx + 1].x2
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineX = sortRoofLines[nextIndex].x2
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue') 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') getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange')
@ -910,8 +839,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
newPStart.y = aStartY newPStart.y = aStartY
newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber() newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber()
let idx = 0 >= index - 1 ? roofLines.length : index // let idx = 0 >= index - 1 ? sortRoofLines.length : index
const newLine = roofLines[idx - 1] // const newLine = sortRoofLines[idx - 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
if (inLine) { if (inLine) {
@ -960,8 +893,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
newPEnd.y = aStartY newPEnd.y = aStartY
newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber() newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber()
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const newLine = roofLines[idx + 1] // const newLine = sortRoofLines[idx + 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) { if (Math.abs(wallBaseLine.y2 - wallLine.y2) < 0.1) {
if (inLine) { if (inLine) {
@ -1015,8 +953,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber() const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber()
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber() const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
let idx = 0 >= index - 1 ? roofLines.length : index // let idx = 0 >= index - 1 ? sortRoofLines.length : index
const pLineX = roofLines[idx - 1].x1 // const pLineX = sortRoofLines[idx - 1].x1
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineX = sortRoofLines[prevIndex].x1
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue') getAddLine({ x: newPStart.x, y: newPStart.y }, { x: ePoint.x, y: ePoint.y }, 'blue')
//getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange') //getAddLine({ x: roofLine.x2, y: roofLine.y2 }, { x: newPointX, y: roofLine.y2 }, 'orange')
@ -1039,8 +981,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const newPointX = Big(roofLine.x1).minus(moveDist).toNumber() const newPointX = Big(roofLine.x1).minus(moveDist).toNumber()
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber() const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
const pLineY = Big(roofLine.y2).minus(0).abs().toNumber() const pLineY = Big(roofLine.y2).minus(0).abs().toNumber()
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const pLineX = roofLines[idx + 1].x2 // const pLineX = sortRoofLines[idx + 1].x2
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineX = sortRoofLines[nextIndex].x2
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: ePoint.x, y: ePoint.y }, 'blue') 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') getAddLine({ x: roofLine.x1, y: roofLine.y1 }, { x: newPointX, y: roofLine.y1 }, 'orange')
@ -1063,8 +1009,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber() const eLineY = Big(bStartY).minus(wallLine.y1).abs().toNumber()
newPStart.y = aStartY newPStart.y = aStartY
newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber() newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber()
let idx = 0 >= index - 1 ? roofLines.length : index // let idx = 0 >= index - 1 ? sortRoofLines.length : index
const newLine = roofLines[idx - 1] // const newLine = sortRoofLines[idx - 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) { if (Math.abs(wallBaseLine.y1 - wallLine.y1) < 0.1) {
if (inLine) { if (inLine) {
@ -1113,8 +1063,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber() const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
newPEnd.y = aStartY newPEnd.y = aStartY
newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber() newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber()
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const newLine = roofLines[idx + 1] // const newLine = sortRoofLines[idx + 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex]
if (inLine) { if (inLine) {
if (inLine.x2 < inLine.x1) { if (inLine.x2 < inLine.x1) {
getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink') getAddLine({ y: bStartY, x: wallLine.x1 }, { y: inLine.y2, x: inLine.x2 }, 'pink')
@ -1172,7 +1127,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const positionType = isInPosition ? 'in' : 'out' const positionType = isInPosition ? 'in' : 'out'
const condition = `${mLine.position}_${positionType}` const condition = `${mLine.position}_${positionType}`
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines) let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
let sPoint, ePoint let sPoint, ePoint
@ -1186,8 +1141,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber() const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
const pLineX = Big(roofLine.x1).minus(0).toNumber() const pLineX = Big(roofLine.x1).minus(0).toNumber()
let idx = 0 >= index - 1 ? roofLines.length : index // let idx = 0 >= index - 1 ? sortRoofLines.length : index
const pLineY = roofLines[idx - 1].y1 // const pLineY = sortRoofLines[idx - 1].y1
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineY = sortRoofLines[prevIndex].y1
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue') getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' }) findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_start' })
@ -1207,8 +1168,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber() const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber() const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
let idx = roofLines.length < index + 1 ? 0 : index
const pLineY = roofLines[idx + 1].y2 // let idx = sortRoofLines.length < index + 1 ? 0 : index
// const pLineY = sortRoofLines[idx + 1].y2
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineY = sortRoofLines[nextIndex].y2
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue') getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_end' }) findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'top_in_end' })
@ -1231,8 +1198,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber() newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber()
newPStart.x = aStartX newPStart.x = aStartX
let idx = 0 > index - 1 ? roofLines.length : index // let idx = 0 > index - 1 ? sortRoofLines.length : index
const newLine = roofLines[idx - 1] // const newLine = sortRoofLines[idx - 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
if (inLine) { if (inLine) {
@ -1279,8 +1250,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber() newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber()
newPEnd.x = aStartX newPEnd.x = aStartX
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const newLine = roofLines[idx + 1] // const newLine = sortRoofLines[idx + 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
if (inLine) { if (inLine) {
@ -1329,8 +1304,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber() const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
const pLineX = Big(roofLine.x1).minus(0).abs().toNumber() const pLineX = Big(roofLine.x1).minus(0).abs().toNumber()
let idx = 0 > index - 1 ? roofLines.length : index
const pLineY = roofLines[idx - 1].y1 // let idx = 0 > index - 1 ? sortRoofLines.length : index
// const pLineY = sortRoofLines[idx - 1].y1
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineY = sortRoofLines[prevIndex].y1
getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue') getAddLine({ x: newPStart.x, y: newPStart.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' }) findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_start' })
@ -1350,8 +1331,15 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber() const pDist = Big(wallLine.y1).minus(roofLine.y1).abs().toNumber()
const pLineX = Big(roofLine.x2).minus(0).abs().toNumber() const pLineX = Big(roofLine.x2).minus(0).abs().toNumber()
let idx = roofLines.length < index + 1 ? 0 : index
const pLineY = roofLines[idx + 1].y2 // let idx = sortRoofLines.length < index + 1 ? 0 : index
// const pLineY = sortRoofLines[idx + 1].y2
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length
const nextIndex = (index + 1) % sortRoofLines.length
const pLineY = sortRoofLines[nextIndex].y2
getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue') getAddLine({ x: newPEnd.x, y: newPEnd.y }, { x: sPoint.x, y: sPoint.y }, 'blue')
findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_end' }) findPoints.push({ x: sPoint.x, y: sPoint.y, position: 'bottom_in_end' })
@ -1372,8 +1360,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber() const eLineX = Big(bStartX).minus(wallLine.x1).abs().toNumber()
newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber() newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber()
newPStart.x = aStartX newPStart.x = aStartX
let idx = 0 > index - 1 ? roofLines.length : index // let idx = 0 > index - 1 ? sortRoofLines.length : index
const newLine = roofLines[idx - 1] // const newLine = sortRoofLines[idx - 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[prevIndex]
if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) { if (Math.abs(wallBaseLine.x1 - wallLine.x1) < 0.1) {
if (inLine) { if (inLine) {
@ -1422,8 +1414,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber() const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
newPEnd.x = aStartX newPEnd.x = aStartX
newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber() newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber()
let idx = roofLines.length < index + 1 ? 0 : index // let idx = sortRoofLines.length < index + 1 ? 0 : index
const newLine = roofLines[idx + 1] // const newLine = sortRoofLines[idx + 1]
const prevIndex = (index - 1 + sortRoofLines.length) % sortRoofLines.length;
const nextIndex = (index + 1) % sortRoofLines.length;
const newLine = sortRoofLines[nextIndex]
if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) { if (Math.abs(wallBaseLine.x2 - wallLine.x2) < 0.1) {
if (inLine) { if (inLine) {
@ -1473,8 +1469,8 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
} }
canvas.renderAll() canvas.renderAll()
}); })
} }
getMoveUpDownLine() getMoveUpDownLine()
} }
@ -1766,6 +1762,115 @@ const isSameLine = (edgeStartX, edgeStartY, edgeEndX, edgeEndY, baseLine) => {
// --- Disconnected Line Processing --- // --- Disconnected Line Processing ---
/**
* 라인들이 반시계 방향이 되도록 정렬하고, 왼쪽 상단에서 시작하는 배열 반환
* @param {Array} lines - x1, y1, x2, y2 속성을 가진 라인 객체 배열
* @returns {Array} 반시계 방향으로 정렬된 라인 배열
*/
export function ensureCounterClockwiseLines(lines) {
if (!lines || lines.length < 3) return [...(lines || [])];
// 1. 모든 점을 연결 그래프로 구성
const graph = new Map();
// 각 점에서 연결된 점들을 저장
lines.forEach(line => {
const p1 = `${line.x1},${line.y1}`;
const p2 = `${line.x2},${line.y2}`;
if (!graph.has(p1)) graph.set(p1, []);
if (!graph.has(p2)) graph.set(p2, []);
// 양방향 연결
graph.get(p1).push({ x: line.x2, y: line.y2, line });
graph.get(p2).push({ x: line.x1, y: line.y1, line });
});
// 2. 왼쪽 상단 점 찾기
let startPoint = null;
let minY = Infinity;
let minX = Infinity;
for (const [pointStr] of graph) {
const [x, y] = pointStr.split(',').map(Number);
if (y < minY || (y === minY && x < minX)) {
minY = y;
minX = x;
startPoint = { x, y };
}
}
if (!startPoint) return [...lines];
// 3. 점들을 순회하며 라인 구성
const visited = new Set();
const result = [];
let current = `${startPoint.x},${startPoint.y}`;
let prev = null;
while (true) {
if (visited.has(current)) break;
visited.add(current);
const neighbors = graph.get(current) || [];
if (neighbors.length === 0) break;
// 이전 점 제외
const nextPoints = neighbors.filter(n =>
!prev || `${n.x},${n.y}` !== `${prev.x},${prev.y}`
);
if (nextPoints.length === 0) break;
// 각도가 가장 작은(반시계 방향) 이웃 선택
const [cx, cy] = current.split(',').map(Number);
const next = nextPoints.reduce((best, curr) => {
const angleBest = Math.atan2(best.y - cy, best.x - cx);
const angleCurr = Math.atan2(curr.y - cy, curr.x - cx);
return angleCurr > angleBest ? curr : best;
}, nextPoints[0]);
// 라인 추가 (방향 유지)
const line = next.line;
const isReversed = (line.x1 !== next.x || line.y1 !== next.y);
result.push({
...line,
x1: isReversed ? line.x2 : line.x1,
y1: isReversed ? line.y2 : line.y1,
x2: isReversed ? line.x1 : line.x2,
y2: isReversed ? line.y1 : line.y2,
idx: result.length
});
prev = { x: cx, y: cy };
current = `${next.x},${next.y}`;
}
// 4. 시계 방향이면 뒤집기
let area = 0;
for (let i = 0; i < result.length; i++) {
const current = result[i];
const next = result[(i + 1) % result.length];
area += (next.x1 - current.x1) * (next.y1 + current.y1);
}
if (area > 0) {
return result.reverse().map((line, idx) => ({
...line,
x1: line.x2,
y1: line.y2,
x2: line.x1,
y2: line.y1,
idx
}));
}
return result;
}
/** /**
* 점을 선분에 투영한 점의 좌표를 반환합니다. * 점을 선분에 투영한 점의 좌표를 반환합니다.
* @param {object} point - 투영할 {x, y} * @param {object} point - 투영할 {x, y}
@ -2992,17 +3097,32 @@ function pointToLineDistance(point, lineP1, lineP2) {
const getOrientation = (line, eps = 0.1) => { const getOrientation = (line, eps = 0.1) => {
const x1 = line.get('x1') if (!line) {
const y1 = line.get('y1') console.error('line 객체가 유효하지 않습니다:', line);
const x2 = line.get('x2') return null; // 또는 적절한 기본값 반환
const y2 = line.get('y2') }
const dx = Math.abs(x2 - x1)
const dy = Math.abs(y2 - y1)
if (dx < eps && dy >= eps) return 'vertical' // get 메서드가 있으면 사용하고, 없으면 직접 프로퍼티에 접근
if (dy < eps && dx >= eps) return 'horizontal' const getValue = (obj, key) =>
if (dx < eps && dy < eps) return 'point' obj && typeof obj.get === 'function' ? obj.get(key) : obj[key];
return 'diagonal'
try {
const x1 = getValue(line, 'x1');
const y1 = getValue(line, 'y1');
const x2 = getValue(line, 'x2');
const y2 = getValue(line, 'y2');
const dx = Math.abs(x2 - x1);
const dy = Math.abs(y2 - y1);
if (dx < eps && dy >= eps) return 'vertical';
if (dy < eps && dx >= eps) return 'horizontal';
if (dx < eps && dy < eps) return 'point';
return 'diagonal';
} catch (e) {
console.error('방향 계산 중 오류 발생:', e);
return null;
}
} }
@ -3371,102 +3491,3 @@ function findInteriorPoint(line, polygonLines) {
}; };
} }
/**
* baseLines의 순서를 wallLines의 순서와 일치시킵니다.
* 1순위: 공통 ID(id, matchingId, parentId ) 이용한 직접 매칭
* 2순위: 기하학적 유사성(기울기, 길이, 위치) 점수화하여 매칭
*
* @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 = new Array(wallLines.length).fill(null);
const usedBaseIndices = new Set();
// [1단계] ID 매칭 (기존 로직 유지 - 혹시 ID가 있는 경우를 대비)
// ... (ID 매칭 코드는 생략하거나 유지) ...
// [2단계] 'originPoint' 또는 좌표 일치성을 이용한 강력한 기하학적 매칭
wallLines.forEach((wLine, wIndex) => {
if (sortedBaseLines[wIndex]) return;
// 비교할 기준 좌표 설정 (originPoint가 있으면 그것을, 없으면 현재 좌표 사용)
const wStart = wLine.attributes?.originPoint
? { x: wLine.attributes.originPoint.x1, y: wLine.attributes.originPoint.y1 }
: { x: wLine.x1, y: wLine.y1 };
const wEnd = wLine.attributes?.originPoint
? { x: wLine.attributes.originPoint.x2, y: wLine.attributes.originPoint.y2 }
: { x: wLine.x2, y: wLine.y2 };
// 수직/수평 여부 판단
const isVertical = Math.abs(wStart.x - wEnd.x) < 0.1;
const isHorizontal = Math.abs(wStart.y - wEnd.y) < 0.1;
let bestMatchIndex = -1;
let minDiff = Infinity;
baseLines.forEach((bLine, bIndex) => {
if (usedBaseIndices.has(bIndex)) return;
let diff = Infinity;
// 1. 수직선인 경우: X좌표가 일치해야 함 (예: 230.8 == 230.8)
if (isVertical) {
// bLine도 수직선인지 확인 (x1, x2 차이가 거의 없어야 함)
if (Math.abs(bLine.x1 - bLine.x2) < 1.0) {
// X좌표 차이를 오차(diff)로 계산
diff = Math.abs(wStart.x - bLine.x1);
}
}
// 2. 수평선인 경우: Y좌표가 일치해야 함
else if (isHorizontal) {
// bLine도 수평선인지 확인
if (Math.abs(bLine.y1 - bLine.y2) < 1.0) {
diff = Math.abs(wStart.y - bLine.y1);
}
}
// 3. 대각선인 경우: 기울기와 절편 비교 (복잡하므로 거리로 대체)
else {
// 중점 간 거리 + 기울기 차이
// (이전 답변의 로직 사용 가능)
}
// 오차가 매우 작으면(예: 1px 미만) 같은 라인으로 간주
if (diff < 1.0 && diff < minDiff) {
minDiff = diff;
bestMatchIndex = bIndex;
}
});
if (bestMatchIndex !== -1) {
sortedBaseLines[wIndex] = baseLines[bestMatchIndex];
usedBaseIndices.add(bestMatchIndex);
}
});
// [3단계] 남은 라인 처리 (Fallback)
// 매칭되지 않은 wallLine들에 대해 남은 baseLines를 순서대로 배정하거나
// 거리 기반 근사 매칭을 수행
// ... (기존 fallback 로직) ...
// 빈 구멍 채우기 (null 방지)
for(let i=0; i<sortedBaseLines.length; i++) {
if(!sortedBaseLines[i]) {
const unused = baseLines.findIndex((_, idx) => !usedBaseIndices.has(idx));
if(unused !== -1) {
sortedBaseLines[i] = baseLines[unused];
usedBaseIndices.add(unused);
} else {
sortedBaseLines[i] = baseLines[0]; // 최후의 수단
}
}
}
return sortedBaseLines;
};