This commit is contained in:
ysCha 2025-11-26 18:05:54 +09:00
parent ee0517a952
commit 89a6787aee
2 changed files with 362 additions and 171 deletions

View File

@ -29,6 +29,8 @@ import { QcastContext } from '@/app/QcastProvider'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
import { roofsState } from '@/store/roofAtom' import { roofsState } from '@/store/roofAtom'
import { useText } from '@/hooks/useText' import { useText } from '@/hooks/useText'
import { processEaveHelpLines } from '@/util/skeleton-utils'
import { QLine } from '@/components/fabric/QLine'
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -404,6 +406,56 @@ export function useRoofAllocationSetting(id) {
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => { roofBases.forEach((roofBase) => {
try { try {
const roofEaveHelpLines = canvas.getObjects().filter(obj =>
obj.name === 'eaveHelpLine' && obj.roofId === roofBase.id
);
const mergedEaveLines = processEaveHelpLines(roofEaveHelpLines);
// 기존 라인 제거
roofEaveHelpLines.forEach(line => canvas.remove(line));
mergedEaveLines.forEach(line => {
const newLine = new QLine([line.x1, line.y1, line.x2, line.y2], {
// 필요한 속성들 유지
parentId : roofBase.id,
fontSize : 10,
stroke : 'blue',
strokeWidth: 4,
name : 'eaveHelpLine',
lineName : 'eaveHelpLine',
selectable : true,
visible : true,
roofId : roofBase.id,
attributes : {
type: 'eaveHelpLine'
}
// 기타 속성들...
});
canvas.add(newLine);
});
canvas.renderAll();
if (roofBase.lines) {
// Filter out any eaveHelpLines that are already in lines to avoid duplicates
const existingEaveLineIds = new Set(roofBase.lines.map(line => line.id));
const newEaveLines = mergedEaveLines.filter(line => !existingEaveLineIds.has(line.id));
roofBase.lines = [...roofBase.lines, ...newEaveLines];
} else {
roofBase.lines = [...roofEaveHelpLines];
}
if (!roofBase.innerLines) {
roofBase.innerLines = [];
}
// Add only eaveHelpLines that belong to this roofBase
// const baseEaveHelpLines = roofEaveHelpLines.filter(line => line.roofId === roofBase.id);
// roofBase.innerLines = [...new Set([...roofBase.innerLines, ...baseEaveHelpLines])];
if (roofBase.separatePolygon.length > 0) { if (roofBase.separatePolygon.length > 0) {
splitPolygonWithSeparate(roofBase.separatePolygon) splitPolygonWithSeparate(roofBase.separatePolygon)
} else { } else {

View File

@ -437,6 +437,55 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const processedInnerEdges = new Set() const processedInnerEdges = new Set()
const textElements = {};
const coordinateText = (line) => {
// Generate a stable ID for this line
const lineKey = `${line.x1},${line.y1},${line.x2},${line.y2}`;
// Remove existing text elements for this line
if (textElements[lineKey]) {
textElements[lineKey].forEach(text => {
if (canvas.getObjects().includes(text)) {
canvas.remove(text);
}
});
}
// Create start point text
const startText = new fabric.Text(`(${Math.round(line.x1)}, ${Math.round(line.y1)})`, {
left: line.x1 + 5,
top: line.y1 - 20,
fontSize: 10,
fill: 'green',
fontFamily: 'Arial',
selectable: false,
hasControls: false,
hasBorders: false
});
// Create end point text
const endText = new fabric.Text(`(${Math.round(line.x2)}, ${Math.round(line.y2)})`, {
left: line.x2 + 5,
top: line.y2 - 20,
fontSize: 10,
fill: 'orange',
fontFamily: 'Arial',
selectable: false,
hasControls: false,
hasBorders: false
});
// Add to canvas
canvas.add(startText, endText);
// Store references
textElements[lineKey] = [startText, endText];
// Bring lines to front
canvas.bringToFront(startText);
canvas.bringToFront(endText);
};
// 1. 모든 Edge를 순회하며 기본 스켈레톤 선(용마루)을 수집합니다. // 1. 모든 Edge를 순회하며 기본 스켈레톤 선(용마루)을 수집합니다.
skeleton.Edges.forEach((edgeResult, index) => { skeleton.Edges.forEach((edgeResult, index) => {
@ -556,10 +605,9 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
lineName: (sktLine.attributes.isOuterEdge)?'roofLine': attributes.type, lineName: (sktLine.attributes.isOuterEdge)?'roofLine': attributes.type,
selectable:(!sktLine.attributes.isOuterEdge), selectable:(!sktLine.attributes.isOuterEdge),
visible: (!sktLine.attributes.isOuterEdge), visible: (!sktLine.attributes.isOuterEdge),
lineNo: (sktLine.attributes.isOuterEdge)? skIndex:0,//그려지는 외곽라인의 순서를 찾아서...
}); });
coordinateText(skeletonLine)
canvas.add(skeletonLine); canvas.add(skeletonLine);
skeletonLine.bringToFront(); skeletonLine.bringToFront();
existingLines.add(lineKey); // 추가된 라인을 추적 existingLines.add(lineKey); // 추가된 라인을 추적
@ -588,22 +636,22 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
canvas.renderAll(); canvas.renderAll();
}); });
if(roof.moveUpDown??0 > 0) {
// 같은 라인이 없으므로 새 다각형 라인 생성 // 같은 라인이 없으므로 새 다각형 라인 생성
//라인 편집 //라인 편집
// let i = 0 // let i = 0
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()
}) })
let helpLines = canvas.getObjects().filter((obj) => obj.lineName === 'helpLine' && obj.roofId === roofId) let helpLines = canvas.getObjects().filter((obj) => obj.lineName === 'helpLine' && obj.roofId === roofId)
helpLines.forEach((helpLine) => { helpLines.forEach((helpLine) => {
canvas.remove(helpLine) canvas.remove(helpLine)
canvas.renderAll() canvas.renderAll()
}) })
@ -637,29 +685,81 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
} }
// function sortCurrentRoofLines(lines) { // function sortCurrentRoofLines(lines) {
// return [...lines].sort((a, b) => { // return [...lines].sort((a, b) => {
// const aX = a.x1 ?? a.get('x1') // const aX = a.x1 ?? a.get('x1')
// const aY = a.y1 ?? a.get('y1') // const aY = a.y1 ?? a.get('y1')
// const bX = b.x1 ?? b.get('x1') // const bX = b.x1 ?? b.get('x1')
// const bY = b.y1 ?? b.get('y1') // const bY = b.y1 ?? b.get('y1')
// if (aX !== bX) return aX - bX // if (aX !== bX) return aX - bX
// return aY - bY // return aY - bY
// }) // })
// } // }
// 각 라인 집합 정렬 // 각 라인 집합 정렬
// roofLines의 방향에 맞춰 currentRoofLines의 방향을 조정
const alignLineDirection = (sourceLines, targetLines) => {
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
};
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
);
return !nearest || distance < nearest.distance
? { line: targetLine, distance }
: nearest;
}, null)?.line;
if (!nearestTarget) return sourceLine;
// 방향이 반대인지 확인 (벡터 내적을 사용)
const sourceVec = {
x: sourceLine.x2 - sourceLine.x1,
y: sourceLine.y2 - sourceLine.y1
};
const targetVec = {
x: nearestTarget.x2 - nearestTarget.x1,
y: nearestTarget.y2 - nearestTarget.y1
};
const dotProduct = sourceVec.x * targetVec.x + sourceVec.y * targetVec.y;
// 내적이 음수이면 방향이 반대이므로 뒤집기
if (dotProduct < 0) {
return {
...sourceLine,
x1: sourceLine.x2,
y1: sourceLine.y2,
x2: sourceLine.x1,
y2: sourceLine.y1
};
}
return sourceLine;
});
};
const sortedWallLines = sortCurrentRoofLines(wall.lines); const sortedWallLines = sortCurrentRoofLines(wall.lines);
const sortedCurrentRoofLines = sortCurrentRoofLines(currentRoofLines); // roofLines의 방향에 맞춰 currentRoofLines 조정 후 정렬
const alignedCurrentRoofLines = alignLineDirection(currentRoofLines, roofLines);
const sortedCurrentRoofLines = sortCurrentRoofLines(alignedCurrentRoofLines);
const sortedRoofLines = sortCurrentRoofLines(roofLines); const sortedRoofLines = sortCurrentRoofLines(roofLines);
const sortedWallBaseLines = sortCurrentRoofLines(wall.baseLines); const sortedWallBaseLines = sortCurrentRoofLines(wall.baseLines);
//wall.lines 는 기본 벽 라인 //wall.lines 는 기본 벽 라인
//wall.baseLine은 움직인라인 //wall.baseLine은 움직인라인
const movedLines = [] const movedLines = []
@ -672,6 +772,23 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
const moveLine = sortedWallBaseLines[index] const moveLine = sortedWallBaseLines[index]
const wallBaseLine = sortedWallBaseLines[index] const wallBaseLine = sortedWallBaseLines[index]
console.log('=== Line Coordinates ===');
console.table({
'Point' : ['X', 'Y'],
'roofLine' : [roofLine.x1, roofLine.y1],
'currentRoofLine': [currentRoofLine.x1, currentRoofLine.y1],
'moveLine' : [moveLine.x1, moveLine.y1],
'wallBaseLine' : [wallBaseLine.x1, wallBaseLine.y1]
});
console.log('End Points:');
console.table({
'Point' : ['X', 'Y'],
'roofLine' : [roofLine.x2, roofLine.y2],
'currentRoofLine': [currentRoofLine.x2, currentRoofLine.y2],
'moveLine' : [moveLine.x2, moveLine.y2],
'wallBaseLine' : [wallBaseLine.x2, wallBaseLine.y2]
});
const origin = moveLine.attributes?.originPoint const origin = moveLine.attributes?.originPoint
if (!origin) return if (!origin) return
@ -698,184 +815,160 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
fontSize : roof.fontSize, fontSize : roof.fontSize,
stroke : stroke, stroke : stroke,
strokeWidth: 4, strokeWidth: 4,
name : 'helpLine', name : 'eaveHelpLine',
lineName : 'helpLine', lineName : 'eaveHelpLine',
selectable : true, selectable : true,
visible : true, visible : true,
roofId : roofId, roofId : roofId,
attributes : { attributes : {
type: 'helpLine' type: 'eaveHelpLine'
} }
}); });
coordinateText(line)
const coordinateText = new fabric.Text(`(${Math.round(line.x1)}, ${Math.round(line.y1)})`, {
left: line.x1 + 5, // 좌표점에서 약간 오른쪽으로 이동
top: line.y1 - 20, // 좌표점에서 약간 위로 이동
fontSize: 13,
fill: 'red',
fontFamily: 'Arial',
selectable: true,
lockMovementX: false,
lockMovementY: false,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
name: 'lengthText'
})
canvas?.add(coordinateText)
canvas.add(line) canvas.add(line)
canvas.renderAll(); canvas.renderAll();
return line return line
} }
//두 포인트가 변경된 라인인 //두 포인트가 변경된 라인인
if (fullyMoved) { if (fullyMoved) {
//반시계방향향 //반시계방향향
newPStart = {x:roofLine.x1, y:roofLine.y1} newPStart = { x: roofLine.x1, y: roofLine.y1 }
newPEnd = {x:roofLine.x2, y:roofLine.y2} newPEnd = { x: roofLine.x2, y: roofLine.y2 }
console.log("moveFully:::::::::::::", wallBaseLine, newPStart, newPEnd) console.log("moveFully:::::::::::::", wallBaseLine, newPStart, newPEnd)
if(getOrientation(roofLine) === 'vertical'){ if (getOrientation(roofLine) === 'vertical') {
if(newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPStart.y && newPStart.y <= wallBaseLine.y1){ if (newPEnd.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPStart.y && newPStart.y <= wallBaseLine.y1) {
newPStart.y = wallBaseLine.y1;
getAddLine({x:newPEnd.x, y:wallBaseLine.y1}, {x: wallBaseLine.x1, y:wallBaseLine.y1 })
} else if(wallBaseLine.y2 <= newPEnd.y && newPEnd.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y){
newPEnd.y = wallBaseLine.y2;
getAddLine({x:newPEnd.x, y:wallBaseLine.y2}, {x: wallBaseLine.x2, y:wallBaseLine.y2 })
} else if(newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPEnd.y && newPEnd.y <= wallBaseLine.y2){
newPEnd.y = wallBaseLine.y2;
getAddLine({x:newPEnd.x, y:wallBaseLine.y2}, {x: wallBaseLine.x2, y:wallBaseLine.y2 })
} else if(wallBaseLine.y1 <= newPStart.y && newPStart.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPEnd.y){
newPStart.y = wallBaseLine.y1; newPStart.y = wallBaseLine.y1;
getAddLine({x:newPEnd.x, y:wallBaseLine.y1}, {x: wallBaseLine.x1, y:wallBaseLine.y1 }) getAddLine({ x: newPEnd.x, y: wallBaseLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
} else if(wallBaseLine.y2 <= newPEnd.y && newPStart.y <= wallBaseLine.y1 ) { // 위가운데 } else if (wallBaseLine.y2 <= newPEnd.y && newPEnd.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPStart.y) {
newPEnd.y = wallBaseLine.y2; newPEnd.y = wallBaseLine.y2;
getAddLine({x:newPEnd.x, y:wallBaseLine.y2}, {x: wallBaseLine.x2, y:wallBaseLine.y2 }) getAddLine({ x: newPEnd.x, y: wallBaseLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
newPStart.y = wallBaseLine.y1; } else if (newPStart.y <= wallBaseLine.y1 && wallBaseLine.y1 <= newPEnd.y && newPEnd.y <= wallBaseLine.y2) {
getAddLine({x:newPEnd.x, y:wallBaseLine.y1}, {x: wallBaseLine.x1, y:wallBaseLine.y1 }) newPEnd.y = wallBaseLine.y2;
} else if(wallBaseLine.y1 <= newPStart.y && newPEnd.y <= wallBaseLine.y2 ) { // 아래가운데 getAddLine({ x: newPEnd.x, y: wallBaseLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
newPEnd.y = wallBaseLine.y1; } else if (wallBaseLine.y1 <= newPStart.y && newPStart.y <= wallBaseLine.y2 && wallBaseLine.y2 <= newPEnd.y) {
getAddLine({x:newPEnd.x, y:wallBaseLine.y1}, {x: wallBaseLine.x1, y:wallBaseLine.y1 }) newPStart.y = wallBaseLine.y1;
newPStart.y = wallBaseLine.y2; getAddLine({ x: newPEnd.x, y: wallBaseLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
getAddLine({x:newPStart.x, y:wallBaseLine.y2}, {x: wallBaseLine.x2, y:wallBaseLine.y2 }) } else if (wallBaseLine.y2 <= newPEnd.y && newPStart.y <= wallBaseLine.y1) { // 위가운데
} newPEnd.y = wallBaseLine.y2;
getAddLine({ x: newPEnd.x, y: wallBaseLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
newPStart.y = wallBaseLine.y1;
}else if(getOrientation(roofLine) === 'horizontal') { getAddLine({ x: newPEnd.x, y: wallBaseLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
} else if (wallBaseLine.y1 <= newPStart.y && newPEnd.y <= wallBaseLine.y2) { // 아래가운데
newPEnd.y = wallBaseLine.y1;
getAddLine({ x: newPEnd.x, y: wallBaseLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
newPStart.y = wallBaseLine.y2;
getAddLine({ x: newPStart.x, y: wallBaseLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
}
if(newPEnd.x <= wallBaseLine.x2 && wallBaseLine.x2 <= newPStart.x && newPStart.x <= wallBaseLine.x1){ //위 왼쪽 } else if (getOrientation(roofLine) === 'horizontal') {
newPStart.x = wallBaseLine.x1;
getAddLine({x:wallBaseLine.x1, y:newPEnd.y}, {x:wallBaseLine.x1, y: wallBaseLine.y1})
} else if(wallBaseLine.x2 <= newPEnd.x && newPEnd.x <= wallBaseLine.x1 && wallBaseLine.x1 <= newPStart.x){ //아래오르쪽
newPEnd.x = wallBaseLine.x2;
getAddLine({x:wallBaseLine.x2, y:newPEnd.y}, {x:wallBaseLine.x2, y: wallBaseLine.y2})
} else if(newPStart.x <= wallBaseLine.x1 && wallBaseLine.x1 <= newPEnd.x && newPEnd.x <= wallBaseLine.x2){ //위 오른쪽
newPEnd.x = wallBaseLine.x2;
getAddLine({x:wallBaseLine.x2, y:newPEnd.y}, {x:wallBaseLine.x2, y: wallBaseLine.y2})
} else if(wallBaseLine.x1 <= newPStart.x && newPStart.x <= wallBaseLine.x2 && wallBaseLine.x2 <= newPEnd.x){ //아래 왼쪽 if (newPEnd.x <= wallBaseLine.x2 && wallBaseLine.x2 <= newPStart.x && newPStart.x <= wallBaseLine.x1) { //위 왼쪽
newPStart.x = wallBaseLine.x1; newPStart.x = wallBaseLine.x1;
getAddLine({x:wallBaseLine.x1, y:newPEnd.y}, {x:wallBaseLine.x1, y: wallBaseLine.y1}) getAddLine({ x: wallBaseLine.x1, y: newPEnd.y }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
} else if (wallBaseLine.x2 <= newPEnd.x && newPEnd.x <= wallBaseLine.x1 && wallBaseLine.x1 <= newPStart.x) { //아래오르쪽
newPEnd.x = wallBaseLine.x2;
getAddLine({ x: wallBaseLine.x2, y: newPEnd.y }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
} else if(wallBaseLine.x2 <= newPEnd.x && newPEnd.x <= newPStart.x && newPStart.x <= wallBaseLine.x1 ) { // 위가운데 } else if (newPStart.x <= wallBaseLine.x1 && wallBaseLine.x1 <= newPEnd.x && newPEnd.x <= wallBaseLine.x2) { //위 오른쪽
newPEnd.x = wallBaseLine.x2;
getAddLine({ x: wallBaseLine.x2, y: newPEnd.y }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
newPEnd.x = wallBaseLine.x2; } else if (wallBaseLine.x1 <= newPStart.x && newPStart.x <= wallBaseLine.x2 && wallBaseLine.x2 <= newPEnd.x) { //아래 왼쪽
getAddLine({x:wallBaseLine.x2, y:newPEnd.y}, {x:wallBaseLine.x2, y: wallBaseLine.y2}) newPStart.x = wallBaseLine.x1;
newPStart.x = wallBaseLine.x1; getAddLine({ x: wallBaseLine.x1, y: newPEnd.y }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
getAddLine({x:wallBaseLine.x1, y:newPEnd.y}, {x:wallBaseLine.x1, y: wallBaseLine.y1})
} else if(wallBaseLine.x1 <= newPStart.x &&newPStart.x <= newPEnd.x&&newPEnd.x <= wallBaseLine.x2 ) { // 아래가운데 } else if (wallBaseLine.x2 <= newPEnd.x && newPEnd.x <= newPStart.x && newPStart.x <= wallBaseLine.x1) { // 위가운데
newPEnd.x = wallBaseLine.x1;
getAddLine({x:wallBaseLine.x1, y:newPEnd.y}, {x:wallBaseLine.x1, y: wallBaseLine.y1}) newPEnd.x = wallBaseLine.x2;
newPStart.x = wallBaseLine.x2; getAddLine({ x: wallBaseLine.x2, y: newPEnd.y }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
getAddLine({x:wallBaseLine.x2, y:newPEnd.y}, {x:wallBaseLine.x2, y: wallBaseLine.y2}) newPStart.x = wallBaseLine.x1;
getAddLine({ x: wallBaseLine.x1, y: newPEnd.y }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
} else if (wallBaseLine.x1 <= newPStart.x && newPStart.x <= newPEnd.x && newPEnd.x <= wallBaseLine.x2) { // 아래가운데
newPEnd.x = wallBaseLine.x1;
getAddLine({ x: wallBaseLine.x1, y: newPEnd.y }, { x: wallBaseLine.x1, y: wallBaseLine.y1 })
newPStart.x = wallBaseLine.x2;
getAddLine({ x: wallBaseLine.x2, y: newPEnd.y }, { x: wallBaseLine.x2, y: wallBaseLine.y2 })
}
} }
} getAddLine(newPStart, newPEnd, 'red')
getAddLine(newPStart, newPEnd) movedLines.push({ index, newPStart, newPEnd })
movedLines.push({ index, newPStart, newPEnd })
}
else if(movedStart) { //end 변경경
newPStart = {x:roofLine.x1, y:roofLine.y1}
if(getOrientation(roofLine) === 'vertical'){ } else if (movedStart) { //end 변경경
let isCross = false
if(Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1){ newPStart = { x: roofLine.x1, y: roofLine.y1 }
if (getOrientation(roofLine) === 'vertical') {
let isCross = false
if (Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1) {
isCross = true; isCross = true;
} }
newPEnd = {x:roofLine.x1, y:(isCross)? currentRoofLine.y2:origin.y1} newPEnd = { x: roofLine.x1, y: (isCross) ? currentRoofLine.y1 : origin.y1 }
}else if(getOrientation(roofLine) === 'horizontal') { } else if (getOrientation(roofLine) === 'horizontal') {
let isCross = false let isCross = false
if(Math.abs(currentRoofLine.y1 - roofLine.y2) < 0.1 || Math.abs(currentRoofLine.y2 - roofLine.y1) < 0.1){ if (Math.abs(currentRoofLine.y1 - roofLine.y2) < 0.1 || Math.abs(currentRoofLine.y2 - roofLine.y1) < 0.1) {
isCross = true; isCross = true;
}
newPEnd = { x: (isCross) ? currentRoofLine.x1 : origin.x1, y: roofLine.y1 } //수직라인 접점까지지
} }
newPEnd = {x:(isCross)? currentRoofLine.x2:origin.x1, y:roofLine.y1} //수직라인 접점까지지 movedLines.push({ index, newPStart, newPEnd })
console.log("moveStart:::::::::::::", origin, newPStart, newPEnd)
getAddLine(newPStart, newPEnd, 'red')
} else if (movedEnd) { //start변경
//반시계방향
newPStart = { x: roofLine.x2, y: roofLine.y2 }
if (getOrientation(roofLine) === 'vertical') {
let isCross = false
if (Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1) {
isCross = true;
}
newPEnd = { x: roofLine.x2, y: (isCross) ? currentRoofLine.y2 : origin.y2 } //수직라인 접점까지지
} else if (getOrientation(roofLine) === 'horizontal') {
let isCross = false
if (Math.abs(currentRoofLine.y2 - roofLine.y1) < 0.1 || Math.abs(currentRoofLine.y1 - roofLine.y2) < 0.1) {
isCross = true;
}
newPEnd = { x: (isCross) ? currentRoofLine.x2 : origin.x2, y: roofLine.y2 } //수직라인 접점까지지
}
console.log("movedEnd:::::::::::::", origin, newPStart, newPEnd)
getAddLine(newPStart, newPEnd, 'orange')
movedLines.push({ index, newPStart, newPEnd })
} }
movedLines.push({ index, newPStart, newPEnd }) canvas.renderAll()
console.log("moveStart:::::::::::::", origin, newPStart, newPEnd)
getAddLine(newPStart, newPEnd)
}else if(movedEnd) { //start변경 });
//반시계방향
newPStart = {x:roofLine.x2, y:roofLine.y2}
if(getOrientation(roofLine) === 'vertical'){
let isCross = false
if(Math.abs(currentRoofLine.x2 - roofLine.x1) < 0.1 || Math.abs(currentRoofLine.x1 - roofLine.x2) < 0.1){
isCross = true;
}
newPEnd = {x:roofLine.x2, y:(isCross)? currentRoofLine.y1:origin.y2} //수직라인 접점까지지
}else if(getOrientation(roofLine) === 'horizontal') {
let isCross = false
if(Math.abs(currentRoofLine.y2 - roofLine.y1) < 0.1 || Math.abs(currentRoofLine.y1 - roofLine.y2) < 0.1){
isCross = true;
}
newPEnd = {x:(isCross)? currentRoofLine.x1:origin.x2, y:roofLine.y2} //수직라인 접점까지지
}
console.log("movedEnd:::::::::::::", origin, newPStart, newPEnd)
getAddLine(newPStart, newPEnd)
movedLines.push({ index, newPStart, newPEnd })
}
canvas.renderAll()
});
//polygon 만들기 //polygon 만들기
console.log("innerLines:::::", innerLines) console.log("innerLines:::::", innerLines)
console.log("movedLines", movedLines) console.log("movedLines", movedLines)
}
// --- 사용 예시 --- // --- 사용 예시 ---
// const polygons = findConnectedLines(movedLines, innerLines, canvas, roofId, roof); // const polygons = findConnectedLines(movedLines, innerLines, canvas, roofId, roof);
// console.log("polygon", polygons); // console.log("polygon", polygons);
@ -886,18 +979,18 @@ console.log("movedLines", movedLines)
let p1,p2,p3, p4 let p1,p2,p3, p4
let idx = 0; let idx = 0;
let isMoveLine = false; let isMoveLine = false;
if (wallLine.startPoint.x !== wallLine.x1) wallLine.startPoint.x = wallLine.x1 if (wallLine.startPoint.x !== wallLine.x1) wallLine.startPoint.x = wallLine.x1
if (wallLine.startPoint.y !== wallLine.y1) wallLine.startPoint.y = wallLine.y1 if (wallLine.startPoint.y !== wallLine.y1) wallLine.startPoint.y = wallLine.y1
if (wallLine.endPoint.x !== wallLine.x2) wallLine.endPoint.x = wallLine.x2 if (wallLine.endPoint.x !== wallLine.x2) wallLine.endPoint.x = wallLine.x2
if (wallLine.endPoint.y !== wallLine.y2) wallLine.endPoint.y = wallLine.y2 if (wallLine.endPoint.y !== wallLine.y2) wallLine.endPoint.y = wallLine.y2
const wallLineStartPoint = {x:wallLine.x1, y:wallLine.y1} const wallLineStartPoint = {x:wallLine.x1, y:wallLine.y1}
const wallLineEndPoint = {x:wallLine.x2, y:wallLine.y2} const wallLineEndPoint = {x:wallLine.x2, y:wallLine.y2}
const moveLine = wall.baseLines[index] //이동한 wall 존재여부 (초기 wall line = base line) const moveLine = wall.baseLines[index] //이동한 wall 존재여부 (초기 wall line = base line)
if(index === 2){ if(index === 2){
} }
// // 사용자가 라인을 드래그하기 시작할 때 // // 사용자가 라인을 드래그하기 시작할 때
@ -910,13 +1003,13 @@ console.log("movedLines", movedLines)
// .map((line, index) => ({ index, line })) // .map((line, index) => ({ index, line }))
// .filter(({ line }) => line.attributes.isUserMoved) // .filter(({ line }) => line.attributes.isUserMoved)
// Then in your code: // Then in your code:
if (!moveLine?.attributes?.originPoint) return if (!moveLine?.attributes?.originPoint) return
const { originPoint } = moveLine.attributes const { originPoint } = moveLine.attributes
const moved = const moved =
(Math.abs(moveLine.x1 - originPoint.x1) > 0.1 || (Math.abs(moveLine.x1 - originPoint.x1) > 0.1 ||
Math.abs(moveLine.y1 - originPoint.y1) > 0.1 )||( Math.abs(moveLine.y1 - originPoint.y1) > 0.1 )||(
Math.abs(moveLine.x2 - originPoint.x2) > 0.1 || Math.abs(moveLine.x2 - originPoint.x2) > 0.1 ||
@ -938,7 +1031,7 @@ console.log("movedLines", movedLines)
const getPolygonOrientation = baseLines => { const getPolygonOrientation = baseLines => {
if (!baseLines?.length) return 0 if (!baseLines?.length) return 0
const area2 = baseLines.reduce((sum, line) => { const area2 = baseLines.reduce((sum, line) => {
const x1 = line.get('x1') const x1 = line.get('x1')
const y1 = line.get('y1') const y1 = line.get('y1')
@ -946,10 +1039,10 @@ console.log("movedLines", movedLines)
const y2 = line.get('y2') const y2 = line.get('y2')
return sum + (x2 - x1) * (y2 + y1) // shoelace 변형 return sum + (x2 - x1) * (y2 + y1) // shoelace 변형
}, 0) }, 0)
return Math.sign(area2) // +1: CCW, -1: CW, 0: 불명 return Math.sign(area2) // +1: CCW, -1: CW, 0: 불명
} }
const lineOrientation = (line, polygonOrientation) => { const lineOrientation = (line, polygonOrientation) => {
const x1 = line.get('x1') const x1 = line.get('x1')
const y1 = line.get('y1') const y1 = line.get('y1')
@ -978,7 +1071,7 @@ console.log("movedLines", movedLines)
}) })
// 기존 로직도 이어서 실행 // 기존 로직도 이어서 실행
console.log("moveLine", movedLines) console.log("moveLine", movedLines)
movedLines.forEach(({ index, moveLine, wallLine }) => { movedLines.forEach(({ index, moveLine, wallLine }) => {
console.log(`사용자가 움직인 선 index: ${index}, wallLineId: ${wallLine.id}`) console.log(`사용자가 움직인 선 index: ${index}, wallLineId: ${wallLine.id}`)
@ -996,7 +1089,7 @@ console.log("movedLines", movedLines)
same(a.x2, a.y2, b.x2, b.y2) same(a.x2, a.y2, b.x2, b.y2)
) )
} }
const angleBetween = a => b => { const angleBetween = a => b => {
const va = { x: a.x2 - a.x1, y: a.y2 - a.y1 } const va = { x: a.x2 - a.x1, y: a.y2 - a.y1 }
const vb = { x: b.x2 - b.x1, y: b.y2 - b.y1 } const vb = { x: b.x2 - b.x1, y: b.y2 - b.y1 }
@ -1004,7 +1097,7 @@ console.log("movedLines", movedLines)
const mag = Math.hypot(va.x, va.y) * Math.hypot(vb.x, vb.y) const mag = Math.hypot(va.x, va.y) * Math.hypot(vb.x, vb.y)
return Math.acos(Math.min(Math.max(dot / mag, -1), 1)) * 180 / Math.PI return Math.acos(Math.min(Math.max(dot / mag, -1), 1)) * 180 / Math.PI
} }
const rightAngles = [] const rightAngles = []
movedLines.forEach((a, i) => { movedLines.forEach((a, i) => {
movedLines.slice(i + 1).forEach(b => { movedLines.slice(i + 1).forEach(b => {
@ -1024,14 +1117,14 @@ console.log("movedLines", movedLines)
canvas.add(testLine) canvas.add(testLine)
canvas.renderAll() canvas.renderAll()
} }
} }
// movedLines => [{ line: moveLineObj, index }, ...] : 이동된 것만 담김 // movedLines => [{ line: moveLineObj, index }, ...] : 이동된 것만 담김
if (moved) { if (moved) {
} }
@ -3987,3 +4080,49 @@ function findConnectedLines(aLines, bLines, canvas, roofId, roof) {
return results; return results;
} }
export const processEaveHelpLines = (lines) => {
if (!lines || lines.length === 0) return [];
// 수직/수평 라인 분류
const verticalLines = lines.filter(line => line.x1 === line.x2);
const horizontalLines = lines.filter(line => line.y1 === line.y2);
// 라인 정렬 및 병합
const mergedVertical = mergeLines(verticalLines, 'vertical');
const mergedHorizontal = mergeLines(horizontalLines, 'horizontal');
return [...mergedVertical, ...mergedHorizontal];
};
const mergeLines = (lines, direction) => {
if (lines.length < 2) return lines;
// 방향에 따라 정렬
lines.sort((a, b) => {
const aPos = direction === 'vertical' ? a.y1 : a.x1;
const bPos = direction === 'vertical' ? b.y1 : b.x1;
return aPos - bPos;
});
const merged = [];
let current = { ...lines[0] };
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
const isConnected = direction === 'vertical'
? current.y2 >= line.y1 - 1
: current.x2 >= line.x1 - 1;
if (isConnected) {
// 라인 병합
current.y2 = Math.max(current.y2, line.y2);
current.x2 = direction === 'vertical' ? current.x1 : current.x2;
} else {
merged.push(current);
current = { ...line };
}
}
merged.push(current);
return merged;
};