a,b 패턴 지붕 수정

용마루 생성 끝, 지붕면 생성 중
This commit is contained in:
Jaeyoung Lee 2025-08-27 10:02:53 +09:00
parent 8c153430cd
commit e35cacf520
2 changed files with 574 additions and 4 deletions

View File

@ -2,7 +2,7 @@ import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import { calculateAngle, drawGableRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf'
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import Big from 'big.js'
@ -330,6 +330,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
} else if (isGableRoof(types)) {
// A형, B형 박공 지붕
console.log('패턴 지붕')
drawGableRoof(this.id, this.canvas, textMode)
} else if (isShedRoof(types, this.lines)) {
console.log('한쪽흐름 지붕')
drawShedRoof(this.id, this.canvas, textMode)

View File

@ -495,9 +495,7 @@ export const isSamePoint = (a, b) => {
* @param canvas
* @param textMode
*/
export const drawEavesRoof = (roofId, canvas, textMode) => {
}
export const drawEavesRoof = (roofId, canvas, textMode) => {}
/**
* 박공지붕(A,B 패턴)
@ -506,7 +504,578 @@ export const drawEavesRoof = (roofId, canvas, textMode) => {
* @param textMode
*/
export const drawGableRoof = (roofId, canvas, textMode) => {
const roof = canvas?.getObjects().find((object) => object.id === roofId)
const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0)
const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 }))
/** baseLine을 기준으로 확인용 polygon 작성 */
const checkWallPolygon = new QPolygon(baseLinePoints, {})
const eavesLines = baseLines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
const gableLines = baseLines.filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.GABLE)
const ridgeLines = []
const innerLines = []
/**
* 처마 라인 속성 판단
* @param line
* @returns {{startPoint: {x, y}, endPoint: {x, y}, length: number, angleDegree: number, normalizedAngle: number, isHorizontal: boolean, isVertical: boolean, isDiagonal: boolean, directionVector: {x: number, y: number}, roofLine}}
*/
const analyzeEavesLine = (line) => {
const tolerance = 1
const dx = Big(line.x2).minus(Big(line.x1)).toNumber()
const dy = Big(line.y2).minus(Big(line.y1)).toNumber()
const length = Math.sqrt(dx * dx + dy * dy)
const angleDegree = (Math.atan2(dy, dx) * 180) / Math.PI
const normalizedAngle = Math.abs((Math.atan2(dy, dx) * 180) / Math.PI) % 180
const directionVector = { x: dx / length, y: dy / length }
let isHorizontal = false,
isVertical = false,
isDiagonal = false
if (normalizedAngle < tolerance || normalizedAngle >= 180 - tolerance) {
isHorizontal = true
} else if (Math.abs(normalizedAngle - 90) <= tolerance) {
isVertical = true
} else {
isDiagonal = true
}
const originPoint = line.attributes.originPoint
const midX = (originPoint.x1 + originPoint.x2) / 2
const midY = (originPoint.y1 + originPoint.y2) / 2
const offset = line.attributes.offset
const checkRoofLines = roof.lines.filter((roof) => {
const roofDx = Big(roof.x2).minus(Big(roof.x1)).toNumber()
const roofDy = Big(roof.y2).minus(Big(roof.y1)).toNumber()
const roofLength = Math.sqrt(roofDx * roofDx + roofDy * roofDy)
const roofVector = { x: roofDx / roofLength, y: roofDy / roofLength }
return directionVector.x === roofVector.x && directionVector.y === roofVector.y
})
let roofVector = { x: 0, y: 0 }
if (isHorizontal) {
const checkPoint = { x: midX, y: midY + offset }
if (wall.inPolygon(checkPoint)) {
roofVector = { x: 0, y: -1 }
} else {
roofVector = { x: 0, y: 1 }
}
}
if (isVertical) {
const checkPoint = { x: midX + offset, y: midY }
if (wall.inPolygon(checkPoint)) {
roofVector = { x: -1, y: 0 }
} else {
roofVector = { x: 1, y: 0 }
}
}
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofVector.x * offset, y: midY + roofVector.y * offset } }
const edgeDx = Big(findEdge.vertex2.x).minus(Big(findEdge.vertex1.x)).toNumber()
const edgeDy = Big(findEdge.vertex2.y).minus(Big(findEdge.vertex1.y)).toNumber()
const edgeLength = Math.sqrt(edgeDx * edgeDx + edgeDy * edgeDy)
const edgeVector = { x: edgeDx / edgeLength, y: edgeDy / edgeLength }
const intersectRoofLines = []
checkRoofLines.forEach((roofLine) => {
const lineEdge = { vertex1: { x: roofLine.x1, y: roofLine.y1 }, vertex2: { x: roofLine.x2, y: roofLine.y2 } }
const intersect = edgesIntersection(lineEdge, findEdge)
if (intersect) {
const intersectDx = Big(intersect.x).minus(Big(findEdge.vertex1.x)).toNumber()
const intersectDy = Big(intersect.y).minus(Big(findEdge.vertex1.y)).toNumber()
const intersectLength = Math.sqrt(intersectDx * intersectDx + intersectDy * intersectDy)
const intersectVector = { x: intersectDx / intersectLength, y: intersectDy / intersectLength }
if (edgeVector.x === intersectVector.x && edgeVector.y === intersectVector.y) {
intersectRoofLines.push({ roofLine, intersect, length: intersectLength })
}
}
})
let currentRoof = intersectRoofLines.find((roof) => isPointOnLineNew(roof.roofLine, roof.intersect))
if (!currentRoof) {
currentRoof = intersectRoofLines.sort((a, b) => a.length - b.length)[0]
}
let startPoint, endPoint
if (isHorizontal) {
startPoint = { x: Math.min(line.x1, line.x2, currentRoof.roofLine.x1, currentRoof.roofLine.x2), y: line.y1 }
endPoint = { x: Math.max(line.x1, line.x2, currentRoof.roofLine.x1, currentRoof.roofLine.x2), y: line.y2 }
}
if (isVertical) {
startPoint = { x: line.x1, y: Math.min(line.y1, line.y2, currentRoof.roofLine.y1, currentRoof.roofLine.y2) }
endPoint = { x: line.x2, y: Math.max(line.y1, line.y2, currentRoof.roofLine.y1, currentRoof.roofLine.y2) }
}
if (isDiagonal) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
}
return {
startPoint,
endPoint,
length,
angleDegree,
normalizedAngle,
isHorizontal,
isVertical,
isDiagonal,
directionVector: { x: dx / length, y: dy / length },
roofLine: currentRoof.roofLine,
}
}
const isOverlapLine = (currAnalyze, checkAnalyze) => {
// 허용 오차
const tolerance = 1
// 같은 방향인지 확인
if (
currAnalyze.isHorizontal !== checkAnalyze.isHorizontal ||
currAnalyze.isVertical !== checkAnalyze.isVertical ||
currAnalyze.isDiagonal !== checkAnalyze.isDiagonal
) {
return false
}
if (currAnalyze.isHorizontal && !(Math.abs(currAnalyze.startPoint.y - checkAnalyze.startPoint.y) < tolerance)) {
// 수평선: y좌표가 다른경우 false
return false
} else if (currAnalyze.isVertical && !(Math.abs(currAnalyze.startPoint.x - checkAnalyze.startPoint.x) < tolerance)) {
// 수직선: x좌표가 다른경우 false
return false
}
// 3. 선분 구간 겹침 확인
let range1, range2
if (currAnalyze.isHorizontal) {
// 수평선: x 범위로 비교
range1 = {
min: Math.min(currAnalyze.startPoint.x, currAnalyze.endPoint.x),
max: Math.max(currAnalyze.startPoint.x, currAnalyze.endPoint.x),
}
range2 = {
min: Math.min(checkAnalyze.startPoint.x, checkAnalyze.endPoint.x),
max: Math.max(checkAnalyze.startPoint.x, checkAnalyze.endPoint.x),
}
} else {
// 수직선: y 범위로 비교
range1 = {
min: Math.min(currAnalyze.startPoint.y, currAnalyze.endPoint.y),
max: Math.max(currAnalyze.startPoint.y, currAnalyze.endPoint.y),
}
range2 = {
min: Math.min(checkAnalyze.startPoint.y, checkAnalyze.endPoint.y),
max: Math.max(checkAnalyze.startPoint.y, checkAnalyze.endPoint.y),
}
}
// 구간 겹침 확인
const overlapStart = Math.max(range1.min, range2.min)
const overlapEnd = Math.min(range1.max, range2.max)
return Math.max(0, overlapEnd - overlapStart) > 0
}
/**
* 전체 처마 라인의 속성 확인 정리
* @param lines
* @returns {{forwardLines: Array, backwardLines: Array}}
*/
const analyzeAllEavesLines = (lines) => {
let forwardLines = []
let backwardLines = []
lines.forEach((line) => {
const analyze = analyzeEavesLine(line)
if (analyze.isHorizontal) {
if (analyze.directionVector.x > 0) {
const overlapLines = forwardLines.filter((forwardLine) => forwardLine !== line && isOverlapLine(analyze, forwardLine.analyze))
if (overlapLines.length > 0) {
overlapLines.forEach((overlap) => {
const overlapAnalyze = overlap.analyze
const startPoint = {
x: Math.min(analyze.startPoint.x, analyze.endPoint.x, overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x),
y: analyze.startPoint.y,
}
const endPoint = {
x: Math.max(analyze.startPoint.x, analyze.endPoint.x, overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x),
y: analyze.endPoint.y,
}
analyze.startPoint = startPoint
analyze.endPoint = endPoint
forwardLines = forwardLines.filter((forwardLine) => forwardLine !== overlap)
})
}
forwardLines.push({ eaves: line, analyze })
}
if (analyze.directionVector.x < 0) {
const overlapLines = backwardLines.filter((backwardLine) => backwardLine !== line && isOverlapLine(analyze, backwardLine.analyze))
if (overlapLines.length > 0) {
overlapLines.forEach((overlap) => {
const overlapAnalyze = overlap.analyze
const startPoint = {
x: Math.min(analyze.startPoint.x, analyze.endPoint.x, overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x),
y: analyze.startPoint.y,
}
const endPoint = {
x: Math.max(analyze.startPoint.x, analyze.endPoint.x, overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x),
y: analyze.endPoint.y,
}
analyze.startPoint = startPoint
analyze.endPoint = endPoint
backwardLines = backwardLines.filter((backwardLine) => backwardLine !== overlap)
})
}
backwardLines.push({ eaves: line, analyze })
}
}
if (analyze.isVertical) {
if (analyze.directionVector.y > 0) {
const overlapLines = forwardLines.filter((forwardLine) => forwardLine !== line && isOverlapLine(analyze, forwardLine.analyze))
if (overlapLines.length > 0) {
overlapLines.forEach((overlap) => {
const overlapAnalyze = overlap.analyze
const startPoint = {
x: analyze.startPoint.x,
y: Math.min(analyze.startPoint.y, analyze.endPoint.y, overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y),
}
const endPoint = {
x: analyze.endPoint.x,
y: Math.max(analyze.startPoint.y, analyze.endPoint.y, overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y),
}
analyze.startPoint = startPoint
analyze.endPoint = endPoint
forwardLines = forwardLines.filter((forwardLine) => forwardLine !== overlap)
})
}
forwardLines.push({ eaves: line, analyze })
}
if (analyze.directionVector.y < 0) {
const overlapLines = backwardLines.filter((backwardLine) => backwardLine !== line && isOverlapLine(analyze, backwardLine.analyze))
if (overlapLines.length > 0) {
overlapLines.forEach((overlap) => {
const overlapAnalyze = overlap.analyze
const startPoint = {
x: analyze.startPoint.x,
y: Math.min(analyze.startPoint.y, analyze.endPoint.y, overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y),
}
const endPoint = {
x: analyze.endPoint.x,
y: Math.max(analyze.startPoint.y, analyze.endPoint.y, overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y),
}
analyze.startPoint = startPoint
analyze.endPoint = endPoint
backwardLines = backwardLines.filter((backwardLine) => backwardLine !== overlap)
})
}
backwardLines.push({ eaves: line, analyze })
}
}
})
forwardLines.sort((a, b) => {
if (a.analyze.isHorizontal && b.analyze.isHorizontal) {
return a.analyze.startPoint.x - b.analyze.startPoint.x
} else if (a.analyze.isVertical && b.analyze.isVertical) {
return a.analyze.startPoint.y - b.analyze.startPoint.y
}
})
backwardLines.sort((a, b) => {
if (a.analyze.isHorizontal && b.analyze.isHorizontal) {
return a.analyze.startPoint.x - b.analyze.startPoint.x
} else if (a.analyze.isVertical && b.analyze.isVertical) {
return a.analyze.startPoint.y - b.analyze.startPoint.y
}
})
return { forwardLines, backwardLines }
}
/**
* 라인의 지붕 면을 찾는다.
* @param currentLine
* @returns {*}
*/
const findCurrentRoof = (currentLine) => {
const analyze = analyzeEavesLine(currentLine)
const originPoint = currentLine.attributes.originPoint
const midX = (originPoint.x1 + originPoint.x2) / 2
const midY = (originPoint.y1 + originPoint.y2) / 2
const offset = currentLine.attributes.offset
let currentRoof
let roofFindVector = { x: 0, y: 0 }
const checkRoofLines = roof.lines.filter((roof) => {
const dx = Big(roof.x2).minus(Big(roof.x1)).toNumber()
const dy = Big(roof.y2).minus(Big(roof.y1)).toNumber()
const length = Math.sqrt(dx * dx + dy * dy)
const directionVector = { x: dx / length, y: dy / length }
return analyze.directionVector.x === directionVector.x && analyze.directionVector.y === directionVector.y
})
if (analyze.isHorizontal) {
const checkPoint = { x: midX, y: midY + offset }
if (wall.inPolygon(checkPoint)) {
roofFindVector = { x: 0, y: -1 }
} else {
roofFindVector = { x: 0, y: 1 }
}
}
if (analyze.isVertical) {
const checkPoint = { x: midX + offset, y: midY }
if (wall.inPolygon(checkPoint)) {
roofFindVector = { x: -1, y: 0 }
} else {
roofFindVector = { x: 1, y: 0 }
}
}
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofFindVector.x * offset, y: midY + roofFindVector.y * offset } }
const edgeDx = Big(findEdge.vertex2.x).minus(Big(findEdge.vertex1.x)).toNumber()
const edgeDy = Big(findEdge.vertex2.y).minus(Big(findEdge.vertex1.y)).toNumber()
const edgeLength = Math.sqrt(edgeDx * edgeDx + edgeDy * edgeDy)
const edgeVector = { x: edgeDx / edgeLength, y: edgeDy / edgeLength }
const intersectRoofLines = []
checkRoofLines.forEach((roofLine) => {
const lineEdge = { vertex1: { x: roofLine.x1, y: roofLine.y1 }, vertex2: { x: roofLine.x2, y: roofLine.y2 } }
const intersect = edgesIntersection(lineEdge, findEdge)
if (intersect) {
const intersectDx = Big(intersect.x).minus(Big(findEdge.vertex1.x)).toNumber()
const intersectDy = Big(intersect.y).minus(Big(findEdge.vertex1.y)).toNumber()
const intersectLength = Math.sqrt(intersectDx * intersectDx + intersectDy * intersectDy)
const intersectVector = { x: intersectDx / intersectLength, y: intersectDy / intersectLength }
if (edgeVector.x === intersectVector.x && edgeVector.y === intersectVector.y) {
intersectRoofLines.push({ roofLine, intersect, length: intersectLength })
}
}
})
currentRoof = intersectRoofLines.find((roof) => isPointOnLineNew(roof.roofLine, roof.intersect))
if (!currentRoof) {
currentRoof = intersectRoofLines.sort((a, b) => a.length - b.length)[0]
}
return currentRoof
}
/**
* 지점 사이의 길이와 지점별 각도에 따라서 교점까지의 길이를 구한다.
* @param distance
* @param angleA
* @param angleB
* @returns {{a: number, b: number}}
*/
const calculateIntersection = (distance, angleA, angleB) => {
// A에서 B방향으로의 각도 (0도가 B방향)
const tanA = Math.tan((angleA * Math.PI) / 180)
// B에서 A방향으로의 각도 (180도가 A방향, 실제 계산에서는 180-angleB)
const tanB = Math.tan(((180 - angleB) * Math.PI) / 180)
const a = Math.round(((distance * tanB) / (tanB - tanA)) * 10) / 10
const b = Math.round(Math.abs(distance - a) * 10) / 10
return { a, b }
}
const { forwardLines, backwardLines } = analyzeAllEavesLines(eavesLines)
forwardLines.forEach((current) => {
const currentLine = current.eaves
const analyze = current.analyze
const currentDegree = getDegreeByChon(currentLine.attributes.pitch)
const currentX1 = Math.min(currentLine.x1, currentLine.x2)
const currentX2 = Math.max(currentLine.x1, currentLine.x2)
const currentY1 = Math.min(currentLine.y1, currentLine.y2)
const currentY2 = Math.max(currentLine.y1, currentLine.y2)
const x1 = analyze.startPoint.x
const x2 = analyze.endPoint.x
const y1 = analyze.startPoint.y
const y2 = analyze.endPoint.y
const checkLine = new fabric.Line([analyze.startPoint.x, analyze.startPoint.y, analyze.endPoint.x, analyze.endPoint.y], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
const overlapLines = []
backwardLines.forEach((backward) => {
const backX1 = Math.min(backward.eaves.x1, backward.eaves.x2)
const backX2 = Math.max(backward.eaves.x1, backward.eaves.x2)
const backY1 = Math.min(backward.eaves.y1, backward.eaves.y2)
const backY2 = Math.max(backward.eaves.y1, backward.eaves.y2)
if (
analyze.isHorizontal &&
((currentX1 <= backX1 && currentX2 >= backX1) ||
(currentX1 <= backX2 && currentX2 >= backX2) ||
(backX1 < currentX1 && backX1 < currentX2 && backX2 > currentX1 && backX2 > currentX2))
) {
overlapLines.push(backward)
}
if (
analyze.isVertical &&
((currentY1 <= backY1 && currentY2 >= backY1) ||
(currentY1 <= backY2 && currentY2 >= backY2) ||
(backY1 < currentY1 && backY1 < currentY2 && backY2 > currentY1 && backY2 > currentY2))
) {
overlapLines.push(backward)
}
})
overlapLines.forEach((overlap) => {
const overlapAnalyze = overlap.analyze
const overlapDegree = getDegreeByChon(overlap.eaves.attributes.pitch)
const ridgePoint = []
if (analyze.isHorizontal) {
const overlapX1 = Math.min(overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x)
const overlapX2 = Math.max(overlapAnalyze.startPoint.x, overlapAnalyze.endPoint.x)
// 각 라인 사이의 길이를 구해서 각도에 대한 중간 지점으로 마루를 생성.
const currentMidY = (currentLine.y1 + currentLine.y2) / 2
const overlapMidY = (overlap.eaves.y1 + overlap.eaves.y2) / 2
const distance = calculateIntersection(Math.abs(currentMidY - overlapMidY), currentDegree, overlapDegree)
const vectorToOverlap = Math.sign(overlap.eaves.y1 - currentLine.y1)
const pointY = currentLine.y1 + vectorToOverlap * distance.a
if (x1 <= overlapX1 && overlapX1 <= x2) {
ridgePoint.push({ x: overlapX1, y: pointY })
} else {
ridgePoint.push({ x: x1, y: pointY })
}
if (x1 <= overlapX2 && overlapX2 <= x2) {
ridgePoint.push({ x: overlapX2, y: pointY })
} else {
ridgePoint.push({ x: x2, y: pointY })
}
}
if (analyze.isVertical) {
const overlapY1 = Math.min(overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y)
const overlapY2 = (overlapAnalyze.startPoint.y, overlapAnalyze.endPoint.y)
// 각 라인 사이의 길이를 구해서 각도에 대한 중간 지점으로 마루를 생성.
const currentMidX = (currentLine.x1 + currentLine.x2) / 2
const overlapMidX = (overlap.eaves.x1 + overlap.eaves.x2) / 2
const distance = calculateIntersection(Math.abs(currentMidX - overlapMidX), currentDegree, overlapDegree)
const vectorToOverlap = Math.sign(overlap.eaves.x1 - currentLine.x1)
const pointX = currentLine.x1 + vectorToOverlap * distance.a
if (y1 <= overlapY1 && overlapY1 <= y2) {
ridgePoint.push({ x: pointX, y: overlapY1 })
} else {
ridgePoint.push({ x: pointX, y: y1 })
}
if (y1 <= overlapY2 && overlapY2 <= y2) {
ridgePoint.push({ x: pointX, y: overlapY2 })
} else {
ridgePoint.push({ x: pointX, y: y2 })
}
}
ridgeLines.push(drawRidgeLine([ridgePoint[0].x, ridgePoint[0].y, ridgePoint[1].x, ridgePoint[1].y], canvas, roof, textMode))
})
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
})
/**
* 지붕면을 그린다.
* @param currentLine
*/
const drawRoofPlane = (currentLine) => {
const analyze = currentLine.analyze
const checkLine = new fabric.Line([analyze.startPoint.x, analyze.startPoint.y, analyze.endPoint.x, analyze.endPoint.y], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
const innerRidgeLines = []
if (analyze.isHorizontal) {
ridgeLines
.filter((ridgeLine) => {
const tolerance = 1
const dx = Big(ridgeLine.x2).minus(Big(ridgeLine.x1)).toNumber()
const dy = Big(ridgeLine.y2).minus(Big(ridgeLine.y1)).toNumber()
const normalizedAngle = Math.abs((Math.atan2(dy, dx) * 180) / Math.PI) % 180
return normalizedAngle < tolerance || normalizedAngle >= 180 - tolerance
})
.filter((ridgeLine) => {
const minX = Math.min(analyze.startPoint.x, analyze.endPoint.x)
const maxX = Math.max(analyze.startPoint.x, analyze.endPoint.x)
const ridgeLineX = (ridgeLine.x1 + ridgeLine.x2) / 2
return ridgeLineX >= minX && ridgeLineX <= maxX
})
.forEach((ridgeLine) => innerRidgeLines.push(ridgeLine))
}
if (analyze.isVertical) {
ridgeLines
.filter((ridgeLine) => {
const tolerance = 1
const dx = Big(ridgeLine.x2).minus(Big(ridgeLine.x1)).toNumber()
const dy = Big(ridgeLine.y2).minus(Big(ridgeLine.y1)).toNumber()
const normalizedAngle = Math.abs((Math.atan2(dy, dx) * 180) / Math.PI) % 180
return Math.abs(normalizedAngle - 90) <= tolerance
})
.filter((ridgeLine) => {
const minY = Math.min(analyze.startPoint.y, analyze.endPoint.y)
const maxY = Math.max(analyze.startPoint.y, analyze.endPoint.y)
const ridgeLineY = (ridgeLine.y1 + ridgeLine.y2) / 2
return ridgeLineY >= minY && ridgeLineY <= maxY
})
.forEach((ridgeLine) => innerRidgeLines.push(ridgeLine))
}
innerRidgeLines.forEach((ridgeLine) => {
const checkLine = new fabric.Line([ridgeLine.x1, ridgeLine.y1, ridgeLine.x2, ridgeLine.y2], {
stroke: 'red',
strokeWidth: 4,
parentId: roofId,
name: 'check',
})
canvas.add(checkLine)
canvas.renderAll()
})
if (innerRidgeLines.length === 1) {
const innerRidgeLine = innerRidgeLines[0]
if (analyze.isHorizontal) {
}
if (analyze.isVertical) {
}
}
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
}
forwardLines.forEach((forward) => {
const analyze = forward.analyze
drawRoofPlane(forward)
})
backwardLines.forEach((backward) => {
const analyze = backward.analyze
drawRoofPlane(backward)
})
canvas
.getObjects()
.filter((object) => object.name === 'check')
.forEach((object) => canvas.remove(object))
canvas.renderAll()
}
/**