sk3
This commit is contained in:
parent
ad8990b74c
commit
20e820e298
@ -631,7 +631,7 @@ export function useMovementSetting(id) {
|
||||
}
|
||||
return Big(0); // 기본값으로 0 반환 또는 다른 적절한 기본값
|
||||
})();
|
||||
if (target.y1 === target.y2) {
|
||||
if (Math.abs(target.y1 - target.y2) < 0.5) {
|
||||
value = value.neg()
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -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. 연결이 끊어진 라인이 있을경우 찾아서 추가한다(동 이동일때)
|
||||
|
||||
@ -549,17 +527,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
{ 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], {
|
||||
@ -613,7 +580,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
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)
|
||||
|
||||
|
||||
roofLineRects.forEach((roofLineRect) => {
|
||||
canvas.remove(roofLineRect)
|
||||
canvas.renderAll()
|
||||
@ -629,69 +595,64 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
return [...lines].sort((a, b) => {
|
||||
// Get all coordinates in a consistent order
|
||||
const getCoords = (line) => {
|
||||
const x1 = line.x1 ?? line.get('x1');
|
||||
const y1 = line.y1 ?? line.get('y1');
|
||||
const x2 = line.x2 ?? line.get('x2');
|
||||
const y2 = line.y2 ?? line.get('y2');
|
||||
const x1 = line.x1 ?? line.get('x1')
|
||||
const y1 = line.y1 ?? line.get('y1')
|
||||
const x2 = line.x2 ?? line.get('x2')
|
||||
const y2 = line.y2 ?? line.get('y2')
|
||||
|
||||
// Sort points left-to-right, then top-to-bottom
|
||||
return x1 < x2 || (x1 === x2 && y1 < y2)
|
||||
? [x1, y1, x2, y2]
|
||||
: [x2, y2, x1, y1];
|
||||
};
|
||||
return x1 < x2 || (x1 === x2 && y1 < y2) ? [x1, y1, x2, y2] : [x2, y2, x1, y1]
|
||||
}
|
||||
|
||||
const aCoords = getCoords(a);
|
||||
const bCoords = getCoords(b);
|
||||
const aCoords = getCoords(a)
|
||||
const bCoords = getCoords(b)
|
||||
|
||||
// Compare each coordinate in order
|
||||
for (let i = 0; i < 4; i++) {
|
||||
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의 방향을 조정
|
||||
const alignLineDirection = (sourceLines, targetLines) => {
|
||||
return sourceLines.map(sourceLine => {
|
||||
return sourceLines.map((sourceLine) => {
|
||||
// 가장 가까운 targetLine 찾기
|
||||
const nearestTarget = targetLines.reduce((nearest, targetLine) => {
|
||||
const sourceCenter = {
|
||||
x: (sourceLine.x1 + sourceLine.x2) / 2,
|
||||
y: (sourceLine.y1 + sourceLine.y2) / 2
|
||||
};
|
||||
y: (sourceLine.y1 + sourceLine.y2) / 2,
|
||||
}
|
||||
const targetCenter = {
|
||||
x: (targetLine.x1 + targetLine.x2) / 2,
|
||||
y: (targetLine.y1 + targetLine.y2) / 2
|
||||
};
|
||||
const distance = Math.hypot(
|
||||
sourceCenter.x - targetCenter.x,
|
||||
sourceCenter.y - targetCenter.y
|
||||
);
|
||||
y: (targetLine.y1 + targetLine.y2) / 2,
|
||||
}
|
||||
const distance = Math.hypot(sourceCenter.x - targetCenter.x, sourceCenter.y - targetCenter.y)
|
||||
|
||||
return !nearest || distance < nearest.distance
|
||||
? { line: targetLine, distance }
|
||||
: nearest;
|
||||
}, null)?.line;
|
||||
return !nearest || distance < nearest.distance ? { line: targetLine, distance } : nearest
|
||||
}, null)?.line
|
||||
|
||||
if (!nearestTarget) return sourceLine;
|
||||
if (!nearestTarget) return sourceLine
|
||||
|
||||
// 방향이 반대인지 확인 (벡터 내적을 사용)
|
||||
const sourceVec = {
|
||||
x: sourceLine.x2 - sourceLine.x1,
|
||||
y: sourceLine.y2 - sourceLine.y1
|
||||
};
|
||||
y: sourceLine.y2 - sourceLine.y1,
|
||||
}
|
||||
const targetVec = {
|
||||
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) {
|
||||
@ -700,81 +661,43 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
x1: sourceLine.x2,
|
||||
y1: sourceLine.y2,
|
||||
x2: sourceLine.x1,
|
||||
y2: sourceLine.y1
|
||||
};
|
||||
y2: sourceLine.y1,
|
||||
}
|
||||
}
|
||||
|
||||
return sourceLine;
|
||||
});
|
||||
};
|
||||
return sourceLine
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
console.log('wallBaseLines', wall.baseLines)
|
||||
|
||||
// 원본 wallLines를 복사하여 사용
|
||||
const sortedWallLines = [...wallLines];
|
||||
const sortedBaseLines = sortBaseLinesByWallLines(wall.baseLines, sortedWallLines);
|
||||
const sortedRoofLines = sortBaseLinesByWallLines(roofLines, sortedWallLines);
|
||||
|
||||
//wall.lines 는 기본 벽 라인
|
||||
//wall.baseLine은 움직인라인
|
||||
const movedLines = []
|
||||
let movedLines = []
|
||||
|
||||
// 조건에 맞는 라인들만 필터링
|
||||
const validWallLines = [...wallLines]
|
||||
.sort((a, b) => a.idx - b.idx)
|
||||
.filter((wallLine, index) => wallLine.idx - 1 === index);
|
||||
const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index)
|
||||
|
||||
wallLines.length > 3 && wallLines.forEach((wallLine, index) => {
|
||||
const originalIndex = wallLines.indexOf(wallLine)
|
||||
const roofLine = sortRoofLines[originalIndex]
|
||||
const currentRoofLine = currentRoofLines[originalIndex]
|
||||
const moveLine = wall.baseLines[originalIndex]
|
||||
const wallBaseLine = wall.baseLines[originalIndex]
|
||||
console.log('', sortRoofLines, sortWallLines, sortWallBaseLines)
|
||||
sortWallLines.length > 3 &&
|
||||
sortWallLines.forEach((wallLine, index) => {
|
||||
|
||||
// const roofLine = sortRoofLines[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);
|
||||
const roofLine = sortRoofLines[index]
|
||||
const wallBaseLine = sortWallBaseLines[index]
|
||||
|
||||
//roofline 외곽선 설정
|
||||
|
||||
const origin = moveLine.attributes?.originPoint
|
||||
if (!origin) return
|
||||
console.log('index::::', index)
|
||||
console.log("", roofLines, wallLines, wall.baseLines)
|
||||
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('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2)
|
||||
console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine))
|
||||
|
||||
|
||||
console.log('isSamePoint result:', isSameLine2(moveLine, wallLine))
|
||||
|
||||
if (isSameLine2(moveLine, wallLine)) {
|
||||
if (isSameLine2(wallBaseLine, wallLine)) {
|
||||
return
|
||||
}
|
||||
|
||||
const movedStart = Math.abs(moveLine.x1 - wallLine.x1) > EPSILON || Math.abs(moveLine.y1 - origin.y1) > EPSILON
|
||||
const movedEnd = Math.abs(moveLine.x2 - wallLine.x2) > EPSILON || Math.abs(moveLine.y2 - origin.y2) > EPSILON
|
||||
const movedStart = Math.abs(wallBaseLine.x1 - wallLine.x1) > EPSILON || Math.abs(wallBaseLine.y1 - wallLine.y1) > EPSILON
|
||||
const movedEnd = Math.abs(wallBaseLine.x2 - wallLine.x2) > EPSILON || Math.abs(wallBaseLine.y2 - wallLine.y2) > EPSILON
|
||||
|
||||
const fullyMoved = movedStart && movedEnd
|
||||
|
||||
@ -787,8 +710,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const getAddLine = (p1, p2, stroke = '') => {
|
||||
movedLines.push({ index, p1, p2 })
|
||||
|
||||
// Usage:
|
||||
// let mergeLines = mergeMovedLines(movedLines);
|
||||
//console.log("mergeLines:::::::", mergeLines);
|
||||
const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||
parentId: roof.id,
|
||||
@ -846,7 +767,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const positionType = isInPosition ? 'in' : 'out'
|
||||
|
||||
const condition = `${mLine.position}_${positionType}`
|
||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines)
|
||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
|
||||
let sPoint, ePoint
|
||||
if (condition === 'left_in') {
|
||||
isIn = true
|
||||
@ -863,8 +784,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const newPointX = Big(roofLine.x1).plus(moveDist).abs().toNumber()
|
||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).toNumber()
|
||||
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
||||
let idx = 0 > index - 1 ? roofLines.length : index
|
||||
const pLineX = roofLines[idx - 1].x1
|
||||
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||
// 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: 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 pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||
const pLineY = Big(roofLine.y2).minus(0).toNumber()
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const pLineX = roofLines[idx + 1].x2
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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: 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()
|
||||
newPStart.y = aStartY
|
||||
newPEnd.y = roofLine.y2 //Big(roofLine.y2).minus(eLineY).toNumber()
|
||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
||||
const newLine = roofLines[idx - 1]
|
||||
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||
// 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 (inLine) {
|
||||
@ -960,8 +893,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
||||
newPEnd.y = aStartY
|
||||
newPStart.y = roofLine.y1 //Big(roofLine.y1).plus(eLineY).toNumber()
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const newLine = roofLines[idx + 1]
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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 (inLine) {
|
||||
@ -1015,8 +953,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const newPointX = Big(roofLine.x1).minus(moveDist).abs().toNumber()
|
||||
const pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||
const pLineY = Big(roofLine.y1).minus(0).abs().toNumber()
|
||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
||||
const pLineX = roofLines[idx - 1].x1
|
||||
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||
// 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: 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 pDist = Big(wallLine.x1).minus(roofLine.x1).abs().toNumber()
|
||||
const pLineY = Big(roofLine.y2).minus(0).abs().toNumber()
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const pLineX = roofLines[idx + 1].x2
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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: 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()
|
||||
newPStart.y = aStartY
|
||||
newPEnd.y = roofLine.y2 //Big(roofLine.y2).plus(eLineY).toNumber()
|
||||
let idx = 0 >= index - 1 ? roofLines.length : index
|
||||
const newLine = roofLines[idx - 1]
|
||||
// let idx = 0 >= index - 1 ? sortRoofLines.length : index
|
||||
// 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 (inLine) {
|
||||
@ -1113,8 +1063,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const eLineY = Big(bStartY).minus(wallLine.y2).abs().toNumber()
|
||||
newPEnd.y = aStartY
|
||||
newPStart.y = roofLine.y1 //Big(roofLine.y1).minus(eLineY).toNumber()
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const newLine = roofLines[idx + 1]
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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.x2 < inLine.x1) {
|
||||
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 condition = `${mLine.position}_${positionType}`
|
||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortedBaseLines)
|
||||
let isStartEnd = findInteriorPoint(wallBaseLine, sortWallBaseLines)
|
||||
|
||||
let sPoint, ePoint
|
||||
|
||||
@ -1186,8 +1141,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
|
||||
const pDist = Big(wallLine.y2).minus(roofLine.y2).abs().toNumber()
|
||||
const pLineX = Big(roofLine.x1).minus(0).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')
|
||||
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 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')
|
||||
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()
|
||||
newPEnd.x = roofLine.x2 //Big(newPEnd.x).plus(eLineX).toNumber()
|
||||
newPStart.x = aStartX
|
||||
let idx = 0 > index - 1 ? roofLines.length : index
|
||||
const newLine = roofLines[idx - 1]
|
||||
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||
// 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 (inLine) {
|
||||
@ -1279,8 +1250,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
||||
newPStart.x = roofLine.x1 //Big(newPStart.x).minus(eLineX).abs().toNumber()
|
||||
newPEnd.x = aStartX
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const newLine = roofLines[idx + 1]
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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 (inLine) {
|
||||
@ -1329,8 +1304,14 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
|
||||
const pDist = Big(wallLine.y2).minus(roofLine.y2).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')
|
||||
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 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')
|
||||
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()
|
||||
newPEnd.x = roofLine.x2 //Big(roofLine.x2).minus(eLineX).toNumber()
|
||||
newPStart.x = aStartX
|
||||
let idx = 0 > index - 1 ? roofLines.length : index
|
||||
const newLine = roofLines[idx - 1]
|
||||
// let idx = 0 > index - 1 ? sortRoofLines.length : index
|
||||
// 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 (inLine) {
|
||||
@ -1422,8 +1414,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
const eLineX = Big(bStartX).minus(wallLine.x2).abs().toNumber()
|
||||
newPEnd.x = aStartX
|
||||
newPStart.x = roofLine.x1 //Big(roofLine.x1).plus(eLineX).toNumber()
|
||||
let idx = roofLines.length < index + 1 ? 0 : index
|
||||
const newLine = roofLines[idx + 1]
|
||||
// let idx = sortRoofLines.length < index + 1 ? 0 : index
|
||||
// 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 (inLine) {
|
||||
@ -1473,8 +1469,8 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
getMoveUpDownLine()
|
||||
|
||||
}
|
||||
@ -1766,6 +1762,115 @@ const isSameLine = (edgeStartX, edgeStartY, edgeEndX, edgeEndY, baseLine) => {
|
||||
|
||||
// --- 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}
|
||||
@ -2992,17 +3097,32 @@ function pointToLineDistance(point, lineP1, lineP2) {
|
||||
|
||||
|
||||
const getOrientation = (line, eps = 0.1) => {
|
||||
const x1 = line.get('x1')
|
||||
const y1 = line.get('y1')
|
||||
const x2 = line.get('x2')
|
||||
const y2 = line.get('y2')
|
||||
const dx = Math.abs(x2 - x1)
|
||||
const dy = Math.abs(y2 - y1)
|
||||
if (!line) {
|
||||
console.error('line 객체가 유효하지 않습니다:', line);
|
||||
return null; // 또는 적절한 기본값 반환
|
||||
}
|
||||
|
||||
if (dx < eps && dy >= eps) return 'vertical'
|
||||
if (dy < eps && dx >= eps) return 'horizontal'
|
||||
if (dx < eps && dy < eps) return 'point'
|
||||
return 'diagonal'
|
||||
// get 메서드가 있으면 사용하고, 없으면 직접 프로퍼티에 접근
|
||||
const getValue = (obj, key) =>
|
||||
obj && typeof obj.get === 'function' ? obj.get(key) : obj[key];
|
||||
|
||||
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;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user