From 494b06bfe3e922eb9749f0a937f150ab66001205 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Fri, 19 Dec 2025 17:35:53 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=98=EB=8B=A8=EC=A7=80=EB=B6=95=EC=84=A0?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/qpolygon-utils.js | 932 ++++++++----------------------------- 1 file changed, 200 insertions(+), 732 deletions(-) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 7d671a3a..93b78a62 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -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)) - } - })*/ } /**