From 3a8769bd9128ca1643b46dc3b6a5b970e789de5c Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Wed, 17 Dec 2025 17:33:16 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B3=80=EB=B3=84=EB=A1=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=201=EC=B0=A8=20=EC=9A=B4=EC=98=81=EC=84=9C=EB=B2=84?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QPolygon.js | 21 +- src/util/qpolygon-utils.js | 2465 +++++++++++++++++++++++------ 2 files changed, 2013 insertions(+), 473 deletions(-) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 6021afc2..164462eb 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -2,11 +2,10 @@ 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, drawGableRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils' +import { calculateAngle, drawGableRoof, drawRoofByAttribute, 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' -import { drawSkeletonRidgeRoof } from '@/util/skeleton-utils' export const QPolygon = fabric.util.createClass(fabric.Polygon, { type: 'QPolygon', @@ -313,14 +312,18 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { } const getParallelEavesLines = function (shedLines, lines) { - const eavesLines = lines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.EAVES) - const referenceAngle = calculateAngle(shedLines[0].startPoint, shedLines[0].endPoint) - - return eavesLines.filter((line) => { - const eavesAngle = calculateAngle(line.startPoint, line.endPoint) - return Math.abs(referenceAngle - eavesAngle) === 180 + const otherSideLines = lines.filter((line) => { + const lineAngle = calculateAngle(line.startPoint, line.endPoint) + return Math.abs(referenceAngle - lineAngle) === 180 }) + const containNotEaves = otherSideLines.filter((line) => line.attributes?.type !== LINE_TYPE.WALLLINE.EAVES) + + if (containNotEaves.length === 0) { + return otherSideLines + } else { + return [] + } } const parallelEaves = getParallelEavesLines(shedLines, lines) @@ -346,7 +349,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { drawShedRoof(this.id, this.canvas, textMode) } else { console.log('변별로 설정') - drawRidgeRoof(this.id, this.canvas, textMode) + drawRoofByAttribute(this.id, this.canvas, textMode) } }, diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index ea0254f7..576926f0 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -971,7 +971,7 @@ export const drawGableRoof = (roofId, canvas, textMode) => { } if (analyze.isVertical) { const overlapY1 = Math.min(overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y) - const overlapY2 = (overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y) + const overlapY2 = Math.max(overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y) // 각 라인 사이의 길이를 구해서 각도에 대한 중간 지점으로 마루를 생성. const currentMidX = (currentLine.x1 + currentLine.x2) / 2 @@ -1657,6 +1657,7 @@ const findIntersectionPoint = (startPoint, direction, polygonVertices, divisionL * @param textMode */ export const drawRoofByAttribute = (roofId, canvas, textMode) => { + const TYPES = { HIP: 'hip', RIDGE: 'ridge', GABLE_LINE: 'gableLine', NEW: 'new' } let roof = canvas?.getObjects().find((object) => object.id === roofId) const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId) @@ -2144,12 +2145,16 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { const intersection = edgesIntersection(prevHipEdge, checkEdge) if (intersection) { - const checkVector = { x: Math.sign(prevLine.x1 - testPoint[0]), y: Math.sign(prevLine.y1 - testPoint[1]) } - const intersectVector = { x: Math.sign(prevLine.x1 - intersection.x), y: Math.sign(prevLine.y1 - intersection.y) } + const checkVector = { x: Math.sign(testPoint[0] - testPoint[2]), y: Math.sign(testPoint[1] - testPoint[3]) } + const intersectVector = { x: Math.sign(testPoint[0] - intersection.x), y: Math.sign(testPoint[1] - intersection.y) } if (checkVector.x === intersectVector.x && checkVector.y === intersectVector.y) { const distance = Math.sqrt((intersection.x - testPoint[0]) ** 2 + (intersection.y - testPoint[1]) ** 2) prevPoint = { intersection, distance } } + + if (almostEqual(intersection.x, testPoint[0]) && almostEqual(intersection.y, testPoint[1])) { + prevPoint = { intersection, distance: 0 } + } } } } @@ -2187,12 +2192,15 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } const intersection = edgesIntersection(nextHipEdge, checkEdge) if (intersection) { - const checkVector = { x: Math.sign(nextLine.x2 - testPoint[0]), y: Math.sign(nextLine.y2 - testPoint[1]) } - const intersectVector = { x: Math.sign(nextLine.x2 - intersection.x), y: Math.sign(nextLine.y2 - intersection.y) } + const checkVector = { x: Math.sign(testPoint[0] - testPoint[2]), y: Math.sign(testPoint[1] - testPoint[3]) } + const intersectVector = { x: Math.sign(testPoint[0] - intersection.x), y: Math.sign(testPoint[1] - intersection.y) } if (checkVector.x === intersectVector.x && checkVector.y === intersectVector.y) { const distance = Math.sqrt((intersection.x - testPoint[0]) ** 2 + (intersection.y - testPoint[1]) ** 2) nextPoint = { intersection, distance } } + if (almostEqual(intersection.x, testPoint[0]) && almostEqual(intersection.y, testPoint[1])) { + nextPoint = { intersection, distance: 0 } + } } } } @@ -2200,7 +2208,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { if (prevHasGable || nextHasGable) { const prevLength = Math.sqrt((prevLine.x1 - prevLine.x2) ** 2 + (prevLine.y1 - prevLine.y2) ** 2) const nextLength = Math.sqrt((nextLine.x1 - nextLine.x2) ** 2 + (nextLine.y1 - nextLine.y2) ** 2) - if (prevPoint && prevHasGable && prevLength < nextLength) { + if (prevPoint && prevHasGable && prevLength <= nextLength) { return prevPoint.intersection } if (nextPoint && nextHasGable && prevLength > nextLength) { @@ -2403,7 +2411,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: points1[2], y: points1[3] }, left: beforePrevIndex, right: prevIndex, - type: 'hip', + type: TYPES.HIP, degree: shedDegree, }, { @@ -2411,7 +2419,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: points2[2], y: points2[3] }, left: nextIndex, right: afterNextIndex, - type: 'hip', + type: TYPES.HIP, degree: shedDegree, }, ) @@ -2434,6 +2442,17 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { return } + let beforePrevLine, afterNextLine + baseLines.forEach((baseLine, index) => { + if (baseLine === prevLine) { + beforePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + } + if (baseLine === nextLine) { + afterNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + + const currentVector = { x: Math.sign(clamp01(currentLine.x1 - currentLine.x2)), y: Math.sign(clamp01(currentLine.y1 - currentLine.y2)) } 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) } //반절마루 생성불가이므로 지붕선만 추가하고 끝냄 @@ -2546,8 +2565,44 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { //마루가 최대로 뻗어나갈수 있는 포인트. null일때는 맞은편 지붕선 까지로 판단한다. const drivePoint = getRidgeDrivePoint(point, prevLine, nextLine, baseLines) - console.log('drivePoint : ', drivePoint) - if (drivePoint) { + const isOverlapBefore = analyze.isHorizontal + ? almostEqual(Math.min(beforePrevLine.x1, beforePrevLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(beforePrevLine.x1, beforePrevLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(beforePrevLine.y1, beforePrevLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(beforePrevLine.y1, beforePrevLine.y2), Math.max(currentLine.y1, currentLine.y2)) + const isOverlapAfter = analyze.isHorizontal + ? almostEqual(Math.min(afterNextLine.x1, afterNextLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(afterNextLine.x1, afterNextLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(afterNextLine.y1, afterNextLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(afterNextLine.y1, afterNextLine.y2), Math.max(currentLine.y1, currentLine.y2)) + + if (isOverlapBefore || isOverlapAfter) { + const oppLine = isOverlapBefore ? beforePrevLine : afterNextLine + const otherGable = baseLines.find( + (l) => l !== currentLine && l !== prevLine && l !== nextLine && l !== oppLine && l.attributes.type === LINE_TYPE.WALLLINE.GABLE, + ) + const pointVector = { x: Math.sign(clamp01(point[0] - point[2])), y: Math.sign(clamp01(point[1] - point[3])) } + if (!otherGable) { + let offset = 0 + switch (oppLine.attributes.type) { + case LINE_TYPE.WALLLINE.HIPANDGABLE: + offset = oppLine.attributes.width + break + case LINE_TYPE.WALLLINE.JERKINHEAD: + offset = oppLine.attributes.width / 2 + break + default: + break + } + point[2] += pointVector.x * offset + point[3] += pointVector.y * offset + } else { + if (drivePoint) { + point[2] = drivePoint.x + point[3] = drivePoint.y + } + } + } else if (drivePoint) { point[2] = drivePoint.x point[3] = drivePoint.y } @@ -2557,7 +2612,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: point[2], y: point[3] }, left: baseLines.findIndex((line) => line === prevLine), right: baseLines.findIndex((line) => line === nextLine), - type: 'ridge', + type: TYPES.RIDGE, degree: 0, }) } @@ -2592,15 +2647,375 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: intersect.x, y: intersect.y }, left: baseLines.findIndex((line) => line === prevLine), right: baseLines.findIndex((line) => line === nextLine), - type: 'ridge', + type: TYPES.RIDGE, degree: 0, }) } } } }) + + eaves.sort((a, b) => a.attributes.planeSize - b.attributes.planeSize) //3. 처마지붕 판단, 처마는 옆이 처마여야한다. - eaves.forEach((currentLine) => { + + const ridgeEaves = eaves.filter((currentLine) => { + let prevLine, nextLine + baseLines.forEach((baseLine, index) => { + if (baseLine === currentLine) { + nextLine = baseLines[(index + 1) % baseLines.length] + 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) } + const inPolygonPoint = { + x: (currentLine.x1 + currentLine.x2) / 2 + Math.sign(nextLine.x2 - nextLine.x1), + y: (currentLine.y1 + currentLine.y2) / 2 + Math.sign(nextLine.y2 - nextLine.y1), + } + + //좌우 라인이 서로 다른방향이고 지붕 안쪽으로 들어가지 않을때 + const isAbleShape = (prevLineVector.x !== nextLineVector.x || prevLineVector.y !== nextLineVector.y) && checkWallPolygon.inPolygon(inPolygonPoint) + const isAbleAttribute = prevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && nextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES + return isAbleAttribute && isAbleShape + }) + + const hipedEaves = eaves.filter((line) => !ridgeEaves.includes(line)) + + ridgeEaves.sort((a, b) => a.attributes.planeSize - b.attributes.planeSize) + + let proceedEaves = [] // left: 이전, right:다음, point:그려지는 포인트, length:길이 + let proceedRidges = [] // left: 이전, right:다음, point:그려지는 포인트, length:길이 + let hipLines = [] + ridgeEaves.forEach((currentLine) => { + /*const checkLine = new fabric.Line([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine).renderAll()*/ + let prevLine, nextLine, currentI, prevI, nextI + baseLines.forEach((baseLine, index) => { + if (baseLine === currentLine) { + currentI = index + prevI = (index - 1 + baseLines.length) % baseLines.length + nextI = (index + 1) % baseLines.length + } + }) + prevLine = baseLines[prevI] + nextLine = baseLines[nextI] + + let beforePrevLine, afterNextLine + baseLines.forEach((baseLine, index) => { + if (baseLine === prevLine) { + beforePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + } + if (baseLine === nextLine) { + afterNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + + const analyze = analyzeLine(currentLine) + const currentDegree = getDegreeByChon(currentLine.attributes.pitch) + let pHipVector = getHalfAngleVector(currentLine, prevLine) + let nHipVector = getHalfAngleVector(currentLine, nextLine) + const pCheckPoint = { + x: currentLine.x1 + (pHipVector.x * 10) / 2, + y: currentLine.y1 + (pHipVector.y * 10) / 2, + } + const nCheckPoint = { + x: currentLine.x2 + (nHipVector.x * 10) / 2, + y: currentLine.y2 + (nHipVector.y * 10) / 2, + } + + if (!checkWallPolygon.inPolygon(pCheckPoint)) { + pHipVector = { x: -pHipVector.x, y: -pHipVector.y } + } + if (!checkWallPolygon.inPolygon(nCheckPoint)) { + nHipVector = { x: -nHipVector.x, y: -nHipVector.y } + } + + const prevCheckPoint = [currentLine.x1, currentLine.y1, currentLine.x1 + pHipVector.x * 1000, currentLine.y1 + pHipVector.y * 1000] + const nextCheckPoint = [currentLine.x2, currentLine.y2, currentLine.x2 + nHipVector.x * 1000, currentLine.y2 + nHipVector.y * 1000] + + const findRoofPoints = (points) => { + const hipEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } } + const hipForwardVector = { x: Math.sign(hipEdge.vertex1.x - hipEdge.vertex2.x), y: Math.sign(hipEdge.vertex1.y - hipEdge.vertex2.y) } + const hipBackwardVector = { x: -hipForwardVector.x, y: -hipForwardVector.y } + const isForwardPoints = [] + const isBackwardPoints = [] + + roof.lines.forEach((roofLine) => { + const lineEdge = { vertex1: { x: roofLine.x1, y: roofLine.y1 }, vertex2: { x: roofLine.x2, y: roofLine.y2 } } + const intersect = edgesIntersection(lineEdge, hipEdge) + if (intersect && isPointOnLineNew(roofLine, intersect)) { + const intersectVector = { x: Math.sign(hipEdge.vertex1.x - intersect.x), y: Math.sign(hipEdge.vertex1.y - intersect.y) } + if ( + (intersectVector.x === hipForwardVector.x && intersectVector.y === hipForwardVector.y) || + (intersectVector.x === 0 && intersectVector.y === 0) + ) { + const dx = hipEdge.vertex1.x - intersect.x + const dy = hipEdge.vertex1.y - intersect.y + const length = Math.sqrt(dx * dx + dy * dy) + isForwardPoints.push({ intersect, length }) + } + if (intersectVector.x === hipBackwardVector.x && intersectVector.y === hipBackwardVector.y) { + const dx = hipEdge.vertex2.x - intersect.x + const dy = hipEdge.vertex2.y - intersect.y + const length = Math.sqrt(dx * dx + dy * dy) + isBackwardPoints.push({ intersect, length }) + } + } + }) + isForwardPoints.sort((a, b) => a.length - b.length) + isBackwardPoints.sort((a, b) => a.length - b.length) + return { forward: isForwardPoints[0].intersect, backward: isBackwardPoints[0].intersect } + } + + const pRoofPoints = findRoofPoints(prevCheckPoint) + const nRoofPoints = findRoofPoints(nextCheckPoint) + + let prevHipPoint = { x1: pRoofPoints.backward.x, y1: pRoofPoints.backward.y, x2: pRoofPoints.forward.x, y2: pRoofPoints.forward.y } + let nextHipPoint = { x1: nRoofPoints.backward.x, y1: nRoofPoints.backward.y, x2: nRoofPoints.forward.x, y2: nRoofPoints.forward.y } + + const prevEdge = { vertex1: { x: prevHipPoint.x1, y: prevHipPoint.y1 }, vertex2: { x: prevHipPoint.x2, y: prevHipPoint.y2 } } + const nextEdge = { vertex1: { x: nextHipPoint.x1, y: nextHipPoint.y1 }, vertex2: { x: nextHipPoint.x2, y: nextHipPoint.y2 } } + const intersect = edgesIntersection(prevEdge, nextEdge) + if (intersect && isPointOnLineNew(prevHipPoint, intersect) && isPointOnLineNew(nextHipPoint, intersect)) { + prevHipPoint.x2 = intersect.x + prevHipPoint.y2 = intersect.y + nextHipPoint.x2 = intersect.x + nextHipPoint.y2 = intersect.y + } + + let isRidgePrev, isRidgeNext + let minPrevDist = Infinity + let minNextDist = Infinity + proceedRidges.forEach((ridge) => { + console.log('ridge : ', ridge) + const ridgeEdge = { vertex1: { x: ridge.point.x1, y: ridge.point.y1 }, vertex2: { x: ridge.point.x2, y: ridge.point.y2 } } + const isPrev = edgesIntersection(ridgeEdge, prevEdge) + const isNext = edgesIntersection(ridgeEdge, nextEdge) + if ( + isPrev && + isPointOnLineNew(prevHipPoint, isPrev) && + (ridge.prev === prevI || ridge.prev === nextI || ridge.next === prevI || ridge.next === nextI) + ) { + const distance = Math.sqrt((isPrev.x - prevHipPoint.x1) ** 2 + (isPrev.y - prevHipPoint.y1) ** 2) + if (distance < minPrevDist) { + minPrevDist = distance + isRidgePrev = isPrev + } + } + if ( + isNext && + isPointOnLineNew(nextHipPoint, isNext) && + (ridge.prev === prevI || ridge.prev === nextI || ridge.next === prevI || ridge.next === nextI) + ) { + const distance = Math.sqrt((isNext.x - nextHipPoint.x1) ** 2 + (isNext.y - nextHipPoint.y1) ** 2) + if (distance < minNextDist) { + minNextDist = distance + isRidgeNext = isNext + } + } + }) + + //접하는 라인에서 파생된 마루선이 겹칠경우 라인보정을 종료 + if (isRidgePrev) { + prevHipPoint = { x1: pRoofPoints.backward.x, y1: pRoofPoints.backward.y, x2: pRoofPoints.forward.x, y2: pRoofPoints.forward.y } + } + if (isRidgeNext) { + nextHipPoint = { x1: nRoofPoints.backward.x, y1: nRoofPoints.backward.y, x2: nRoofPoints.forward.x, y2: nRoofPoints.forward.y } + } + + let prevHipLength = Math.sqrt((prevHipPoint.x2 - prevHipPoint.x1) ** 2 + (prevHipPoint.y2 - prevHipPoint.y1) ** 2) + let nextHipLength = Math.sqrt((nextHipPoint.x2 - nextHipPoint.x1) ** 2 + (nextHipPoint.y2 - nextHipPoint.y1) ** 2) + + const alreadyPrev = proceedEaves.filter((line) => almostEqual(line.point.x1, prevHipPoint.x1) && almostEqual(line.point.y1, prevHipPoint.y1)) + const alreadyNext = proceedEaves.filter((line) => almostEqual(line.point.x1, nextHipPoint.x1) && almostEqual(line.point.y1, nextHipPoint.y1)) + if (alreadyPrev.length) { + alreadyPrev.sort((a, b) => a.length - b.length) + const alrPrev = alreadyPrev[0] + if (isRidgePrev) { + alrPrev.prev = prevI + alrPrev.current = currentI + alrPrev.point = prevHipPoint + alrPrev.length = prevHipLength + } else { + if (prevHipLength < alrPrev.length) { + //겹치는데 지금 만들어지는 라인이 더 짧은경우 다른 라인제거 하고 현재 라인을 추가. + alrPrev.prev = prevI + alrPrev.current = currentI + alrPrev.point = prevHipPoint + alrPrev.length = prevHipLength + } else { + prevHipPoint = alrPrev.point + } + } + } else { + proceedEaves.push({ prev: prevI, current: currentI, point: prevHipPoint, length: prevHipLength }) + } + if (alreadyNext.length) { + alreadyNext.sort((a, b) => a.length - b.length) + const alrNext = alreadyNext[0] + if (isRidgeNext) { + alrNext.prev = currentI + alrNext.current = nextI + alrNext.point = nextHipPoint + alrNext.length = nextHipLength + } else { + if (nextHipLength < alrNext.length) { + //겹치는데 지금 만들어지는 라인이 더 짧은경우 다른 라인제거 하고 현재 라인을 추가. + alrNext.prev = currentI + alrNext.current = nextI + alrNext.point = nextHipPoint + alrNext.length = nextHipLength + } else { + nextHipPoint = alrNext.point + } + } + } else { + proceedEaves.push({ prev: currentI, current: nextI, point: nextHipPoint, length: nextHipLength }) + } + + let ridgePoint + if (almostEqual(prevHipPoint.x2, nextHipPoint.x2) && almostEqual(prevHipPoint.y2, nextHipPoint.y2)) { + const ridgeStartPoint = { x: prevHipPoint.x2, y: prevHipPoint.y2 } + + let ridgeVector = { x: Math.sign(clamp01(nextLine.x2 - nextLine.x1)), y: Math.sign(clamp01(nextLine.y2 - nextLine.y1)) } + const midX = (currentLine.x1 + currentLine.x2) / 2 + const midY = (currentLine.y1 + currentLine.y2) / 2 + let checkPoint = { x: midX + ridgeVector.x, y: midY + ridgeVector.y } + if (!checkWallPolygon.inPolygon(checkPoint)) { + ridgeVector = { x: -ridgeVector.x, y: -ridgeVector.y } + } + ridgePoint = { x1: ridgeStartPoint.x, y1: ridgeStartPoint.y, x2: ridgeStartPoint.x + ridgeVector.x, y2: ridgeStartPoint.y + ridgeVector.y } + const ridgeEdge = { vertex1: { x: ridgePoint.x1, y: ridgePoint.y1 }, vertex2: { x: ridgePoint.x2, y: ridgePoint.y2 } } + + let roofIs + let minDistance = Infinity + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersect = edgesIntersection(lineEdge, ridgeEdge) + if (intersect && isPointOnLineNew(line, intersect)) { + const isVector = { x: Math.sign(clamp01(intersect.x - ridgeStartPoint.x)), y: Math.sign(clamp01(intersect.y - ridgeStartPoint.y)) } + const distance = Math.sqrt(Math.pow(ridgeStartPoint.x - intersect.x, 2) + Math.pow(ridgeStartPoint.y - intersect.y, 2)) + if (distance < minDistance && isVector.x === ridgeVector.x && isVector.y === ridgeVector.y) { + minDistance = distance + roofIs = intersect + } + } + }) + if (roofIs) { + ridgePoint.x2 = roofIs.x + ridgePoint.y2 = roofIs.y + } + + const drivePoint = getRidgeDrivePoint([ridgePoint.x1, ridgePoint.y1, ridgePoint.x2, ridgePoint.y2], prevLine, nextLine, baseLines) + const isOverlapBefore = analyze.isHorizontal + ? almostEqual(Math.min(beforePrevLine.x1, beforePrevLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(beforePrevLine.x1, beforePrevLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(beforePrevLine.y1, beforePrevLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(beforePrevLine.y1, beforePrevLine.y2), Math.max(currentLine.y1, currentLine.y2)) + const isOverlapAfter = analyze.isHorizontal + ? almostEqual(Math.min(afterNextLine.x1, afterNextLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(afterNextLine.x1, afterNextLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(afterNextLine.y1, afterNextLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(afterNextLine.y1, afterNextLine.y2), Math.max(currentLine.y1, currentLine.y2)) + if (isOverlapBefore || isOverlapAfter) { + const oppLine = isOverlapBefore ? beforePrevLine : afterNextLine + const otherGable = baseLines.find( + (l) => l !== currentLine && l !== prevLine && l !== nextLine && l !== oppLine && l.attributes.type === LINE_TYPE.WALLLINE.GABLE, + ) + const pointVector = { x: Math.sign(clamp01(ridgePoint.x1 - ridgePoint.x2)), y: Math.sign(clamp01(ridgePoint.y1 - ridgePoint.y2)) } + let offset = 0 + switch (oppLine.attributes.type) { + case LINE_TYPE.WALLLINE.HIPANDGABLE: + offset = oppLine.attributes.width + break + case LINE_TYPE.WALLLINE.JERKINHEAD: + offset = oppLine.attributes.width / 2 + break + default: + break + } + if (!otherGable) { + if (oppLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + const oppIndex = baseLines.findIndex((l) => l === oppLine) + const oppPrevLine = baseLines[(oppIndex - 1 + baseLines.length) % baseLines.length] + const oppNextLine = baseLines[(oppIndex + 1) % baseLines.length] + + if (oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES || oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + const currentLength = Math.sqrt(Math.pow(currentLine.x1 - currentLine.x2, 2) + Math.pow(currentLine.y1 - currentLine.y2, 2)) + const oppLength = Math.sqrt(Math.pow(oppLine.x1 - oppLine.x2, 2) + Math.pow(oppLine.y1 - oppLine.y2, 2)) + if (almostEqual(currentLength, oppLength)) { + if (drivePoint) { + ridgePoint.x2 = drivePoint.x + ridgePoint.y2 = drivePoint.y + } + } else { + ridgePoint.x2 += pointVector.x * offset + ridgePoint.y2 += pointVector.y * offset + } + } else { + ridgePoint.x2 += pointVector.x * offset + ridgePoint.y2 += pointVector.y * offset + } + } + } else if (drivePoint) { + ridgePoint.x2 = drivePoint.x + ridgePoint.y2 = drivePoint.y + } + } else if (drivePoint) { + ridgePoint.x2 = drivePoint.x + ridgePoint.y2 = drivePoint.y + } + } + if (ridgePoint) { + const alreadyRidge = proceedRidges.find( + (line) => + almostEqual(line.point.x1, ridgePoint.x1) && + almostEqual(line.point.y1, ridgePoint.y1) && + almostEqual(line.point.x2, ridgePoint.x2) && + almostEqual(line.point.y2, ridgePoint.y2), + ) + if (!alreadyRidge) { + proceedRidges.push({ prev: prevI, next: nextI, point: ridgePoint }) + } + + /*const checkRidgeStart = new fabric.Circle({ left: ridgePoint.x1, top: ridgePoint.y1, radius: 4, parentId: roofId, name: 'check' }) + const checkRidgeLine = new fabric.Line([ridgePoint.x1, ridgePoint.y1, ridgePoint.x2, ridgePoint.y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkRidgeLine, checkRidgeStart).renderAll()*/ + } + + /*const checkLine1 = new fabric.Line([prevHipPoint.x1, prevHipPoint.y1, prevHipPoint.x2, prevHipPoint.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + const checkLine2 = new fabric.Line([nextHipPoint.x1, nextHipPoint.y1, nextHipPoint.x2, nextHipPoint.y2], { + stroke: 'blue', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine1, checkLine2).renderAll() +*/ + canvas + .getObjects() + .filter((o) => o.name === 'check') + .forEach((o) => canvas.remove(o)) + canvas.renderAll() + }) + + console.log('hipedEaves', hipedEaves) + hipedEaves.forEach((currentLine) => { let prevLine baseLines.forEach((baseLine, index) => { if (baseLine === currentLine) { @@ -2664,32 +3079,146 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { isForwardPoints.sort((a, b) => a.length - b.length) isBackwardPoints.sort((a, b) => a.length - b.length) - let hipPoint = [] + let hipPoint if (isForwardPoints.length > 0 && isBackwardPoints.length > 0) { - hipPoint = [isBackwardPoints[0].intersect.x, isBackwardPoints[0].intersect.y, isForwardPoints[0].intersect.x, isForwardPoints[0].intersect.y] + hipPoint = { + x1: isBackwardPoints[0].intersect.x, + y1: isBackwardPoints[0].intersect.y, + x2: isForwardPoints[0].intersect.x, + y2: isForwardPoints[0].intersect.y, + } } else { if (isBackwardPoints.length === 0 && isForwardPoints.length > 1) { - hipPoint = [isForwardPoints[0].intersect.x, isForwardPoints[0].intersect.y, isForwardPoints[1].intersect.x, isForwardPoints[1].intersect.y] + hipPoint = { + x1: isForwardPoints[0].intersect.x, + y1: isForwardPoints[0].intersect.y, + x2: isForwardPoints[1].intersect.x, + y2: isForwardPoints[1].intersect.y, + } } if (isForwardPoints.length === 0 && isBackwardPoints.length > 1) { - hipPoint = [ - isBackwardPoints[0].intersect.x, - isBackwardPoints[0].intersect.y, - isBackwardPoints[1].intersect.x, - isBackwardPoints[1].intersect.y, - ] + hipPoint = { + x1: isBackwardPoints[0].intersect.x, + y1: isBackwardPoints[0].intersect.y, + x2: isBackwardPoints[1].intersect.x, + y2: isBackwardPoints[1].intersect.y, + } } } - linesAnalysis.push({ - start: { x: hipPoint[0], y: hipPoint[1] }, - end: { x: hipPoint[2], y: hipPoint[3] }, - left: baseLines.findIndex((line) => line === prevLine), - right: baseLines.findIndex((line) => line === currentLine), - type: 'hip', - degree: currentDegree, - }) + if (hipPoint) { + const hipLength = Math.sqrt((hipPoint.x2 - hipPoint.x1) ** 2 + (hipPoint.y2 - hipPoint.y1) ** 2) + const alreadyLine = proceedEaves.filter((line) => almostEqual(line.point.x1, hipPoint.x1) && almostEqual(line.point.y1, hipPoint.y1)) + //겹쳐지는 라인이 있는경우 조정한다. + if (alreadyLine.length === 0) { + linesAnalysis.push({ + start: { x: hipPoint.x1, y: hipPoint.y1 }, + end: { x: hipPoint.x2, y: hipPoint.y2 }, + left: baseLines.findIndex((line) => line === prevLine), + right: baseLines.findIndex((line) => line === currentLine), + type: TYPES.HIP, + degree: currentDegree, + }) + } + } }) + + //작성된 라인을 analyze에 추가한다. + const pIndexEaves = [] + proceedEaves.forEach((eaves, index) => { + if (pIndexEaves.includes(index)) return + const { prev, current, point } = eaves + const currentDegree = getDegreeByChon(baseLines[current].attributes.pitch) + const jointEaves = proceedEaves + .filter((e) => e !== eaves) + .filter((e) => almostEqual(e.point.x2, eaves.point.x2) && almostEqual(e.point.y2, eaves.point.y2)) + if (jointEaves.length === 1 && ridgeEaves.length > 1) { + innerLines.push(drawHipLine([point.x1, point.y1, point.x2, point.y2], canvas, roof, textMode, null, currentDegree, currentDegree)) + pIndexEaves.push(index) + } else if (jointEaves.length === 2) { + console.log('jointEaves : ', jointEaves) + const jointIndex = [index] + let jointLines = [] + jointEaves.forEach((e) => jointIndex.push(proceedEaves.findIndex((p) => p === e))) + const jointVectors = [] + //연결된 라인 생성 + jointIndex.forEach((i) => { + const ev = proceedEaves[i] + const degree = getDegreeByChon(baseLines[ev.current].attributes.pitch) + innerLines.push(drawHipLine([ev.point.x1, ev.point.y1, ev.point.x2, ev.point.y2], canvas, roof, textMode, null, degree, degree)) + pIndexEaves.push(i) + jointLines.push(ev.prev, ev.current) + jointVectors.push({ x: Math.sign(ev.point.x2 - ev.point.x1), y: Math.sign(ev.point.y2 - ev.point.y1) }) + }) + //연결된 지점에서 파생된 마루선 제거 + const removeRidge = proceedRidges.filter((ridge) => almostEqual(ridge.point.x1, eaves.point.x2) && almostEqual(ridge.point.y1, eaves.point.y2)) + proceedRidges = proceedRidges.filter((ridge) => !removeRidge.includes(ridge)) + console.log('pIndexEaves : ', pIndexEaves) + console.log('jointLines : ', jointLines) + console.log('jointVectors : ', jointVectors) + let dneVector = jointVectors.find((v) => !jointVectors.find((v2) => v2.x === -v.x && v2.y === -v.y)) + console.log('dneVector : ', dneVector) + const findRoofEdge = { + vertex1: { x: eaves.point.x2, y: eaves.point.y2 }, + vertex2: { x: eaves.point.x2 + dneVector.x, y: eaves.point.y2 + dneVector.y }, + } + let minDistance = Infinity + let isPoint + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersect = edgesIntersection(lineEdge, findRoofEdge) + if (intersect && isPointOnLineNew(line, intersect)) { + const distance = Math.sqrt(Math.pow(intersect.x - eaves.point.x2, 2) + Math.pow(intersect.y - eaves.point.y2, 2)) + if (distance < minDistance) { + minDistance = distance + isPoint = intersect + } + } + }) + if (isPoint) { + const countMap = new Map() + jointLines.forEach((value) => { + countMap.set(value, (countMap.get(value) || 0) + 1) + }) + + const uniqueLine = jointLines.filter((value) => countMap.get(value) === 1) + console.log('uniqueLine : ', uniqueLine) + linesAnalysis.push({ + start: { x: eaves.point.x2, y: eaves.point.y2 }, + end: { x: isPoint.x, y: isPoint.y }, + left: uniqueLine[0], + right: uniqueLine[1], + type: TYPES.HIP, + degree: currentDegree, + }) + } + console.log('=============') + } else { + linesAnalysis.push({ + start: { x: point.x1, y: point.y1 }, + end: { x: point.x2, y: point.y2 }, + left: prev, + right: current, + type: TYPES.HIP, + degree: currentDegree, + }) + pIndexEaves.push(index) + } + }) + proceedRidges.forEach(({ prev, next, point }) => + linesAnalysis.push({ + start: { x: point.x1, y: point.y1 }, + end: { x: point.x2, y: point.y2 }, + left: prev, + right: next, + type: TYPES.RIDGE, + degree: 0, + }), + ) + + console.log('proceedEaves :', proceedEaves) + console.log('proceedRidges :', proceedRidges) + //4. 반절처(반절마루) 판단, 반절마루는 양옆이 처마여야 한다. jerkinHeads.forEach((currentLine) => { let prevLine, nextLine @@ -2705,6 +3234,16 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { return } + let beforePrevLine, afterNextLine + baseLines.forEach((baseLine, index) => { + if (baseLine === prevLine) { + beforePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] + } + if (baseLine === nextLine) { + afterNextLine = baseLines[(index + 1) % baseLines.length] + } + }) + const analyze = analyzeLine(currentLine) const roofPoints = [analyze.roofLine.x1, analyze.roofLine.y1, analyze.roofLine.x2, analyze.roofLine.y2] @@ -2830,7 +3369,42 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { //마루가 최대로 뻗어나갈수 있는 포인트. null일때는 맞은편 지붕선 까지로 판단한다. const drivePoint = getRidgeDrivePoint(point, prevLine, nextLine, baseLines) - if (drivePoint) { + const isOverlapBefore = analyze.isHorizontal + ? almostEqual(Math.min(beforePrevLine.x1, beforePrevLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(beforePrevLine.x1, beforePrevLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(beforePrevLine.y1, beforePrevLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(beforePrevLine.y1, beforePrevLine.y2), Math.max(currentLine.y1, currentLine.y2)) + const isOverlapAfter = analyze.isHorizontal + ? almostEqual(Math.min(afterNextLine.x1, afterNextLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(afterNextLine.x1, afterNextLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(afterNextLine.y1, afterNextLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(afterNextLine.y1, afterNextLine.y2), Math.max(currentLine.y1, currentLine.y2)) + + if (isOverlapBefore || isOverlapAfter) { + const oppLine = isOverlapBefore ? beforePrevLine : afterNextLine + const otherGable = baseLines.find( + (l) => l !== currentLine && l !== prevLine && l !== nextLine && l !== oppLine && l.attributes.type === LINE_TYPE.WALLLINE.GABLE, + ) + if (!otherGable) { + const pointVector = { x: Math.sign(clamp01(point[0] - point[2])), y: Math.sign(clamp01(point[1] - point[3])) } + let offset = 0 + switch (oppLine.attributes.type) { + case LINE_TYPE.WALLLINE.HIPANDGABLE: + offset = oppLine.attributes.width + break + case LINE_TYPE.WALLLINE.JERKINHEAD: + offset = oppLine.attributes.width / 2 + break + default: + break + } + point[2] += pointVector.x * offset + point[3] += pointVector.y * offset + } else if (drivePoint) { + point[2] = drivePoint.x + point[3] = drivePoint.y + } + } else if (drivePoint) { point[2] = drivePoint.x point[3] = drivePoint.y } @@ -2840,7 +3414,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: point[2], y: point[3] }, left: baseLines.findIndex((line) => line === prevLine), right: baseLines.findIndex((line) => line === nextLine), - type: 'ridge', + type: TYPES.RIDGE, degree: 0, }) } @@ -2862,22 +3436,22 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { return } - 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) } + const prevLineVector = { x: Math.sign(clamp01(prevLine.x1 - prevLine.x2)), y: Math.sign(clamp01(prevLine.y1 - prevLine.y2)) } + const nextLineVector = { x: Math.sign(clamp01(nextLine.x1 - nextLine.x2)), y: Math.sign(clamp01(nextLine.y1 - nextLine.y2)) } const inPolygonPoint = { x: (currentLine.x1 + currentLine.x2) / 2 + Math.sign(nextLine.x2 - nextLine.x1), y: (currentLine.y1 + currentLine.y2) / 2 + Math.sign(nextLine.y2 - nextLine.y1), } - const checkCurrLine = new fabric.Line([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], { + /* const checkCurrLine = new fabric.Line([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check', }) canvas.add(checkCurrLine) - canvas.renderAll() + canvas.renderAll()*/ //좌우 라인이 서로 다른방향일때 if ((prevLineVector.x !== nextLineVector.x || prevLineVector.y !== nextLineVector.y) && checkWallPolygon.inPolygon(inPolygonPoint)) { @@ -2894,392 +3468,711 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } }) - const currentVector = { x: Math.sign(currentLine.x1 - currentLine.x2), y: Math.sign(currentLine.y1 - currentLine.y2) } + const currentVector = { x: Math.sign(clamp01(currentLine.x1 - currentLine.x2)), y: Math.sign(clamp01(currentLine.y1 - currentLine.y2)) } // 마루 진행 최대 길이 let prevDistance = 0, nextDistance = 0 - if (beforePrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { - const beforeVector = { x: Math.sign(beforePrevLine.x1 - beforePrevLine.x2), y: Math.sign(beforePrevLine.y1 - beforePrevLine.y2) } - let distance = Math.sqrt(Math.pow(prevLine.x2 - prevLine.x1, 2) + Math.pow(prevLine.y2 - prevLine.y1, 2)) + currentLine.attributes.offset - if (beforeVector.x === currentVector.x && beforeVector.y === currentVector.y) { - prevDistance = distance - beforePrevLine.attributes.offset - } else { - prevDistance = distance + beforePrevLine.attributes.offset - } - } - if (afterNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { - const afterVector = { x: Math.sign(afterNextLine.x1 - afterNextLine.x2), y: Math.sign(afterNextLine.y1 - afterNextLine.y2) } - let distance = Math.sqrt(Math.pow(nextLine.x2 - nextLine.x1, 2) + Math.pow(nextLine.y2 - nextLine.y1, 2)) + currentLine.attributes.offset - if (afterVector.x === currentVector.x && afterVector.y === currentVector.y) { - nextDistance = distance - afterNextLine.attributes.offset - } else { - nextDistance = distance + afterNextLine.attributes.offset + // 반대쪽 라인이 특정조건일때 마루선을 반대쪽까지로 처리한다.. + const isOverlapBefore = analyze.isHorizontal + ? almostEqual(Math.min(beforePrevLine.x1, beforePrevLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(beforePrevLine.x1, beforePrevLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(beforePrevLine.y1, beforePrevLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(beforePrevLine.y1, beforePrevLine.y2), Math.max(currentLine.y1, currentLine.y2)) + const isOverlapAfter = analyze.isHorizontal + ? almostEqual(Math.min(afterNextLine.x1, afterNextLine.x2), Math.min(currentLine.x1, currentLine.x2)) && + almostEqual(Math.max(afterNextLine.x1, afterNextLine.x2), Math.max(currentLine.x1, currentLine.x2)) + : almostEqual(Math.min(afterNextLine.y1, afterNextLine.y2), Math.min(currentLine.y1, currentLine.y2)) && + almostEqual(Math.max(afterNextLine.y1, afterNextLine.y2), Math.max(currentLine.y1, currentLine.y2)) + + let isOverlap = false + if (isOverlapBefore || isOverlapAfter) { + const oppLine = isOverlapBefore ? beforePrevLine : afterNextLine + const otherGable = baseLines.find( + (l) => l !== currentLine && l !== prevLine && l !== nextLine && l !== oppLine && l.attributes.type === LINE_TYPE.WALLLINE.GABLE, + ) + if (!otherGable) { + isOverlap = true } } - //좌우 선분 중 긴 쪽이 기준선이 된다 - const stdLine = nextDistance <= prevDistance ? prevLine : nextLine - let stdDistance = nextDistance <= prevDistance ? prevDistance : nextDistance - const stdAnalyze = analyzeLine(stdLine) - let stdPoints = [] - - const stdPrevLine = baseLines[(baseLines.indexOf(stdLine) - 1 + baseLines.length) % baseLines.length] - const stdNextLine = baseLines[(baseLines.indexOf(stdLine) + 1) % baseLines.length] - const stdVector = { x: Math.sign(stdLine.x1 - stdLine.x2), y: Math.sign(stdLine.y1 - stdLine.y2) } - const stdPrevVector = { x: Math.sign(stdPrevLine.x1 - stdPrevLine.x2), y: Math.sign(stdPrevLine.y1 - stdPrevLine.y2) } - const stdNextVector = { x: Math.sign(stdNextLine.x1 - stdNextLine.x2), y: Math.sign(stdNextLine.y1 - stdNextLine.y2) } - if (stdPrevVector.x === stdNextVector.x && stdPrevVector.y === stdNextVector.y) { - stdPoints = [ - stdLine.x1 + stdVector.x * stdPrevLine.attributes.offset, - stdLine.y1 + stdVector.y * stdPrevLine.attributes.offset, - stdLine.x2 + stdVector.x * stdNextLine.attributes.offset, - stdLine.y2 + stdVector.y * stdNextLine.attributes.offset, + console.log('isOverlap : ', isOverlap) + if (isOverlap) { + const oppLine = isOverlapBefore ? beforePrevLine : afterNextLine + const cMidX = (currentLine.x1 + currentLine.x2) / 2 + const cMidY = (currentLine.y1 + currentLine.y2) / 2 + const oMidX = (oppLine.x1 + oppLine.x2) / 2 + const oMidY = (oppLine.y1 + oppLine.y2) / 2 + const cOffset = currentLine.attributes.offset + const oOffset = oppLine.attributes.offset + const ridgeVector = { x: Math.sign(clamp01(cMidX - oMidX)), y: Math.sign(clamp01(cMidY - oMidY)) } + const ridgePoint = [ + cMidX + ridgeVector.x * cOffset, + cMidY + ridgeVector.y * cOffset, + oMidX + -ridgeVector.x * oOffset, + oMidY + -ridgeVector.y * oOffset, ] + let offset = 0 + switch (oppLine.attributes.type) { + case LINE_TYPE.WALLLINE.HIPANDGABLE: + offset = oppLine.attributes.width + break + case LINE_TYPE.WALLLINE.JERKINHEAD: + offset = oppLine.attributes.width / 2 + break + default: + break + } + ridgePoint[2] += ridgeVector.x * offset + ridgePoint[3] += ridgeVector.y * offset + + linesAnalysis.push({ + start: { x: ridgePoint[0], y: ridgePoint[1] }, + end: { x: ridgePoint[2], y: ridgePoint[3] }, + left: baseLines.findIndex((line) => line === prevLine), + right: baseLines.findIndex((line) => line === nextLine), + type: TYPES.RIDGE, + degree: 0, + }) } else { - stdPoints = [ - stdLine.x1 + stdVector.x * stdPrevLine.attributes.offset, - stdLine.y1 + stdVector.y * stdPrevLine.attributes.offset, - stdLine.x2 + -stdVector.x * stdNextLine.attributes.offset, - stdLine.y2 + -stdVector.y * stdNextLine.attributes.offset, - ] - } - const checkLine = new fabric.Line([stdLine.x1, stdLine.y1, stdLine.x2, stdLine.y2], { - stroke: 'red', - strokeWidth: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLine) - canvas.renderAll() - - //기준지붕선의 반대쪽선 - const oppositeLine = [] - - const startX = Math.min(stdLine.x1, stdLine.x2) - const endX = Math.max(stdLine.x1, stdLine.x2) - const startY = Math.min(stdLine.y1, stdLine.y2) - const endY = Math.max(stdLine.y1, stdLine.y2) - - baseLines - .filter((line) => line !== stdLine && line !== currentLine) - .filter((line) => { - const vector = { x: Math.sign(stdLine.x1 - stdLine.x2), y: Math.sign(stdLine.y1 - stdLine.y2) } - const lineVector = { x: Math.sign(line.x1 - line.x2), y: Math.sign(line.y1 - line.y2) } - return (vector.x === lineVector.x && vector.y !== lineVector.y) || (vector.x !== lineVector.x && vector.y === lineVector.y) - }) - .forEach((line) => { - const lineStartX = Math.min(line.x1, line.x2) - const lineEndX = Math.max(line.x1, line.x2) - const lineStartY = Math.min(line.y1, line.y2) - const lineEndY = Math.max(line.y1, line.y2) - - if (stdAnalyze.isHorizontal) { - //full overlap - if (lineStartX <= startX && endX <= lineEndX) { - oppositeLine.push({ line, distance: Math.abs(lineStartY - startY) }) - } - if ( - (startX < lineStartX && lineStartX < endX) || - (startX < lineStartX && lineEndX < endX) || - (lineStartX === startX && lineEndX <= endX) || - (lineEndX === endX && lineStartX <= startX) - ) { - oppositeLine.push({ line, distance: Math.abs(lineStartY - startY) }) - } + if (beforePrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + const beforeVector = { + x: Math.sign(clamp01(beforePrevLine.x1 - beforePrevLine.x2)), + y: Math.sign(clamp01(beforePrevLine.y1 - beforePrevLine.y2)), } - if (stdAnalyze.isVertical) { - //full overlap - if (lineStartY <= startY && endY <= lineEndY) { - oppositeLine.push({ line, distance: Math.abs(lineStartX - startX) }) - } - if ( - (startY < lineStartY && lineStartY < endY) || - (startY < lineEndY && lineEndY < endY) || - (lineStartY === startY && lineEndY <= endY) || - (lineEndY === endY && lineStartY <= startY) - ) { - oppositeLine.push({ line, distance: Math.abs(lineStartX - startX) }) - } - } - }) - - if (oppositeLine.length > 0) { - const ridgePoints = [] - //지붕선 출발 지점 확인을 위한 기준 포인트 - let ridgeStdPoint = { x: (currentLine.x1 + currentLine.x2) / 2, y: (currentLine.y1 + currentLine.y2) / 2 } - - oppositeLine.sort((a, b) => a.distance - b.distance) - oppositeLine.forEach((opposite) => { - const oppLine = opposite.line - const checkOppLine = new fabric.Line([oppLine.x1, oppLine.y1, oppLine.x2, oppLine.y2], { - stroke: 'yellow', - strokeW: 4, - parentId: roofId, - name: 'check', - }) - canvas.add(checkOppLine) - canvas.renderAll() - - const oppIndex = baseLines.findIndex((line) => line === oppLine) - //마주하는 라인의 이전 다음 라인. - const oppPrevLine = baseLines[(oppIndex - 1 + baseLines.length) % baseLines.length] - const oppNextLine = baseLines[(oppIndex + 1) % baseLines.length] - const oppAnalyze = analyzeLine(oppLine) - const oppVector = { x: Math.sign(oppLine.x2 - oppLine.x1), y: Math.sign(oppLine.y2 - oppLine.y1) } - let ridgePoint - - const oppPrevVector = { x: Math.sign(oppPrevLine.x1 - oppPrevLine.x2), y: Math.sign(oppPrevLine.y1 - oppPrevLine.y2) } - const oppNextVector = { x: Math.sign(oppNextLine.x1 - oppNextLine.x2), y: Math.sign(oppNextLine.y1 - oppNextLine.y2) } - if (oppPrevVector.x === oppNextVector.x && oppPrevVector.y === oppNextVector.y) { - const addOffsetX1 = oppPrevVector.y * oppPrevLine.attributes.offset - const addOffsetY1 = -oppPrevVector.x * oppPrevLine.attributes.offset - const addOffsetX2 = oppPrevVector.y * oppNextLine.attributes.offset - const addOffsetY2 = -oppPrevVector.x * oppNextLine.attributes.offset - ridgePoint = [oppLine.x1 + addOffsetX1, oppLine.y1 + addOffsetY1, oppLine.x2 + addOffsetX2, oppLine.y2 + addOffsetY2] + prevDistance = Math.sqrt(Math.pow(prevLine.x2 - prevLine.x1, 2) + Math.pow(prevLine.y2 - prevLine.y1, 2)) + currentLine.attributes.offset + if (beforeVector.x === currentVector.x && beforeVector.y === currentVector.y) { + prevDistance = prevDistance - beforePrevLine.attributes.offset } else { - const inPolygonPoint = { - x: (oppLine.x1 + oppLine.x2) / 2 + Math.sign(oppNextLine.x2 - oppNextLine.x1), - y: (oppLine.y1 + oppLine.y2) / 2 + Math.sign(oppNextLine.y2 - oppNextLine.y1), - } - if (checkWallPolygon.inPolygon(inPolygonPoint)) { - ridgePoint = [ - oppLine.x1 + -oppVector.x * oppPrevLine.attributes.offset, - oppLine.y1 + -oppVector.y * oppPrevLine.attributes.offset, - oppLine.x2 + oppVector.x * oppNextLine.attributes.offset, - oppLine.y2 + oppVector.y * oppNextLine.attributes.offset, - ] + prevDistance = prevDistance + beforePrevLine.attributes.offset + } + } else { + prevDistance = + Math.sqrt(Math.pow(prevLine.x2 - prevLine.x1, 2) + Math.pow(prevLine.y2 - prevLine.y1, 2)) + + currentLine.attributes.offset + + beforePrevLine.attributes.offset + } + + if (afterNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + const afterVector = { + x: Math.sign(clamp01(afterNextLine.x1 - afterNextLine.x2)), + y: Math.sign(clamp01(afterNextLine.y1 - afterNextLine.y2)), + } + nextDistance = Math.sqrt(Math.pow(nextLine.x2 - nextLine.x1, 2) + Math.pow(nextLine.y2 - nextLine.y1, 2)) + currentLine.attributes.offset + if (afterVector.x === currentVector.x && afterVector.y === currentVector.y) { + nextDistance = nextDistance - afterNextLine.attributes.offset + } else { + nextDistance = nextDistance + afterNextLine.attributes.offset + } + } else { + nextDistance = + Math.sqrt(Math.pow(nextLine.x2 - nextLine.x1, 2) + Math.pow(nextLine.y2 - nextLine.y1, 2)) + + currentLine.attributes.offset + + afterNextLine.attributes.offset + } + + //좌우 선분 의 이전 다음 선이 케라바인경우 둘 중 긴 쪽이 기준선이 된다. 아닌경우 짧은쪽 + let stdLine + let stdFindOppVector + if (beforePrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE && afterNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + if (nextDistance <= prevDistance) { + stdLine = prevLine + if (prevLineVector.x === 0) { + stdFindOppVector = { x: Math.sign(clamp01(prevLine.x1 - nextLine.x1)), y: 0 } } else { - ridgePoint = [ - oppLine.x1 + oppVector.x * oppPrevLine.attributes.offset, - oppLine.y1 + oppVector.y * oppPrevLine.attributes.offset, - oppLine.x2 + -oppVector.x * oppNextLine.attributes.offset, - oppLine.y2 + -oppVector.y * oppNextLine.attributes.offset, + stdFindOppVector = { x: 0, y: Math.sign(clamp01(prevLine.y1 - nextLine.y1)) } + } + } else { + stdLine = nextLine + if (nextLineVector.x === 0) { + stdFindOppVector = { x: Math.sign(clamp01(nextLine.x1 - prevLine.x1)), y: 0 } + } else { + stdFindOppVector = { x: 0, y: Math.sign(clamp01(nextLine.y1 - prevLine.y1)) } + } + } + } else { + if (nextDistance <= prevDistance) { + stdLine = nextLine + if (nextLineVector.x === 0) { + stdFindOppVector = { x: Math.sign(clamp01(nextLine.x1 - prevLine.x1)), y: 0 } + } else { + stdFindOppVector = { x: 0, y: Math.sign(clamp01(nextLine.y1 - prevLine.y1)) } + } + } else { + stdLine = prevLine + if (prevLineVector.x === 0) { + stdFindOppVector = { x: Math.sign(clamp01(prevLine.x1 - nextLine.x1)), y: 0 } + } else { + stdFindOppVector = { x: 0, y: Math.sign(clamp01(prevLine.y1 - nextLine.y1)) } + } + } + } + let stdDistance = nextDistance <= prevDistance ? prevDistance : nextDistance + const stdAnalyze = analyzeLine(stdLine) + let stdPoints = [] + + const stdPrevLine = baseLines[(baseLines.indexOf(stdLine) - 1 + baseLines.length) % baseLines.length] + const stdNextLine = baseLines[(baseLines.indexOf(stdLine) + 1) % baseLines.length] + const stdVector = { x: Math.sign(clamp01(stdLine.x1 - stdLine.x2)), y: Math.sign(clamp01(stdLine.y1 - stdLine.y2)) } + const stdPrevVector = { x: Math.sign(clamp01(stdPrevLine.x1 - stdPrevLine.x2)), y: Math.sign(clamp01(stdPrevLine.y1 - stdPrevLine.y2)) } + const stdNextVector = { x: Math.sign(clamp01(stdNextLine.x1 - stdNextLine.x2)), y: Math.sign(clamp01(stdNextLine.y1 - stdNextLine.y2)) } + let stdAdjustVector = { x: 0, y: 0 } + + if (stdPrevVector.x === stdNextVector.x && stdPrevVector.y === stdNextVector.y) { + if (stdAnalyze.isHorizontal) { + if (stdVector.x === 1) { + if (stdPrevLine.y1 > stdNextLine.y1) { + stdPoints.push(stdLine.x1 + stdPrevLine.attributes.offset, stdLine.y1) + if (stdNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x2 + stdNextLine.attributes.offset, stdLine.y2) + } else { + stdPoints.push(stdLine.x2, stdLine.y2) + } + } else { + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x1 - stdPrevLine.attributes.offset, stdLine.y1) + } else { + stdPoints.push(stdLine.x1, stdLine.y1) + } + stdPoints.push(stdLine.x2 - stdNextLine.attributes.offset, stdLine.y2) + } + } else { + if (stdPrevLine.y1 < stdNextLine.y1) { + stdPoints.push(stdLine.x1 - stdPrevLine.attributes.offset, stdLine.y1) + if (stdNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x2 - stdNextLine.attributes.offset, stdLine.y2) + } else { + stdPoints.push(stdLine.x2, stdLine.y2) + } + } else { + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x1 + stdPrevLine.attributes.offset, stdLine.y1) + } else { + stdPoints.push(stdLine.x1, stdLine.y1) + } + stdPoints.push(stdLine.x2 + stdNextLine.attributes.offset, stdLine.y2) + } + } + } + if (stdAnalyze.isVertical) { + if (stdVector.y === 1) { + if (stdPrevLine.x1 > stdNextLine.x1) { + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x1, stdLine.y1 - stdPrevLine.attributes.offset) + } else { + stdPoints.push(stdLine.x1, stdLine.y1) + } + stdPoints.push(stdLine.x2, stdLine.y2 - stdNextLine.attributes.offset) + } else { + stdPoints.push(stdLine.x1, stdLine.y1 + stdPrevLine.attributes.offset) + if (stdNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x2, stdLine.y2 + stdNextLine.attributes.offset) + } else { + stdPoints.push(stdLine.x2, stdLine.y2) + } + } + } else { + if (stdPrevLine.x1 < stdNextLine.x1) { + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x1, stdLine.y1 + stdPrevLine.attributes.offset) + } else { + stdPoints.push(stdLine.x1, stdLine.y1) + } + stdPoints.push(stdLine.x2, stdLine.y2 + stdNextLine.attributes.offset) + } else { + stdPoints.push(stdLine.x1, stdLine.y1 - stdPrevLine.attributes.offset) + if (stdNextLine.attributes.type === LINE_TYPE.WALLLINE.GABLE) { + stdPoints.push(stdLine.x2, stdLine.y2 - stdNextLine.attributes.offset) + } else { + stdPoints.push(stdLine.x2, stdLine.y2) + } + } + } + } + console.log('stdAdjustVector', stdAdjustVector) + } else { + const stdAddPrevVector = { x: Math.sign(clamp01(stdLine.x1 - stdLine.x2)), y: Math.sign(clamp01(stdLine.y1 - stdLine.y2)) } + const stdAddNextVector = { x: Math.sign(clamp01(stdLine.x2 - stdLine.x1)), y: Math.sign(clamp01(stdLine.y2 - stdLine.y1)) } + console.log('stdAddPrevVector', stdAddPrevVector) + stdPoints = [ + stdLine.x1 + stdAddPrevVector.x * stdPrevLine.attributes.offset, + stdLine.y1 + stdAddPrevVector.y * stdPrevLine.attributes.offset, + stdLine.x2 + stdAddNextVector.x * stdNextLine.attributes.offset, + stdLine.y2 + stdAddNextVector.y * stdNextLine.attributes.offset, + ] + } + /*const checkLine = new fabric.Line([stdLine.x1, stdLine.y1, stdLine.x2, stdLine.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + const checkLine2 = new fabric.Line(stdPoints, { + stroke: 'green', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine, checkLine2).renderAll() +*/ + //기준지붕선의 반대쪽선 + const oppositeLine = [] + + const startX = Math.min(stdLine.x1, stdLine.x2) + const endX = Math.max(stdLine.x1, stdLine.x2) + const startY = Math.min(stdLine.y1, stdLine.y2) + const endY = Math.max(stdLine.y1, stdLine.y2) + console.log('stdFindOppVector', stdFindOppVector) + baseLines + .filter((line) => line !== stdLine && line !== currentLine && line.attributes.type !== LINE_TYPE.WALLLINE.SHED) + .filter((line) => { + const lineVector = { x: Math.sign(clamp01(line.x1 - line.x2)), y: Math.sign(clamp01(line.y1 - line.y2)) } + let oppVector = { x: 0, y: 0 } + if (stdVector.x === 0) { + oppVector = { x: Math.sign(clamp01(stdLine.x1 - line.x1)), y: 0 } + } + if (stdVector.y === 0) { + oppVector = { x: 0, y: Math.sign(clamp01(stdLine.y1 - line.y1)) } + } + const rightDirection = + (stdVector.x === lineVector.x && stdVector.y !== lineVector.y) || (stdVector.x !== lineVector.x && stdVector.y === lineVector.y) + const rightOpp = stdFindOppVector.x === oppVector.x && stdFindOppVector.y === oppVector.y + return rightDirection && rightOpp + }) + .forEach((line) => { + const lineStartX = Math.min(line.x1, line.x2) + const lineEndX = Math.max(line.x1, line.x2) + const lineStartY = Math.min(line.y1, line.y2) + const lineEndY = Math.max(line.y1, line.y2) + + if (stdAnalyze.isHorizontal) { + //full overlap + if (lineStartX <= startX && endX <= lineEndX) { + oppositeLine.push({ line, distance: Math.abs(lineStartY - startY) }) + } else if ( + (startX < lineStartX && lineStartX < endX) || + (startX < lineStartX && lineEndX < endX) || + (lineStartX === startX && lineEndX <= endX) || + (lineEndX === endX && lineStartX <= startX) + ) { + oppositeLine.push({ line, distance: Math.abs(lineStartY - startY) }) + } + } + if (stdAnalyze.isVertical) { + //full overlap + if (lineStartY <= startY && endY <= lineEndY) { + oppositeLine.push({ line, distance: Math.abs(lineStartX - startX) }) + } else if ( + (startY < lineStartY && lineStartY < endY) || + (startY < lineEndY && lineEndY < endY) || + (lineStartY === startY && lineEndY <= endY) || + (lineEndY === endY && lineStartY <= startY) + ) { + oppositeLine.push({ line, distance: Math.abs(lineStartX - startX) }) + } + } + }) + console.log('oppositeLine', oppositeLine) + if (oppositeLine.length > 0) { + const ridgePoints = [] + //지붕선 출발 지점 확인을 위한 기준 포인트 + let ridgeStdPoint = { x: (currentLine.x1 + currentLine.x2) / 2, y: (currentLine.y1 + currentLine.y2) / 2 } + + oppositeLine.sort((a, b) => a.distance - b.distance) + oppositeLine.forEach((opposite) => { + const oppLine = opposite.line + /*const checkOppLine = new fabric.Line([oppLine.x1, oppLine.y1, oppLine.x2, oppLine.y2], { + stroke: 'yellow', + strokeWidth: 6, + parentId: roofId, + name: 'check', + }) + canvas.add(checkOppLine) + canvas.renderAll()*/ + + const oppIndex = baseLines.findIndex((line) => line === oppLine) + //마주하는 라인의 이전 다음 라인. + const oppPrevLine = baseLines[(oppIndex - 1 + baseLines.length) % baseLines.length] + const oppNextLine = baseLines[(oppIndex + 1) % baseLines.length] + const oppAnalyze = analyzeLine(oppLine) + const oppVector = { x: Math.sign(oppLine.x2 - oppLine.x1), y: Math.sign(oppLine.y2 - oppLine.y1) } + let ridgePoint + + const oppPrevVector = { x: Math.sign(oppPrevLine.x1 - oppPrevLine.x2), y: Math.sign(oppPrevLine.y1 - oppPrevLine.y2) } + const oppNextVector = { x: Math.sign(oppNextLine.x1 - oppNextLine.x2), y: Math.sign(oppNextLine.y1 - oppNextLine.y2) } + if (oppPrevVector.x === oppNextVector.x && oppPrevVector.y === oppNextVector.y) { + const addOffsetX1 = oppPrevVector.y * oppPrevLine.attributes.offset + const addOffsetY1 = -oppPrevVector.x * oppPrevLine.attributes.offset + const addOffsetX2 = oppPrevVector.y * oppNextLine.attributes.offset + const addOffsetY2 = -oppPrevVector.x * oppNextLine.attributes.offset + ridgePoint = [oppLine.x1 + addOffsetX1, oppLine.y1 + addOffsetY1, oppLine.x2 + addOffsetX2, oppLine.y2 + addOffsetY2] + } else { + const inPolygonPoint = { + x: (oppLine.x1 + oppLine.x2) / 2 + Math.sign(oppNextLine.x2 - oppNextLine.x1), + y: (oppLine.y1 + oppLine.y2) / 2 + Math.sign(oppNextLine.y2 - oppNextLine.y1), + } + if (checkWallPolygon.inPolygon(inPolygonPoint)) { + ridgePoint = [ + oppLine.x1 + -oppVector.x * oppPrevLine.attributes.offset, + oppLine.y1 + -oppVector.y * oppPrevLine.attributes.offset, + oppLine.x2 + oppVector.x * oppNextLine.attributes.offset, + oppLine.y2 + oppVector.y * oppNextLine.attributes.offset, + ] + } else { + ridgePoint = [ + oppLine.x1 + oppVector.x * oppPrevLine.attributes.offset, + oppLine.y1 + oppVector.y * oppPrevLine.attributes.offset, + oppLine.x2 + -oppVector.x * oppNextLine.attributes.offset, + oppLine.y2 + -oppVector.y * oppNextLine.attributes.offset, + ] + } + } + if (stdAnalyze.isHorizontal) { + ridgePoint[1] = (stdLine.y1 + oppLine.y1) / 2 + ridgePoint[3] = (stdLine.y2 + oppLine.y2) / 2 + } + if (stdAnalyze.isVertical) { + ridgePoint[0] = (stdLine.x1 + oppLine.x1) / 2 + ridgePoint[2] = (stdLine.x2 + oppLine.x2) / 2 + } + + // 지붕선 출발 지점과의 거리를 통해 가까운쪽이 start point로 처리. + /*const distRidgeStandard1 = Math.sqrt(Math.pow(ridgeStdPoint.x - ridgePoint[0], 2) + Math.pow(ridgeStdPoint.y - ridgePoint[1], 2)) + const distRidgeStandard2 = Math.sqrt(Math.pow(ridgeStdPoint.x - ridgePoint[2], 2) + Math.pow(ridgeStdPoint.y - ridgePoint[3], 2)) + if (distRidgeStandard1 > distRidgeStandard2) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + }*/ + + if ( + (oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES || oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) && + !(oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) + ) { + const oppLineLength = Math.sqrt(Math.pow(oppLine.x2 - oppLine.x1, 2) + Math.pow(oppLine.y2 - oppLine.y1, 2)) + const stdLineLength = Math.sqrt(Math.pow(stdLine.x2 - stdLine.x1, 2) + Math.pow(stdLine.y2 - stdLine.y1, 2)) + //기준선이 반대선보다 길이가 짧을때는 추녀마루에 대한 교점 처리 패스 + if (stdLineLength >= oppLineLength) { + if (oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + //처마라인이 아닌 반대쪽부터 마루선을 시작하도록 포인트 변경 + const oppNextAnalyze = analyzeLine(oppNextLine) + if (oppNextAnalyze.isHorizontal) { + const dist1 = Math.abs(oppNextLine.y1 - ridgePoint[1]) + const dist2 = Math.abs(oppNextLine.y1 - ridgePoint[3]) + if (dist1 > dist2) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + } + } + if (oppNextAnalyze.isVertical) { + const dist1 = Math.abs(oppNextLine.x1 - ridgePoint[0]) + const dist2 = Math.abs(oppNextLine.x1 - ridgePoint[2]) + if (dist1 > dist2) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + } + } + + const checkEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } + const checkVector = getHalfAngleVector(oppLine, oppPrevLine) + const checkPoint = { + x: oppLine.x1 + (checkVector.x * 10) / 2, + y: oppLine.y1 + (checkVector.y * 10) / 2, + } + + let hipVector + if (checkWallPolygon.inPolygon(checkPoint)) { + hipVector = { x: checkVector.x, y: checkVector.y } + } else { + hipVector = { x: -checkVector.x, y: -checkVector.y } + } + + const ridgeVector = { x: Math.sign(ridgePoint[0] - ridgePoint[2]), y: Math.sign(ridgePoint[1] - ridgePoint[3]) } + const hipEdge = { vertex1: { x: oppLine.x1, y: oppLine.y1 }, vertex2: { x: oppLine.x1 + hipVector.x, y: oppLine.y1 + hipVector.y } } + const intersect = edgesIntersection(hipEdge, checkEdge) + if (intersect) { + const intersectVector = { x: Math.sign(ridgePoint[0] - intersect.x), y: Math.sign(ridgePoint[1] - intersect.y) } + if (ridgeVector.x === intersectVector.x && ridgeVector.y === intersectVector.y) { + ridgePoint = [ridgePoint[0], ridgePoint[1], intersect.x, intersect.y] + } else { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[2], ridgePoint[3]] + } + } + } + if (oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + //처마라인이 아닌 반대쪽부터 마루선을 시작하도록 포인트 변경 + const oppPrevAnalyze = analyzeLine(oppPrevLine) + if (oppPrevAnalyze.isHorizontal) { + const dist1 = Math.abs(oppPrevLine.y1 - ridgePoint[1]) + const dist2 = Math.abs(oppPrevLine.y1 - ridgePoint[3]) + if (dist1 > dist2) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + } + } + if (oppPrevAnalyze.isVertical) { + const dist1 = Math.abs(oppPrevLine.x1 - ridgePoint[0]) + const dist2 = Math.abs(oppPrevLine.x1 - ridgePoint[2]) + if (dist1 > dist2) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + } + } + const checkEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } + const checkVector = getHalfAngleVector(oppLine, oppNextLine) + const checkPoint = { x: oppLine.x2 + (checkVector.x * 10) / 2, y: oppLine.y2 + (checkVector.y * 10) / 2 } + + let hipVector + if (checkWallPolygon.inPolygon(checkPoint)) { + hipVector = { x: checkVector.x, y: checkVector.y } + } else { + hipVector = { x: -checkVector.x, y: -checkVector.y } + } + + const ridgeVector = { x: Math.sign(ridgePoint[0] - ridgePoint[2]), y: Math.sign(ridgePoint[1] - ridgePoint[3]) } + const hipEdge = { vertex1: { x: oppLine.x2, y: oppLine.y2 }, vertex2: { x: oppLine.x2 + hipVector.x, y: oppLine.y2 + hipVector.y } } + const intersect = edgesIntersection(hipEdge, checkEdge) + if (intersect) { + const intersectVector = { x: Math.sign(ridgePoint[0] - intersect.x), y: Math.sign(ridgePoint[1] - intersect.y) } + if (ridgeVector.x === intersectVector.x && ridgeVector.y === intersectVector.y) { + ridgePoint = [ridgePoint[0], ridgePoint[1], intersect.x, intersect.y] + } else { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[2], ridgePoint[3]] + } + } + } + } else { + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES || stdNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + const vectorLine = stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES ? stdPrevLine : stdNextLine + let driveVector = getHalfAngleVector(vectorLine, stdLine) + const allPoints = [ + { x: stdLine.x1, y: stdLine.y1 }, + { x: stdLine.x2, y: stdLine.y2 }, + { x: vectorLine.x1, y: vectorLine.y1 }, + { x: vectorLine.x2, y: vectorLine.y2 }, + ] + let startPoint + for (let i = 0; i < allPoints.length; i++) { + const point = allPoints[i] + for (let j = i + 1; j < allPoints.length; j++) { + if (i === j) continue + const point2 = allPoints[j] + if (almostEqual(point.x, point2.x) && almostEqual(point.y, point2.y)) { + startPoint = point2 + break + } + } + if (startPoint) break + } + + const checkDrivePoint = { x: startPoint.x + driveVector.x * 5, y: startPoint.y + driveVector.y * 5 } + if (!checkWallPolygon.inPolygon(checkDrivePoint)) { + driveVector = { x: -driveVector.x, y: -driveVector.y } + } + + const driveEdge = { + vertex1: { x: startPoint.x, y: startPoint.y }, + vertex2: { x: startPoint.x + driveVector.x, y: startPoint.y + driveVector.y }, + } + const ridgeEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } + const intersect = edgesIntersection(driveEdge, ridgeEdge) + const rLength = Math.sqrt(Math.pow(ridgePoint[2] - ridgePoint[0], 2) + Math.pow(ridgePoint[3] - ridgePoint[1], 2)) + if (intersect) { + const length1 = Math.sqrt(Math.pow(intersect.x - ridgePoint[0], 2) + Math.pow(intersect.y - ridgePoint[1], 2)) + const length2 = Math.sqrt(Math.pow(intersect.x - ridgePoint[2], 2) + Math.pow(intersect.y - ridgePoint[3], 2)) + if (rLength < length1 && rLength < length2) { + if (length1 >= length2) { + ridgePoint = [ridgePoint[0], ridgePoint[1], intersect.x, intersect.y] + } else { + ridgePoint = [ridgePoint[2], ridgePoint[3], intersect.x, intersect.y] + } + } + } + } + } + } + + if (stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES || stdNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { + const vectorLine = stdPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES ? stdPrevLine : stdNextLine + let driveVector = getHalfAngleVector(vectorLine, stdLine) + const allPoints = [ + { x: stdLine.x1, y: stdLine.y1 }, + { x: stdLine.x2, y: stdLine.y2 }, + { x: vectorLine.x1, y: vectorLine.y1 }, + { x: vectorLine.x2, y: vectorLine.y2 }, ] - } - } - if (stdAnalyze.isHorizontal) { - ridgePoint[1] = (stdLine.y1 + oppLine.y1) / 2 - ridgePoint[3] = (stdLine.y2 + oppLine.y2) / 2 - } - if (stdAnalyze.isVertical) { - ridgePoint[0] = (stdLine.x1 + oppLine.x1) / 2 - ridgePoint[2] = (stdLine.x2 + oppLine.x2) / 2 - } - - //지붕선 출발 지점과의 거리를 통해 가까운쪽이 start point로 처리. - // const distRidgeStandard1 = Math.sqrt(Math.pow(ridgeStdPoint.x - ridgePoint[0], 2) + Math.pow(ridgeStdPoint.y - ridgePoint[1], 2)) - // const distRidgeStandard2 = Math.sqrt(Math.pow(ridgeStdPoint.x - ridgePoint[2], 2) + Math.pow(ridgeStdPoint.y - ridgePoint[3], 2)) - // if (distRidgeStandard1 > distRidgeStandard2) { - // ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] - // } - - if ( - (oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES || oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) && - !(oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES && oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) - ) { - const oppLineLength = Math.sqrt(Math.pow(oppLine.x2 - oppLine.x1, 2) + Math.pow(oppLine.y2 - oppLine.y1, 2)) - const stdLineLength = Math.sqrt(Math.pow(stdLine.x2 - stdLine.x1, 2) + Math.pow(stdLine.y2 - stdLine.y1, 2)) - //기준선이 반대선보다 길이가 짧을때는 추녀마루에 대한 교점 처리 패스 - if (stdLineLength >= oppLineLength) { - if (oppPrevLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { - //처마라인이 아닌 반대쪽부터 마루선을 시작하도록 포인트 변경 - const oppNextAnalyze = analyzeLine(oppNextLine) - if (oppNextAnalyze.isHorizontal) { - const dist1 = Math.abs(oppNextLine.y1 - ridgePoint[1]) - const dist2 = Math.abs(oppNextLine.y1 - ridgePoint[3]) - if (dist1 > dist2) { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] - } - } - if (oppNextAnalyze.isVertical) { - const dist1 = Math.abs(oppNextLine.x1 - ridgePoint[0]) - const dist2 = Math.abs(oppNextLine.x1 - ridgePoint[2]) - if (dist1 > dist2) { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] - } - } - - const checkEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } - const checkVector = getHalfAngleVector(oppLine, oppPrevLine) - const checkPoint = { - x: oppLine.x1 + (checkVector.x * 10) / 2, - y: oppLine.y1 + (checkVector.y * 10) / 2, - } - - let hipVector - if (checkWallPolygon.inPolygon(checkPoint)) { - hipVector = { x: checkVector.x, y: checkVector.y } - } else { - hipVector = { x: -checkVector.x, y: -checkVector.y } - } - - const ridgeVector = { x: Math.sign(ridgePoint[0] - ridgePoint[2]), y: Math.sign(ridgePoint[1] - ridgePoint[3]) } - const hipEdge = { vertex1: { x: oppLine.x1, y: oppLine.y1 }, vertex2: { x: oppLine.x1 + hipVector.x, y: oppLine.y1 + hipVector.y } } - const intersect = edgesIntersection(hipEdge, checkEdge) - if (intersect) { - const intersectVector = { x: Math.sign(ridgePoint[0] - intersect.x), y: Math.sign(ridgePoint[1] - intersect.y) } - if (ridgeVector.x === intersectVector.x && ridgeVector.y === intersectVector.y) { - ridgePoint = [ridgePoint[0], ridgePoint[1], intersect.x, intersect.y] - } else { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[2], ridgePoint[3]] + let startPoint + for (let i = 0; i < allPoints.length; i++) { + const point = allPoints[i] + for (let j = i + 1; j < allPoints.length; j++) { + if (i === j) continue + const point2 = allPoints[j] + if (almostEqual(point.x, point2.x) && almostEqual(point.y, point2.y)) { + startPoint = point2 + break } } + if (startPoint) break } - if (oppNextLine.attributes.type === LINE_TYPE.WALLLINE.EAVES) { - //처마라인이 아닌 반대쪽부터 마루선을 시작하도록 포인트 변경 - const oppPrevAnalyze = analyzeLine(oppPrevLine) - if (oppPrevAnalyze.isHorizontal) { - const dist1 = Math.abs(oppPrevLine.y1 - ridgePoint[1]) - const dist2 = Math.abs(oppPrevLine.y1 - ridgePoint[3]) - if (dist1 > dist2) { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] - } - } - if (oppPrevAnalyze.isVertical) { - const dist1 = Math.abs(oppPrevLine.x1 - ridgePoint[0]) - const dist2 = Math.abs(oppPrevLine.x1 - ridgePoint[2]) - if (dist1 > dist2) { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] - } - } - const checkEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } - const checkVector = getHalfAngleVector(oppLine, oppNextLine) - const checkPoint = { x: oppLine.x2 + (checkVector.x * 10) / 2, y: oppLine.y2 + (checkVector.y * 10) / 2 } - let hipVector - if (checkWallPolygon.inPolygon(checkPoint)) { - hipVector = { x: checkVector.x, y: checkVector.y } - } else { - hipVector = { x: -checkVector.x, y: -checkVector.y } - } - - const ridgeVector = { x: Math.sign(ridgePoint[0] - ridgePoint[2]), y: Math.sign(ridgePoint[1] - ridgePoint[3]) } - const hipEdge = { vertex1: { x: oppLine.x2, y: oppLine.y2 }, vertex2: { x: oppLine.x2 + hipVector.x, y: oppLine.y2 + hipVector.y } } - const intersect = edgesIntersection(hipEdge, checkEdge) - if (intersect) { - const intersectVector = { x: Math.sign(ridgePoint[0] - intersect.x), y: Math.sign(ridgePoint[1] - intersect.y) } - if (ridgeVector.x === intersectVector.x && ridgeVector.y === intersectVector.y) { - ridgePoint = [ridgePoint[0], ridgePoint[1], intersect.x, intersect.y] - } else { - ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[2], ridgePoint[3]] - } - } + const checkDrivePoint = { x: startPoint.x + driveVector.x * 5, y: startPoint.y + driveVector.y * 5 } + if (!checkWallPolygon.inPolygon(checkDrivePoint)) { + driveVector = { x: -driveVector.x, y: -driveVector.y } } - } - } - const stdMinX = Math.min(stdPoints[0], stdPoints[2]) - const stdMaxX = Math.max(stdPoints[0], stdPoints[2]) - const stdMinY = Math.min(stdPoints[1], stdPoints[3]) - const stdMaxY = Math.max(stdPoints[1], stdPoints[3]) - - const rMinX = Math.min(ridgePoint[0], ridgePoint[2]) - const rMaxX = Math.max(ridgePoint[0], ridgePoint[2]) - const rMinY = Math.min(ridgePoint[1], ridgePoint[3]) - const rMaxY = Math.max(ridgePoint[1], ridgePoint[3]) - - const overlapMinX = Math.max(stdMinX, rMinX) - const overlapMaxX = Math.min(stdMaxX, rMaxX) - const overlapMinY = Math.max(stdMinY, rMinY) - const overlapMaxY = Math.min(stdMaxY, rMaxY) - - if (stdAnalyze.isHorizontal) { - if (overlapMinX > overlapMaxX) { - return - } - ridgePoint = [overlapMinX, ridgePoint[1], overlapMaxX, ridgePoint[3]] - } - if (stdAnalyze.isVertical) { - if (overlapMinY > overlapMaxY) { - return - } - ridgePoint = [ridgePoint[0], overlapMinY, ridgePoint[2], overlapMaxY] - } - - ridgePoints.push({ point: ridgePoint, left: stdLine, right: oppLine }) - canvas - .getObjects() - .filter((obj) => obj.name === 'check') - .forEach((obj) => canvas.remove(obj)) - canvas.renderAll() - }) - - ridgePoints.forEach((r) => { - let point = r.point - const inPolygon1 = - roof.inPolygon({ x: point[0], y: point[1] }) || - roof.lines.find((line) => isPointOnLineNew(line, { x: point[0], y: point[1] })) !== undefined - const inPolygon2 = - roof.inPolygon({ x: point[2], y: point[3] }) || - roof.lines.find((line) => isPointOnLineNew(line, { x: point[2], y: point[3] })) !== undefined - - //시작점이 지붕 밖에 있을때 vector내의 가까운 지붕선으로 변경 - if (!inPolygon1) { - const checkVector = { x: Math.sign(point[0] - point[2]), y: Math.sign(point[1] - point[3]) } - const checkEdge = { vertex1: { x: point[0], y: point[1] }, vertex2: { x: point[2], y: point[3] } } - let minDistance = Infinity - let correctPoint - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersect = edgesIntersection(lineEdge, checkEdge) + const driveEdge = { + vertex1: { x: startPoint.x, y: startPoint.y }, + vertex2: { x: startPoint.x + driveVector.x, y: startPoint.y + driveVector.y }, + } + const ridgeEdge = { vertex1: { x: ridgePoint[0], y: ridgePoint[1] }, vertex2: { x: ridgePoint[2], y: ridgePoint[3] } } + const intersect = edgesIntersection(driveEdge, ridgeEdge) if (intersect) { - const distance = Math.sqrt(Math.pow(intersect.x - point[0], 2) + Math.pow(intersect.y - point[1], 2)) - const intersectVector = { x: Math.sign(point[0] - intersect.x), y: Math.sign(point[1] - intersect.y) } - if (distance < minDistance && intersectVector.x === checkVector.x && intersectVector.y === checkVector.y) { - minDistance = distance - correctPoint = intersect + if (almostEqual(stdLine.x1, startPoint.x) && stdAnalyze.isHorizontal) { + stdPoints[0] = intersect.x + } + if (almostEqual(stdLine.y1, startPoint.y) && stdAnalyze.isVertical) { + stdPoints[1] = intersect.y + } + if (almostEqual(stdLine.x2, startPoint.x) && stdAnalyze.isHorizontal) { + stdPoints[2] = intersect.x + } + if (almostEqual(stdLine.y2, startPoint.y) && stdAnalyze.isVertical) { + stdPoints[3] = intersect.y } } - }) - if (correctPoint) { - point = [correctPoint.x, correctPoint.y, point[2], point[3]] } - } - //종료점이 지붕밖에 있을때 vector내의 가까운 지붕선으로 변경 - if (!inPolygon2) { - const checkVector = { x: Math.sign(point[2] - point[0]), y: Math.sign(point[3] - point[1]) } - const checkEdge = { vertex1: { x: point[2], y: point[3] }, vertex2: { x: point[0], y: point[1] } } - let minDistance = Infinity - let correctPoint - roof.lines.forEach((line) => { - const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } - const intersect = edgesIntersection(lineEdge, checkEdge) - if (intersect) { - const distance = Math.sqrt(Math.pow(intersect.x - point[2], 2) + Math.pow(intersect.y - point[3], 2)) - const intersectVector = { x: Math.sign(point[2] - intersect.x), y: Math.sign(point[3] - intersect.y) } - if (distance < minDistance && intersectVector.x === checkVector.x && intersectVector.y === checkVector.y) { - minDistance = distance - correctPoint = intersect - } + /* const checkLine1 = new fabric.Line(stdPoints, { stroke: 'red', strokeWidth: 4, parentId: roofId, name: 'check' }) + const checkLine2 = new fabric.Line(ridgePoint, { stroke: 'cyan', strokeWidth: 4, parentId: roofId, name: 'check' }) + canvas.add(checkLine1, checkLine2).renderAll()*/ + + const stdMinX = Math.min(stdPoints[0], stdPoints[2]) + const stdMaxX = Math.max(stdPoints[0], stdPoints[2]) + const stdMinY = Math.min(stdPoints[1], stdPoints[3]) + const stdMaxY = Math.max(stdPoints[1], stdPoints[3]) + + const rMinX = Math.min(ridgePoint[0], ridgePoint[2]) + const rMaxX = Math.max(ridgePoint[0], ridgePoint[2]) + const rMinY = Math.min(ridgePoint[1], ridgePoint[3]) + const rMaxY = Math.max(ridgePoint[1], ridgePoint[3]) + + const overlapMinX = Math.max(stdMinX, rMinX) + const overlapMaxX = Math.min(stdMaxX, rMaxX) + const overlapMinY = Math.max(stdMinY, rMinY) + const overlapMaxY = Math.min(stdMaxY, rMaxY) + + if (stdAnalyze.isHorizontal) { + if (overlapMinX > overlapMaxX) { + return + } + const dist1 = Math.abs(ridgePoint[0] - overlapMinX) + const dist2 = Math.abs(ridgePoint[0] - overlapMaxX) + if (dist1 < dist2) { + ridgePoint = [overlapMinX, ridgePoint[1], overlapMaxX, ridgePoint[3]] + } else { + ridgePoint = [overlapMaxX, ridgePoint[1], overlapMinX, ridgePoint[3]] + } + + const ridgeVector = Math.sign(clamp01(ridgePoint[0] - ridgePoint[2])) + if (stdVector.x !== ridgeVector) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] + } + } + if (stdAnalyze.isVertical) { + if (overlapMinY > overlapMaxY) { + return + } + const dist1 = Math.abs(ridgePoint[1] - overlapMinY) + const dist2 = Math.abs(ridgePoint[1] - overlapMaxY) + if (dist1 < dist2) { + ridgePoint = [ridgePoint[0], overlapMinY, ridgePoint[2], overlapMaxY] + } else { + ridgePoint = [ridgePoint[0], overlapMaxY, ridgePoint[2], overlapMinY] + } + const ridgeVector = Math.sign(clamp01(ridgePoint[1] - ridgePoint[3])) + if (stdVector.y !== ridgeVector) { + ridgePoint = [ridgePoint[2], ridgePoint[3], ridgePoint[0], ridgePoint[1]] } - }) - if (correctPoint) { - point = [point[0], point[1], correctPoint.x, correctPoint.y] } - } - const ridgeLength = Math.sqrt(Math.pow(point[2] - point[0], 2) + Math.pow(point[3] - point[1], 2)) + ridgePoints.push({ point: ridgePoint, left: stdLine, right: oppLine }) + canvas + .getObjects() + .filter((obj) => obj.name === 'check') + .forEach((obj) => canvas.remove(obj)) + canvas.renderAll() + }) - if (ridgeLength > EPSILON) { - linesAnalysis.push({ - start: { x: point[0], y: point[1] }, - end: { x: point[2], y: point[3] }, - left: baseLines.findIndex((line) => line === r.left), - right: baseLines.findIndex((line) => line === r.right), - type: 'ridge', - degree: 0, - }) - } - }) + ridgePoints.forEach((r) => { + let point = r.point + const inPolygon1 = + roof.inPolygon({ x: point[0], y: point[1] }) || + roof.lines.find((line) => isPointOnLineNew(line, { x: point[0], y: point[1] })) !== undefined + const inPolygon2 = + roof.inPolygon({ x: point[2], y: point[3] }) || + roof.lines.find((line) => isPointOnLineNew(line, { x: point[2], y: point[3] })) !== undefined + + //시작점이 지붕 밖에 있을때 vector내의 가까운 지붕선으로 변경 + if (!inPolygon1) { + const checkVector = { x: Math.sign(point[0] - point[2]), y: Math.sign(point[1] - point[3]) } + const checkEdge = { vertex1: { x: point[0], y: point[1] }, vertex2: { x: point[2], y: point[3] } } + let minDistance = Infinity + let correctPoint + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersect = edgesIntersection(lineEdge, checkEdge) + if (intersect) { + const distance = Math.sqrt(Math.pow(intersect.x - point[0], 2) + Math.pow(intersect.y - point[1], 2)) + const intersectVector = { x: Math.sign(point[0] - intersect.x), y: Math.sign(point[1] - intersect.y) } + if (distance < minDistance && intersectVector.x === checkVector.x && intersectVector.y === checkVector.y) { + minDistance = distance + correctPoint = intersect + } + } + }) + if (correctPoint) { + point = [correctPoint.x, correctPoint.y, point[2], point[3]] + } + } + + //종료점이 지붕밖에 있을때 vector내의 가까운 지붕선으로 변경 + if (!inPolygon2) { + const checkVector = { x: Math.sign(point[2] - point[0]), y: Math.sign(point[3] - point[1]) } + const checkEdge = { vertex1: { x: point[2], y: point[3] }, vertex2: { x: point[0], y: point[1] } } + let minDistance = Infinity + let correctPoint + roof.lines.forEach((line) => { + const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } + const intersect = edgesIntersection(lineEdge, checkEdge) + if (intersect) { + const distance = Math.sqrt(Math.pow(intersect.x - point[2], 2) + Math.pow(intersect.y - point[3], 2)) + const intersectVector = { x: Math.sign(point[2] - intersect.x), y: Math.sign(point[3] - intersect.y) } + if (distance < minDistance && intersectVector.x === checkVector.x && intersectVector.y === checkVector.y) { + minDistance = distance + correctPoint = intersect + } + } + }) + if (correctPoint) { + point = [point[0], point[1], correctPoint.x, correctPoint.y] + } + } + + const ridgeLength = Math.sqrt(Math.pow(point[2] - point[0], 2) + Math.pow(point[3] - point[1], 2)) + + if (ridgeLength > EPSILON) { + linesAnalysis.push({ + start: { x: point[0], y: point[1] }, + end: { x: point[2], y: point[3] }, + left: baseLines.findIndex((line) => line === r.left), + right: baseLines.findIndex((line) => line === r.right), + type: TYPES.RIDGE, + degree: 0, + }) + } + }) + } } } //반절마루 생성불가이므로 지붕선 분기를 해야하는지 확인 후 처리. if (prevLineVector.x === nextLineVector.x && prevLineVector.y === nextLineVector.y) { const analyze = analyzeLine(currentLine) const roofLine = analyze.roofLine - const checkRoof = new fabric.Line([roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2], { + /*const checkRoof = new fabric.Line([roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2], { stroke: 'red', strokeWidth: 4, parentId: roofId, @@ -3293,7 +4186,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { name: 'check', }) canvas.add(checkLine) - canvas.renderAll() + canvas.renderAll()*/ const checkVector = { x: Math.sign(prevLine.y2 - prevLine.y1), y: Math.sign(prevLine.x1 - prevLine.x2) } const checkEdge = { vertex1: { x: roofLine.x1, y: roofLine.y1 }, vertex2: { x: roofLine.x2, y: roofLine.y2 } } @@ -3307,16 +4200,16 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { return line !== roofLine && line !== prevRoofLine && line !== nextRoofLine }) .forEach((line) => { - const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + /*const checkLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { stroke: 'red', strokeWidth: 4, parentId: roofId, name: 'check', }) - canvas.add(checkLine).renderAll() + canvas.add(checkLine).renderAll()*/ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } } const intersect = edgesIntersection(lineEdge, checkEdge) - if (intersect) { + /* if (intersect) { const checkCircle = new fabric.Circle({ left: intersect.x, top: intersect.y, @@ -3327,7 +4220,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { }) console.log('isPointOnLineNew(line, intersect)', isPointOnLineNew(line, intersect)) canvas.add(checkCircle).renderAll() - } + }*/ if (intersect && isPointOnLineNew(line, intersect)) { intersect.x = almostEqual(intersect.x, roofLine.x1) ? roofLine.x1 : intersect.x intersect.y = almostEqual(intersect.y, roofLine.y1) ? roofLine.y1 : intersect.y @@ -3351,7 +4244,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: correctPoint.x, y: correctPoint.y }, left: baseLines.findIndex((line) => line === prevLine), right: baseLines.findIndex((line) => line === nextLine), - type: 'gableLine', + type: TYPES.GABLE_LINE, degree: getDegreeByChon(prevLine.attributes.pitch), gableId: baseLines.findIndex((line) => line === currentLine), connectCnt: 0, @@ -3372,9 +4265,647 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { console.log('baseLines', baseLines) console.log('linesAnalysis', linesAnalysis) + 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).renderAll() + // canvas.remove(checkLine).renderAll() + })*/ + + const intersections = [] + linesAnalysis.forEach((currLine, i) => { + let minDistance = Infinity + let intersectPoint = null + let linePoint = null + let partner = null + + /*const checkCLine = new fabric.Line([currLine.start.x, currLine.start.y, currLine.end.x, currLine.end.y], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkCLine).renderAll()*/ + + const cLength = Math.sqrt(Math.pow(currLine.end.x - currLine.start.x, 2) + Math.pow(currLine.end.y - currLine.start.y, 2)) + //남은 길이가 0이면 무시 + if (cLength < EPSILON) return + // 하단레벨 케라바 라인인데 연결점이 2개 이상이면 무시 + if (currLine.type === TYPES.GABLE_LINE && currLine.connectCnt > 1) return + + linesAnalysis.forEach((nextLine, j) => { + if (i === j) return + if (currLine.type === TYPES.GABLE_LINE && nextLine.type === TYPES.GABLE_LINE && currLine.gableId === nextLine.gableId) return + if (nextLine.type === TYPES.GABLE_LINE && nextLine.connectCnt > 1) return + /*const checkNLine = new fabric.Line([nextLine.start.x, nextLine.start.y, nextLine.end.x, nextLine.end.y], { + stroke: 'green', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkNLine).renderAll()*/ + + const intersect = lineIntersection(currLine.start, currLine.end, nextLine.start, nextLine.end, canvas) + if (intersect) { + const checkCircle = new fabric.Circle({ left: intersect.x, top: intersect.y, radius: 5, fill: 'blue', parentId: roofId, name: 'check' }) + canvas.add(checkCircle).renderAll() + let distance1 = Math.sqrt(Math.pow(intersect.x - currLine.start.x, 2) + Math.pow(intersect.y - currLine.start.y, 2)) + let distance2 = Math.sqrt(Math.pow(intersect.x - nextLine.start.x, 2) + Math.pow(intersect.y - nextLine.start.y, 2)) + let point = [currLine.start.x, currLine.start.y, intersect.x, intersect.y] + if (distance1 < EPSILON) { + distance1 = Math.sqrt(Math.pow(intersect.x - currLine.end.x, 2) + Math.pow(intersect.y - currLine.end.y, 2)) + distance2 = Math.sqrt(Math.pow(intersect.x - nextLine.end.x, 2) + Math.pow(intersect.y - nextLine.end.y, 2)) + point = [currLine.end.x, currLine.end.y, intersect.x, intersect.y] + } + // 하단레벨 케라바 라인인데 남은 길이가 없는 경우 무시 + if (currLine.type === TYPES.GABLE_LINE && distance1 < EPSILON) return + //교점까지의 길이가 최소 길이보다 작을 경우, 최소 길이와 교점을 교체 + if (distance1 < minDistance && !almostEqual(distance1, minDistance) && !(distance1 < EPSILON && distance2 < EPSILON)) { + minDistance = distance1 + intersectPoint = intersect + linePoint = point + partner = j + } else if (almostEqual(distance1, minDistance)) { + const pLine = linesAnalysis[partner] + const isSameLine = + currLine.left === pLine.left || currLine.left === pLine.right || currLine.right === pLine.left || currLine.right === pLine.right + if (!isSameLine) { + partner = j + } + } + canvas.remove(checkCircle).renderAll() + } + canvas.remove(checkNLine).renderAll() + }) + canvas.remove(checkCLine).renderAll() + if (intersectPoint) { + intersections.push({ index: i, intersect: intersectPoint, linePoint, partner }) + } + }) + console.log('intersections', intersections) + const intersectPoints = intersections + .map((item) => item.intersect) + .filter((point, index, self) => self.findIndex((p) => almostEqual(p.x, point.x) && almostEqual(p.y, point.y)) === index) + console.log('intersectPoints', intersectPoints) + + if (intersectPoints.length === 1 && intersections.length > 1) { + intersections[0].partner = intersections[1].index + intersections[1].partner = intersections[0].index + } + + let newAnalysis = [] //신규 발생 선 + const processed = new Set() //처리된 선 + + for (const { index, intersect, linePoint, partner } of intersections) { + const cLine = linesAnalysis[index] + const pLine = linesAnalysis[partner] + + //교점이 없거나, 이미 처리된 선분이면 처리하지 않는다. + if (!intersect || processed.has(index)) continue + + //교점이 없거나, 교점 선분이 없으면 처리하지 않는다. + const pIntersection = intersections.find((p) => p.index === partner) + if (!pIntersection || !pIntersection.intersect) continue + + //상호 최단 교점이 아닐경우 처리하지 않는다. + if (pIntersection.partner !== index) continue + + //공통선분이 없으면 처리하지 않는다. + const isSameLine = cLine.left === pLine.left || cLine.left === pLine.right || cLine.right === pLine.left || cLine.right === pLine.right + if (!isSameLine) continue + + //처리 된 라인으로 설정 + processed.add(index).add(partner) + + let point1 = linePoint + let point2 = pIntersection.linePoint + let length1 = Math.sqrt(Math.pow(point1[2] - point1[0], 2) + Math.pow(point1[3] - point1[1], 2)) + let length2 = Math.sqrt(Math.pow(point2[2] - point2[0], 2) + Math.pow(point2[3] - point2[1], 2)) + + //gable라인과 붙는경우 length가 0에 가까우면 포인트를 뒤집는다. + if (cLine.type === TYPES.GABLE_LINE && length2 < EPSILON) { + point2 = [pLine.end.x, pLine.end.y, intersect.x, intersect.y] + length2 = Math.sqrt((point2[2] - point2[0]) ** 2 + (point2[3] - point2[1]) ** 2) + } + if (pLine.type === TYPES.GABLE_LINE && length1 < EPSILON) { + point1 = [cLine.end.x, cLine.end.y, intersect.x, intersect.y] + length1 = Math.sqrt((point1[2] - point1[0]) ** 2 + (point1[3] - point1[1]) ** 2) + } + + if (length1 > 0 && !alreadyPoints(innerLines, point1)) { + if (cLine.type === TYPES.HIP) { + innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, cLine.degree, cLine.degree)) + } else if (cLine.type === TYPES.RIDGE) { + innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) + } else if (cLine.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, cLine.degree, cLine.degree)) + } else { + innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) + } + } else if (cLine.type === TYPES.GABLE_LINE) { + if (cLine.degree > 0) { + innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, cLine.degree, cLine.degree)) + } else { + innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) + } + } + } + + if (length2 > 0 && !alreadyPoints(innerLines, point2)) { + if (pLine.type === TYPES.HIP) { + innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, pLine.degree, pLine.degree)) + } else if (pLine.type === TYPES.RIDGE) { + innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) + } else if (pLine.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, pLine.degree, pLine.degree)) + } else { + innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) + } + } else if (pLine.type === TYPES.GABLE_LINE) { + if (pLine.degree > 0) { + innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, pLine.degree, pLine.degree)) + } else { + innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) + } + } + } + const otherIs = intersections + .filter((is) => is.index !== index && is.index !== partner) + .filter((is) => almostEqual(is.intersect.x, intersect.x) && almostEqual(is.intersect.y, intersect.y)) + + let relationBaseLines = [cLine.left, cLine.right, pLine.left, pLine.right] + if (otherIs.length > 0 && cLine.type !== TYPES.GABLE_LINE && pLine.type !== TYPES.GABLE_LINE) { + for (let i = 0; i < otherIs.length; i++) { + const oLine = linesAnalysis[otherIs[i].index] + if (oLine.type === TYPES.RIDGE && linesAnalysis.length > 3) continue + const isSameLine = relationBaseLines.includes(oLine.left) || relationBaseLines.includes(oLine.right) + if (isSameLine) { + let oPoint = otherIs[i].linePoint + let oLength = Math.sqrt(Math.pow(oPoint[2] - oPoint[0], 2) + Math.pow(oPoint[3] - oPoint[1], 2)) + if (oLength > 0 && !alreadyPoints(innerLines, oPoint)) { + if (oLine.type === TYPES.HIP) { + innerLines.push(drawHipLine(oPoint, canvas, roof, textMode, null, oLine.degree, oLine.degree)) + } else if (oLine.type === TYPES.RIDGE) { + innerLines.push(drawRidgeLine(oPoint, canvas, roof, textMode)) + } else if (oLine.type === TYPES.NEW) { + const isDiagonal = Math.abs(oPoint[0] - oPoint[2]) >= 1 && Math.abs(oPoint[1] - oPoint[3]) >= 1 + if (isDiagonal) { + innerLines.push(drawHipLine(oPoint, canvas, roof, textMode, null, oLine.degree, oLine.degree)) + } else { + innerLines.push(drawRidgeLine(oPoint, canvas, roof, textMode)) + } + } else if (oLine.type === TYPES.GABLE_LINE) { + if (oLine.degree > 0) { + innerLines.push(drawHipLine(oPoint, canvas, roof, textMode, null, oLine.degree, oLine.degree)) + } else { + innerLines.push(drawRidgeLine(oPoint, canvas, roof, textMode)) + } + } + } + processed.add(otherIs[i].index) + relationBaseLines.push(oLine.left, oLine.right) + } + } + } + if (cLine.type === TYPES.GABLE_LINE || pLine.type === TYPES.GABLE_LINE) { + relationBaseLines = [cLine.left, cLine.right, pLine.left, pLine.right] + console.log('gableLine newAnalyze start') + const gableLine = cLine.type === TYPES.GABLE_LINE ? cLine : pLine + 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: intersect.x, + top: intersect.y, + radius: 5, + parentId: roofId, + name: 'check', + }) + canvas.add(checkLine, checkCircle) + canvas.renderAll()*/ + + const uniqueBaseLines = [...new Set(relationBaseLines)] + // 연결점에서 새로운 가선분을 생성 + if (uniqueBaseLines.length > 2) { + 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: intersect.x, y: intersect.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: intersect.x, y: intersect.y }, + end: { x: intersect.x, y: intersect.y }, + left: gableLine.gableId, + right: gableLine.gableId, + type: TYPES.HIP, + degree: 0, + }) + } + } + } + console.log('gableLine newAnalyze end') + } else { + const uniqueBaseLines = [...new Set(relationBaseLines)] + // 연결점에서 새로운 가선분을 생성 + if (uniqueBaseLines.length > 2) { + 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) + + console.log('uniqueLines', uniqueLines) + if (uniqueLines.length === 2) { + // 두 변의 이등분선 방향 계산 + // uniqueLines.sort((a, b) => a - b) + console.log('uniqueLines : ', uniqueLines) + const baseLine1 = baseLines[uniqueLines[0]] + const baseLine2 = baseLines[uniqueLines[1]] + + let bisector + console.log('isParallel(baseLine1, baseLine2)', isParallel(baseLine1, baseLine2)) + if (isParallel(baseLine1, baseLine2)) { + bisector = getBisectLines( + { x1: cLine.start.x, x2: cLine.end.x, y1: cLine.start.y, y2: cLine.end.y }, + { x1: pLine.start.x, y1: pLine.start.y, x2: pLine.end.x, y2: pLine.end.y }, + ) + } else { + bisector = getBisectBaseLines(baseLine1, baseLine2, intersect, canvas) + } + + //마주하는 지붕선을 찾는다. + const intersectionsByRoof = [] + const checkEdge = { + vertex1: { x: intersect.x, y: intersect.y }, + vertex2: { x: intersect.x + bisector.x, y: intersect.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 - intersect.x) ** 2 + (is.y - intersect.y) ** 2) + const isVector = { x: Math.sign(intersect.x - is.x), y: Math.sign(intersect.y - is.y) } + if (isVector.x === checkVector.x && isVector.y === checkVector.y) { + intersectionsByRoof.push({ is, distance }) + } + } + }) + intersectionsByRoof.sort((a, b) => a.distance - b.distance) + console.log('intersectionsByRoof : ', intersectionsByRoof) + + //기 존재하는 analyze 라인이 있으면 newAnalyzeLine을 생성하지 않고 교체한다. + const otherIs = intersections.filter( + (is) => !processed.has(is.index) && almostEqual(is.intersect.x, intersect.x) && almostEqual(is.intersect.y, intersect.y), + ) + + console.log('otherIs', otherIs) + if (otherIs.length > 0) { + const analyze = linesAnalysis[otherIs[0].index] + processed.add(otherIs[0].index) + newAnalysis.push({ + start: { x: intersect.x, y: intersect.y }, + end: { x: analyze.end.x, y: analyze.end.y }, + left: analyze.left, + right: analyze.right, + type: analyze.type, + degree: analyze.degree, + gableId: analyze.gableId, + connectCnt: analyze.connectCnt, + }) + } else if (intersectionsByRoof.length > 0) { + //지붕선과 교점이 발생할때 + let is = intersectionsByRoof[0].is + let linePoint = [intersect.x, intersect.y, is.x, is.y] + const isDiagonal = Math.abs(is.x - intersect.x) >= 1 && Math.abs(is.y - intersect.y) >= 1 + const length = Math.sqrt((linePoint[2] - linePoint[0]) ** 2 + (linePoint[3] - linePoint[1]) ** 2) + + 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 checkPrevLine = new fabric.Line([prevLine.x1, prevLine.y1, prevLine.x2, prevLine.y2], { + stroke: 'yellow', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + const checkNextLine = new fabric.Line([nextLine.x1, nextLine.y1, nextLine.x2, nextLine.y2], { + stroke: 'red', + strokeWidth: 4, + parentId: roofId, + name: 'check', + }) + canvas.add(checkPrevLine, checkNextLine).renderAll()*/ + + if (!isDiagonal) { + const drivePoint = getRidgeDrivePoint(linePoint, prevLine, nextLine, baseLines) + if (drivePoint !== null) { + const driveLength = Math.sqrt((drivePoint.x - intersect.x) ** 2 + (drivePoint.y - intersect.y) ** 2) + if (driveLength < length) { + linePoint = [intersect.x, intersect.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 ? cLine.degree : 0, + }) + } + } + } + } + canvas + .getObjects() + .filter((object) => object.name === 'check' || object.name === 'checkAnalysis') + .forEach((object) => canvas.remove(object)) + canvas.renderAll() + + if (newAnalysis.length > 0) 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() + if (newAnalysis.length === 0) break + } + + // 가선분 중 처리가 안되어있는 붙어있는 라인에 대한 예외처리. + 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)) + + // 최종 라인중 벽에서 시작해서 벽에서 끝나는 라인이 남을 경우(벽취함) + 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 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) + } + }) + + //지붕선에 따라 라인추가 작업 처리. + 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((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 splitPoint = [] + let hasOverlapLine = false + const minX = Math.min(currentLine.x1, currentLine.x2) + const maxX = Math.max(currentLine.x1, currentLine.x2) + const minY = Math.min(currentLine.y1, currentLine.y2) + const maxY = Math.max(currentLine.y1, currentLine.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(currentLine, point) && + !(almostEqual(currentLine.x1, point.x) && almostEqual(currentLine.y1, point.y)) && + !(almostEqual(currentLine.x2, point.x) && almostEqual(currentLine.y2, point.y)) + ) { + const distance = Math.sqrt((point.x - currentLine.x1) ** 2 + (point.y - currentLine.y1) ** 2) + splitPoint.push({ point, distance }) + } + }) + if (splitPoint.length > 0) { + splitPoint.sort((a, b) => a[1] - b[1]) + let startPoint = { x: currentLine.x1, y: currentLine.y1 } + for (let i = 0; i < splitPoint.length; i++) { + const point = splitPoint[i].point + if (i === 0) { + innerLines.push(drawHipLine([startPoint.x, startPoint.y, point.x, point.y], canvas, roof, textMode, null, prevDegree, prevDegree)) + } else { + innerLines.push(drawRoofLine([startPoint.x, startPoint.y, point.x, point.y], canvas, roof, textMode)) + } + startPoint = point + } + innerLines.push(drawHipLine([startPoint.x, startPoint.y, currentLine.x2, currentLine.y2], canvas, roof, textMode, null, nextDegree, nextDegree)) + } else { + innerLines.push(drawRoofLine([currentLine.x1, currentLine.y1, currentLine.x2, currentLine.y2], canvas, roof, textMode)) + } + }) + + //지붕 innerLines에 추가. + roof.innerLines = innerLines + + canvas + .getObjects() + .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, { @@ -3394,6 +4925,8 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { 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, @@ -3402,36 +4935,47 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { }) canvas.add(checkLineI).renderAll() - if (lineI.type === 'gableLine' && lineI.connectCnt > 1) continue + 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 === 'gableLine' && lineJ.type === 'gableLine' && i.gableId === lineJ.gableId) continue - if (lineJ.type === 'gableLine' && lineJ.connectCnt > 1) continue - - const checkLineJ = new fabric.Line([lineJ.start.x, lineJ.start.y, lineJ.end.x, lineJ.end.y], { - stroke: 'green', - strokeWidth: 2, - parentId: roofId, - name: 'check', - }) - canvas.add(checkLineJ).renderAll() + 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 checkCircle = new fabric.Circle({ - left: intersection.x, - top: intersection.y, - radius: 5, - fill: 'red', - parentId: roofId, - name: 'check', - }) - canvas.add(checkCircle).renderAll() 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 === 'gableLine' && distance < EPSILON) continue + if (lineI.type === TYPES.GABLE_LINE && distance < EPSILON) continue if (distance < minDistance && !almostEqual(distance, minDistance) && !(distance < EPSILON && distance2 < EPSILON)) { minDistance = distance intersectPoint = intersection @@ -3439,9 +4983,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } else if (almostEqual(distance, minDistance)) { partners.add(j) } - canvas.remove(checkCircle).renderAll() } - canvas.remove(checkLineJ).renderAll() } if (intersectPoint) { intersections.push({ index: i, intersectPoint, partners, distance: minDistance }) @@ -3515,28 +5057,28 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { isProceed = true //gable라인과 붙는경우 length가 0에 가까우면 포인트를 뒤집는다. - if (line1.type === 'gableLine' && length2 < EPSILON) { + 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 === 'gableLine' && length1 < EPSILON) { + 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 === 'hip') { + if (line1.type === TYPES.HIP) { innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) - } else if (line1.type === 'ridge') { + } else if (line1.type === TYPES.RIDGE) { innerLines.push(drawRidgeLine(point1, canvas, roof, textMode)) - } else if (line1.type === 'new') { + } 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 === 'gableLine') { + } else if (line1.type === TYPES.GABLE_LINE) { if (line1.degree > 0) { innerLines.push(drawHipLine(point1, canvas, roof, textMode, null, line1.degree, line1.degree)) } else { @@ -3546,18 +5088,18 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } if (length2 > 0 && !alreadyPoints(innerLines, point2)) { - if (line2.type === 'hip') { + if (line2.type === TYPES.HIP) { innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) - } else if (line2.type === 'ridge') { + } else if (line2.type === TYPES.RIDGE) { innerLines.push(drawRidgeLine(point2, canvas, roof, textMode)) - } else if (line2.type === 'new') { + } 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 === 'gableLine') { + } else if (line2.type === TYPES.GABLE_LINE) { if (line2.degree > 0) { innerLines.push(drawHipLine(point2, canvas, roof, textMode, null, line2.degree, line2.degree)) } else { @@ -3566,9 +5108,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } } - if (line1.type === 'gableLine' || line2.type === 'gableLine') { + if (line1.type === TYPES.GABLE_LINE || line2.type === TYPES.GABLE_LINE) { console.log('gableLine newAnalyze start') - const gableLine = line1.type === 'gableLine' ? line1 : line2 + 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], { @@ -3605,7 +5147,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: gableLine.end.x, y: gableLine.end.y }, left: gableLine.left, right: gableLine.right, - type: 'gableLine', + type: TYPES.GABLE_LINE, degree: getDegreeByChon(baseLines[gableLine.right].attributes.pitch), gableId: gableLine.gableId, connectCnt: gableLine.connectCnt, @@ -3618,7 +5160,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: intersectPoint.x, y: intersectPoint.y }, left: gableLine.gableId, right: gableLine.gableId, - type: 'hip', + type: TYPES.HIP, degree: 0, }) } @@ -3724,7 +5266,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { end: { x: linePoint[2], y: linePoint[3] }, left: uniqueLines[0], right: uniqueLines[1], - type: 'new', + type: TYPES.NEW, degree: isDiagonal ? line1.degree : 0, }) } @@ -3763,10 +5305,10 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { console.log('newAnalysis.length : ', newAnalysis.length) if (newAnalysis.length === 0) break } - console.log('lineAnalysis: end ', linesAnalysis) + console.log('lineAnalysis: end ', linesAnalysis)*/ // 가선분 중 처리가 안되어있는 붙어있는 라인에 대한 예외처리. - const proceedAnalysis = [] + /* const proceedAnalysis = [] linesAnalysis .filter((line) => { const dx = line.end.x - line.start.x @@ -3809,11 +5351,11 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { const length = Math.sqrt((points[0] - points[2]) ** 2 + (points[1] - points[3]) ** 2) if (length > 0) { - if (currentLine.type === 'hip') { + if (currentLine.type === TYPES.HIP) { innerLines.push(drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)) - } else if (currentLine.type === 'ridge') { + } else if (currentLine.type === TYPES.RIDGE) { innerLines.push(drawRidgeLine(points, canvas, roof, textMode)) - } else if (currentLine.type === 'new') { + } 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)) @@ -3851,10 +5393,10 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } }) }) - linesAnalysis = linesAnalysis.filter((line) => !proceedAnalysis.find((p) => p === line)) + linesAnalysis = linesAnalysis.filter((line) => !proceedAnalysis.find((p) => p === line))*/ //하단 지붕 라인처리 - const downRoofLines = [] + /* const downRoofLines = [] baseLines.forEach((baseLine, index) => { const nextLine = baseLines[(index + 1) % baseLines.length] const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length] @@ -3870,9 +5412,9 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { ) { downRoofLines.push(index) } - }) + })*/ - if (downRoofLines.length > 0) { + /*if (downRoofLines.length > 0) { downRoofLines.forEach((index) => { const currentLine = baseLines[index] // const nextLine = baseLines[(index + 1) % baseLines.length] @@ -3937,7 +5479,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } }) - /*const oppositeLines = [] + /!*const oppositeLines = [] baseLines .filter((line) => { //평행한 반대방향 라인인지 확인. @@ -3992,13 +5534,13 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { }) canvas.add(checkLine) canvas.renderAll() - })*/ + })*!/ // innerLines.push(drawRoofLine(roofPoint, canvas, roof, textMode)) }) - } + }*/ - if (linesAnalysis.length > 0) { + /*if (linesAnalysis.length > 0) { linesAnalysis .filter((line) => { const dx = line.end.x - line.start.x @@ -4030,10 +5572,10 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } } }) - } + }*/ //지붕선에 따라 라인추가 작업 처리. - const innerLinesPoints = [] + /*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) @@ -4083,16 +5625,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => { } else { innerLines.push(drawRoofLine([line.x1, line.y1, line.x2, line.y2], canvas, roof, textMode)) } - }) - - //지붕 innerLines에 추가. - roof.innerLines = innerLines - - canvas - .getObjects() - .filter((object) => object.name === 'check') - .forEach((object) => canvas.remove(object)) - canvas.renderAll() + })*/ } /** @@ -4110,7 +5643,6 @@ const lineIntersection = (line1Start, line1End, line2Start, line2End) => { // const denominator = (line2End.y - line2Start.y) * (line1End.x - line1Start.x) - (line2End.x - line2Start.x) * (line1End.y - line1Start.y) const denominator = dy2 * dx1 - dx2 * dy1 if (Math.abs(denominator) < EPSILON) { - console.log('평행') const isOpposite = dx1 * dx2 + dy1 * dy2 < 0 const isOverlap = isPointOnLineNew({ x1: line1Start.x, y1: line1Start.y, x2: line1End.x, y2: line1End.y }, { x: line2Start.x, y: line2Start.y }) @@ -4124,12 +5656,6 @@ const lineIntersection = (line1Start, line1End, line2Start, line2End) => { let ua = (dx2 * (line1Start.y - line2Start.y) - dy2 * (line1Start.x - line2Start.x)) / denominator let ub = (dx1 * (line1Start.y - line2Start.y) - dy1 * (line1Start.x - line2Start.x)) / denominator - // 경계값 보정용 헬퍼 - const clamp01 = (t) => { - if (almostEqual(t, 0, EPSILON)) return 0 - if (almostEqual(t, 1, EPSILON)) return 1 - return t - } ua = clamp01(ua) ub = clamp01(ub) @@ -4142,6 +5668,17 @@ const lineIntersection = (line1Start, line1End, line2Start, line2End) => { return null } +/** + * 경계값 보정용 헬퍼 + * @param t + * @returns {*|number} + */ +const clamp01 = (t) => { + if (almostEqual(t, 0, EPSILON)) return 0 + if (almostEqual(t, 1, EPSILON)) return 1 + return t +} + /** * 실제 각도를 계산한다 대각선인 경우 각도 조정 * @param points