Merge branch 'feature/design-remake' into dev

This commit is contained in:
Jaeyoung Lee 2025-12-19 17:36:15 +09:00
commit 8c92f14f7d

View File

@ -4676,8 +4676,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
}
}
}
const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' })
canvas.add(checkNewLine).renderAll()
// const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' })
// canvas.add(checkNewLine).renderAll()
newAnalysis.push({
start: { x: linePoint[0], y: linePoint[1] },
@ -4839,8 +4839,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
})
}
//하단 지붕 라인처리
const downRoofLines = []
//케라바에서 파생된 하단 지붕 라인처리
const downRoofGable = []
baseLines.forEach((baseLine, index) => {
const nextLine = baseLines[(index + 1) % baseLines.length]
const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
@ -4852,12 +4852,205 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
if (
prevLineVector.x === nextLineVector.x &&
prevLineVector.y === nextLineVector.y &&
baseLine.attributes.type === LINE_TYPE.WALLLINE.EAVES &&
(prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE)
) {
downRoofLines.push(index)
downRoofGable.push({ currLine: baseLine, currIndex: index })
}
})
const downRoofLines = [] // 하단지붕 파생 라인 처리후 innerLines에 추가.
downRoofGable.forEach(({ currLine, currIndex }) => {
// 라인의 방향
const currVector = { x: Math.sign(clamp01(currLine.x1 - currLine.x2)), y: Math.sign(clamp01(currLine.y1 - currLine.y2)) }
//어느쪽이 기준인지 확인.
//대각선 제외
if (currVector.x !== 0 && currVector.y !== 0) return
const prevLine = baseLines[(currIndex - 1 + baseLines.length) % baseLines.length]
const nextLine = baseLines[(currIndex + 1) % baseLines.length]
const prevVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) }
const nextVector = { x: Math.sign(clamp01(nextLine.x1 - nextLine.x2)), y: Math.sign(clamp01(nextLine.y1 - nextLine.y2)) }
let gableLine
//가로선
if (currVector.y === 0) {
if (currVector.x === 1) {
if (prevVector.y === 1 && nextVector.y === 1) {
gableLine = nextLine
}
if (prevVector.y === -1 && nextVector.y === -1) {
gableLine = prevLine
}
}
if (currVector.x === -1) {
if (prevVector.y === 1 && nextVector.y === 1) {
gableLine = prevLine
}
if (prevVector.y === -1 && nextVector.y === -1) {
gableLine = nextLine
}
}
}
//세로선
if (currVector.x === 0) {
if (currVector.y === 1) {
if (prevVector.x === 1 && nextVector.x === 1) {
gableLine = prevLine
}
if (prevVector.x === -1 && nextVector.x === -1) {
gableLine = nextLine
}
}
if (currVector.y === -1) {
if (prevVector.x === 1 && nextVector.x === 1) {
gableLine = nextLine
}
if (prevVector.x === -1 && nextVector.x === -1) {
gableLine = prevLine
}
}
}
//기준점
let stdPoint = []
//반대쪽 라인을 찾기위한 vector
let oppFindVector
if (gableLine === prevLine) {
stdPoint.push(currLine.x1, currLine.y1)
stdPoint.push(currLine.x2 + -currVector.x * nextLine.attributes.offset, currLine.y2 + -currVector.y * nextLine.attributes.offset)
oppFindVector = { x: Math.sign(clamp01(nextLine.x2 - nextLine.x1)), y: Math.sign(clamp01(nextLine.y2 - nextLine.y1)) }
} else {
stdPoint.push(currLine.x2, currLine.y2)
stdPoint.push(currLine.x1 + currVector.x * prevLine.attributes.offset, currLine.y1 + currVector.y * prevLine.attributes.offset)
oppFindVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) }
}
//반대쪽 라인들 (마루선을 위함)
const oppLines = baseLines.filter((line) => {
const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) }
const oppVector =
lineVector.x === 0 ? { x: Math.sign(clamp01(line.x1 - stdPoint[0])), y: 0 } : { x: 0, y: Math.sign(clamp01(line.y1 - stdPoint[1])) }
const rightDirection =
(currVector.x === lineVector.x && currVector.y !== lineVector.y) || (currVector.x !== lineVector.x && currVector.y === lineVector.y)
const rightOpp = oppFindVector.x === oppVector.x && oppFindVector.y === oppVector.y
return rightDirection && rightOpp
})
const innerRidge = innerLines
.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE)
.filter((line) => {
const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) }
//마루선을 찾는다.
if ((currVector.x === 0 && lineVector.x === 0) || (currVector.y === 0 && lineVector.y === 0)) {
//세로선
if (lineVector.x === 0) {
const minY = Math.min(line.y1, line.y2)
const maxY = Math.max(line.y1, line.y2)
// 기준 라인 안에 들어있는 경우에만 처리.
if (
Math.min(stdPoint[1], stdPoint[3]) <= minY &&
maxY <= Math.max(stdPoint[1], stdPoint[3]) &&
Math.min(stdPoint[0], ...oppLines.map((line) => line.x1)) <= line.x1 &&
line.x1 <= Math.max(stdPoint[0], ...oppLines.map((line) => line.x1))
) {
return true
}
}
//가로선
if (lineVector.y === 0) {
const minX = Math.min(line.x1, line.x2)
const maxX = Math.max(line.x1, line.x2)
// 기준 라인 안에 들어있는 경우에만 처리
if (
Math.min(stdPoint[0], stdPoint[2]) <= minX &&
maxX <= Math.max(stdPoint[0], stdPoint[2]) &&
Math.min(stdPoint[1], ...oppLines.map((line) => line.y1)) <= line.y1 &&
line.y1 <= Math.max(stdPoint[1], ...oppLines.map((line) => line.y1))
) {
return true
}
}
}
})
//1. 현재 라인을 기준으로 지붕선 추가.
//1-1 stdPoint을 현재라인의 지붕 출폭 만큼 조정
const currOffset = currLine.attributes.offset
const noGableLine = gableLine === prevLine ? nextLine : prevLine
let roofLinePoint = stdPoint
if (currVector.x === 0) {
//세로일때
//x축 조정
roofLinePoint[0] = roofLinePoint[0] + currVector.y * currOffset
roofLinePoint[2] = roofLinePoint[2] + currVector.y * currOffset
} else if (currVector.y === 0) {
//가로일때
//y축 조정
roofLinePoint[1] = roofLinePoint[1] - currVector.x * currOffset
roofLinePoint[3] = roofLinePoint[3] - currVector.x * currOffset
}
//지붕선추가.
downRoofLines.push(drawRoofLine(roofLinePoint, canvas, roof, textMode))
//1-2 지붕선에서 oppLine으로 향하는 중 가장 가까운 마루선까지의 연결선 생성
const findRidgeEdge = {
vertex1: { x: roofLinePoint[0], y: roofLinePoint[1] },
vertex2: { x: roofLinePoint[0] + oppFindVector.x, y: roofLinePoint[1] + oppFindVector.y },
}
let minDistance = Infinity
let minPoint
let isLine
innerRidge.forEach((line) => {
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
const intersect = edgesIntersection(lineEdge, findRidgeEdge)
if (intersect) {
let distance = Infinity
if (currVector.x === 0) {
const lineDistance1 = Math.abs(line.y1 - roofLinePoint[1])
const lineDistance2 = Math.abs(line.y2 - roofLinePoint[1])
distance = Math.min(lineDistance1, lineDistance2)
} else if (currVector.y === 0) {
const lineDistance1 = Math.abs(line.x1 - roofLinePoint[0])
const lineDistance2 = Math.abs(line.x2 - roofLinePoint[0])
distance = Math.min(lineDistance1, lineDistance2)
}
if (distance < minDistance) {
minDistance = distance
minPoint = intersect
isLine = line
}
}
})
if (minPoint) {
const hipPoint = [roofLinePoint[0], roofLinePoint[1], minPoint.x, minPoint.y]
downRoofLines.push(
drawHipLine(hipPoint, canvas, roof, textMode, null, getDegreeByChon(currLine.attributes.pitch), getDegreeByChon(currLine.attributes.pitch)),
)
if (isLine) {
const newRidgePoint = [minPoint.x, minPoint.y]
const distance1 = Math.sqrt(Math.pow(minPoint.x - isLine.x1, 2) + Math.pow(minPoint.y - isLine.y1, 2))
const distance2 = Math.sqrt(Math.pow(minPoint.x - isLine.x2, 2) + Math.pow(minPoint.y - isLine.y2, 2))
if (distance2 < distance1) {
newRidgePoint.push(isLine.x1, isLine.y1)
} else {
newRidgePoint.push(isLine.x2, isLine.y2)
}
downRoofLines.push(drawRoofLine(newRidgePoint, canvas, roof, textMode))
}
}
})
//추가된 하단 지붕 라인 innerLines에 추가.
innerLines.push(...downRoofLines)
//지붕선에 따라 라인추가 작업 처리.
const innerLinesPoints = []
innerLines.forEach((line) => {
@ -4869,9 +5062,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
roof.lines.forEach((currentLine, index) => {
const prevLine = roof.lines[(index - 1 + roof.lines.length) % roof.lines.length]
const nextLine = roof.lines[(index + 1) % roof.lines.length]
const prevDegree = getDegreeByChon(prevLine.attributes.pitch)
const nextDegree = getDegreeByChon(nextLine.attributes.pitch)
console.log('prevDegree, nextDegree: ', prevDegree, nextDegree)
const prevDegree = prevLine.attributes.pitch ? getDegreeByChon(prevLine.attributes.pitch) : 0
const nextDegree = nextLine.attributes.pitch ? getDegreeByChon(nextLine.attributes.pitch) : 0
const splitPoint = []
let hasOverlapLine = false
@ -4929,730 +5121,6 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
//return
/*while (linesAnalysis.length > 0 && iterations < MAX_ITERATIONS) {
iterations++
linesAnalysis.forEach((line) => {
const point = [line.start.x, line.start.y, line.end.x, line.end.y]
const checkLine = new fabric.Line(point, {
stroke: 'red',
strokeWidth: 2,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
})
// 각 가선분의 최단 교점 찾기
const intersections = []
for (let i = 0; i < linesAnalysis.length; i++) {
let minDistance = Infinity //최단거리
let intersectPoint = null //교점 좌표
let partners = new Set() //교점 선분의 index
const lineI = linesAnalysis[i]
const lengthI = Math.sqrt(Math.pow(lineI.end.x - lineI.start.x, 2) + Math.pow(lineI.end.y - lineI.start.y, 2))
console.log('lengthI', lengthI)
const checkLineI = new fabric.Line([lineI.start.x, lineI.start.y, lineI.end.x, lineI.end.y], {
stroke: 'blue',
strokeWidth: 2,
parentId: roofId,
name: 'check',
})
canvas.add(checkLineI).renderAll()
if (lineI.type === TYPES.GABLE_LINE && lineI.connectCnt > 1) continue
if (lengthI > EPSILON) {
const otherLines = linesAnalysis.filter((j) => j !== lineI)
const zeroLines = linesAnalysis.filter((j) => Math.sqrt(Math.pow(j.end.x - j.start.x, 2) + Math.pow(j.end.y - j.start.y, 2)) < EPSILON)
if (otherLines.length === zeroLines.length) {
zeroLines.forEach((j) => {
const jIndex = linesAnalysis.indexOf(j)
if (isPointOnLineNew({ x1: lineI.start.x, y1: lineI.start.y, x2: lineI.end.x, y2: lineI.end.y }, { x: j.start.x, y: j.start.y })) {
const distance = Math.sqrt(Math.pow(j.start.x - lineI.start.x, 2) + Math.pow(j.start.y - lineI.start.y, 2))
if (distance < minDistance) {
minDistance = distance
intersectPoint = { x: j.start.x, y: j.start.y }
partners = new Set([jIndex])
} else if (almostEqual(distance, minDistance)) {
partners.add(jIndex)
}
}
})
if (intersectPoint) {
intersections.push({ index: i, intersectPoint, partners, distance: minDistance })
partners.forEach((j) => {
const p = new Set([i])
intersections.push({ index: j, intersectPoint, partners: p, distance: 0 })
})
continue
}
}
}
for (let j = 0; j < linesAnalysis.length; j++) {
const lineJ = linesAnalysis[j]
if (lineI === lineJ) continue
if (lineI.type === TYPES.GABLE_LINE && lineJ.type === TYPES.GABLE_LINE && i.gableId === lineJ.gableId) continue
if (lineJ.type === TYPES.GABLE_LINE && lineJ.connectCnt > 1) continue
const intersection = lineIntersection(lineI.start, lineI.end, lineJ.start, lineJ.end, canvas)
if (intersection) {
const distance = Math.sqrt((intersection.x - lineI.start.x) ** 2 + (intersection.y - lineI.start.y) ** 2)
const distance2 = Math.sqrt((intersection.x - lineJ.start.x) ** 2 + (intersection.y - lineJ.start.y) ** 2)
if (lineI.type === TYPES.GABLE_LINE && distance < EPSILON) continue
if (distance < minDistance && !almostEqual(distance, minDistance) && !(distance < EPSILON && distance2 < EPSILON)) {
minDistance = distance
intersectPoint = intersection
partners = new Set([j])
} else if (almostEqual(distance, minDistance)) {
partners.add(j)
}
}
}
if (intersectPoint) {
intersections.push({ index: i, intersectPoint, partners, distance: minDistance })
}
canvas.remove(checkLineI).renderAll()
}
// 제일 가까운 교점부터 처리
intersections.sort((a, b) => a.distance - b.distance)
console.log('intersections', intersections)
// 교점에 대한 적합 여부 판단 및 처리.
let newAnalysis = [] //신규 발생 선
const processed = new Set() //처리된 선
for (const { index, intersectPoint, partners } of intersections) {
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
let isProceed = false
for (const pIndex of partners) {
console.log('pIndex : ', pIndex)
const check1 = linesAnalysis[index]
const checkLine1 = new fabric.Line([check1.start.x, check1.start.y, check1.end.x, check1.end.y], {
stroke: 'red',
strokeWidth: 2,
parentId: roofId,
name: 'check',
})
const checkCircle1 = new fabric.Circle({ left: check1.start.x, top: check1.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' })
const checkCircle2 = new fabric.Circle({ left: check1.end.x, top: check1.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' })
canvas.add(checkLine1, checkCircle1, checkCircle2)
canvas.renderAll()
const check2 = linesAnalysis[pIndex]
const checkLine2 = new fabric.Line([check2.start.x, check2.start.y, check2.end.x, check2.end.y], {
stroke: 'green',
strokeWidth: 2,
parentId: roofId,
name: 'check',
})
const checkCircle3 = new fabric.Circle({ left: check2.start.x, top: check2.start.y, radius: 5, fill: 'red', parentId: roofId, name: 'check' })
const checkCircle4 = new fabric.Circle({ left: check2.end.x, top: check2.end.y, radius: 5, fill: 'green', parentId: roofId, name: 'check' })
canvas.add(checkLine2, checkCircle3, checkCircle4)
canvas.renderAll()
console.log('!intersectPoint || processed.has(index)', !intersectPoint, processed.has(index))
//교점이 없거나, 이미 처리된 선분이면 처리하지 않는다.
if (!intersectPoint || processed.has(index)) continue
const partner = intersections.find((p) => p.index === pIndex)
//교점이 없거나, 교점 선분이 없으면 처리하지 않는다.
if (!partner || !partner.intersectPoint) continue
//상호 최단 교점 여부 확인.
if (partner.partners.has(index)) {
const line1 = linesAnalysis[index]
const line2 = linesAnalysis[pIndex]
//좌,우 선 중 공통 선 존재 확인.
const isSameLine = line1.left === line2.left || line1.left === line2.right || line1.right === line2.left || line1.right === line2.right
if (isSameLine) {
// 현재 선이 처리 되었음을 표기
let point1 = [line1.start.x, line1.start.y, intersectPoint.x, intersectPoint.y]
let point2 = [line2.start.x, line2.start.y, intersectPoint.x, intersectPoint.y]
let length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2)
let length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2)
if (length1 < EPSILON && length2 < EPSILON) continue
isProceed = true
//gable라인과 붙는경우 length가 0에 가까우면 포인트를 뒤집는다.
if (line1.type === TYPES.GABLE_LINE && length2 < EPSILON) {
point2 = [line2.end.x, line2.end.y, intersectPoint.x, intersectPoint.y]
length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2)
}
if (line2.type === TYPES.GABLE_LINE && length1 < EPSILON) {
point1 = [line1.end.x, line1.end.y, intersectPoint.x, intersectPoint.y]
length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2)
}
if (length1 > 0 && !alreadyPoints(innerLines, point1)) {
if (line1.type === TYPES.HIP) {
innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree))
} else if (line1.type === TYPES.RIDGE) {
innerLines.push(drawRidgeLine(point1, canvas, roof, textMode))
} else if (line1.type === TYPES.NEW) {
const isDiagonal = Math.abs(point1[0] - point1[2]) >= 1 && Math.abs(point1[1] - point1[3]) >= 1
if (isDiagonal) {
innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree))
} else {
innerLines.push(drawRidgeLine(point1, canvas, roof, textMode))
}
} else if (line1.type === TYPES.GABLE_LINE) {
if (line1.degree > 0) {
innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree))
} else {
innerLines.push(drawRidgeLine(point1, canvas, roof, textMode))
}
}
}
if (length2 > 0 && !alreadyPoints(innerLines, point2)) {
if (line2.type === TYPES.HIP) {
innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree))
} else if (line2.type === TYPES.RIDGE) {
innerLines.push(drawRidgeLine(point2, canvas, roof, textMode))
} else if (line2.type === TYPES.NEW) {
const isDiagonal = Math.abs(point2[0] - point2[2]) >= 1 && Math.abs(point2[1] - point2[3]) >= 1
if (isDiagonal) {
innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree))
} else {
innerLines.push(drawRidgeLine(point2, canvas, roof, textMode))
}
} else if (line2.type === TYPES.GABLE_LINE) {
if (line2.degree > 0) {
innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree))
} else {
innerLines.push(drawRidgeLine(point2, canvas, roof, textMode))
}
}
}
if (line1.type === TYPES.GABLE_LINE || line2.type === TYPES.GABLE_LINE) {
console.log('gableLine newAnalyze start')
const gableLine = line1.type === TYPES.GABLE_LINE ? line1 : line2
gableLine.connectCnt++
const checkLine = new fabric.Line([gableLine.start.x, gableLine.start.y, gableLine.end.x, gableLine.end.y], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
const checkCircle = new fabric.Circle({
left: intersectPoint.x,
top: intersectPoint.y,
radius: 5,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine, checkCircle)
canvas.renderAll()
const relationBaseLines = [line1.left, line1.right, line2.left, line2.right]
const uniqueBaseLines = [...new Set(relationBaseLines)]
if (uniqueBaseLines.length === 3) {
const linesCounts = new Map()
relationBaseLines.forEach((e) => {
linesCounts.set(e, (linesCounts.get(e) || 0) + 1)
})
const uniqueLines = Array.from(linesCounts.entries())
.filter(([_, count]) => count === 1)
.map(([line, _]) => line)
if (uniqueLines.length === 2) {
if (gableLine.connectCnt < 2) {
newAnalysis.push({
start: { x: intersectPoint.x, y: intersectPoint.y },
end: { x: gableLine.end.x, y: gableLine.end.y },
left: gableLine.left,
right: gableLine.right,
type: TYPES.GABLE_LINE,
degree: getDegreeByChon(baseLines[gableLine.right].attributes.pitch),
gableId: gableLine.gableId,
connectCnt: gableLine.connectCnt,
})
}
if (gableLine.connectCnt >= 2) {
//가선분 발생만 시키는 더미 생성.
newAnalysis.push({
start: { x: intersectPoint.x, y: intersectPoint.y },
end: { x: intersectPoint.x, y: intersectPoint.y },
left: gableLine.gableId,
right: gableLine.gableId,
type: TYPES.HIP,
degree: 0,
})
}
}
}
console.log('gableLine newAnalyze end')
} else {
// 연결점에서 새로운 가선분을 생성
const relationBaseLines = [line1.left, line1.right, line2.left, line2.right]
const uniqueBaseLines = [...new Set(relationBaseLines)]
if (uniqueBaseLines.length === 3) {
const linesCounts = new Map()
relationBaseLines.forEach((e) => {
linesCounts.set(e, (linesCounts.get(e) || 0) + 1)
})
const uniqueLines = Array.from(linesCounts.entries())
.filter(([_, count]) => count === 1)
.map(([line, _]) => line)
if (uniqueLines.length === 2) {
// 두 변의 이등분선 방향 계산
// uniqueLines.sort((a, b) => a - b)
console.log('uniqueLines : ', uniqueLines)
const baseLine1 = baseLines[uniqueLines[0]]
const baseLine2 = baseLines[uniqueLines[1]]
const checkLine1 = new fabric.Line([baseLine1.x1, baseLine1.y1, baseLine1.x2, baseLine1.y2], {
stroke: 'yellow',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
const checkLine2 = new fabric.Line([baseLine2.x1, baseLine2.y1, baseLine2.x2, baseLine2.y2], {
stroke: 'blue',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine1, checkLine2)
canvas.renderAll()
let bisector
console.log('isParallel(baseLine1, baseLine2)', isParallel(baseLine1, baseLine2))
if (isParallel(baseLine1, baseLine2)) {
bisector = getBisectLines(
{ x1: line1.start.x, x2: line1.end.x, y1: line1.start.y, y2: line1.end.y },
{ x1: line2.start.x, y1: line2.start.y, x2: line2.end.x, y2: line2.end.y },
)
} else {
//이등분선
bisector = getBisectBaseLines(baseLine1, baseLine2, intersectPoint, canvas)
}
//마주하는 지붕선을 찾는다.
const intersectionsByRoof = []
const checkEdge = {
vertex1: { x: intersectPoint.x, y: intersectPoint.y },
vertex2: { x: intersectPoint.x + bisector.x, y: intersectPoint.y + bisector.y },
}
const checkVector = {
x: Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x),
y: Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y),
}
roof.lines.forEach((line) => {
const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
const is = edgesIntersection(lineEdge, checkEdge)
if (is && isPointOnLineNew(line, is)) {
const distance = Math.sqrt((is.x - intersectPoint.x) ** 2 + (is.y - intersectPoint.y) ** 2)
const isVector = { x: Math.sign(intersectPoint.x - is.x), y: Math.sign(intersectPoint.y - is.y) }
if (isVector.x === checkVector.x && isVector.y === checkVector.y) {
intersectionsByRoof.push({ is, distance })
}
}
})
intersectionsByRoof.sort((a, b) => a.distance - b.distance)
//지붕 선과의 교점이 존재 할때
if (intersectionsByRoof.length > 0) {
let is = intersectionsByRoof[0].is
let linePoint = [intersectPoint.x, intersectPoint.y, is.x, is.y]
const isDiagonal = Math.abs(is.x - intersectPoint.x) >= 1 && Math.abs(is.y - intersectPoint.y) >= 1
const length = Math.sqrt((linePoint[2] - linePoint[0]) ** 2 + (linePoint[3] - linePoint[1]) ** 2)
if (!isDiagonal) {
const line1 = baseLines[uniqueLines[0]]
const line2 = baseLines[uniqueLines[1]]
const vector1 = { x: Math.sign(line1.x1 - line1.x2), y: Math.sign(line1.y1 - line1.y2) }
const prevLine = checkVector.x === vector1.x && checkVector.y === vector1.y ? line2 : line1
const nextLine = checkVector.x === vector1.x && checkVector.y === vector1.y ? line1 : line2
const drivePoint = getRidgeDrivePoint(linePoint, prevLine, nextLine, baseLines)
if (drivePoint !== null) {
const driveLength = Math.sqrt((drivePoint.x - intersectPoint.x) ** 2 + (drivePoint.y - intersectPoint.y) ** 2)
if (driveLength < length) {
linePoint = [intersectPoint.x, intersectPoint.y, drivePoint.x, drivePoint.y]
}
}
}
const checkNewLine = new fabric.Line(linePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' })
canvas.add(checkNewLine).renderAll()
newAnalysis.push({
start: { x: linePoint[0], y: linePoint[1] },
end: { x: linePoint[2], y: linePoint[3] },
left: uniqueLines[0],
right: uniqueLines[1],
type: TYPES.NEW,
degree: isDiagonal ? line1.degree : 0,
})
}
}
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
}
}
processed.add(pIndex)
}
}
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
}
if (isProceed) {
processed.add(index)
break
}
}
// 처리된 가선분 제외
linesAnalysis = newAnalysis.concat(linesAnalysis.filter((_, index) => !processed.has(index)))
console.log('lineAnalysis: ', linesAnalysis)
canvas
.getObjects()
.filter((object) => object.name === 'check' || object.name === 'checkAnalysis')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
// 새로운 가선분이 없을때 종료
console.log('newAnalysis.length : ', newAnalysis.length)
if (newAnalysis.length === 0) break
}
console.log('lineAnalysis: end ', linesAnalysis)*/
// 가선분 중 처리가 안되어있는 붙어있는 라인에 대한 예외처리.
/* const proceedAnalysis = []
linesAnalysis
.filter((line) => {
const dx = line.end.x - line.start.x
const dy = line.end.y - line.start.y
const length = Math.sqrt(dx ** 2 + dy ** 2)
return length > 0
})
.forEach((currentLine) => {
if (proceedAnalysis.find((p) => p === currentLine)) return
//현재와 같으면 제외, 이미 처리된 라인은 제외, 현재와 공통선분이 존재하지 않으면 제외
linesAnalysis
.filter((line) => {
const dx = line.end.x - line.start.x
const dy = line.end.y - line.start.y
const length = Math.sqrt(dx ** 2 + dy ** 2)
return length > 0
})
.filter(
(partnerLine) =>
partnerLine !== currentLine &&
!proceedAnalysis.find((p) => p === partnerLine) &&
(currentLine.left === partnerLine.left ||
currentLine.left === partnerLine.right ||
partnerLine.left === currentLine.left ||
partnerLine.left === currentLine.right),
)
.forEach((partnerLine) => {
const dx1 = currentLine.end.x - currentLine.start.x
const dy1 = currentLine.end.y - currentLine.start.y
const dx2 = partnerLine.end.x - partnerLine.start.x
const dy2 = partnerLine.end.y - partnerLine.start.y
const denominator = dy2 * dx1 - dx2 * dy1
const isOpposite = dx1 * dx2 + dy1 * dy2 < 0
//평행하지 않으면 제외
if (Math.abs(denominator) > EPSILON) return
const currentDegree = getDegreeByChon(baseLines[currentLine.left].attributes.pitch)
if (isOpposite) {
const points = [currentLine.start.x, currentLine.start.y, partnerLine.start.x, partnerLine.start.y]
const length = Math.sqrt((points[0] - points[2]) ** 2 + (points[1] - points[3]) ** 2)
if (length > 0) {
if (currentLine.type === TYPES.HIP) {
innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree))
} else if (currentLine.type === TYPES.RIDGE) {
innerLines.push(drawRidgeLine(points, canvas, roof, textMode))
} else if (currentLine.type === TYPES.NEW) {
const isDiagonal = Math.abs(points[0] - points[2]) >= 1 && Math.abs(points[1] - points[3]) >= 1
if (isDiagonal && almostEqual(Math.abs(points[0] - points[2]), Math.abs(points[1] - points[3]))) {
innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree))
}
if (!isDiagonal) {
innerLines.push(drawRidgeLine(points, canvas, roof, textMode))
}
}
proceedAnalysis.push(currentLine, partnerLine)
}
} else {
const allPoints = [currentLine.start, currentLine.end, partnerLine.start, partnerLine.end]
let points = []
allPoints.forEach((point) => {
let count = 0
allPoints.forEach((p) => {
if (almostEqual(point.x, p.x) && almostEqual(point.y, p.y)) count++
})
if (count === 1) points.push(point)
})
if (points.length === 2) {
const length = Math.sqrt((points[0].x - points[1].x) ** 2 + (points[0].y - points[1].y) ** 2)
if (length < EPSILON) return
const isDiagonal = Math.abs(points[0].x - points[1].x) >= 1 && Math.abs(points[0].y - points[1].y) >= 1
if (isDiagonal) {
innerLines.push(
drawHipLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode, null, currentDegree, currentDegree),
)
} else {
innerLines.push(drawRidgeLine([points[0].x, points[0].y, points[1].x, points[1].y], canvas, roof, textMode))
}
proceedAnalysis.push(currentLine, partnerLine)
}
}
})
})
linesAnalysis = linesAnalysis.filter((line) => !proceedAnalysis.find((p) => p === line))*/
//하단 지붕 라인처리
/* const downRoofLines = []
baseLines.forEach((baseLine, index) => {
const nextLine = baseLines[(index + 1) % baseLines.length]
const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
const prevLineVector = { x: Math.sign(prevLine.x1 - prevLine.x2), y: Math.sign(prevLine.y1 - prevLine.y2) }
const nextLineVector = { x: Math.sign(nextLine.x1 - nextLine.x2), y: Math.sign(nextLine.y1 - nextLine.y2) }
//반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리.
if (
prevLineVector.x === nextLineVector.x &&
prevLineVector.y === nextLineVector.y &&
(prevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE || nextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE)
) {
downRoofLines.push(index)
}
})*/
/*if (downRoofLines.length > 0) {
downRoofLines.forEach((index) => {
const currentLine = baseLines[index]
// const nextLine = baseLines[(index + 1) % baseLines.length]
// const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
const analyze = analyzeLine(currentLine)
if (analyze.isDiagonal) {
return
}
const roofLine = analyze.roofLine
let roofPoint = [analyze.startPoint.x, analyze.startPoint.y, analyze.endPoint.x, analyze.endPoint.y]
if (analyze.isVertical) {
roofPoint[0] = roofLine.x1
roofPoint[2] = roofLine.x2
}
if (analyze.isHorizontal) {
roofPoint[1] = roofLine.y1
roofPoint[3] = roofLine.y2
}
console.log('analyze: ', analyze)
const findRidgeVector = { x: 0, y: 0 }
if (analyze.isVertical) {
// noinspection JSSuspiciousNameCombination
findRidgeVector.x = analyze.directionVector.y
}
if (analyze.isHorizontal) {
// noinspection JSSuspiciousNameCombination
findRidgeVector.y = analyze.directionVector.x
}
console.log('findRidgeVector: ', findRidgeVector)
innerLines
.filter((line) => {
if (line.name === LINE_TYPE.SUBLINE.RIDGE) {
if (analyze.isVertical) {
const signX = Math.sign(currentLine.x1 - line.x1)
console.log('signX: ', signX)
return signX === findRidgeVector.x
}
if (analyze.isHorizontal) {
const signY = Math.sign(currentLine.y1 - line.y1)
console.log('signY: ', signY)
return signY === findRidgeVector.y
}
return false
}
return false
})
.forEach((line) => {
if (line.name === LINE_TYPE.SUBLINE.RIDGE) {
const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
}
})
/!*const oppositeLines = []
baseLines
.filter((line) => {
//평행한 반대방향 라인인지 확인.
const lineAnalyze = analyzeLine(line)
return (
(analyze.isHorizontal &&
lineAnalyze.directionVector.x !== analyze.directionVector.x &&
lineAnalyze.directionVector.y === analyze.directionVector.y) ||
(analyze.isVertical &&
lineAnalyze.directionVector.x === analyze.directionVector.x &&
lineAnalyze.directionVector.y !== analyze.directionVector.y)
)
})
.filter((line) => {
//라인이 현재라인에 overlap 되거나, 현재 라인을 full overlap하는지 확인.
if (analyze.isHorizontal) {
const currentMinX = Math.min(currentLine.x1, currentLine.x2)
const currentMaxX = Math.max(currentLine.x1, currentLine.x2)
const minX = Math.min(line.x1, line.x2)
const maxX = Math.max(line.x1, line.x2)
//full overlap
if (minX <= currentMinX && maxX >= currentMaxX) {
return true
}
//라인 포인트중 하나라도 현재 라인의 범위에 들어와있으면 overlap으로 판단.
if ((currentMinX <= minX && minX <= currentMaxX) || (currentMinX <= maxX && maxX <= currentMaxX)) {
return true
}
}
if (analyze.isVertical) {
const currentMinY = Math.min(currentLine.y1, currentLine.y2)
const currentMaxY = Math.max(currentLine.y1, currentLine.y2)
const minY = Math.min(line.y1, line.y2)
const maxY = Math.max(line.y1, line.y2)
//full overlap
if (minY <= currentMinY && maxY >= currentMaxY) {
return true
}
//라인 포인트중 하나라도 현재 라인의 범위에 들어와있으면 overlap으로 판단.
if ((currentMinY <= minY && minY <= currentMaxY) || (currentMinY <= maxY && maxY <= currentMaxY)) {
return true
}
}
return false
})
.forEach((line, i) => {
const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
stroke: 'green',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
})*!/
// innerLines.push(drawRoofLine(roofPoint, canvas, roof, textMode))
})
}*/
/*if (linesAnalysis.length > 0) {
linesAnalysis
.filter((line) => {
const dx = line.end.x - line.start.x
const dy = line.end.y - line.start.y
const length = Math.sqrt(dx ** 2 + dy ** 2)
return length > 0
})
.forEach((line) => {
const startOnLine = roof.lines.find((l) => isPointOnLineNew(l, line.start))
const endOnLine = roof.lines.find((l) => isPointOnLineNew(l, line.end))
console.log('startOnLine, endOnLine: ', startOnLine, endOnLine)
const allLinesPoints = []
innerLines.forEach((innerLine) => {
allLinesPoints.push({ x: innerLine.x1, y: innerLine.y1 }, { x: innerLine.x2, y: innerLine.y2 })
})
if (
allLinesPoints.filter((p) => almostEqual(p.x, line.start.x) && almostEqual(p.y, line.start.y)).length < 3 &&
allLinesPoints.filter((p) => almostEqual(p.x, line.end.x) && almostEqual(p.y, line.end.y)).length === 0
) {
if (startOnLine || endOnLine) {
if (line.degree === 0) {
innerLines.push(drawRoofLine([line.start.x, line.start.y, line.end.x, line.end.y], canvas, roof, textMode))
} else {
innerLines.push(
drawHipLine([line.start.x, line.start.y, line.end.x, line.end.y], canvas, roof, textMode, null, line.degree, line.degree),
)
}
}
}
})
}*/
//지붕선에 따라 라인추가 작업 처리.
/*const innerLinesPoints = []
innerLines.forEach((line) => {
const hasCoord1 = innerLinesPoints.find((p) => p.x === line.x1 && p.y === line.y1)
const hasCoord2 = innerLinesPoints.find((p) => p.x === line.x2 && p.y === line.y2)
if (!hasCoord1) innerLinesPoints.push({ x: line.x1, y: line.y1 })
if (!hasCoord2) innerLinesPoints.push({ x: line.x2, y: line.y2 })
})
roof.lines.forEach((line) => {
const splitPoint = []
let hasOverlapLine = false
const minX = Math.min(line.x1, line.x2)
const maxX = Math.max(line.x1, line.x2)
const minY = Math.min(line.y1, line.y2)
const maxY = Math.max(line.y1, line.y2)
innerLines.forEach((innerLine) => {
const innerLineMinX = Math.min(innerLine.x1, innerLine.x2)
const innerLineMaxX = Math.max(innerLine.x1, innerLine.x2)
const innerLineMinY = Math.min(innerLine.y1, innerLine.y2)
const innerLineMaxY = Math.max(innerLine.y1, innerLine.y2)
if (innerLineMinX <= minX && innerLineMaxX >= maxX && innerLineMinY <= minY && innerLineMaxY >= maxY) {
hasOverlapLine = true
}
if (minX <= innerLineMinX && maxX >= innerLineMaxX && minY <= innerLineMinY && maxY >= innerLineMaxY) {
hasOverlapLine = true
}
})
if (hasOverlapLine) return
innerLinesPoints.forEach((point) => {
if (
isPointOnLineNew(line, point) &&
!(almostEqual(line.x1, point.x) && almostEqual(line.y1, point.y)) &&
!(almostEqual(line.x2, point.x) && almostEqual(line.y2, point.y))
) {
const distance = Math.sqrt((point.x - line.x1) ** 2 + (point.y - line.y1) ** 2)
splitPoint.push({ point, distance })
}
})
if (splitPoint.length > 0) {
splitPoint.sort((a, b) => a[1] - b[1])
let startPoint = { x: line.x1, y: line.y1 }
for (let i = 0; i < splitPoint.length; i++) {
const point = splitPoint[i].point
innerLines.push(drawRoofLine([startPoint.x, startPoint.y, point.x, point.y], canvas, roof, textMode))
startPoint = point
}
innerLines.push(drawRoofLine([startPoint.x, startPoint.y, line.x2, line.y2], canvas, roof, textMode))
} else {
innerLines.push(drawRoofLine([line.x1, line.y1, line.x2, line.y2], canvas, roof, textMode))
}
})*/
}
/**