diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 65fe9d11..fc2ba2ab 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -2,7 +2,7 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' -import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' +import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' import * as turf from '@turf/turf' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import Big from 'big.js' @@ -247,12 +247,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED) // A형, B형 박공 지붕 - if ( + /* if ( (gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) || (gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type))) ) { drawGabledRoof(this.id, this.canvas, textMode) - } else if (hasShed) { + } else*/ + if (hasShed) { const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED) const areLinesParallel = function (line1, line2) { const angle1 = calculateAngle(line1.startPoint, line1.endPoint) diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js index fc667ec3..d7680150 100644 --- a/src/hooks/roofcover/useMovementSetting.js +++ b/src/hooks/roofcover/useMovementSetting.js @@ -299,6 +299,17 @@ export function useMovementSetting(id) { } const handleSave = () => { + if (CONFIRM_LINE_REF.current !== null) { + canvas.remove(CONFIRM_LINE_REF.current) + CONFIRM_LINE_REF.current = null + canvas.renderAll() + } + if (FOLLOW_LINE_REF.current !== null) { + canvas.remove(FOLLOW_LINE_REF.current) + FOLLOW_LINE_REF.current = null + canvas.renderAll() + } + const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target if (!target) return @@ -429,17 +440,6 @@ export function useMovementSetting(id) { canvas.renderAll() }) - if (CONFIRM_LINE_REF.current !== null) { - canvas.remove(CONFIRM_LINE_REF.current) - CONFIRM_LINE_REF.current = null - canvas.renderAll() - } - if (FOLLOW_LINE_REF.current !== null) { - canvas.remove(FOLLOW_LINE_REF.current) - FOLLOW_LINE_REF.current = null - canvas.renderAll() - } - roof.drawHelpLine() initEvent() closePopup(id) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 07ca86e2..9fbfc6a3 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -743,6 +743,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) console.log('wall :', wall) + console.log('roof.lines :', roof.lines) //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1 const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1)) if (hasNonParallelLines.length > 0) { @@ -813,25 +814,40 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const drawEavesFirstLines = [] const drawEavesSecondLines = [] + const drawGableRidgeFirst = [] + const drawGableRidgeSecond = [] + const drawGableFirstLines = [] + const drawGableSecondLines = [] + const drawGablePolygonFirst = [] + const drawGablePolygonSecond = [] /** 모양을 판단한다. */ drawBaseLines.forEach((currentBaseLine, index) => { let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] - console.log('currentLine :', currentBaseLine) - console.log('prevLine :', prevBaseLine) - console.log('nextLine :', nextBaseLine) + // console.log('currentLine :', currentBaseLine) + // console.log('prevLine :', prevBaseLine) + // console.log('nextLine :', nextBaseLine) const currentLine = currentBaseLine.line const prevLine = prevBaseLine.line const nextLine = nextBaseLine.line + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + + const checkScale = Big(10) + const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) + const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + + const checkPoints = { + x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), + y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), + } + + // console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** 현재 라인이 처마유형일 경우 */ if (eavesType.includes(currentLine.attributes?.type)) { if (eavesType.includes(nextLine.attributes?.type)) { - /** - * 현재 라인이 처마인 경우 - */ - const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) - const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) - console.log('prevAngle :', prevAngle, 'nextAngle :', nextAngle) /** * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다. */ @@ -840,21 +856,10 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { eavesType.includes(nextLine.attributes?.type) && Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) ) { - const checkScale = Big(10) - const xVector = Big(nextLine.x2).minus(Big(nextLine.x1)) - const yVector = Big(nextLine.y2).minus(Big(nextLine.y1)) - const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) - const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) - /** * 다음라인 방향에 포인트를 확인해서 역방향 ㄷ 모양인지 판단한다. * @type {{x: *, y: *}} */ - const checkPoints = { - x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), - y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), - } - if (checkWallPolygon.inPolygon(checkPoints)) { drawEavesFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } else { @@ -863,24 +868,1513 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } else { drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) } + } else if (gableType.includes(nextLine.attributes?.type) && gableType.includes(prevLine.attributes?.type)) { + console.log('currentLine :', currentBaseLine.size, 'prevAngle :', prevAngle, 'nextAngle :', nextAngle) + if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) { + const checkPoints = { + x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(), + y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(), + } + + if (checkWallPolygon.inPolygon(checkPoints)) { + drawGablePolygonFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + // if (!checkWallPolygon.inPolygon(checkPoints)) { + drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + // } + } else { + drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + } + } + + if (gableType.includes(currentLine.attributes?.type)) { + if (eavesType.includes(nextLine.attributes?.type)) { + if ( + eavesType.includes(prevLine.attributes?.type) && + eavesType.includes(nextLine.attributes?.type) && + Big(prevAngle).minus(Big(nextAngle)).abs().eq(180) + ) { + if (checkWallPolygon.inPolygon(checkPoints)) { + drawGableRidgeFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + drawGableFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } else { + drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } + } else { + drawGableSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine }) + } } } }) drawEavesFirstLines.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) - console.log('drawEavesFirstLines :', drawEavesFirstLines) - console.log( - 'drawEavesFirstLines :', - drawEavesFirstLines.map((line) => line.currentBaseLine.size), - ) + drawGableRidgeFirst.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) + drawGableRidgeSecond.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size) /** 추녀마루 */ let baseHipLines = [] /** 용마루 */ let baseRidgeLines = [] + /** 박공지붕 마루*/ + let baseGableRidgeLines = [] + + /** 박공지붕 라인*/ + let baseGableLines = [] /** 용마루의 갯수*/ let baseRidgeCount = 0 + // console.log('drawGableFirstLines :', drawGableFirstLines) + // console.log('drawGableSecondLines :', drawGableSecondLines) + console.log('drawEavesFirstLines :', drawEavesFirstLines) + console.log('drawEavesSecondLines :', drawEavesSecondLines) + console.log('drawGableRidgeFirst : ', drawGableRidgeFirst) + console.log('drawGableRidgeSecond : ', drawGableRidgeSecond) + console.log('drawGablePolygonFirst :', drawGablePolygonFirst) + console.log('drawGablePolygonSecond :', drawGablePolygonSecond) + + /** 박공지붕에서 파생되는 마루를 그린다. ㄷ 형태*/ + drawGableRidgeFirst.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ + drawBaseLines.forEach((line, index) => { + if (line === prevBaseLine) { + beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + } + if (line === nextBaseLine) { + afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + } + }) + + const beforePrevLine = beforePrevBaseLine?.line + const afterNextLine = afterNextBaseLine?.line + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + + /** 현재라인의 vector*/ + const currentVectorX = Math.sign(Big(x2).minus(Big(x1)).toNumber()) + const currentVectorY = Math.sign(Big(y2).minus(Big(y1)).toNumber()) + + /** 이전라인의 vector*/ + const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1))) + const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1))) + + /** 다음라인의 vector*/ + const nextVectorX = Math.sign(Big(nextLine.x2).minus(Big(nextLine.x1))) + const nextVectorY = Math.sign(Big(nextLine.y2).minus(Big(nextLine.y1))) + + /** 현재라인의 기준점*/ + const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) + const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) + + if (beforePrevBaseLine === afterNextBaseLine) { + const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) + const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) + const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) + const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) + + let oppositeMidX, oppositeMidY + if (eavesType.includes(afterNextLine.attributes?.type)) { + const checkSize = currentMidX + .minus(afterNextMidX) + .pow(2) + .plus(currentMidY.minus(afterNextMidY).pow(2)) + .sqrt() + .minus(Big(afterNextLine.attributes.planeSize).div(20)) + .round(1) + oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) + oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) + + const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) + const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) + const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) + const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) + + let addOppositeX1 = 0, + addOppositeY1 = 0, + addOppositeX2 = 0, + addOppositeY2 = 0 + + if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() + addOppositeX1 = checkScale.times(xVector1).toNumber() + addOppositeY1 = checkScale.times(yVector1).toNumber() + addOppositeX2 = checkScale.times(xVector2).toNumber() + addOppositeY2 = checkScale.times(yVector2).toNumber() + } + + let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale1 = scale1.eq(0) ? Big(1) : scale1 + let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale2 = scale2.eq(0) ? Big(1) : scale2 + + const checkHip1 = { + x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), + y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + } + + const checkHip2 = { + x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), + y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + } + + const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) + const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + + const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree + + if (intersection1) { + const hipLine = drawHipLine( + [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], + canvas, + roof, + textMode, + null, + nextDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x1, + y1: afterNextLine.y1, + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + line: hipLine, + }) + } + if (intersection2) { + const hipLine = drawHipLine( + [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + canvas, + roof, + textMode, + null, + prevDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x2, + y1: afterNextLine.y2, + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + line: hipLine, + }) + } + } else { + oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) + oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) + } + + const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) + const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + + if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridge = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) + baseGableRidgeLines.push(ridge) + baseRidgeCount++ + } + } else { + const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) + const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) + let oppositeMidX = currentMidX, + oppositeMidY = currentMidY + let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY + const beforePrevOffset = + currentAngle === beforePrevAngle + ? Big(beforePrevLine.attributes.offset) + : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) + const afterNextOffset = + currentAngle === afterNextAngle + ? Big(afterNextLine.attributes.offset) + : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) + const prevSize = Big(prevLine.attributes.planeSize).div(10) + const nextSize = Big(nextLine.attributes.planeSize).div(10) + + let prevHipCoords, nextHipCoords + + /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(afterNextLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() + + const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine) + let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)), + y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() } + } + + const nextEndPoint = { + x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)), + y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)), + } + + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: nextLine.x2, y: nextLine.y2 }, vertex2: { x: nextEndPoint.x, y: nextEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y } + nextOppositeMidY = Big(intersection.y) + nextOppositeMidX = Big(intersection.x) + } + } else { + if (vectorMidX === 0) { + nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) + nextOppositeMidX = currentMidX + } else { + nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) + nextOppositeMidY = currentMidY + } + } + + /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(beforePrevLine.attributes?.type)) { + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt() + + const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine) + let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)), + y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)), + } + + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() } + } + + const prevEndPoint = { + x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)), + y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)), + } + + let ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(), + y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(), + }, + } + let hipEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevEndPoint.x, y: prevEndPoint.y } } + let intersection = edgesIntersection(ridgeEdge, hipEdge) + if (intersection) { + prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y } + prevOppositeMidY = Big(intersection.y) + prevOppositeMidX = Big(intersection.x) + } + } else { + if (vectorMidX === 0) { + prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) + prevOppositeMidX = currentMidX + } else { + prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) + prevOppositeMidY = currentMidY + } + } + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() + + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + if (nextHipCoords) { + let intersectPoints = [] + const hipEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 } } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) && + Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(nextHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: nextHipCoords.x1, + y1: nextHipCoords.y1, + x2: nextHipCoords.x2, + y2: nextHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + if (prevHipCoords) { + let intersectPoints = [] + const hipEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 } } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(hipEdge, lineEdge) + if ( + intersection && + Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) && + Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y) + ) { + const intersectEdge = { vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 }, vertex2: { x: intersection.x, y: intersection.y } } + const is = edgesIntersection(intersectEdge, lineEdge) + if (!is.isIntersectionOutside) { + const intersectSize = Big(prevHipCoords.x2) + .minus(Big(intersection.x)) + .pow(2) + .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + line, + }) + } + } + }) + const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0] + + if (intersect) { + const degree = intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree + const hipLine = drawHipLine( + [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()], + canvas, + roof, + textMode, + null, + degree, + degree, + ) + baseHipLines.push({ + x1: prevHipCoords.x1, + y1: prevHipCoords.y1, + x2: prevHipCoords.x2, + y2: prevHipCoords.y2, + line: hipLine, + }) + } + } + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + /** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */ + const ridgeEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() }, + } + const ridgeVectorX = Math.sign(currentMidX.minus(oppositeMidX).toNumber()) + const ridgeVectorY = Math.sign(currentMidY.minus(oppositeMidY).toNumber()) + + roof.lines + .filter((line) => { + const lineVectorX = Math.sign(Big(line.x2).minus(Big(line.x1)).toNumber()) + const lineVectorY = Math.sign(Big(line.y2).minus(Big(line.y1)).toNumber()) + return ( + (lineVectorX === currentVectorX && lineVectorY !== currentVectorY) || (lineVectorX !== currentVectorX && lineVectorY === currentVectorY) + ) + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(ridgeEdge, lineEdge) + if (intersection && !intersection.isIntersectionOutside) { + const isVectorX = Math.sign(Big(currentMidX).minus(intersection.x).toNumber()) + const isVectorY = Math.sign(Big(currentMidY).minus(intersection.y).toNumber()) + if (isVectorX === ridgeVectorX && isVectorY === ridgeVectorY) { + oppositeMidX = Big(intersection.x) + oppositeMidY = Big(intersection.y) + } + } + }) + + const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) + baseGableRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + } + }) + + /** 박공지붕에서 파생되는 마루를 그린다. 첫번째에서 처리 하지 못한 라인이 있는 경우 */ + drawGableRidgeSecond.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const currentVectorX = Big(currentLine.x2).minus(currentLine.x1) + const currentVectorY = Big(currentLine.y2).minus(currentLine.y1) + const checkVectorX = Big(nextLine.x2).minus(Big(nextLine.x1)) + const checkVectorY = Big(nextLine.y2).minus(Big(nextLine.y1)) + const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2) + const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2) + const checkSize = Big(10) + + const checkPoints = { + x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.toNumber()))).toNumber(), + y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.toNumber()))).toNumber(), + } + if (!checkWallPolygon.inPolygon(checkPoints)) { + const currentMidEdge = { + vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() }, + vertex2: { + x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.neg().toNumber()))).toNumber(), + y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.neg().toNumber()))).toNumber(), + }, + } + + let oppositeLines = [] + baseLines + .filter((line, index) => { + let nextLine = baseLines[(index + 1) % baseLines.length] + let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + if ( + (gableType.includes(nextLine.attributes.type) && gableType.includes(prevLine.attributes.type)) || + (eavesType.includes(nextLine.attributes.type) && eavesType.includes(prevLine.attributes.type)) + ) { + const angle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return angle === -90 + case -90: + return angle === 90 + case 0: + return angle === 180 + case 180: + return angle === 0 + } + } + return false + }) + .forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(currentMidEdge, lineEdge) + if (intersection) { + oppositeLines.push({ + line, + intersection, + size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(), + }) + } + }) + + if (oppositeLines.length === 0) { + return + } + const oppositeLine = oppositeLines.sort((a, b) => a.size - b.size)[0] + + let points = [] + if (eavesType.includes(oppositeLine.line.attributes.type)) { + const oppositeCurrentLine = oppositeLine.line + let oppositePrevLine, oppositeNextLine + baseLines.forEach((line, index) => { + if (line === oppositeCurrentLine) { + oppositePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + oppositeNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + if (gableType.includes(oppositeNextLine.attributes.type) && gableType.includes(oppositePrevLine.attributes.type)) { + if (currentVectorX.eq(0)) { + const centerX = currentMidX.plus(oppositeLine.intersection.x).div(2).toNumber() + points = [centerX, currentLine.y1, centerX, currentLine.y2] + } else { + const centerY = currentMidY.plus(oppositeLine.intersection.y).div(2).toNumber() + points = [currentLine.x1, centerY, currentLine.x2, centerY] + } + } + if (eavesType.includes(oppositeNextLine.attributes.type) && eavesType.includes(oppositePrevLine.attributes.type)) { + /** 이전, 다음라인의 사잇각의 vector를 구한다. */ + let prevVector = getHalfAngleVector(oppositePrevLine, oppositeCurrentLine) + let nextVector = getHalfAngleVector(oppositeCurrentLine, oppositeNextLine) + + let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) } + let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) } + + /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const prevCheckPoint = { + x: Big(oppositeCurrentLine.x1).plus(Big(prevHipVector.x).times(10)), + y: Big(oppositeCurrentLine.y1).plus(Big(prevHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(prevCheckPoint)) { + prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() } + } + + /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/ + const nextCheckPoint = { + x: Big(oppositeCurrentLine.x2).plus(Big(nextHipVector.x).times(10)), + y: Big(oppositeCurrentLine.y2).plus(Big(nextHipVector.y).times(10)), + } + if (!checkWallPolygon.inPolygon(nextCheckPoint)) { + nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() } + } + + /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/ + let hipLength = Big(oppositeCurrentLine.attributes.planeSize) + .div(2) + .pow(2) + .plus(Big(oppositeCurrentLine.attributes.planeSize).div(2).pow(2)) + .sqrt() + .div(10) + .round(2) + + const ridgeEndPoint = { + x: Big(oppositeCurrentLine.x1).plus(hipLength.times(prevHipVector.x)).round(1), + y: Big(oppositeCurrentLine.y1).plus(hipLength.times(prevHipVector.y)).round(1), + } + + const prevHypotenuse = Big(oppositePrevLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt() + const prevHipPoints = { + x1: Big(oppositeCurrentLine.x1).plus(prevHypotenuse.times(prevHipVector.x.neg())).round(1).toNumber(), + y1: Big(oppositeCurrentLine.y1).plus(prevHypotenuse.times(prevHipVector.y.neg())).round(1).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + + const nextHypotenuse = Big(oppositeNextLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt() + const nextHipPoints = { + x1: Big(oppositeCurrentLine.x2).plus(nextHypotenuse.times(nextHipVector.x.neg())).round(1).toNumber(), + y1: Big(oppositeCurrentLine.y2).plus(nextHypotenuse.times(nextHipVector.y.neg())).round(1).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + + const prevIntersection = findRoofIntersection(roof, prevHipPoints, ridgeEndPoint) + const nextIntersection = findRoofIntersection(roof, nextHipPoints, ridgeEndPoint) + + if (prevIntersection) { + const prevHip = drawHipLine( + [prevIntersection.intersection.x, prevIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + prevDegree, + prevDegree, + ) + baseHipLines.push({ + x1: oppositeCurrentLine.x1, + y1: oppositeCurrentLine.y1, + x2: ridgeEndPoint.x, + y2: ridgeEndPoint.y, + line: prevHip, + }) + } + if (nextIntersection) { + const nextHip = drawHipLine( + [nextIntersection.intersection.x, nextIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()], + canvas, + roof, + textMode, + null, + nextDegree, + nextDegree, + ) + baseHipLines.push({ + x1: ridgeEndPoint.x, + y1: ridgeEndPoint.y, + x2: oppositeCurrentLine.x2, + y2: oppositeCurrentLine.y2, + line: nextHip, + }) + } + + const ridgeVectorX = Math.sign(currentMidX.minus(ridgeEndPoint.x).toNumber()) + const ridgeVectorY = Math.sign(currentMidY.minus(ridgeEndPoint.y).toNumber()) + const ridgePoints = { + x1: currentMidX.plus(Big(currentLine.attributes.offset).times(ridgeVectorX)).toNumber(), + y1: currentMidY.plus(Big(currentLine.attributes.offset).times(ridgeVectorY)).toNumber(), + x2: ridgeEndPoint.x.toNumber(), + y2: ridgeEndPoint.y.toNumber(), + } + const ridgeIntersection = findRoofIntersection(roof, ridgePoints, { x: Big(ridgePoints.x2), y: Big(ridgePoints.y2) }) + if (ridgeIntersection) { + points = [ridgeIntersection.intersection.x, ridgeIntersection.intersection.y, ridgeEndPoint.x, ridgeEndPoint.y] + } + } + } else { + if (currentVectorX.eq(0)) { + points = [oppositeLine.intersection.x, currentLine.y1, oppositeLine.intersection.x, currentLine.y2] + } else { + points = [currentLine.x1, oppositeLine.intersection.y, currentLine.x2, oppositeLine.intersection.y] + } + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) + baseGableRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + } else { + const oppositeLines = baseLines.filter((line) => { + const lineAngle = calculateAngle(line.startPoint, line.endPoint) + switch (currentAngle) { + case 90: + return lineAngle === -90 + case -90: + return lineAngle === 90 + case 0: + return lineAngle === 180 + case 180: + return lineAngle === 0 + } + }) + + if (oppositeLines.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { + let ridgePoints = [] + const oppositeLine = oppositeLines.sort((a, b) => { + let diffCurrentA, diffCurrentB + if (Math.sign(currentVectorX) === 0) { + diffCurrentA = currentMidY.minus(a.y1).abs() + diffCurrentB = currentMidY.minus(b.y1).abs() + } else { + diffCurrentA = currentMidX.minus(a.x1).abs() + diffCurrentB = currentMidX.minus(b.x1).abs() + } + return diffCurrentA.minus(diffCurrentB).toNumber() + })[0] + + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + if (Math.sign(currentVectorX) === 0) { + const prevY = Big(currentLine.y1) + .plus(Big(Math.sign(currentVectorY)).neg().times(prevOffset)) + .toNumber() + const nextY = Big(currentLine.y2) + .plus(Big(Math.sign(currentVectorY)).times(nextOffset)) + .toNumber() + const midX = Big(currentLine.x1).plus(oppositeLine.x1).div(2).toNumber() + ridgePoints = [midX, prevY, midX, nextY] + } else { + const prevX = Big(currentLine.x1) + .plus(Big(Math.sign(currentVectorX)).neg().times(prevOffset)) + .toNumber() + const nextX = Big(currentLine.x2) + .plus(Big(Math.sign(currentVectorX)).times(nextOffset)) + .toNumber() + const midY = Big(currentLine.y1).plus(oppositeLine.y1).div(2).toNumber() + ridgePoints = [prevX, midY, nextX, midY] + } + const ridge = drawRidgeLine(ridgePoints, canvas, roof, textMode) + baseGableRidgeLines.push(ridge) + baseRidgeCount++ + } + } + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + }) + + /** 케라바 지붕으로 생성된 마루의 지붕선을 그린다.*/ + baseGableRidgeLines.forEach((ridge) => { + return + const { x1, x2, y1, y2 } = ridge + const checkLine = new fabric.Line([x1, y1, x2, y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + + const ridgeVectorX = Math.sign(Big(x1).minus(x2).toNumber()) + const ridgeVectorY = Math.sign(Big(y1).minus(y2).toNumber()) + const firstPoint = { x: x1, y: y1 } + const secondPoint = { x: x2, y: y2 } + let firstRoofLine, secondRoofLine + roof.lines + .filter((line) => { + if (ridgeVectorX === 0) { + return line.x1 !== line.x2 + } else { + return line.y1 !== line.y2 + } + }) + .forEach((line) => { + if (ridgeVectorX === 0) { + if (line.y1 === firstPoint.y) { + firstRoofLine = line + } + if (line.y1 === secondPoint.y) { + secondRoofLine = line + } + } else { + if (line.x1 === firstPoint.x) { + firstRoofLine = line + } + if (line.x1 === secondPoint.x) { + secondRoofLine = line + } + } + }) + /** 마루 1개에 (위,아래), (좌,우) 로 두가지의 지붕면이 생길 수 있다.*/ + let firstCheckPoints = [] + let secondCheckPoints = [] + + /** + * @param startPoint + * @param nextPoint + * @param endPoint + */ + const findPointToPolygon = (startPoint, nextPoint, endPoint) => { + let findPoint = nextPoint + const points = [startPoint, nextPoint] + let index = 1 + while (!(points[points.length - 1].x === endPoint.x && points[points.length - 1].y === endPoint.y)) { + const prevVector = { x: Math.sign(points[index - 1].x - points[index].x), y: Math.sign(points[index - 1].y - points[index].y) } + const currentPoint = points[index] + + const hipLine = baseHipLines.find( + (line) => + (line.line.x1 === currentPoint.x && line.line.y1 === currentPoint.y) || + (line.line.x2 === currentPoint.x && line.line.y2 === currentPoint.y), + ) + if (hipLine) { + if (hipLine.line.x1 === currentPoint.x && hipLine.line.y1 === currentPoint.y) { + points.push({ x: hipLine.line.x2, y: hipLine.line.y2 }) + } else { + points.push({ x: hipLine.line.x1, y: hipLine.line.y1 }) + } + } else { + const nextRoofLine = roof.lines.find((line) => { + if (prevVector.x !== 0) { + return ( + line.y1 !== line.y2 && + ((line.x1 <= currentPoint.x && currentPoint.x <= line.x2) || (line.x2 <= currentPoint.x && currentPoint.x <= line.x1)) + ) + } else { + return ( + line.x1 !== line.x2 && + ((line.y1 <= currentPoint.y && currentPoint.y <= line.y2) || (line.y2 <= currentPoint.y && currentPoint.y <= line.y1)) + ) + } + }) + const checkLine = new fabric.Line([nextRoofLine.x1, nextRoofLine.y1, nextRoofLine.x2, nextRoofLine.y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + + const lineEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } + let ridgeIntersection + baseGableRidgeLines.forEach((ridgeLine) => { + const ridgeEdge = { vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 }, vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 } } + const intersection = edgesIntersection(lineEdge, ridgeEdge) + console.log('intersection', intersection) + if (intersection && !intersection.isIntersectionOutside) { + ridgeIntersection = { x: intersection.x, y: intersection.y } + } + }) + if (ridgeIntersection) { + points.push(ridgeIntersection) + } else { + if (nextRoofLine.x1 === currentPoint.x && nextRoofLine.y1 === currentPoint.y) { + points.push({ x: nextRoofLine.x2, y: nextRoofLine.y2 }) + } else { + points.push({ x: nextRoofLine.x1, y: nextRoofLine.y1 }) + } + } + } + console.log('points', points, points[points.length - 1], endPoint) + index = index + 1 + } + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkPoint') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + return points + } + + let startVector + console.log('!firstRoofLine && !secondRoofLine', !firstRoofLine && !secondRoofLine) + let firstPoints, secondPoints + if (!firstRoofLine && !secondRoofLine) { + } else { + if (firstRoofLine) { + firstPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x1, y: firstRoofLine.y1 }, secondPoint) + secondPoints = findPointToPolygon(firstPoint, { x: firstRoofLine.x2, y: firstRoofLine.y2 }, secondPoint) + } else { + firstPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x1, y: secondRoofLine.y1 }, firstPoint) + secondPoints = findPointToPolygon(secondPoint, { x: secondRoofLine.x2, y: secondRoofLine.y2 }, firstPoint) + } + } + const firstPolygonPoints = getSortedPoint(firstPoints) + const secondPolygonPoints = getSortedPoint(secondPoints) + + firstPolygonPoints.forEach((point, index) => { + let endPoint + if (index === firstPolygonPoints.length - 1) { + endPoint = firstPolygonPoints[0] + } else { + endPoint = firstPolygonPoints[index + 1] + } + const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) + + const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + }) + secondPolygonPoints.forEach((point, index) => { + let endPoint + if (index === secondPolygonPoints.length - 1) { + endPoint = secondPolygonPoints[0] + } else { + endPoint = secondPolygonPoints[index + 1] + } + const hipLine = drawHipLine([point.x, point.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + baseHipLines.push({ x1: point.x, y1: point.y, x2: endPoint.x, y2: endPoint.y, line: hipLine }) + + const checkLine = new fabric.Line([point.x, point.y, endPoint.x, endPoint.y], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'checkLine', + }) + canvas.add(checkLine) + canvas.renderAll() + }) + }) + + drawGableFirstLines.forEach((current) => { + return + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2, size } = currentBaseLine + let beforePrevBaseLine, afterNextBaseLine + + /** 이전 라인의 경사 */ + const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree + /** 다음 라인의 경사 */ + const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree + /** 현재 라인의 경사 */ + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + + /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */ + drawBaseLines.forEach((line, index) => { + if (line === prevBaseLine) { + beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length] + } + if (line === nextBaseLine) { + afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length] + } + }) + + const beforePrevLine = beforePrevBaseLine?.line + const afterNextLine = afterNextBaseLine?.line + + /** 각 라인의 흐름 방향을 확인한다. */ + const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint) + const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint) + const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint) + const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint) + const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint) + + /** 이전라인의 vector*/ + const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1))) + const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1))) + + /** 현재라인의 기준점*/ + const currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset)) + const currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset)) + + if (beforePrevBaseLine === afterNextBaseLine) { + console.log('박공지붕 사각') + + const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2) + const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2) + const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX)) + const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY)) + + let oppositeMidX, oppositeMidY + if (eavesType.includes(afterNextLine.attributes?.type)) { + const checkSize = currentMidX + .minus(afterNextMidX) + .pow(2) + .plus(currentMidY.minus(afterNextMidY).pow(2)) + .sqrt() + .minus(Big(afterNextLine.attributes.planeSize).div(20)) + .round(1) + oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg()) + oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg()) + + const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber()) + const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber()) + const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber()) + const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber()) + + let addOppositeX1 = 0, + addOppositeY1 = 0, + addOppositeX2 = 0, + addOppositeY2 = 0 + + if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) { + const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt() + addOppositeX1 = checkScale.times(xVector1).toNumber() + addOppositeY1 = checkScale.times(yVector1).toNumber() + addOppositeX2 = checkScale.times(xVector2).toNumber() + addOppositeY2 = checkScale.times(yVector2).toNumber() + } + + let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt() + scale1 = scale1.eq(0) ? Big(1) : scale1 + let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt() + scale2 = scale2.eq(0) ? Big(1) : scale2 + + const checkHip1 = { + x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(), + y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(), + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + } + + const checkHip2 = { + x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(), + y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(), + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + } + + const intersection1 = findRoofIntersection(roof, checkHip1, { x: oppositeMidX.plus(addOppositeX1), y: oppositeMidY.plus(addOppositeY1) }) + const intersection2 = findRoofIntersection(roof, checkHip2, { x: oppositeMidX.plus(addOppositeX2), y: oppositeMidY.plus(addOppositeY2) }) + + const afterNextDegree = afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree + + if (intersection1) { + const hipLine = drawHipLine( + [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)], + canvas, + roof, + textMode, + null, + nextDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x1, + y1: afterNextLine.y1, + x2: oppositeMidX.plus(addOppositeX1).toNumber(), + y2: oppositeMidY.plus(addOppositeY1).toNumber(), + line: hipLine, + }) + } + if (intersection2) { + const hipLine = drawHipLine( + [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)], + canvas, + roof, + textMode, + null, + prevDegree, + afterNextDegree, + ) + baseHipLines.push({ + x1: afterNextLine.x2, + y1: afterNextLine.y2, + x2: oppositeMidX.plus(addOppositeX2).toNumber(), + y2: oppositeMidY.plus(addOppositeY2).toNumber(), + line: hipLine, + }) + } + } else { + oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset)) + oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset)) + } + + const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX)) + const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY)) + + if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridge = drawRidgeLine( + [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()], + canvas, + roof, + textMode, + ) + baseRidgeLines.push(ridge) + baseRidgeCount++ + } + } else { + console.log('4각 아님') + const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1)) + const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1)) + let oppositeMidX = currentMidX, + oppositeMidY = currentMidY + let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY + const beforePrevOffset = + currentAngle === beforePrevAngle + ? Big(beforePrevLine.attributes.offset) + : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset) + const afterNextOffset = + currentAngle === afterNextAngle + ? Big(afterNextLine.attributes.offset) + : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset) + const prevSize = Big(prevLine.attributes.planeSize).div(10) + const nextSize = Big(nextLine.attributes.planeSize).div(10) + + /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(afterNextLine.attributes?.type)) { + } else { + if (vectorMidX === 0) { + prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY)) + prevOppositeMidX = currentMidX + } else { + prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX)) + prevOppositeMidY = currentMidY + } + } + + /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/ + if (eavesType.includes(beforePrevLine.attributes?.type)) { + } else { + if (vectorMidX === 0) { + nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY)) + nextOppositeMidX = currentMidX + } else { + nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX)) + nextOppositeMidY = currentMidY + } + } + + const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt() + const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt() + + /** 두 포인트 중에 current와 가까운 포인트를 사용*/ + if (checkPrevSize.gt(checkNextSize)) { + oppositeMidY = nextOppositeMidY + oppositeMidX = nextOppositeMidX + } else { + oppositeMidY = prevOppositeMidY + oppositeMidX = prevOppositeMidX + } + + if (baseRidgeCount < getMaxRidge(baseLines.length)) { + const ridgeLine = drawRidgeLine([currentMidX, currentMidY, oppositeMidX, oppositeMidY], canvas, roof, textMode) + baseRidgeLines.push(ridgeLine) + baseRidgeCount++ + } + } + }) + + /** 박공지붕 polygon 생성 */ + drawGablePolygonFirst.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2 } = currentBaseLine + let prevLineRidges = [], + nextLineRidges = [] + + const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) + const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) + const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) + const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) + const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) + const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + const currentOffset = currentLine.attributes.offset + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + + let roofX1, roofY1, roofX2, roofY2 + if (currentVectorX === 0) { + roofX1 = Big(x1).plus(Big(currentLine.attributes.offset).times(prevVectorX)) + roofX2 = roofX1 + roofY1 = Big(y1).plus(Big(prevLine.attributes.offset).times(currentVectorY * -1)) + roofY2 = Big(y2).plus(Big(nextLine.attributes.offset).times(currentVectorY)) + } else { + roofX1 = Big(x1).plus(Big(prevLine.attributes.offset).times(currentVectorX * -1)) + roofX2 = Big(x2).plus(Big(nextLine.attributes.offset).times(currentVectorX)) + roofY1 = Big(y1).plus(Big(currentLine.attributes.offset).times(prevVectorY)) + roofY2 = roofY1 + } + + const prevRoofLine = roof.lines.find( + (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x2 === roofX1.toNumber() && line.y2 === roofY1.toNumber(), + ) + const nextRoofLine = roof.lines.find( + (line) => currentVectorX !== Math.sign(line.x2 - line.x1) && line.x1 === roofX2.toNumber() && line.y1 === roofY2.toNumber(), + ) + + const prevRoofEdge = { vertex1: { x: prevRoofLine.x1, y: prevRoofLine.y1 }, vertex2: { x: prevRoofLine.x2, y: prevRoofLine.y2 } } + const nextRoofEdge = { vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 }, vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 } } + + baseGableRidgeLines.forEach((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const prevIs = edgesIntersection(prevRoofEdge, ridgeEdge) + const nextIs = edgesIntersection(nextRoofEdge, ridgeEdge) + if (prevIs) { + prevLineRidges.push({ + ridge, + size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: prevIs.x, y2: prevIs.y }), + }) + } + if (nextIs) { + nextLineRidges.push({ + ridge, + size: calcLinePlaneSize({ x1: ridgeEdge.vertex1.x, y1: ridgeEdge.vertex1.y, x2: nextIs.x, y2: nextIs.y }), + }) + } + }) + + const polygonPoints = [ + { x: roofX1.toNumber(), y: roofY1.toNumber() }, + { x: roofX2.toNumber(), y: roofY2.toNumber() }, + ] + + let prevLineRidge, nextLineRidge + if (prevLineRidges.length > 0) { + prevLineRidges = prevLineRidges + .filter((line) => { + const ridge = line.ridge + if (currentVectorX === 0) { + return ridge.y1 === roofY1.toNumber() || ridge.y2 === roofY1.toNumber() + } else { + return ridge.x1 === roofX1.toNumber() || ridge.x2 === roofX1.toNumber() + } + }) + .sort((a, b) => a.size - b.size) + prevLineRidge = prevLineRidges[0].ridge + } + if (nextLineRidges.length > 0) { + nextLineRidges = nextLineRidges + .filter((line) => { + const ridge = line.ridge + if (currentVectorX === 0) { + return ridge.y1 === roofY2.toNumber() || ridge.y2 === roofY2.toNumber() + } else { + return ridge.x1 === roofX2.toNumber() || ridge.x2 === roofX2.toNumber() + } + }) + .sort((a, b) => a.size - b.size) + nextLineRidge = nextLineRidges[0].ridge + } + + if (prevLineRidge === nextLineRidge) { + polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) + } else { + let isOverLap = + currentVectorX === 0 + ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) || + (prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) || + (prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) || + (prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2) + : (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) || + (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) || + (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) || + (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2) + console.log('isOverLap : ', isOverLap) + if (isOverLap) { + const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber()) + const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber()) + /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */ + if (prevDistance < nextDistance) { + polygonPoints.push({ x: nextLineRidge.x1, y: nextLineRidge.y1 }, { x: nextLineRidge.x2, y: nextLineRidge.y2 }) + /** 이전라인과 교차한 마루의 포인트*/ + const prevRidgePoint1 = + currentVectorX === 0 + ? roofY1.toNumber() === prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() === prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + + polygonPoints.push(prevRidgePoint1) + + /** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY2.toNumber() !== nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() !== nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + + const prevRidgePoint2 = + currentVectorX === 0 ? { x: prevRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: prevRidgePoint1.y } + polygonPoints.push(prevRidgePoint2) + } else { + polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 }) + + /** 다음라인과 교차한 마루의 포인트*/ + const nextRidgePoint1 = + currentVectorX === 0 + ? roofY2.toNumber() === nextLineRidge.y1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + : roofX2.toNumber() === nextLineRidge.x1 + ? { x: nextLineRidge.x1, y: nextLineRidge.y1 } + : { x: nextLineRidge.x2, y: nextLineRidge.y2 } + polygonPoints.push(nextRidgePoint1) + + /** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/ + const checkRidgePoint = + currentVectorX === 0 + ? roofY1.toNumber() !== prevLineRidge.y1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + : roofX1.toNumber() !== prevLineRidge.x1 + ? { x: prevLineRidge.x1, y: prevLineRidge.y1 } + : { x: prevLineRidge.x2, y: prevLineRidge.y2 } + const nextRidgePoint2 = + currentVectorX === 0 ? { x: nextRidgePoint1.x, y: checkRidgePoint.y } : { x: checkRidgePoint.x, y: nextRidgePoint1.y } + polygonPoints.push(nextRidgePoint2) + } + } else { + } + } + + const sortedPolygonPoints = getSortedPoint(polygonPoints) + + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] + } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) + }) + + canvas + .getObjects() + .filter((obj) => obj.name === 'checkLine' || obj.name === 'checkRoofLine') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + }) + + drawGablePolygonSecond.forEach((current) => { + const { currentBaseLine, prevBaseLine, nextBaseLine } = current + const currentLine = currentBaseLine.line + const prevLine = prevBaseLine.line + const nextLine = nextBaseLine.line + let { x1, x2, y1, y2 } = currentBaseLine + let prevLineRidges = [], + nextLineRidges = [] + + const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1) + const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1) + const prevVectorX = Math.sign(prevLine.x2 - prevLine.x1) + const prevVectorY = Math.sign(prevLine.y2 - prevLine.y1) + const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2) + const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2) + const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree + const currentOffset = currentLine.attributes.offset + const prevOffset = prevLine.attributes.offset + const nextOffset = nextLine.attributes.offset + + const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } } + const prevRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(prevEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } } + const nextRidge = baseGableRidgeLines.find((ridge) => { + const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } } + const is = edgesIntersection(nextEdge, ridgeEdge) + if (is && !is.isIntersectionOutside) { + return ridge + } + }) + + let currentRidge + + if (prevRidge) { + if ( + currentVectorX === 0 && + ((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) || + (prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2)) + ) { + currentRidge = prevRidge + } + if ( + currentVectorY === 0 && + ((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) || + (prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2)) + ) { + currentRidge = prevRidge + } + } + if (nextRidge) { + if ( + currentVectorX === 0 && + ((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) || + (nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2)) + ) { + currentRidge = nextRidge + } + if ( + currentVectorY === 0 && + ((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) || + (nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2)) + ) { + currentRidge = nextRidge + } + } + if (currentRidge) { + const vectorX = currentVectorX === 0 ? Math.sign(currentLine.x1 - currentRidge.x1) : 0 + const vectorY = currentVectorY === 0 ? Math.sign(currentLine.y1 - currentRidge.y1) : 0 + + const polygonPoints = [ + { x: currentRidge.x1, y: currentRidge.y1 }, + { x: currentRidge.x2, y: currentRidge.y2 }, + ] + if (currentVectorX === 0) { + polygonPoints.push( + { x: Big(currentLine.x1).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y1 }, + { x: Big(currentLine.x2).plus(Big(currentOffset).times(vectorX)).toNumber(), y: currentRidge.y2 }, + ) + } else { + polygonPoints.push( + { x: currentRidge.x1, y: Big(currentLine.y1).plus(Big(currentOffset).times(vectorY)).toNumber() }, + { x: currentRidge.x2, y: Big(currentLine.y2).plus(Big(currentOffset).times(vectorY)).toNumber() }, + ) + } + + const sortedPolygonPoints = getSortedPoint(polygonPoints) + + sortedPolygonPoints.forEach((startPoint, index) => { + let endPoint + if (index === sortedPolygonPoints.length - 1) { + endPoint = sortedPolygonPoints[0] + } else { + endPoint = sortedPolygonPoints[index + 1] + } + const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree) + if (currentVectorX === 0) { + if (Math.sign(startPoint.x - endPoint.x) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } else { + if (Math.sign(startPoint.y - endPoint.y) === 0) { + hipLine.attributes.actualSize = hipLine.attributes.planeSize + } + } + baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine }) + }) + } + }) + /** ⨆ 모양 처마에 추녀마루를 그린다. */ drawEavesFirstLines.forEach((current) => { // 확인용 라인, 포인트 제거 @@ -1408,13 +2902,14 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }, } - /*const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { + const checkLine = new fabric.Line([checkEdges.vertex1.x, checkEdges.vertex1.y, checkEdges.vertex2.x, checkEdges.vertex2.y], { stroke: 'purple', strokeWidth: 2, parentId: roof.id, + name: 'checkLine', }) canvas.add(checkLine) - canvas.renderAll()*/ + canvas.renderAll() /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/ const intersectBaseLine = [] @@ -1464,7 +2959,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersection = edgesIntersection(checkEdges, lineEdge) if (intersection) { - /*const intersectPoint = new fabric.Circle({ + /* const intersectPoint = new fabric.Circle({ left: intersection.x - 2, top: intersection.y - 2, radius: 4, @@ -1494,7 +2989,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { return prevDistance < currentDistance ? prev : current }, intersectBaseLine[0]) - // console.log('oppositeLine : ', oppositeLine) + console.log('oppositeLine : ', oppositeLine) /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */ const oppositeSize = oppositeLine @@ -1521,8 +3016,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('oppositeSize : ', oppositeSize, 'lineMinSize : ', lineMinSize.toNumber(), 'ridgeSize : ', ridgeSize.toNumber()) - if (ridgeSize.gt(0)) { - baseRidgeCount = baseRidgeCount + 1 + if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) { // console.log('baseRidgeCount : ', baseRidgeCount) const points = [ startPoint.x, @@ -1557,10 +3051,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { canvas.add(checkMidLine) canvas.renderAll()*/ // console.log('points : ', points) - - /** 마루 생성 */ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) + baseRidgeCount = baseRidgeCount + 1 /** 포인트 조정*/ baseHipLines @@ -1619,6 +3112,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const nextLine = nextBaseLine.line let { x1, x2, y1, y2, size } = currentBaseLine + // const checkLine = new fabric.Line([x1, y1, x2, y2], { + // stroke: 'yellow', + // strokeWidth: 4, + // parentId: roof.id, + // name: 'checkLine', + // }) + // canvas.add(checkLine) + // canvas.renderAll() + // checkLine.bringToFront() + /** 이전 라인의 경사 */ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree /** 다음 라인의 경사 */ @@ -1675,7 +3178,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { let prevHipLine, nextHipLine /** 이전라인과의 연결지점에 추녀마루를 그린다. */ // console.log('이전라인 : ', baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) - if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) { + if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0 && eavesType.includes(prevLine.attributes.type)) { let prevEndPoint = { x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2), y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2), @@ -1869,7 +3372,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } /** 다음라인과의 연결지점에 추녀마루를 그린다. */ // console.log('다음라인 : ', baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) - if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) { + if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0 && eavesType.includes(nextLine.attributes.type)) { let nextEndPoint = { x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2), y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2), @@ -2097,7 +3600,6 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } } }) - intersectRidgePoints.sort((prev, current) => prev.size.minus(current.size).toNumber()) if (intersectRidgePoints.length > 0) { const oldPlaneSize = hipLine.line.attributes.planeSize @@ -2116,15 +3618,15 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { } }) - // console.log('baseHipLines : ', baseHipLines) - // console.log('baseRidgeLines : ', baseRidgeLines) + console.log('baseHipLines : ', baseHipLines) + console.log('baseRidgeLines : ', baseRidgeLines) /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/ /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/ let unFinishedRidge = [] baseRidgeLines.forEach((current) => { let checkPoint = [ - { x: current.x1, y: current.y1, line: current, cnt: 0 }, - { x: current.x2, y: current.y2, line: current, cnt: 0 }, + { x: current.x1, y: current.y1, line: current, cnt: 0, onRoofLine: false }, + { x: current.x2, y: current.y2, line: current, cnt: 0, onRoofLine: false }, ] baseHipLines.forEach((line) => { if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) { @@ -2134,11 +3636,43 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { checkPoint[1].cnt = checkPoint[1].cnt + 1 } }) + + /** 마루의 포인트가 지붕선 위에 있는경우는 제외 (케라바 등)*/ + roof.lines.forEach((line) => { + if ( + line.x1 === line.x2 && + checkPoint[0].x === line.x1 && + ((line.y1 <= checkPoint[0].y && line.y2 >= checkPoint[0].y) || (line.y1 >= checkPoint[0].y && line.y2 <= checkPoint[0].y)) + ) { + checkPoint[0].onRoofLine = true + } + if ( + line.y1 === line.y2 && + checkPoint[0].y === line.y1 && + ((line.x1 <= checkPoint[0].x && line.x2 >= checkPoint[0].x) || (line.x1 >= checkPoint[0].x && line.x2 <= checkPoint[0].x)) + ) { + checkPoint[0].onRoofLine = true + } + if ( + line.x1 === line.x2 && + checkPoint[1].x === line.x1 && + ((line.y1 <= checkPoint[1].y && line.y2 >= checkPoint[1].y) || (line.y1 >= checkPoint[1].y && line.y2 <= checkPoint[1].y)) + ) { + checkPoint[1].onRoofLine = true + } + if ( + line.y1 === line.y2 && + checkPoint[1].y === line.y1 && + ((line.x1 <= checkPoint[1].x && line.x2 >= checkPoint[1].x) || (line.x1 >= checkPoint[1].x && line.x2 <= checkPoint[1].x)) + ) { + checkPoint[1].onRoofLine = true + } + }) // console.log('checkPoint : ', checkPoint) - if (checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) { + if ((checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) && !checkPoint[0].onRoofLine) { unFinishedRidge.push(checkPoint[0]) } - if (checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) { + if ((checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) && !checkPoint[1].onRoofLine) { unFinishedRidge.push(checkPoint[1]) } }) @@ -2185,19 +3719,25 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { /**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/ /**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */ let degreeAllLine = [] - baseLines.forEach((line) => { - const pitch = line.attributes.pitch - const degree = line.attributes.degree - degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) - }) + baseLines + .filter((line) => eavesType.includes(line.attributes.type)) + .forEach((line) => { + const pitch = line.attributes.pitch + const degree = line.attributes.degree + degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree) + }) let currentDegree, prevDegree degreeAllLine = [...new Set(degreeAllLine)] currentDegree = degreeAllLine[0] if (degreeAllLine.length === 1) { prevDegree = currentDegree + } else { + prevDegree = degreeAllLine[1] } + console.log('unFinishedRidge : ', unFinishedRidge) + console.log('unFinishedPoint : ', unFinishedPoint) /** 라인이 부재한 마루에 라인을 찾아 그린다.*/ unFinishedRidge.forEach((current) => { let checkPoints = [] @@ -2270,6 +3810,13 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 } point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y] + const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(line.line.attributes.actualSize))) + .times(180) + .div(Math.PI) + .round(1) + console.log('theta : ', theta.toNumber()) + prevDegree = theta.toNumber() + currentDegree = theta.toNumber() canvas.remove(line.line) baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line) } @@ -2408,6 +3955,16 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { intersectPoints.forEach((is) => { const points = [current.x, current.y, is.x, is.y] + /** 추녀마루의 연결점이 처마라인이 아닌경우 return */ + let hasGable = false + baseLines + .filter((line) => (line.x1 === points[2] && line.y1 === points[3]) || (line.x2 === points[2] && line.y2 === points[3])) + .forEach((line) => { + if (!eavesType.includes(line.attributes.type)) { + hasGable = true + } + }) + if (hasGable) return const pointEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } } const vectorX = Math.sign(Big(points[2]).minus(Big(points[0])).toNumber()) const vectorY = Math.sign(Big(points[3]).minus(Big(points[1])).toNumber()) @@ -2502,7 +4059,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // canvas.renderAll() /** 직교 하는 포인트가 존재 할때 마루를 그린다. */ - if (orthogonalPoints.length > 0) { + if (orthogonalPoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { baseRidgeCount = baseRidgeCount + 1 const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2] const ridgeLine = drawRidgeLine(points, canvas, roof, textMode) @@ -2626,7 +4183,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { }) ridgePoints.sort((a, b) => a.distance - b.distance) - if (ridgePoints.length > 0) { + if (ridgePoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) { const intersection = ridgePoints[0].intersection const isPoint = intersection.intersection const points = [hipPoint.x2, hipPoint.y2, isPoint.x, isPoint.y] @@ -2749,8 +4306,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { // console.log('hipStartPoint : ', hipStartPoint) /** 직선인 경우 마루를 그린다.*/ if ( - (hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || - (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3]) + ((hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) || + (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3])) && + baseRidgeCount < getMaxRidge(baseLines.length) ) { // console.log('릿지1') const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode) @@ -2790,8 +4348,9 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { linePoint.intersectPoint.intersection.y, ] if ( - (isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) || - (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3]) + ((isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) || + (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3])) && + baseRidgeCount < getMaxRidge(baseLines.length) ) { const ridgeLine = drawRidgeLine(isStartPoint, canvas, roof, textMode) baseRidgeCount = baseRidgeCount + 1 @@ -3095,7 +4654,12 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { (line.x1 === ridgePoints[2] && line.y1 === ridgePoints[3] && line.x2 === ridgePoints[0] && line.y2 === ridgePoints[1]), ) - if (!baseIntersection && alreadyRidges.length === 0 && otherRidgeInterSection.length === 0) { + if ( + !baseIntersection && + alreadyRidges.length === 0 && + otherRidgeInterSection.length === 0 && + baseRidgeCount < getMaxRidge(baseLines.length) + ) { baseRidgeCount = baseRidgeCount + 1 const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode) baseRidgeLines.push(ridgeLine) @@ -3109,7 +4673,7 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { const hipEdge = { vertex1: { x: hipPoints[0], y: hipPoints[1] }, vertex2: { x: hipPoints[2], y: hipPoints[3] } } baseLines.forEach((line) => { const intersection = edgesIntersection(hipEdge, { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }) - if (intersection && !intersection.isIntersectionOutside) { + if (intersection && !intersection.isIntersectionOutside && eavesType.includes(line.attributes.type)) { baseIntersection = true } }) @@ -3151,7 +4715,25 @@ export const drawRidgeRoof = (roofId, canvas, textMode) => { baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {}) }) - roof.innerLines = [...baseRidgeLines, ...baseHipLines.map((line) => line.line)] + const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseHipLines.map((line) => line.line)] + const uniqueInnerLines = [] + + innerLines.forEach((currentLine) => { + const sameLines = uniqueInnerLines.filter( + (line) => + line !== currentLine && + ((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) || + (line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)), + ) + + if (sameLines.length === 0) { + uniqueInnerLines.push(currentLine) + } else { + sameLines.forEach((line) => canvas.remove(line)) + } + }) + canvas.renderAll() + roof.innerLines = uniqueInnerLines /** 확인용 라인, 포인트 제거 */ canvas @@ -3210,10 +4792,57 @@ const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, cu }) canvas.add(hip) + hip.bringToFront() canvas.renderAll() return hip } +/** + * 라인의 흐름 방향에서 마주치는 지붕선의 포인트를 찾는다. + * @param roof + * @param baseLine + * @param endPoint + * @returns {*} + */ +const findRoofIntersection = (roof, baseLine, endPoint) => { + let intersectPoints = [] + const { x1, y1, x2, y2 } = baseLine + + const baseEdge = { + vertex1: { x: x1, y: y1 }, + vertex2: { x: x2, y: y2 }, + } + + /** 외벽선에서 라인 겹치는 경우에 대한 확인*/ + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersection = edgesIntersection(baseEdge, lineEdge) + if ( + intersection && + !intersection.isIntersectionOutside && + Math.sign(endPoint.x - baseLine.x1) === Math.sign(endPoint.x - intersection.x) && + Math.sign(endPoint.y - baseLine.y1) === Math.sign(endPoint.y - intersection.y) + ) { + const intersectSize = endPoint.x + .minus(Big(intersection.x)) + .pow(2) + .plus(endPoint.y.minus(Big(intersection.y)).pow(2)) + .abs() + .sqrt() + .toNumber() + + intersectPoints.push({ + intersection, + size: intersectSize, + }) + } + }) + + return intersectPoints.reduce((prev, current) => { + return prev.size < current.size ? prev : current + }, intersectPoints[0]) +} + /** * 마루를 그린다. * @param points @@ -3247,6 +4876,7 @@ const drawRidgeLine = (points, canvas, roof, textMode) => { }, }) canvas.add(ridge) + ridge.bringToFront() canvas.renderAll() return ridge @@ -6068,7 +7698,7 @@ export const calcLinePlaneSize = (points) => { export const calcLineActualSize = (points, degree) => { const planeSize = calcLinePlaneSize(points) const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180))) - return Big(planeSize).div(theta).round(1).toNumber() + return Big(planeSize).div(theta).round().toNumber() } export const createLinesFromPolygon = (points) => { @@ -6085,3 +7715,32 @@ export const createLinesFromPolygon = (points) => { } return lines } + +/** 포인트 정렬 가장왼쪽, 가장위 부터 */ +const getSortedPoint = (points) => { + const startPoint = points + .filter((point) => point.x === Math.min(...points.map((point) => point.x))) + .reduce((prev, curr) => { + return prev.y < curr.y ? prev : curr + }) + const sortedPoints = [] + sortedPoints.push(startPoint) + + let prevPoint = startPoint + + for (let i = 0; i < points.length - 1; i++) { + points + .filter((point) => !sortedPoints.includes(point)) + .forEach((point) => { + if (i % 2 === 1 && prevPoint.y === point.y) { + sortedPoints.push(point) + prevPoint = point + } + if (i % 2 === 0 && prevPoint.x === point.x) { + sortedPoints.push(point) + prevPoint = point + } + }) + } + return sortedPoints +}