@@ -80,7 +80,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js
index 91d65edb..46b71588 100644
--- a/src/hooks/common/useCanvasConfigInitialize.js
+++ b/src/hooks/common/useCanvasConfigInitialize.js
@@ -9,7 +9,7 @@ import { globalFontAtom } from '@/store/fontAtom'
import { useRoof } from '@/hooks/common/useRoof'
import { usePolygon } from '@/hooks/usePolygon'
import { useRoofFn } from '@/hooks/common/useRoofFn'
-import { POLYGON_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
export function useCanvasConfigInitialize() {
const canvas = useRecoilValue(canvasState)
@@ -64,6 +64,7 @@ export function useCanvasConfigInitialize() {
groupDimensionInit()
reGroupInit() //그룹 객체 재그룹
moduleInit()
+ innerLinesInit() // innerLines 세팅 추가
}
const gridInit = () => {
@@ -228,5 +229,23 @@ export function useCanvasConfigInitialize() {
})
}
+ const innerLinesInit = () => {
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ // innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
+ let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
+
+ roofs.forEach((roof) => {
+ roof.innerLines = canvas
+ .getObjects()
+ .filter(
+ (obj) =>
+ obj.type === 'QLine' &&
+ obj.attributes?.type !== 'pitchSizeLine' &&
+ obj.attributes?.roofId === roof.id &&
+ innerLineTypes.includes(obj.name),
+ )
+ })
+ }
+
return { canvasLoadInit, gridInit }
}
diff --git a/src/hooks/common/useRoofFn.js b/src/hooks/common/useRoofFn.js
index c49128e7..38246273 100644
--- a/src/hooks/common/useRoofFn.js
+++ b/src/hooks/common/useRoofFn.js
@@ -23,102 +23,65 @@ export function useRoofFn() {
//면형상 선택 클릭시 지붕 패턴 입히기
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
- if (!polygon) {
- return
- }
- if (polygon.points.length < 3) {
- return
- }
- if (isForceChange && !isDisplay) {
- /*if (polygon.roofMaterial) {
+ try {
+ if (!polygon) {
+ return
+ }
+ if (polygon.points.length < 3) {
+ return
+ }
+ if (isForceChange && !isDisplay) {
+ /*if (polygon.roofMaterial) {
polygon.roofMaterial = null
}*/
- }
- if (!roofMaterial) {
- roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial
- }
-
- const ratio = window.devicePixelRatio || 1
- const layout = roofMaterial.layout
-
- let width = (roofMaterial.width || 226) / 10
- let height = (roofMaterial.length || 158) / 10
- const index = roofMaterial.index ?? 0
- let roofStyle = 2
- const inputPatternSize = { width: width, height: height } //임시 사이즈
- const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
-
- if (polygon.direction === 'east' || polygon.direction === 'west') {
- //세로형이면 width height를 바꿈
- ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
- }
-
- // 패턴 소스를 위한 임시 캔버스 생성
- const patternSourceCanvas = document.createElement('canvas')
- patternSourceCanvas.width = polygon.width * ratio
- patternSourceCanvas.height = polygon.height * ratio
- const ctx = patternSourceCanvas.getContext('2d')
- let offset = roofStyle === 1 ? 0 : patternSize.width / 2
-
- const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
- const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
-
- ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index]
- ctx.lineWidth = 2
- ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
-
- if (trestleMode) {
- ctx.strokeStyle = 'black'
- ctx.lineWidth = 0.2
- ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
- } else {
- ctx.fillStyle = 'rgba(255, 255, 255, 1)'
- }
-
- if (polygon.direction === 'east' || polygon.direction === 'west') {
- offset = roofStyle === 1 ? 0 : patternSize.height / 2
- for (let col = 0; col <= cols; col++) {
- const x = col * patternSize.width
- const yStart = 0
- const yEnd = patternSourceCanvas.height
- ctx.beginPath()
- ctx.moveTo(x, yStart) // 선 시작점
- ctx.lineTo(x, yEnd) // 선 끝점
- ctx.stroke()
- if (mode === 'allPainted' || trestleMode) {
- ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
- }
-
- for (let row = 0; row <= rows; row++) {
- const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height
- const xStart = col * patternSize.width
- const xEnd = xStart + patternSize.width
- ctx.beginPath()
- ctx.moveTo(xStart, y) // 선 시작점
- ctx.lineTo(xEnd, y) // 선 끝점
- ctx.stroke()
- if (mode === 'allPainted' || trestleMode) {
- ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
- }
- }
}
- } else {
- for (let row = 0; row <= rows; row++) {
- const y = row * patternSize.height
+ if (!roofMaterial) {
+ roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial
+ }
- ctx.beginPath()
- ctx.moveTo(0, y) // 선 시작점
- ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
- ctx.stroke()
- if (mode === 'allPainted' || trestleMode) {
- ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
- }
+ const ratio = window.devicePixelRatio || 1
+ const layout = roofMaterial.layout
+ let width = (roofMaterial.width || 226) / 10
+ let height = (roofMaterial.length || 158) / 10
+ const index = roofMaterial.index ?? 0
+ let roofStyle = 2
+ const inputPatternSize = { width: width, height: height } //임시 사이즈
+ const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
+
+ if (polygon.direction === 'east' || polygon.direction === 'west') {
+ //세로형이면 width height를 바꿈
+ ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
+ }
+
+ // 패턴 소스를 위한 임시 캔버스 생성
+ const patternSourceCanvas = document.createElement('canvas')
+ patternSourceCanvas.width = polygon.width * ratio
+ patternSourceCanvas.height = polygon.height * ratio
+ const ctx = patternSourceCanvas.getContext('2d')
+ let offset = roofStyle === 1 ? 0 : patternSize.width / 2
+
+ const rows = Math.floor(patternSourceCanvas.height / patternSize.height)
+ const cols = Math.floor(patternSourceCanvas.width / patternSize.width)
+
+ ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index]
+ ctx.lineWidth = 2
+ ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white'
+
+ if (trestleMode) {
+ ctx.strokeStyle = 'black'
+ ctx.lineWidth = 0.2
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
+ } else {
+ ctx.fillStyle = 'rgba(255, 255, 255, 1)'
+ }
+
+ if (polygon.direction === 'east' || polygon.direction === 'west') {
+ offset = roofStyle === 1 ? 0 : patternSize.height / 2
for (let col = 0; col <= cols; col++) {
- const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width
- const yStart = row * patternSize.height
- const yEnd = yStart + patternSize.height
-
+ const x = col * patternSize.width
+ const yStart = 0
+ const yEnd = patternSourceCanvas.height
ctx.beginPath()
ctx.moveTo(x, yStart) // 선 시작점
ctx.lineTo(x, yEnd) // 선 끝점
@@ -126,49 +89,90 @@ export function useRoofFn() {
if (mode === 'allPainted' || trestleMode) {
ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
}
+
+ for (let row = 0; row <= rows; row++) {
+ const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height
+ const xStart = col * patternSize.width
+ const xEnd = xStart + patternSize.width
+ ctx.beginPath()
+ ctx.moveTo(xStart, y) // 선 시작점
+ ctx.lineTo(xEnd, y) // 선 끝점
+ ctx.stroke()
+ if (mode === 'allPainted' || trestleMode) {
+ ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height)
+ }
+ }
+ }
+ } else {
+ for (let row = 0; row <= rows; row++) {
+ const y = row * patternSize.height
+
+ ctx.beginPath()
+ ctx.moveTo(0, y) // 선 시작점
+ ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점
+ ctx.stroke()
+ if (mode === 'allPainted' || trestleMode) {
+ ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height)
+ }
+
+ for (let col = 0; col <= cols; col++) {
+ const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width
+ const yStart = row * patternSize.height
+ const yEnd = yStart + patternSize.height
+
+ ctx.beginPath()
+ ctx.moveTo(x, yStart) // 선 시작점
+ ctx.lineTo(x, yEnd) // 선 끝점
+ ctx.stroke()
+ if (mode === 'allPainted' || trestleMode) {
+ ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart)
+ }
+ }
}
}
- }
- const hachingPatternSourceCanvas = document.createElement('canvas')
+ const hachingPatternSourceCanvas = document.createElement('canvas')
- if (mode === 'lineHatch') {
- hachingPatternSourceCanvas.width = polygon.width * ratio
- hachingPatternSourceCanvas.height = polygon.height * ratio
+ if (mode === 'lineHatch') {
+ hachingPatternSourceCanvas.width = polygon.width * ratio
+ hachingPatternSourceCanvas.height = polygon.height * ratio
- const ctx1 = hachingPatternSourceCanvas.getContext('2d')
+ const ctx1 = hachingPatternSourceCanvas.getContext('2d')
- const gap = 10
+ const gap = 10
- ctx1.strokeStyle = 'green' // 선 색상
- ctx1.lineWidth = 0.3 // 선 두께
+ ctx1.strokeStyle = 'green' // 선 색상
+ ctx1.lineWidth = 0.3 // 선 두께
- for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) {
- ctx1.beginPath()
- ctx1.moveTo(x, 0) // 선 시작점
- ctx1.lineTo(0, x) // 선 끝점
- ctx1.stroke()
+ for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) {
+ ctx1.beginPath()
+ ctx1.moveTo(x, 0) // 선 시작점
+ ctx1.lineTo(0, x) // 선 끝점
+ ctx1.stroke()
+ }
}
+
+ const combinedPatternCanvas = document.createElement('canvas')
+ combinedPatternCanvas.width = polygon.width * ratio
+ combinedPatternCanvas.height = polygon.height * ratio
+ const combinedCtx = combinedPatternCanvas.getContext('2d')
+ // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘
+ combinedCtx.drawImage(patternSourceCanvas, 0, 0)
+ combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0)
+
+ // 패턴 생성
+ const pattern = new fabric.Pattern({
+ source: combinedPatternCanvas,
+ repeat: 'repeat',
+ })
+
+ polygon.set('fill', null)
+ polygon.set('fill', pattern)
+ polygon.roofMaterial = roofMaterial
+ polygon.canvas?.renderAll()
+ } catch (e) {
+ console.log(e)
}
-
- const combinedPatternCanvas = document.createElement('canvas')
- combinedPatternCanvas.width = polygon.width * ratio
- combinedPatternCanvas.height = polygon.height * ratio
- const combinedCtx = combinedPatternCanvas.getContext('2d')
- // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘
- combinedCtx.drawImage(patternSourceCanvas, 0, 0)
- combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0)
-
- // 패턴 생성
- const pattern = new fabric.Pattern({
- source: combinedPatternCanvas,
- repeat: 'repeat',
- })
-
- polygon.set('fill', null)
- polygon.set('fill', pattern)
- polygon.roofMaterial = roofMaterial
- polygon.canvas?.renderAll()
}
function removeRoofMaterial(roof = currentObject) {
diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js
index 7bde5638..7c75ed30 100644
--- a/src/hooks/option/useCanvasSetting.js
+++ b/src/hooks/option/useCanvasSetting.js
@@ -149,7 +149,7 @@ export function useCanvasSetting(executeEffect = true) {
}
/** 초 1회만 실행하도록 처리 */
- addRoofMaterials()
+ //addRoofMaterials()
}, [])
/**
diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js
index 68d27530..c854ae77 100644
--- a/src/hooks/roofcover/useAuxiliaryDrawing.js
+++ b/src/hooks/roofcover/useAuxiliaryDrawing.js
@@ -675,7 +675,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, interSectionPointsWithRoofLines[0])
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, interSectionPointsWithRoofLines[0])
- if (!(distance1 === 0 || distance2 === 0)) {
+ if (!(distance1 < 1 || distance2 < 1)) {
if (distance1 >= distance2) {
const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
stroke: 'black',
@@ -704,6 +704,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
removeLine(line1)
}
intersectionPoints.current.push(interSectionPointsWithRoofLines[0])
+ return
}
}
@@ -722,7 +723,7 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
const distance1 = distanceBetweenPoints({ x: line1.x1, y: line1.y1 }, intersectionPoint)
const distance2 = distanceBetweenPoints({ x: line1.x2, y: line1.y2 }, intersectionPoint)
- if (distance1 === 0 || distance2 === 0) {
+ if (distance1 < 1 || distance2 < 1) {
return
}
//historyLine에서 기존 line을 제거한다.
@@ -849,8 +850,14 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
if (
lineHistory.current.some(
(history) =>
- JSON.stringify(history.startPoint) === JSON.stringify(line.startPoint) &&
- JSON.stringify(history.endPoint) === JSON.stringify(line.endPoint),
+ (Math.abs(history.startPoint.x - line.startPoint.x) < 2 &&
+ Math.abs(history.startPoint.y - line.startPoint.y) < 2 &&
+ Math.abs(history.endPoint.x - line.endPoint.x) < 2 &&
+ Math.abs(history.endPoint.y - line.endPoint.y) < 2) ||
+ (Math.abs(history.startPoint.x - line.endPoint.x) < 2 &&
+ Math.abs(history.startPoint.y - line.endPoint.y) < 2 &&
+ Math.abs(history.endPoint.x - line.startPoint.x) < 2 &&
+ Math.abs(history.endPoint.y - line.startPoint.y) < 2),
)
) {
canvas.remove(line)
diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js
index 1da5dcc8..d7680150 100644
--- a/src/hooks/roofcover/useMovementSetting.js
+++ b/src/hooks/roofcover/useMovementSetting.js
@@ -4,10 +4,10 @@ import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage'
import { useEffect, useRef, useState } from 'react'
import { useEvent } from '@/hooks/useEvent'
-import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
-import { QPolygon } from '@/components/fabric/QPolygon'
-import { getDegreeByChon } from '@/util/canvas-util'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
+import Big from 'big.js'
+import { calcLinePlaneSize } from '@/util/qpolygon-utils'
//동선이동 형 올림 내림
export function useMovementSetting(id) {
@@ -17,7 +17,6 @@ export function useMovementSetting(id) {
}
const canvas = useRecoilValue(canvasState)
const { initEvent, addCanvasMouseEventListener } = useEvent()
- // const { initEvent, addCanvasMouseEventListener } = useContext(EventContext)
const { closePopup } = usePopup()
const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState)
@@ -31,939 +30,419 @@ export function useMovementSetting(id) {
const { swalFire } = useSwal()
const FLOW_LINE_REF = {
+ POINTER_INPUT_REF: useRef(null),
FILLED_INPUT_REF: useRef(null),
DOWN_LEFT_RADIO_REF: useRef(null),
UP_RIGHT_RADIO_REF: useRef(null),
}
const UP_DOWN_REF = {
+ POINTER_INPUT_REF: useRef(null),
FILLED_INPUT_REF: useRef(null),
UP_RADIO_REF: useRef(null),
DOWN_RADIO_REF: useRef(null),
}
+ const CONFIRM_LINE_REF = useRef(null)
+ const FOLLOW_LINE_REF = useRef(null)
+
+ /** 동선이동, 형이동 선택시 속성 처리*/
useEffect(() => {
typeRef.current = type
+ selectedObject.current = null
+ if (FOLLOW_LINE_REF.current != null) {
+ canvas.remove(FOLLOW_LINE_REF.current)
+ canvas.renderAll()
+ FOLLOW_LINE_REF.current = null
+ }
+ if (CONFIRM_LINE_REF.current != null) {
+ canvas.remove(CONFIRM_LINE_REF.current)
+ canvas.renderAll()
+ CONFIRM_LINE_REF.current = null
+ }
+ clearRef()
+
+ canvas.discardActiveObject()
+ /** 전체 object 선택 불가 */
canvas.getObjects().forEach((obj) => {
obj.set({ selectable: false })
})
- const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // 기존 wallLine의 visible false
+
+ /** 지붕선 관련 속성 처리*/
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofs.forEach((roof) => {
- roof.set({ selectable: false })
- roof.set({ strokeWidth: 1 })
roof.set({ stroke: '#000000' })
roof.innerLines.forEach((line) => {
- line.bringToFront()
- line.set({ selectable: false })
- line.set({ strokeWidth: 1 })
- line.set({ stroke: '#000000' })
- })
- roof.separatePolygon?.forEach((polygon) => {
- polygon.bringToFront()
- polygon.set({ selectable: false })
- polygon.set({ strokeWidth: 1 })
- polygon.set({ stroke: '#000000' })
+ if (type === TYPE.FLOW_LINE && line.name === LINE_TYPE.SUBLINE.RIDGE) {
+ line.set({ selectable: true, strokeWidth: 5, stroke: '#1083E3' })
+ line.bringToFront()
+ } else {
+ line.set({ selectable: false, strokeWidth: 2, stroke: '#000000' })
+ }
})
})
- if (type === TYPE.FLOW_LINE) {
- roofs.forEach((roof) => {
- roof.innerLines.forEach((line) => {
- if (line.name === LINE_TYPE.SUBLINE.RIDGE) {
- line.bringToFront()
- line.set({ visible: true })
- line.set({ selectable: true })
- line.set({ strokeWidth: 4 })
- line.set({ stroke: '#1083E3' })
- }
- })
+ /** 외벽선 관련 속성 처리*/
+ const walls = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
+ walls.forEach((wall) => {
+ if (wall.baseLines.length === 0) {
+ wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id)
+ }
+ wall.baseLines.forEach((line) => {
+ if (type === TYPE.UP_DOWN) {
+ line.set({ selectable: true, visible: true, stroke: '#1083E3', strokeWidth: 5 })
+ line.bringToFront()
+ } else {
+ line.set({ selectable: false, visible: false })
+ }
})
- } else if (type === TYPE.UP_DOWN) {
- const wallLine = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 outerLine의 selectable true
- wallLine.forEach((line) => {
- line.bringToFront()
- line.set({ selectable: true })
- })
- }
+ })
+
+ /** outerLines 속성처리*/
+ const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+ outerLines.forEach((line) => line.set({ visible: false }))
canvas.renderAll()
}, [type])
+ /** 팝업창이 열릴때,닫힐때 속성들을 처리*/
useEffect(() => {
- canvas.renderAll()
addCanvasMouseEventListener('mouse:move', mouseMoveEvent)
addCanvasMouseEventListener('mouse:down', mouseDownEvent)
return () => {
- initEvent()
-
- const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
- wallLines.forEach((line) => {
- line.set({ visible: true })
+ canvas.discardActiveObject()
+ if (FOLLOW_LINE_REF.current != null) {
+ canvas.remove(FOLLOW_LINE_REF.current)
+ FOLLOW_LINE_REF.current = null
+ }
+ if (CONFIRM_LINE_REF.current != null) {
+ canvas.remove(CONFIRM_LINE_REF.current)
+ CONFIRM_LINE_REF.current = null
+ }
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ roofs.forEach((roof) => {
+ roof.set({ stroke: '#1083E3' })
+ roof.innerLines.forEach((line) => line.set({ selectable: true, strokeWidth: 2, stroke: '#1083E3' }))
})
-
- const wallLine = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 outerLine의 selectable true
- wallLine.forEach((line) => {
- let wallStroke, wallStrokeWidth
- switch (line.attributes.type) {
- case LINE_TYPE.WALLLINE.EAVES:
- wallStroke = '#45CD7D'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.HIPANDGABLE:
- wallStroke = '#45CD7D'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.GABLE:
- wallStroke = '#3FBAE6'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.JERKINHEAD:
- wallStroke = '#3FBAE6'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.SHED:
- wallStroke = '#000000'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.WALL:
- wallStroke = '#000000'
- wallStrokeWidth = 4
- break
+ const walls = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
+ walls.forEach((wall) => {
+ if (wall.baseLines.length === 0) {
+ wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id)
}
- line.set({ selectable: false, stroke: wallStroke, strokeWidth: wallStrokeWidth })
+ wall.baseLines.forEach((baseLine) => {
+ baseLine.set({ selectable: false, visible: false })
+ })
})
-
+ const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+ outerLines.forEach((line) => line.set({ visible: true }))
canvas.renderAll()
}
}, [])
+ /** object 선택이 변경될 때 처리*/
useEffect(() => {
- console.log('selectedObject.current : ', selectedObject.current)
- if (selectedObject.current != null) {
- let wallStroke
- switch (selectedObject.current.attributes.type) {
- case LINE_TYPE.WALLLINE.EAVES:
- wallStroke = '#45CD7D'
- break
- case LINE_TYPE.WALLLINE.HIPANDGABLE:
- wallStroke = '#45CD7D'
- break
- case LINE_TYPE.WALLLINE.GABLE:
- wallStroke = '#3FBAE6'
- break
- case LINE_TYPE.WALLLINE.JERKINHEAD:
- wallStroke = '#3FBAE6'
- break
- case LINE_TYPE.WALLLINE.SHED:
- wallStroke = '#000000'
- break
- case LINE_TYPE.WALLLINE.WALL:
- wallStroke = '#000000'
- break
- }
- selectedObject.current.set({ stroke: wallStroke })
- selectedObject.current.bringToFront()
- }
- selectedObject.current = null
- if (!currentObject) {
- return
+ if (FOLLOW_LINE_REF.current != null) {
+ canvas.remove(FOLLOW_LINE_REF.current)
+ canvas.renderAll()
+ FOLLOW_LINE_REF.current = null
}
- clearRef()
- selectedObject.current = currentObject
- if (currentObject.name === 'wallLine') {
- currentObject.set({ stroke: '#EA10AC' })
- currentObject.bringToFront()
+ if (selectedObject.current != null) {
+ selectedObject.current.set({ stroke: '#1083E3' })
+ selectedObject.current = null
}
+ if (!currentObject) return
+
+ clearRef()
+
+ canvas
+ .getObjects()
+ .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine')
+ .forEach((obj) => canvas.remove(obj))
+ canvas.renderAll()
+
+ if (CONFIRM_LINE_REF.current != null) {
+ canvas.remove(CONFIRM_LINE_REF.current)
+ canvas.renderAll()
+ CONFIRM_LINE_REF.current = null
+ }
+
+ currentObject.set({ stroke: '#EA10AC' })
+ selectedObject.current = currentObject
+
+ const followLine = new fabric.Line([currentObject.x1, currentObject.y1, currentObject.x2, currentObject.y2], {
+ stroke: '#000000',
+ strokeWidth: 4,
+ selectable: false,
+ name: 'followLine',
+ })
+ canvas.add(followLine)
+ FOLLOW_LINE_REF.current = followLine
+
+ canvas.on('mouse:move', (event) => {
+ const mousePos = canvas.getPointer(event.e)
+ if (followLine.x1 === followLine.x2) {
+ followLine.left = mousePos.x - 2
+ } else {
+ followLine.top = mousePos.y - 2
+ }
+ canvas.renderAll()
+ })
+
canvas.renderAll()
}, [currentObject])
const clearRef = () => {
if (type === TYPE.FLOW_LINE) {
+ FLOW_LINE_REF.POINTER_INPUT_REF.current.value = ''
FLOW_LINE_REF.FILLED_INPUT_REF.current.value = ''
FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true
FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = false
}
if (type === TYPE.UP_DOWN) {
+ UP_DOWN_REF.POINTER_INPUT_REF.current.value = ''
UP_DOWN_REF.FILLED_INPUT_REF.current.value = ''
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
}
}
- const mouseDownEvent = (e) => {
- if (typeRef.current === TYPE.FLOW_LINE) {
- saveFlowLine(e)
- } else {
- saveUpDownLine(e)
- }
- }
-
const mouseMoveEvent = (e) => {
+ const target = canvas.getActiveObject()
+ if (!target) return
+
+ const { top: targetTop, left: targetLeft } = target
+ const currentX = Big(canvas.getPointer(e.e).x).round(0, Big.roundUp)
+ const currentY = Big(canvas.getPointer(e.e).y).round(0, Big.roundUp)
+ let value = ''
+ if (target.y1 === target.y2) {
+ value = Big(targetTop).minus(currentY).times(10)
+ } else {
+ value = Big(targetLeft).minus(currentX).times(10).neg()
+ }
if (typeRef.current === TYPE.FLOW_LINE) {
- flowLineMoveEvent(e)
+ FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber()
} else {
- updownMoveEvent(e)
+ UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber()
+ const midX = Big(target.x1).plus(target.x2).div(2)
+ const midY = Big(target.y1).plus(target.y2).div(2)
+ const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId)
+ let checkPoint
+ if (target.y1 === target.y2) {
+ checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() }
+ if (wall.inPolygon(checkPoint)) {
+ if (value.s === -1) {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = false
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
+ } else {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = true
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
+ }
+ } else {
+ if (value.s === 1) {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = false
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
+ } else {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = true
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
+ }
+ }
+ } else {
+ checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() }
+ if (wall.inPolygon(checkPoint)) {
+ if (value.s === 1) {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = false
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
+ } else {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = true
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
+ }
+ } else {
+ if (value.s === -1) {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = false
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
+ } else {
+ UP_DOWN_REF.UP_RADIO_REF.current.checked = true
+ UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
+ }
+ }
+ }
}
}
- function edgesIntersection(edgeA, edgeB) {
- const den =
- (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) -
- (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y)
-
- if (den === 0) {
- return null // lines are parallel or coincident
- }
-
- const ua =
- ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
- (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
- den
-
- const ub =
- ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
- (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
- den
-
- // Edges are not intersecting but the lines defined by them are
- const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1
-
- return {
- x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x),
- y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y),
- isIntersectionOutside,
- }
- }
-
- //동선 이동 마우스 클릭 이벤트
- const saveFlowLine = (e) => {
- const target = selectedObject.current
- if (!target) {
- return
- }
-
- let newPoint = []
- if (Math.sign(target.x1 - target.x2) !== 0) {
- const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? 1 : -1
- newPoint = [
- target.x1,
- target.y1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10),
- target.x2,
- target.y2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10),
- ]
- } else {
- const sign = FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked ? -1 : 1
- newPoint = [
- target.x1 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10),
- target.y1,
- target.x2 - sign * Number(FLOW_LINE_REF.FILLED_INPUT_REF.current.value / 10),
- target.y2,
- ]
- }
- newPoint = newPoint.map((point) => Math.round(point * 10) / 10)
- const cloned = new fabric.Line(newPoint, {})
- let currentObject = selectedObject.current
- const currentX1 = currentObject.x1,
- currentY1 = currentObject.y1,
- currentX2 = currentObject.x2,
- currentY2 = currentObject.y2
-
- const currentMidX = (currentX1 + currentX2) / 2
- const currentMidY = (currentY1 + currentY2) / 2
- const clonedMidX = (cloned.x1 + cloned.x2) / 2
- const clonedMidY = (cloned.y1 + cloned.y2) / 2
-
- const roof = canvas.getObjects().find((obj) => obj.id === currentObject.attributes.roofId)
- let isOutside = false
- roof.lines.forEach((line) => {
- const intersection = edgesIntersection(
- { vertex1: { x: currentMidX, y: currentMidY }, vertex2: { x: clonedMidX, y: clonedMidY } },
- { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
- )
- if (intersection && !intersection.isIntersectionOutside) {
- isOutside = true
- }
- })
- if (isOutside) {
- swalFire({ text: getMessage('modal.movement.flow.line.move.alert'), icon: 'error' })
- return
- }
- currentObject.set({ x1: cloned.x1, y1: cloned.y1, x2: cloned.x2, y2: cloned.y2 })
- currentObject.setCoords()
- const otherRidges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.id !== currentObject.id)
- const overlapRidges = otherRidges.filter((line) => {
- if (currentObject.x1 === currentObject.x2 && line.x1 === line.x2 && 0 < Math.abs(currentObject.x1 - line.x1) < 1) {
- if (
- currentObject.y1 <= line.y1 <= currentObject.y2 ||
- currentObject.y1 >= line.y1 >= currentObject.y2 ||
- currentObject.y1 <= line.y2 <= currentObject.y2 ||
- currentObject.y1 >= line.y2 >= currentObject.y2
- ) {
- return true
- }
- }
- if (currentObject.y1 === currentObject.y2 && line.y1 === line.y2 && 0 < Math.abs(currentObject.y1 - line.y1) < 1) {
- if (
- currentObject.x1 <= line.x1 <= currentObject.x2 ||
- currentObject.x1 >= line.x1 >= currentObject.x2 ||
- currentObject.x1 <= line.x2 <= currentObject.x2 ||
- currentObject.x1 >= line.x2 >= currentObject.x2
- ) {
- return true
- }
- }
- })
- if (overlapRidges.length > 0) {
- const minX = Math.min(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2)
- const maxX = Math.max(...overlapRidges.flatMap((line) => [line.x1, line.x2]), currentObject.x1, currentObject.x2)
- const minY = Math.min(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2)
- const maxY = Math.max(...overlapRidges.flatMap((line) => [line.y1, line.y2]), currentObject.y1, currentObject.y2)
- const newRidge = new fabric.Line([minX, minY, maxX, maxY], {
- name: LINE_TYPE.SUBLINE.RIDGE,
- selectable: true,
- stroke: '#1083E3',
- strokeWidth: 4,
- attributes: { roofId: roof.id, currentRoofId: currentObject.attributes.currentRoofId },
- })
-
- overlapRidges.forEach((line) =>
- line.attributes.currentRoofId.forEach((id) => {
- if (!newRidge.attributes.currentRoofId.includes(id)) {
- newRidge.attributes.currentRoofId.push(id)
- }
- }),
- )
- canvas.add(newRidge)
- newRidge.bringToFront()
- roof.innerLines.push(newRidge)
-
- canvas.remove(currentObject)
- roof.innerLines.forEach((innerLine, index) => {
- if (innerLine.id === currentObject.id) {
- roof.innerLines.splice(index, 1)
- }
- })
-
- overlapRidges.forEach((line) => {
- canvas.remove(line)
- roof.innerLines.forEach((innerLine, index) => {
- if (innerLine.id === line.id) {
- roof.innerLines.splice(index, 1)
- }
- })
- })
- }
-
- if (roof.separatePolygon.length > 0) {
- redrawSeparatePolygon(roof)
- } else {
- roof.innerLines
- .filter((line) => currentObject !== line)
- .filter(
- (line) =>
- (line.x1 === currentX1 && line.y1 === currentY1) ||
- (line.x2 === currentX1 && line.y2 === currentY1) ||
- (line.x1 === currentX2 && line.y1 === currentY2) ||
- (line.x2 === currentX2 && line.y2 === currentY2),
- )
- .forEach((line) => {
- const lineDegree = 90 - Math.asin(line.attributes.planeSize / line.attributes.actualSize) * (180 / Math.PI)
- if (line.x1 === currentX1 && line.y1 === currentY1) {
- line.set({ x1: newPoint[0], y1: newPoint[1] })
- } else if (line.x2 === currentX1 && line.y2 === currentY1) {
- line.set({ x2: newPoint[0], y2: newPoint[1] })
- } else if (line.x1 === currentX2 && line.y1 === currentY2) {
- line.set({ x1: newPoint[2], y1: newPoint[3] })
- } else if (line.x2 === currentX2 && line.y2 === currentY2) {
- line.set({ x2: newPoint[2], y2: newPoint[3] })
- }
- line.setCoords()
- line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10)
- line.attributes.actualSize = Math.round(
- Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(line.attributes.planeSize * Math.tan(lineDegree * (Math.PI / 180)), 2)),
- )
- })
- }
-
- currentObject.set({ x1: cloned.x1, y1: cloned.y1, x2: cloned.x2, y2: cloned.y2 })
- currentObject.setCoords()
-
+ const mouseDownEvent = (e) => {
+ canvas
+ .getObjects()
+ .filter((obj) => obj.name === 'checkPoint' || obj.name === 'checkLine')
+ .forEach((obj) => canvas.remove(obj))
canvas.renderAll()
- canvas.discardActiveObject()
- FLOW_LINE_REF.FILLED_INPUT_REF.current.value = ''
- }
-
- //형 올림내림 마우스 클릭 이벤트
- const saveUpDownLine = (e) => {
const target = selectedObject.current
- if (!target) {
- return
- }
+ if (!target) return
- const roof = canvas.getObjects().find((obj) => obj.id === target.attributes.roofId)
- const wallLines = roof.wall.lines
- const roofLines = roof.lines
- const currentWall = wallLines.find((line) => line.id === target.attributes.currentWall)
- let prevWall, nextWall
- let prevEave, nextEave
+ const roofId = target.attributes.roofId
+ const followLine = canvas.getObjects().find((obj) => obj.name === 'followLine')
- wallLines.forEach((wallLine, index) => {
- if (wallLine.id === currentWall.id) {
- prevWall = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1]
- nextWall = index === wallLines.length - 1 ? wallLines[0] : wallLines[index + 1]
- }
- })
-
- const eaves = roofLines.filter((line) => line.attributes?.type === LINE_TYPE.WALLLINE.EAVES)
-
- wallLines
- .filter((line) => line.attributes.type === LINE_TYPE.WALLLINE.EAVES)
- .forEach((eave, index) => {
- if (eave.id === currentWall.id) {
- prevEave = index === 0 ? eaves[eaves.length - 1] : eaves[index - 1]
- nextEave = index === eaves.length - 1 ? eaves[0] : eaves[index + 1]
- }
- })
-
- const currentRoof = roofLines.find((line) => line.id === target.attributes.currentRoof)
- const currentRidges = roof.innerLines.filter(
- (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(currentRoof.id),
- )
- /*const prevWallRidges = roof.innerLines.filter(
- (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(prevEave.id),
- )
- const nextWallRidges = roof.innerLines.filter(
- (line) => line.name === LINE_TYPE.SUBLINE.RIDGE && line.attributes.currentRoof.includes(nextEave.id),
- )
- //라인 확인 작업
- /!* const roofLine = new fabric.Line([currentRoof.x1, currentRoof.y1, currentRoof.x2, currentRoof.y2], {
- stroke: 'red',
- strokeWidth: 5,
+ const confirmLine = new fabric.Line([followLine.x1, followLine.y1, followLine.x2, followLine.y2], {
+ left: followLine.left,
+ top: followLine.top,
+ stroke: '#000000',
+ strokeWidth: 4,
selectable: false,
+ parentId: roofId,
+ name: 'confirmLine',
+ target,
})
- canvas.add(roofLine)
+ canvas.add(confirmLine)
+ canvas.renderAll()
+ CONFIRM_LINE_REF.current = confirmLine
- const prevEaves = new fabric.Line([prevEave.x1, prevEave.y1, prevEave.x2, prevEave.y2], {
- stroke: 'yellow',
- strokeWidth: 5,
- selectable: false,
- })
- canvas.add(prevEaves)
- currentRidges.forEach((ridge) => ridge.set({ stroke: 'purple', strokeWidth: 5 }))
- prevWallRidges.forEach((ridge) => ridge.set({ stroke: 'yellow', strokeWidth: 5 }))
- nextWallRidges.forEach((ridge) => ridge.set({ stroke: 'green', strokeWidth: 5 }))
- canvas.renderAll()*!/*/
-
- console.log(
- 'UP/DOWN',
- UP_DOWN_REF.UP_RADIO_REF.current.checked,
- UP_DOWN_REF.DOWN_RADIO_REF.current.checked,
- UP_DOWN_REF.FILLED_INPUT_REF.current.value,
- )
-
- let compareLine
- if (Math.sign(currentRoof.x1 - currentRoof.x2) === 0) {
- const currentDiff = currentRidges[0].x1 - currentRoof.x1
- const prevDiff = prevEave.x1 - currentRoof.x1
- const nextDiff = nextEave.x1 - currentRoof.x1
-
- console.log('currentDiff', Math.sign(currentDiff), currentDiff)
-
- if (UP_DOWN_REF.UP_RADIO_REF.current.checked) {
- if (Math.sign(currentDiff) === 1) {
- } else {
- if (Math.sign(prevDiff) === 1 && Math.sign(nextDiff) === 1) {
- } else {
- }
- }
- } else {
- if (Math.sign(currentDiff) === -1) {
- } else {
- if (Math.sign(prevDiff) === -1 && Math.sign(nextDiff) === -1) {
- } else {
- }
- }
- }
- } else {
- const currentDiff = currentRidges[0].y1 - currentRoof.y1
- const prevDiff = prevEave.y1 - currentRoof.y1
- const nextDiff = nextEave.y1 - currentRoof.y1
-
- if (UP_DOWN_REF.UP_RADIO_REF.current.checked) {
- if (Math.sign(currentDiff) === 1) {
- } else {
- if (Math.sign(prevDiff) === 1 && Math.sign(nextDiff) === 1) {
- } else {
- }
- }
- } else {
- if (Math.sign(currentDiff) === -1) {
- } else {
- if (Math.sign(prevDiff) === -1 && Math.sign(nextDiff) === -1) {
- } else {
- }
- }
- }
- }
-
- //확인
- /*const compareRoofLine = new fabric.Line([compareRoof.x1, compareRoof.y1, compareRoof.x2, compareRoof.y2], {
- stroke: 'red',
- strokeWidth: 5,
- selectable: false,
- })
- canvas.add(compareRoofLine)
- canvas.renderAll()*/
- }
-
- const redrawSeparatePolygon = (roof) => {
- roof.separatePolygon.forEach((polygon) => canvas.remove(polygon))
- roof.separatePolygon = []
- const roofLines = roof.lines
- const wallLines = roof.wall.lines
- const eaves = []
- roofLines.forEach((currentRoof, index) => {
- if (currentRoof.attributes?.type === LINE_TYPE.WALLLINE.EAVES) {
- eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize })
- }
- })
- const ridges = roof.innerLines.filter((line) => line.name === LINE_TYPE.SUBLINE.RIDGE)
- eaves.sort((a, b) => a.length - b.length)
-
- const ridgeCount = eaves.length - 1
- console.log('ridgeCount', ridgeCount, 'ridges', ridges.length)
-
- const duplicatedEaves = []
- ridges.forEach((ridge) => {
- const positiveLines = []
- const negativeLines = []
- ridge.attributes.currentRoof.forEach((id) => {
- console.log('id : ', id)
- const eave = roofLines.find((obj) => obj.id === id)
- console.log('roof : ', eave)
- if (ridge.x1 === ridge.x2) {
- if (eave.y1 < eave.y2) {
- positiveLines.push(eave)
- } else {
- negativeLines.push(eave)
- }
- } else {
- if (eave.x1 < eave.x2) {
- positiveLines.push(eave)
- } else {
- negativeLines.push(eave)
- }
- }
- })
- if (positiveLines.length > 1) {
- duplicatedEaves.push(positiveLines)
- }
- if (negativeLines.length > 1) {
- duplicatedEaves.push(negativeLines)
- }
- })
- console.log('duplicatedEaves', duplicatedEaves)
-
- duplicatedEaves.forEach((duplicatedEave) => {
- duplicatedEave.forEach((eave) => {
- const index = eaves.findIndex((item) => item.roof.id === eave.id)
- eaves.splice(index, 1)
- })
- })
-
- // if (ridgeCount === ridges.length) {
- eaves.forEach((eave, i) => {
- const index = eave.index,
- currentRoof = eave.roof
- const currentWall = wallLines[index]
- const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.roof.id))
- let points = []
- const signX = Math.sign(currentRoof.x1 - currentRoof.x2)
- let currentX1 = currentRoof.x1,
- currentY1 = currentRoof.y1,
- currentX2 = currentRoof.x2,
- currentY2 = currentRoof.y2
- let changeX1 = [Math.min(currentRoof.x1, currentRoof.x2), Math.min(currentRoof.x1, currentRoof.x2)],
- changeY1 = [Math.min(currentRoof.y1, currentRoof.y2), Math.min(currentRoof.y1, currentRoof.y2)],
- changeX2 = [Math.max(currentRoof.x2, currentRoof.x1), Math.max(currentRoof.x2, currentRoof.x1)],
- changeY2 = [Math.max(currentRoof.y2, currentRoof.y1), Math.max(currentRoof.y2, currentRoof.y1)]
-
- if (signX === 0) {
- currentY1 = Math.min(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2)
- changeY1[1] = currentY1
- currentY2 = Math.max(currentRoof.y1, currentRoof.y2, currentWall.y1, currentWall.y2)
- changeY2[1] = currentY2
- } else {
- currentX1 = Math.min(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2)
- changeX1[1] = currentX1
- currentX2 = Math.max(currentRoof.x1, currentRoof.x2, currentWall.x1, currentWall.x2)
- changeX2[1] = currentX2
- }
- points.push({ x: currentX1, y: currentY1 }, { x: currentX2, y: currentY2 })
-
- currentRidges.forEach((ridge) => {
- let ridgeX1 = ridge.x1,
- ridgeY1 = ridge.y1,
- ridgeX2 = ridge.x2,
- ridgeY2 = ridge.y2
- if (signX === 0) {
- ridgeY1 = Math.min(ridge.y1, ridge.y2)
- ridgeY2 = Math.max(ridge.y1, ridge.y2)
- } else {
- ridgeX1 = Math.min(ridge.x1, ridge.x2)
- ridgeX2 = Math.max(ridge.x1, ridge.x2)
- }
- points.push({ x: ridgeX1, y: ridgeY1 }, { x: ridgeX2, y: ridgeY2 })
- })
-
- points.forEach((point) => {
- if (point.x === changeX1[0] && changeX1[0] !== changeX1[1]) {
- point.x = changeX1[1]
- }
- if (point.x === changeX2[0] && changeX2[0] !== changeX2[1]) {
- point.x = changeX2[1]
- }
- if (point.y === changeY1[0] && changeY1[0] !== changeY1[1]) {
- point.y = changeY1[1]
- }
- if (point.y === changeY2[0] && changeY2[0] !== changeY2[1]) {
- point.y = changeY2[1]
- }
- })
- //중복된 point 제거
- points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
- //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.)
- const startPoint = points
- .filter((point) => point.x === Math.min(...points.map((point) => point.x)))
- .reduce((prev, current) => {
- return prev.y < current.y ? prev : current
- })
-
- const sortedPoints = []
- sortedPoints.push(startPoint)
-
- points.forEach((p, index) => {
- if (index === 0) {
- //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점
- const underStartPoint = points.filter((point) => point.y > startPoint.y)
- const nextPoint = underStartPoint
- .filter((point) => point.x === startPoint.x)
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- }
- return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- } else {
- const nextPoint = underStartPoint.reduce((prev, current) => {
- const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2)))
- const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2)))
- return prevHypos < currentHypos ? prev : current
- }, undefined)
- sortedPoints.push(nextPoint)
- }
- } else {
- const lastPoint = sortedPoints[sortedPoints.length - 1]
- console.log('lastPoint', lastPoint)
- const prevPoint = sortedPoints[sortedPoints.length - 2]
- const otherPoints = points.filter((point) => sortedPoints.includes(point) === false)
- const nextPoint = otherPoints.reduce((prev, current) => {
- if (prev === undefined) {
- const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
- const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
- const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
-
- const angle = Math.round(
- Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI),
- )
- if (angle === 90) {
- return current
- }
- } else {
- return prev
- }
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- } else {
- const nextPoint = otherPoints.reduce((prev, current) => {
- if (prev !== undefined) {
- const height = Math.abs(
- Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))),
- )
- const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
- const hypotenuseC = Math.abs(
- Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))),
- )
- const angleC = Math.round(
- Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI),
- )
- const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2))))
- const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2))))
- const angleP = Math.round(
- Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI),
- )
- if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) {
- return current
- } else {
- return prev
- }
- } else {
- return current
- }
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- }
- }
- }
- })
-
- if (sortedPoints.length > 0) {
- const roofPolygon = new QPolygon(sortedPoints, {
- fill: 'transparent',
- stroke: '#000000',
- strokeWidth: 1,
- selectable: false,
- fontSize: roof.fontSize,
- name: 'roofPolygon',
- attributes: {
- roofId: roof.id,
- currentRoofId: currentRoof.id,
- pitch: currentRoof.attributes.pitch,
- degree: currentRoof.attributes.degree,
- direction: currentRoof.direction,
- },
- })
- const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
- //지붕 각도에 따른 실측치 조정
- roofPolygon.lines.forEach((line) => {
- line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10)
- const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1))
-
- if (currentDegree > 0 && slope(line) !== slope(currentRoof)) {
- const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize
- line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2)))
- } else {
- line.attributes.actualSize = line.attributes.planeSize
- }
- })
- roof.separatePolygon.push(roofPolygon)
- canvas.add(roofPolygon)
- canvas.renderAll()
- }
- })
-
- duplicatedEaves.forEach((duplicatedEave) => {
- const currentRoof = duplicatedEave[0]
- let points = []
- duplicatedEave.forEach((eave) => {
- points.push({ x: eave.x1, y: eave.y1 }, { x: eave.x2, y: eave.y2 })
- const currentRidges = ridges.filter((ridge) => ridge.attributes.currentRoof.includes(eave.id))
- currentRidges.forEach((ridge) => {
- points.push({ x: ridge.x1, y: ridge.y1 }, { x: ridge.x2, y: ridge.y2 })
- })
- })
- console.log('points', points)
- points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
- //point 정렬 (가장 좌측, 최상단의 점을 기준으로 삼는다.)
-
- const startPoint = points
- .filter((point) => point.x === Math.min(...points.map((point) => point.x)))
- .reduce((prev, current) => {
- return prev.y < current.y ? prev : current
- })
-
- const sortedPoints = []
- sortedPoints.push(startPoint)
-
- points.forEach((p, index) => {
- if (index === 0) {
- //시작점 다음 점 찾기, y좌표가 startPoint.y 보다 큰 점 중 x좌표가 가까운 점
- const underStartPoint = points.filter((point) => point.y > startPoint.y)
- const nextPoint = underStartPoint
- .filter((point) => point.x === startPoint.x)
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- }
- return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- } else {
- const nextPoint = underStartPoint.reduce((prev, current) => {
- const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2)))
- const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2)))
- return prevHypos < currentHypos ? prev : current
- }, undefined)
- sortedPoints.push(nextPoint)
- }
- } else {
- const lastPoint = sortedPoints[sortedPoints.length - 1]
- console.log('lastPoint', lastPoint)
- const prevPoint = sortedPoints[sortedPoints.length - 2]
- const otherPoints = points.filter((point) => sortedPoints.includes(point) === false)
- const nextPoint = otherPoints.reduce((prev, current) => {
- if (prev === undefined) {
- const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
- const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
- const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
-
- const angle = Math.round(
- Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI),
- )
- if (angle === 90) {
- return current
- }
- } else {
- return prev
- }
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- } else {
- const nextPoint = otherPoints.reduce((prev, current) => {
- if (prev !== undefined) {
- const height = Math.abs(
- Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))),
- )
- const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
- const hypotenuseC = Math.abs(
- Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))),
- )
- const angleC = Math.round(
- Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI),
- )
- const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2))))
- const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2))))
- const angleP = Math.round(
- Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI),
- )
- if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) {
- return current
- } else {
- return prev
- }
- } else {
- return current
- }
- }, undefined)
- if (nextPoint) {
- sortedPoints.push(nextPoint)
- }
- }
- }
- })
-
- if (sortedPoints.length > 0) {
- const roofPolygon = new QPolygon(sortedPoints, {
- fill: 'transparent',
- stroke: '#000000',
- strokeWidth: 1,
- selectable: false,
- fontSize: roof.fontSize,
- name: 'roofPolygon',
- attributes: {
- roofId: roof.id,
- currentRoofId: currentRoof.id,
- pitch: currentRoof.attributes.pitch,
- degree: currentRoof.attributes.degree,
- direction: currentRoof.direction,
- },
- })
- const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
- //지붕 각도에 따른 실측치 조정
- roofPolygon.lines.forEach((line) => {
- line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x2 - line.x1, 2) + Math.pow(line.y2 - line.y1, 2)) * 10)
- const slope = (line) => (line.x2 - line.x1 === 0 ? Infinity : (line.y2 - line.y1) / (line.x2 - line.x1))
-
- if (currentDegree > 0 && slope(line) !== slope(currentRoof)) {
- const height = Math.tan(currentDegree * (Math.PI / 180)) * line.attributes.planeSize
- line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.attributes.planeSize, 2) + Math.pow(height, 2)))
- } else {
- line.attributes.actualSize = line.attributes.planeSize
- }
- })
- roof.separatePolygon.push(roofPolygon)
- canvas.add(roofPolygon)
- canvas.renderAll()
- }
- })
- console.log('roof.separatePolygon : ', roof.separatePolygon)
-
- ridges.forEach((ridge) => ridge.bringToFront())
- console.log('ridges : ', ridges)
- }
-
- const flowLineMoveEvent = (e) => {
- const target = canvas.getActiveObject()
- if (!target) {
- return
- }
- const { top: targetTop, left: targetLeft } = target
- const currentX = canvas.getPointer(e.e).x
- const currentY = Math.floor(canvas.getPointer(e.e).y)
-
- if (Math.sign(target.x1 - target.x2) !== 0) {
- if (targetTop > currentY) {
- FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true
- FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000)
- } else {
- FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true
- FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(targetTop - currentY))) / 10000).toFixed(5) * 100000)
- }
- } else {
- if (targetLeft < currentX) {
- FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked = true
- FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000)
- } else {
- FLOW_LINE_REF.DOWN_LEFT_RADIO_REF.current.checked = true
- FLOW_LINE_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(Math.round(currentX - targetLeft))) / 10000).toFixed(5) * 100000)
- }
- }
-
- canvas?.renderAll()
- }
-
- const updownMoveEvent = (e) => {
- const target = canvas.getActiveObject()
- if (!target) {
- return
- }
- const { top: targetTop, left: targetLeft } = target
- const currentX = canvas.getPointer(e.e).x
- const currentY = Math.floor(canvas.getPointer(e.e).y)
-
- if (Math.sign(target.x1 - target.x2) !== 0) {
- if (targetTop > currentY) {
- UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
- UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000)
- } else {
- UP_DOWN_REF.UP_RADIO_REF.current.checked = true
- UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetTop - currentY)) / 10000).toFixed(5) * 100000)
- }
- } else {
- if (targetLeft > currentX) {
- UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
- UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000)
- } else {
- UP_DOWN_REF.UP_RADIO_REF.current.checked = true
- UP_DOWN_REF.FILLED_INPUT_REF.current.value = Math.floor((Number(Math.abs(targetLeft - currentX)) / 10000).toFixed(5) * 100000)
- }
- }
-
- canvas?.renderAll()
+ handleSave()
}
const handleSave = () => {
- if (type === TYPE.FLOW_LINE) {
- saveFlowLine()
- } else {
- saveUpDownLine()
+ if (CONFIRM_LINE_REF.current !== null) {
+ canvas.remove(CONFIRM_LINE_REF.current)
+ CONFIRM_LINE_REF.current = null
+ canvas.renderAll()
}
+ if (FOLLOW_LINE_REF.current !== null) {
+ canvas.remove(FOLLOW_LINE_REF.current)
+ FOLLOW_LINE_REF.current = null
+ canvas.renderAll()
+ }
+
+ const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target
+ if (!target) return
+
+ const roofId = target.attributes.roofId
+ const roof = canvas.getObjects().find((obj) => obj.id === roofId)
+ const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId)
+ const baseLines = wall.baseLines
+ let targetBaseLines = []
+
+ if (typeRef.current === TYPE.FLOW_LINE) {
+ const lineVector =
+ target.y1 === target.y2
+ ? FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked
+ ? 'up'
+ : 'down'
+ : FLOW_LINE_REF.UP_RIGHT_RADIO_REF.current.checked
+ ? 'right'
+ : 'left'
+ switch (lineVector) {
+ case 'up':
+ baseLines
+ .filter((line) => line.y1 === line.y2 && line.y1 < target.y1)
+ .forEach((line) => targetBaseLines.push({ line, distance: Big(target.y1).minus(line.y1).abs().toNumber() }))
+ break
+ case 'down':
+ baseLines
+ .filter((line) => line.y1 === line.y2 && line.y1 > target.y1)
+ .forEach((line) => targetBaseLines.push({ line, distance: Big(line.y1).minus(target.y1).abs().toNumber() }))
+ break
+ case 'right':
+ baseLines
+ .filter((line) => line.x1 === line.x2 && line.x1 > target.x1)
+ .forEach((line) => targetBaseLines.push({ line, distance: Big(target.x1).minus(line.x1).abs().toNumber() }))
+
+ break
+ case 'left':
+ baseLines
+ .filter((line) => line.x1 === line.x2 && line.x1 < target.x1)
+ .forEach((line) => targetBaseLines.push({ line, distance: Big(line.x1).minus(target.x1).abs().toNumber() }))
+ break
+ }
+ } else {
+ targetBaseLines.push({ line: target, distance: 0 })
+ }
+
+ targetBaseLines.sort((a, b) => a.distance - b.distance)
+ targetBaseLines = targetBaseLines.filter((line) => line.distance === targetBaseLines[0].distance)
+ let value
+ if (typeRef.current === TYPE.FLOW_LINE) {
+ value =
+ FLOW_LINE_REF.FILLED_INPUT_REF.current.value !== ''
+ ? Big(FLOW_LINE_REF.FILLED_INPUT_REF.current.value).times(2)
+ : Big(FLOW_LINE_REF.POINTER_INPUT_REF.current.value).times(2)
+ if (target.y1 === target.y2) {
+ value = value.neg()
+ }
+ } else {
+ value =
+ UP_DOWN_REF.FILLED_INPUT_REF.current.value !== ''
+ ? Big(UP_DOWN_REF.FILLED_INPUT_REF.current.value)
+ : Big(UP_DOWN_REF.POINTER_INPUT_REF.current.value)
+
+ const midX = Big(target.x1).plus(target.x2).div(2)
+ const midY = Big(target.y1).plus(target.y2).div(2)
+ const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId)
+ let checkPoint
+ if (target.y1 === target.y2) {
+ checkPoint = { x: midX.toNumber(), y: midY.plus(10).times(value.s).toNumber() }
+ } else {
+ checkPoint = { x: midX.plus(10).times(value.s).toNumber(), y: midY.toNumber() }
+ }
+
+ const inPolygon = wall.inPolygon(checkPoint)
+
+ console.log('inPolygon', inPolygon)
+ if (UP_DOWN_REF.UP_RADIO_REF.current.checked && inPolygon) {
+ value = value.neg()
+ } else if (UP_DOWN_REF.DOWN_RADIO_REF.current.checked && !inPolygon) {
+ value = value.neg()
+ }
+ }
+ value = value.div(10)
+ targetBaseLines.forEach((target) => {
+ const currentLine = target.line
+ const index = baseLines.findIndex((line) => line === currentLine)
+ const nextLine = baseLines[(index + 1) % baseLines.length]
+ const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ let deltaX = 0
+ let deltaY = 0
+ if (currentLine.y1 === currentLine.y2) {
+ deltaY = value.toNumber()
+ } else {
+ deltaX = value.toNumber()
+ }
+
+ currentLine.set({
+ x1: currentLine.x1 + deltaX,
+ y1: currentLine.y1 + deltaY,
+ x2: currentLine.x2 + deltaX,
+ y2: currentLine.y2 + deltaY,
+ startPoint: { x: currentLine.x1 + deltaX, y: currentLine.y1 + deltaY },
+ endPoint: { x: currentLine.x2 + deltaX, y: currentLine.y2 + deltaY },
+ })
+ const currentSize = calcLinePlaneSize({ x1: currentLine.x1, y1: currentLine.y1, x2: currentLine.x2, y2: currentLine.y2 })
+ currentLine.attributes.planeSize = currentSize
+ currentLine.attributes.actualSize = currentSize
+
+ const nextOldActualSize = nextLine.attributes.planeSize
+ nextLine.set({
+ x1: nextLine.x1 + deltaX,
+ y1: nextLine.y1 + deltaY,
+ startPoint: { x: nextLine.x1 + deltaX, y: nextLine.y1 + deltaY },
+ })
+ const nextSize = calcLinePlaneSize({ x1: nextLine.x1, y1: nextLine.y1, x2: nextLine.x2, y2: nextLine.y2 })
+ nextLine.attributes.planeSize = nextSize
+ nextLine.attributes.actualSize = nextSize
+
+ const prevOldActualSize = prevLine.attributes.planeSize
+ prevLine.set({
+ x2: prevLine.x2 + deltaX,
+ y2: prevLine.y2 + deltaY,
+ endPoint: { x: prevLine.x2 + deltaX, y: prevLine.y2 + deltaY },
+ })
+ const prevSize = calcLinePlaneSize({ x1: prevLine.x1, y1: prevLine.y1, x2: prevLine.x2, y2: prevLine.y2 })
+ prevLine.attributes.planeSize = prevSize
+ prevLine.attributes.actualSize = prevSize
+
+ canvas.renderAll()
+ })
+
+ roof.drawHelpLine()
+ initEvent()
+ closePopup(id)
}
return {
diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js
index 302a4c9a..00929a6b 100644
--- a/src/hooks/roofcover/useOuterLineWall.js
+++ b/src/hooks/roofcover/useOuterLineWall.js
@@ -148,7 +148,6 @@ export function useOuterLineWall(id, propertiesId) {
const mouseDown = (e) => {
let pointer = getIntersectMousePoint(e)
pointer = { x: Big(pointer.x).round(1).toNumber(), y: Big(pointer.y).round(1).toNumber() }
- console.log('mouseDown', pointer, points)
if (points.length === 0) {
setPoints((prev) => [...prev, pointer])
@@ -156,14 +155,11 @@ export function useOuterLineWall(id, propertiesId) {
const lastPoint = points[points.length - 1]
let newPoint = { x: pointer.x, y: pointer.y }
const length = distanceBetweenPoints(lastPoint, newPoint)
- console.log('length', length)
if (verticalHorizontalMode) {
const vector = {
x: Big(pointer.x).minus(Big(points[points.length - 1].x)),
y: Big(pointer.y).minus(Big(points[points.length - 1].y)),
}
- // const slope = Math.abs(vector.y / vector.x) // 기울기 계산
- console.log('vector', vector.x.toNumber(), vector.y.toNumber(), Math.abs(vector.y.toNumber() / vector.x.toNumber()) >= 1)
const slope = vector.x.eq(0) ? Big(1) : vector.y.div(vector.x).abs() // 기울기 계산
let scaledVector
@@ -172,13 +168,11 @@ export function useOuterLineWall(id, propertiesId) {
// 기울기가 1 이상이면 x축 방향으로 그림
scaledVector = {
x: 0,
- // y: vector.y >= 0 ? Number(length) : -Number(length),
y: vector.y.gte(0) ? Big(length).toNumber() : Big(length).neg().toNumber(),
}
} else {
// 기울기가 1 미만이면 y축 방향으로 그림
scaledVector = {
- // x: vector.x >= 0 ? Number(length) : -Number(length),
x: vector.x.gte(0) ? Big(length).toNumber() : Big(length).neg().toNumber(),
y: 0,
}
@@ -187,8 +181,6 @@ export function useOuterLineWall(id, propertiesId) {
const verticalLength = scaledVector.y
const horizontalLength = scaledVector.x
- console.log('verticalLength', verticalLength, 'horizontalLength', horizontalLength)
-
newPoint = {
x: Big(lastPoint.x).plus(horizontalLength).toNumber(),
y: Big(lastPoint.y).plus(verticalLength).toNumber(),
@@ -885,8 +877,6 @@ export function useOuterLineWall(id, propertiesId) {
const firstPoint = points[0]
- console.log('points 좌표 : ', points)
-
points.forEach((point, idx) => {
if (idx === 0 || !isAllRightAngle) {
return
diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js
index 8109a8bb..fee893a6 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -214,7 +214,6 @@ export function useRoofAllocationSetting(id) {
await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
setIsGlobalLoading(false)
- saveCanvas(false)
})
//Recoil 설정
@@ -357,7 +356,7 @@ export function useRoofAllocationSetting(id) {
roofBases.forEach((roof) => {
if (roof.separatePolygon.length === 0) {
roof.innerLines.forEach((line) => {
- if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
+ if ((!line.attributes.actualSize || line.attributes?.actualSize === 0) && line.length > 1) {
line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
result = true
}
@@ -384,6 +383,7 @@ export function useRoofAllocationSetting(id) {
}
} catch (e) {
console.log(e)
+ canvas.discardActiveObject()
return
}
@@ -429,7 +429,7 @@ export function useRoofAllocationSetting(id) {
setRoofMaterials(newRoofList)
setRoofsStore(newRoofList)
/** 외곽선 삭제 */
- const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine')
+ const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine' || obj.name === 'pitchText')
removeTargets.forEach((obj) => {
canvas.remove(obj)
})
diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js
index 192d8cc0..33931c8e 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -79,13 +79,6 @@ export function useRoofShapeSetting(id) {
}, [jerkinHeadPitch])
useEffect(() => {
- const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
- // if (!outerLineFix || outerLines.length === 0) {
- // swalFire({ text: '외벽선이 없습니다.' })
- // // setShowRoofShapeSettingModal(false)
- // closePopup(id)
- // }
-
return () => {
if (!isFixRef.current) {
return
@@ -93,6 +86,7 @@ export function useRoofShapeSetting(id) {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
const pitchTexts = canvas.getObjects().filter((obj) => obj.name === 'pitchText')
+
canvas.remove(...pitchTexts)
outerLines.forEach((line) => {
let stroke, strokeWidth
@@ -114,6 +108,7 @@ export function useRoofShapeSetting(id) {
})
addPitchText(line)
+ line.bringToFront()
}
})
canvas.renderAll()
@@ -131,14 +126,6 @@ export function useRoofShapeSetting(id) {
return
}
- /*const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
- outerLines.forEach((line) => {
- line.set({
- stroke: '#000000',
- strokeWidth: 4,
- })
- })*/
-
currentObject.set({
stroke: '#EA10AC',
strokeWidth: 4,
diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js
index 245a7f15..31d8f95f 100644
--- a/src/hooks/roofcover/useWallLineOffsetSetting.js
+++ b/src/hooks/roofcover/useWallLineOffsetSetting.js
@@ -6,6 +6,8 @@ import { useEvent } from '@/hooks/useEvent'
import { useLine } from '@/hooks/useLine'
import { useSwal } from '@/hooks/useSwal'
import { usePopup } from '@/hooks/usePopup'
+import Big from 'big.js'
+import { outerLineFixState } from '@/store/outerLineAtom'
// 외벽선 편집 및 오프셋
export function useWallLineOffsetSetting(id) {
@@ -28,6 +30,8 @@ export function useWallLineOffsetSetting(id) {
const [isLoading, setIsLoading] = useState(false)
+ const outerLineFix = useRecoilValue(outerLineFixState)
+
const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => {
const line = addLine([point1.x, point1.y, point2.x, point2.y], {
stroke: 'black',
@@ -59,6 +63,7 @@ export function useWallLineOffsetSetting(id) {
useEffect(() => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+
if (outerLines.length === 0) {
swalFire({ text: getMessage('wall.line.not.found') })
closePopup(id)
@@ -277,7 +282,7 @@ export function useWallLineOffsetSetting(id) {
}
}
- rearrangeOuterLine(currentIdx + 1)
+ reArrangeOuterLine(currentIdx + 1)
drawLine(point1, point2, currentIdx)
drawLine(point2, point3, currentIdx + 1)
@@ -286,229 +291,217 @@ export function useWallLineOffsetSetting(id) {
canvas.remove(currentWallLineRef.current)
currentWallLineRef.current = null
canvas.renderAll()
+
+ canvas
+ .getObjects()
+ .filter((obj) => obj.name === 'outerLine')
+ .forEach((obj) => obj.fire('modified'))
}
- const rearrangeOuterLine = (idxParam) => {
+ /**
+ * outreLine의 index를 조절한다.
+ * @param idxParam
+ * @param isNegative
+ */
+ const reArrangeOuterLine = (idxParam, isNegative = false) => {
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((outerLine) => {
if (outerLine.idx >= idxParam) {
- outerLine.idx = outerLine.idx + 1
+ outerLine.idx = isNegative ? outerLine.idx - 1 : outerLine.idx + 1
}
})
}
+ /**
+ * offset 저장
+ */
const handleOffsetSave = () => {
- const direction = currentWallLineRef.current.direction
- let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right']
- const currentIdx = currentWallLineRef.current.idx
+ if (!currentObject) return
+ const currentLine = currentObject
+ const currentVector = currentLine.y1 === currentLine.y2 ? 'horizontal' : 'vertical'
+ const canDirections = currentVector === 'horizontal' ? ['up', 'down'] : ['left', 'right']
if (!canDirections.includes(arrow1Ref.current)) {
alert('방향을 다시 선택하세요')
return
}
-
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+ outerLines.sort((a, b) => a.idx - b.idx)
- const idx = currentWallLineRef.current.idx
- const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1
- const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1
+ const currentIdx = currentLine.idx
+ const prevIdx = currentIdx - 1 <= 0 ? outerLines.length : currentIdx - 1
+ const nextIdx = currentLine.idx + 1 > outerLines.length ? 1 : currentIdx + 1
- const currentLine = currentWallLineRef.current
const prevLine = outerLines.find((line) => line.idx === prevIdx)
const nextLine = outerLines.find((line) => line.idx === nextIdx)
+ const prevVector = prevLine.y1 === prevLine.y2 ? 'horizontal' : 'vertical'
+ const nextVector = nextLine.y1 === nextLine.y2 ? 'horizontal' : 'vertical'
- const length = length1Ref.current.value / 10
- const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2))
- const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2))
- switch (arrow1Ref.current) {
- case 'up': {
- if (prevLine.direction === currentLine.direction) {
- const newX =
- currentLine.direction === 'left'
- ? Math.floor(Math.max(currentLine.x1, currentLine.x2))
- : Math.floor(Math.min(currentLine.x1, currentLine.x2))
+ const offsetLength = Big(Number(length1Ref.current.value)).div(10)
+ if (offsetLength.eq(0)) return
- const newPoint1 = { x: newX, y: currentLineY - length }
- const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
- rearrangeOuterLine(currentIdx)
- drawLine(newPoint1, newPoint2, currentIdx, 'top')
+ const currentLineMinX = Big(Math.max(currentLine.x1, currentLine.x2))
+ const currentLineMaxX = Big(Math.max(currentLine.x1, currentLine.x2))
+ const currentLineMinY = Big(Math.max(currentLine.y1, currentLine.y2))
+ const currentLineMaxY = Big(Math.max(currentLine.y1, currentLine.y2))
- if (Math.abs(currentLineY - nextLine.y1) < 2) {
- nextLine.set({ y1: currentLineY - length })
- } else {
- nextLine.set({ y2: currentLineY - length })
- }
- } else if (nextLine.direction === currentLine.direction) {
- const newX =
- currentLine.direction === 'left'
- ? Math.floor(Math.min(currentLine.x1, currentLine.x2))
- : Math.floor(Math.max(currentLine.x1, currentLine.x2))
-
- const newPoint1 = { x: newX, y: currentLineY - length }
- const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
- rearrangeOuterLine(currentIdx + 1)
- drawLine(newPoint1, newPoint2, currentIdx + 1, 'top')
-
- if (Math.abs(currentLineY - prevLine.y1) < 2) {
- prevLine.set({ y1: currentLineY - length })
- } else {
- prevLine.set({ y2: currentLineY - length })
- }
+ if (currentVector === 'horizontal') {
+ const prevX = currentLine.x1 < currentLine.x2 ? Math.min(currentLine.x1, currentLine.x2) : Math.max(currentLine.x1, currentLine.x2)
+ const nextX = currentLine.x1 < currentLine.x2 ? Math.max(currentLine.x1, currentLine.x2) : Math.min(currentLine.x1, currentLine.x2)
+ if (arrow1Ref.current === 'up') {
+ currentLine.set({ y1: currentLineMaxY.minus(offsetLength).toNumber(), y2: currentLineMaxY.minus(offsetLength).toNumber() })
+ if (prevVector === currentVector) {
+ const point1 = { x: prevX, y: prevLine.y2 }
+ const point2 = { x: prevX, y: currentLine.y1 }
+ reArrangeOuterLine(currentIdx)
+ drawLine(point1, point2, currentIdx, arrow1Ref.current)
} else {
- if (Math.abs(currentLineY - prevLine.y1) < 2) {
- prevLine.set({ y1: prevLine.y1 - length })
+ if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) {
+ prevLine.set({ y1: currentLine.y1 })
} else {
- prevLine.set({ y2: prevLine.y2 - length })
+ prevLine.set({ y2: currentLine.y1 })
}
- if (Math.abs(currentLineY - nextLine.y1) < 2) {
- nextLine.set({ y1: nextLine.y1 - length })
- } else {
- nextLine.set({ y2: nextLine.y2 - length })
+ if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) {
+ reArrangeOuterLine(currentIdx - 1, true)
+ canvas.remove(prevLine)
+ }
+ }
+ if (nextVector === currentVector) {
+ const point1 = { x: nextX, y: nextLine.y2 }
+ const point2 = { x: nextX, y: currentLine.y1 }
+ reArrangeOuterLine(currentIdx + 1)
+ drawLine(point1, point2, currentIdx + 1, arrow1Ref.current)
+ } else {
+ if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) {
+ nextLine.set({ y1: currentLine.y1 })
+ } else {
+ nextLine.set({ y2: currentLine.y1 })
+ }
+ if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) {
+ reArrangeOuterLine(currentIdx + 1, true)
+ canvas.remove(nextLine)
}
}
-
- currentLine.set({ y1: currentLine.y1 - length, y2: currentLine.y2 - length })
-
- break
}
- case 'down': {
- if (prevLine.direction === currentLine.direction) {
- const newX =
- currentLine.direction === 'left'
- ? Math.floor(Math.max(currentLine.x1, currentLine.x2))
- : Math.floor(Math.min(currentLine.x1, currentLine.x2))
- const newPoint1 = { x: newX, y: currentLineY + length }
- const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
- rearrangeOuterLine(currentIdx)
- drawLine(newPoint1, newPoint2, currentIdx, 'bottom')
- if (Math.abs(currentLineY - nextLine.y1) < 2) {
- nextLine.set({ y1: currentLineY + length })
- } else {
- nextLine.set({ y2: currentLineY + length })
- }
- } else if (nextLine.direction === currentLine.direction) {
- const newX =
- currentLine.direction === 'left'
- ? Math.floor(Math.min(currentLine.x1, currentLine.x2))
- : Math.floor(Math.max(currentLine.x1, currentLine.x2))
- const newPoint1 = { x: newX, y: currentLineY + length }
- const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
- rearrangeOuterLine(currentIdx + 1)
- drawLine(newPoint1, newPoint2, currentIdx + 1, 'bottom')
- if (Math.abs(currentLineY - prevLine.y1) < 2) {
- prevLine.set({ y1: currentLineY + length })
- } else {
- prevLine.set({ y2: currentLineY + length })
- }
+ if (arrow1Ref.current === 'down') {
+ currentLine.set({ y1: currentLineMaxY.plus(offsetLength).toNumber(), y2: currentLineMaxY.plus(offsetLength).toNumber() })
+ if (prevVector === currentVector) {
+ const point1 = { x: prevX, y: prevLine.y2 }
+ const point2 = { x: prevX, y: currentLine.y1 }
+ reArrangeOuterLine(currentIdx)
+ drawLine(point1, point2, currentIdx, arrow1Ref.current)
} else {
- if (Math.abs(currentLineY - prevLine.y1) < 2) {
- prevLine.set({ y1: prevLine.y1 + length })
+ if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) {
+ prevLine.set({ y1: currentLine.y1 })
} else {
- prevLine.set({ y2: prevLine.y2 + length })
+ prevLine.set({ y2: currentLine.y1 })
}
- if (Math.abs(currentLineY - nextLine.y1) < 2) {
- nextLine.set({ y1: nextLine.y1 + length })
- } else {
- nextLine.set({ y2: nextLine.y2 + length })
+ if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) {
+ reArrangeOuterLine(currentIdx - 1, true)
+ canvas.remove(prevLine)
+ }
+ }
+ if (nextVector === currentVector) {
+ const point1 = { x: nextX, y: nextLine.y2 }
+ const point2 = { x: nextX, y: currentLine.y1 }
+ reArrangeOuterLine(currentIdx + 1)
+ drawLine(point1, point2, currentIdx + 1, arrow1Ref.current)
+ } else {
+ if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) {
+ nextLine.set({ y1: currentLine.y1 })
+ } else {
+ nextLine.set({ y2: currentLine.y1 })
+ }
+ if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) {
+ reArrangeOuterLine(currentIdx + 1, true)
+ canvas.remove(nextLine)
}
}
-
- currentLine.set({ y1: currentLine.y1 + length, y2: currentLine.y2 + length })
- break
}
- case 'left': {
- if (prevLine.direction === currentLine.direction) {
- const newY =
- currentLine.direction === 'top'
- ? Math.floor(Math.max(currentLine.y1, currentLine.y2))
- : Math.floor(Math.min(currentLine.y1, currentLine.y2))
- const newPoint1 = { x: currentLineX - length, y: newY }
- const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
- rearrangeOuterLine(currentIdx)
- drawLine(newPoint1, newPoint2, currentIdx, 'left')
- if (Math.abs(currentLineX - nextLine.x1) < 2) {
- nextLine.set({ x1: currentLineX - length })
- } else {
- nextLine.set({ x2: currentLineX - length })
- }
- } else if (nextLine.direction === currentLine.direction) {
- const newY =
- currentLine.direction === 'top'
- ? Math.floor(Math.min(currentLine.y1, currentLine.y2))
- : Math.floor(Math.max(currentLine.y1, currentLine.y2))
- const newPoint1 = { x: currentLineX - length, y: newY }
- const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
- rearrangeOuterLine(currentIdx + 1)
- drawLine(newPoint1, newPoint2, currentIdx + 1, 'left')
- if (Math.abs(currentLineX - prevLine.x1) < 2) {
- prevLine.set({ x1: currentLineX - length })
- } else {
- prevLine.set({ x2: currentLineX - length })
- }
- } else {
- if (Math.abs(currentLineX - prevLine.x1) < 2) {
- prevLine.set({ x1: prevLine.x1 - length })
- } else {
- prevLine.set({ x2: prevLine.x2 - length })
- }
+ } else {
+ const prevY = currentLine.y1 < currentLine.y2 ? Math.min(currentLine.y1, currentLine.y2) : Math.max(currentLine.y1, currentLine.y2)
+ const nextY = currentLine.y1 < currentLine.y2 ? Math.max(currentLine.y1, currentLine.y2) : Math.min(currentLine.y1, currentLine.y2)
+ if (arrow1Ref.current === 'left') {
+ currentLine.set({ x1: currentLineMaxX.minus(offsetLength).toNumber(), x2: currentLineMaxX.minus(offsetLength).toNumber() })
- if (Math.abs(currentLineX - nextLine.x1) < 2) {
- nextLine.set({ x1: nextLine.x1 - length })
+ if (prevVector === currentVector) {
+ const point1 = { x: prevLine.x2, y: prevY }
+ const point2 = { x: currentLine.x1, y: prevY }
+ reArrangeOuterLine(currentIdx)
+ drawLine(point1, point2, currentIdx, arrow1Ref.current)
+ } else {
+ if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) {
+ prevLine.set({ x1: currentLine.x1 })
} else {
- nextLine.set({ x2: nextLine.x2 - length })
+ prevLine.set({ x2: currentLine.x1 })
+ }
+ if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) {
+ reArrangeOuterLine(currentIdx - 1, true)
+ canvas.remove(prevLine)
+ }
+ }
+ if (nextVector === currentVector) {
+ const point1 = { x: currentLine.x2, y: nextY }
+ const point2 = { x: nextLine.x1, y: nextY }
+ reArrangeOuterLine(currentIdx + 1)
+ drawLine(point1, point2, currentIdx + 1, arrow1Ref.current)
+ } else {
+ if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) {
+ nextLine.set({ x1: currentLine.x2 })
+ } else {
+ nextLine.set({ x2: currentLine.x2 })
+ }
+ if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) {
+ reArrangeOuterLine(currentIdx + 1, true)
+ canvas.remove(nextLine)
}
}
-
- currentLine.set({ x1: currentLine.x1 - length, x2: currentLine.x2 - length })
- break
}
- case 'right': {
- if (prevLine.direction === currentLine.direction) {
- const newY =
- currentLine.direction === 'top'
- ? Math.floor(Math.max(currentLine.y1, currentLine.y2))
- : Math.floor(Math.min(currentLine.y1, currentLine.y2))
- const newPoint1 = { x: currentLineX + length, y: newY }
- const newPoint2 = { x: prevLine.x2, y: prevLine.y2 }
- rearrangeOuterLine(currentIdx)
- drawLine(newPoint1, newPoint2, currentIdx, 'right')
- if (Math.abs(currentLineX - nextLine.x1) < 2) {
- nextLine.set({ x1: currentLineX + length })
- } else {
- nextLine.set({ x2: currentLineX + length })
- }
- } else if (nextLine.direction === currentLine.direction) {
- const newY =
- currentLine.direction === 'top'
- ? Math.floor(Math.min(currentLine.y1, currentLine.y2))
- : Math.floor(Math.max(currentLine.y1, currentLine.y2))
- const newPoint1 = { x: currentLineX + length, y: newY }
- const newPoint2 = { x: nextLine.x1, y: nextLine.y1 }
- rearrangeOuterLine(currentIdx + 1)
- drawLine(newPoint1, newPoint2, currentIdx + 1, 'right')
+ if (arrow1Ref.current === 'right') {
+ currentLine.set({ x1: currentLineMaxX.plus(offsetLength).toNumber(), x2: currentLineMaxX.plus(offsetLength).toNumber() })
- if (Math.abs(currentLineX - prevLine.x1) < 2) {
- prevLine.set({ x1: currentLineX + length })
- } else {
- prevLine.set({ x2: currentLineX + length })
- }
+ if (prevVector === currentVector) {
+ const point1 = { x: prevLine.x2, y: prevY }
+ const point2 = { x: currentLine.x1, y: prevY }
+ reArrangeOuterLine(currentIdx)
+ drawLine(point1, point2, currentIdx, arrow1Ref.current)
} else {
- if (Math.abs(currentLineX - prevLine.x1) < 2) {
- prevLine.set({ x1: prevLine.x1 + length })
+ if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) {
+ prevLine.set({ x1: currentLine.x1 })
} else {
- prevLine.set({ x2: prevLine.x2 + length })
+ prevLine.set({ x2: currentLine.x1 })
}
- if (Math.abs(currentLineX - nextLine.x1) < 2) {
- nextLine.set({ x1: nextLine.x1 + length })
- } else {
- nextLine.set({ x2: nextLine.x2 + length })
+
+ if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) {
+ reArrangeOuterLine(currentIdx - 1, true)
+ canvas.remove(prevLine)
}
}
+ if (nextVector === currentVector) {
+ const point1 = { x: currentLine.x2, y: nextY }
+ const point2 = { x: nextLine.x1, y: nextY }
+ reArrangeOuterLine(currentIdx + 1)
+ drawLine(point1, point2, currentIdx + 1, arrow1Ref.current)
+ } else {
+ if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) {
+ nextLine.set({ x1: currentLine.x2 })
+ } else {
+ nextLine.set({ x2: currentLine.x2 })
+ }
- currentLine.set({ x1: currentLine.x1 + length, x2: currentLine.x2 + length })
-
- break
+ if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) {
+ reArrangeOuterLine(currentIdx + 1, true)
+ canvas.remove(nextLine)
+ }
+ }
}
}
+
+ const newOuterLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+ newOuterLines.sort((a, b) => a.idx - b.idx)
+ newOuterLines.forEach((line, idx) => {
+ line.fire('modified')
+ })
+
canvas.renderAll()
}
diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js
index 21fd9aa5..fcf14312 100644
--- a/src/hooks/useLine.js
+++ b/src/hooks/useLine.js
@@ -38,12 +38,12 @@ export const useLine = () => {
line.set({
visible: false,
})
- canvas
- ?.getObjects()
- .find((obj) => obj.parentId === line.id)
- .set({
+ const obj = canvas?.getObjects().find((obj) => obj.parentId === line.id)
+ if (obj) {
+ obj.set({
visible: false,
})
+ }
canvas?.renderAll()
}
diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js
index 392711b0..8615f7ca 100644
--- a/src/hooks/useMode.js
+++ b/src/hooks/useMode.js
@@ -33,7 +33,7 @@ import {
import { QLine } from '@/components/fabric/QLine'
import { fabric } from 'fabric'
import { QPolygon } from '@/components/fabric/QPolygon'
-import offsetPolygon from '@/util/qpolygon-utils'
+import offsetPolygon, { calculateAngle } from '@/util/qpolygon-utils'
import { isObjectNotEmpty } from '@/util/common-utils'
import * as turf from '@turf/turf'
import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common'
@@ -1679,10 +1679,11 @@ export function useMode() {
const offsetEdges = []
polygon.edges.forEach((edge, i) => {
- const offset =
+ /* const offset =
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
? 0.1
- : lines[i % lines.length].attributes.offset
+ : lines[i % lines.length].attributes.offset*/
+ const offset = lines[i % lines.length].attributes.offset
const dx = edge.outwardNormal.x * offset
const dy = edge.outwardNormal.y * offset
offsetEdges.push(createOffsetEdge(edge, dx, dy))
@@ -1717,10 +1718,12 @@ export function useMode() {
const offsetEdges = []
polygon.edges.forEach((edge, i) => {
- const offset =
+ /*const offset =
lines[i % lines.length].attributes.offset === undefined || lines[i % lines.length].attributes.offset === 0
? 0.1
- : lines[i % lines.length].attributes.offset
+ : lines[i % lines.length].attributes.offset*/
+ const offset = lines[i % lines.length].attributes.offset
+
const dx = edge.inwardNormal.x * offset
const dy = edge.inwardNormal.y * offset
offsetEdges.push(createOffsetEdge(edge, dx, dy))
@@ -1767,10 +1770,19 @@ export function useMode() {
afterLine.push(line)
}
})
-
wall.lines = afterLine.concat(beforeLine)
//외벽선을 기준으로 polygon을 생성한다. 지붕선의 기준이 됨.
+ const divWallLines = []
+ wall.lines.forEach((currentWall, index) => {
+ const nextWall = wall.lines[(index + 1) % wall.lines.length]
+ const currentAngle = calculateAngle(currentWall.startPoint, currentWall.endPoint)
+ const nextAngle = calculateAngle(nextWall.startPoint, nextWall.endPoint)
+ if (currentAngle === nextAngle) {
+ divWallLines.push({ currentWall: currentWall, nextWall: nextWall, index: index })
+ }
+ })
+
const polygon = createRoofPolygon(wall.points)
const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
originPolygon.setViewLengthText(false)
@@ -1787,6 +1799,45 @@ export function useMode() {
offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices
}
+ if (divWallLines.length > 0) {
+ /**
+ * 외벽선을 분기한 횟수를 저장한다. 외벽선은 offset이 같지 않을때 분기한다.
+ */
+ let addPoint = 0
+
+ divWallLines.forEach((line) => {
+ const currentWall = line.currentWall
+ const nextWall = line.nextWall
+ const index = line.index + addPoint
+ const xDiff = Big(currentWall.x1).minus(Big(nextWall.x1))
+ const yDiff = Big(currentWall.y1).minus(Big(nextWall.y1))
+ const offsetCurrentPoint = offsetPolygon[index]
+ let offsetNextPoint = offsetPolygon[(index + 1) % offsetPolygon.length]
+ line.index = index
+
+ if (currentWall.attributes.offset !== nextWall.attributes.offset) {
+ const offsetPoint1 = {
+ x: xDiff.eq(0) ? offsetCurrentPoint.x : nextWall.x1,
+ y: yDiff.eq(0) ? offsetCurrentPoint.y : nextWall.y1,
+ }
+ const diffOffset = Big(nextWall.attributes.offset).minus(Big(currentWall.attributes.offset))
+ const offsetPoint2 = {
+ x: yDiff.eq(0) ? offsetPoint1.x : Big(offsetPoint1.x).plus(diffOffset).toNumber(),
+ y: xDiff.eq(0) ? offsetPoint1.y : Big(offsetPoint1.y).plus(diffOffset).toNumber(),
+ }
+ const offsetPoint3 = {
+ x: yDiff.eq(0) ? offsetNextPoint.x : Big(offsetNextPoint.x).plus(diffOffset).toNumber(),
+ y: xDiff.eq(0) ? offsetNextPoint.y : Big(offsetNextPoint.y).plus(diffOffset).toNumber(),
+ }
+ offsetPolygon.splice(index + 1, 0, offsetPoint1, offsetPoint2)
+ offsetNextPoint = offsetPoint3
+ addPoint++
+ } else {
+ addPoint--
+ }
+ })
+ }
+
const roof = makePolygon(
offsetPolygon.map((point) => {
return { x1: point.x, y1: point.y }
@@ -1806,6 +1857,8 @@ export function useMode() {
roof.name = POLYGON_TYPE.ROOF
roof.setWall(wall)
+ let roofWallIndex = 0
+
roof.lines.forEach((line, index) => {
const x1 = Big(line.x1)
const x2 = Big(line.x2)
@@ -1816,18 +1869,21 @@ export function useMode() {
roofId: roof.id,
planeSize: lineLength,
actualSize: lineLength,
- wallLine: wall.lines[index].id,
- type: wall.lines[index].attributes.type,
- offset: wall.lines[index].attributes.offset,
- width: wall.lines[index].attributes.width,
- pitch: wall.lines[index].attributes.pitch,
- sleeve: wall.lines[index].attributes.sleeve || false,
+ wallLine: wall.lines[roofWallIndex].id,
+ type: wall.lines[roofWallIndex].attributes.type,
+ offset: wall.lines[roofWallIndex].attributes.offset,
+ width: wall.lines[roofWallIndex].attributes.width,
+ pitch: wall.lines[roofWallIndex].attributes.pitch,
+ sleeve: wall.lines[roofWallIndex].attributes.sleeve || false,
+ }
+
+ const isDivLine = divWallLines.some((divLine) => divLine.index === index)
+ if (!isDivLine) {
+ roofWallIndex++
}
})
wall.set({
- // originX: 'center',
- // originY: 'center',
attributes: {
roofId: roof.id,
},
@@ -1846,54 +1902,22 @@ export function useMode() {
const y2 = Big(line.y2)
const lineLength = x1.minus(x2).abs().pow(2).plus(y1.minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber()
line.attributes.roofId = roof.id
- line.attributes.currentRoofId = roof.lines[index].id
+ line.attributes.wallId = wall.id
+ // line.attributes.currentRoofId = roof.lines[index].id
line.attributes.planeSize = lineLength
line.attributes.actualSize = lineLength
- let wallStroke, wallStrokeWidth
- switch (line.attributes.type) {
- case LINE_TYPE.WALLLINE.EAVES:
- wallStroke = '#45CD7D'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.HIPANDGABLE:
- wallStroke = '#45CD7D'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.GABLE:
- wallStroke = '#3FBAE6'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.JERKINHEAD:
- wallStroke = '#3FBAE6'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.SHED:
- wallStroke = '#000000'
- wallStrokeWidth = 4
- break
- case LINE_TYPE.WALLLINE.WALL:
- wallStroke = '#000000'
- wallStrokeWidth = 4
- break
- }
-
- //외벽선의 색깔 표시를 위해 라인을 추가한다.
- const wallLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
- parentId: wall.id,
- name: 'wallLine',
- attributes: {
- wallId: wall.id,
- roofId: roof.id,
- type: line.attributes.type,
- currentRoofId: line.attributes.currentRoofId,
- currentWall: line.id,
- },
- stroke: wallStroke,
- strokeWidth: wallStrokeWidth,
- selectable: false,
+ const baseLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], {
+ visible: false,
+ attributes: line.attributes,
+ startPoint: line.startPoint,
+ endPoint: line.endPoint,
+ parentId: roof.id,
+ name: 'baseLine',
})
- canvas.add(wallLine)
+ baseLine.attributes.originPoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }
+ roof.wall.baseLines.push(baseLine)
+ canvas.add(baseLine)
})
setRoof(roof)
diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js
index ccb8f60a..91562948 100644
--- a/src/hooks/usePolygon.js
+++ b/src/hooks/usePolygon.js
@@ -1,7 +1,14 @@
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { fabric } from 'fabric'
-import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util'
+import {
+ distanceBetweenPoints,
+ findAndRemoveClosestPoint,
+ getDegreeByChon,
+ getDegreeInOrientation,
+ isPointOnLine,
+ toFixedWithoutRounding,
+} from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon'
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
import { flowDisplaySelector } from '@/store/settingAtom'
@@ -759,9 +766,9 @@ export const usePolygon = () => {
const splitPolygonWithLines = (polygon) => {
polygon.set({ visible: false })
- let innerLines = [...polygon.innerLines]
+ let innerLines = [...polygon.innerLines].filter((line) => line.visible)
- // innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
+ /*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
if (!innerLines || innerLines.length === 0) {
let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
polygon.innerLines = canvas
@@ -775,8 +782,7 @@ export const usePolygon = () => {
)
innerLines = [...polygon.innerLines]
- }
-
+ }*/
canvas.renderAll()
let polygonLines = [...polygon.lines]
const roofs = []
@@ -827,86 +833,104 @@ export const usePolygon = () => {
line.endPoint = endPoint
})
- polygonLines.forEach((line) => {
+ /*polygonLines.forEach((line) => {
line.set({ strokeWidth: 10 })
canvas.add(line)
})
- canvas.renderAll()
+ canvas.renderAll()*/
polygonLines.forEach((line) => {
+ /*const originStroke = line.stroke
+ line.set({ stroke: 'red' })
+ canvas.renderAll()*/
const intersections = []
innerLines.forEach((innerLine) => {
+ /*const originInnerStroke = innerLine.stroke
+ innerLine.set({ stroke: 'red' })
+ canvas.renderAll()*/
if (isPointOnLine(line, innerLine.startPoint)) {
+ canvas.renderAll()
if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
return
}
intersections.push(innerLine.startPoint)
}
if (isPointOnLine(line, innerLine.endPoint)) {
+ canvas.renderAll()
if (isSamePoint(line.startPoint, innerLine.endPoint) || isSamePoint(line.endPoint, innerLine.endPoint)) {
return
}
intersections.push(innerLine.endPoint)
}
+ /*innerLine.set({ stroke: originInnerStroke })
+ canvas.renderAll()*/
})
line.set({ intersections })
+
+ /*line.set({ stroke: originStroke })
+ canvas.renderAll()*/
})
- const divideLines = polygonLines.filter((line) => line.intersections.length > 0)
+ const divideLines = polygonLines.filter((line) => line.intersections?.length > 0)
let newLines = []
-
- divideLines.forEach((line) => {
+ polygonLines = polygonLines.filter((line) => !line.intersections || line.intersections.length === 0)
+ for (let i = divideLines.length - 1; i >= 0; i--) {
+ const line = divideLines[i]
const { intersections, startPoint, endPoint } = line
if (intersections.length === 1) {
- // 한 점만 교차하는 경우
const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y]
const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2]
+
const newLine1 = new QLine(newLinePoint1, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
+ name: 'newLine',
})
const newLine2 = new QLine(newLinePoint2, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
+ name: 'newLine',
})
+
newLine1.attributes = {
...line.attributes,
- planeSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10,
- actualSize: Math.round(Math.sqrt(Math.pow(newLine1.x1 - newLine1.x2, 2) + Math.pow(newLine1.y1 - newLine1.y2, 2))) * 10,
+ planeSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10,
+ actualSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10,
}
newLine2.attributes = {
...line.attributes,
- planeSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10,
- actualSize: Math.round(Math.sqrt(Math.pow(newLine2.x1 - newLine2.x2, 2) + Math.pow(newLine2.y1 - newLine2.y2, 2))) * 10,
+ planeSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10,
+ actualSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10,
}
- newLines.push(newLine1)
- newLines.push(newLine2)
- } else {
- // 두 점 이상 교차하는 경우
- //1. intersections중에 startPoint와 가장 가까운 점을 찾는다.
- //2. 가장 가까운 점을 기준으로 line을 나눈다.
+ newLines.push(newLine1, newLine2)
+ divideLines.splice(i, 1) // 기존 line 제거
+ } else {
let currentPoint = startPoint
while (intersections.length !== 0) {
const minDistancePoint = findAndRemoveClosestPoint(currentPoint, intersections)
const newLinePoint = [currentPoint.x, currentPoint.y, minDistancePoint.x, minDistancePoint.y]
+
const newLine = new QLine(newLinePoint, {
stroke: 'blue',
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
+ name: 'newLine',
})
+
newLine.attributes = {
...line.attributes,
- planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
- actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
+ planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
+ actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
}
+
newLines.push(newLine)
currentPoint = minDistancePoint
}
@@ -917,21 +941,66 @@ export const usePolygon = () => {
strokeWidth: 3,
fontSize: polygon.fontSize,
attributes: line.attributes,
+ name: 'newLine',
})
newLine.attributes = {
...line.attributes,
- planeSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
- actualSize: Math.round(Math.sqrt(Math.pow(newLine.x1 - newLine.x2, 2) + Math.pow(newLine.y1 - newLine.y2, 2))) * 10,
+ planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
+ actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
}
+
newLines.push(newLine)
+ divideLines.splice(i, 1) // 기존 line 제거
}
- })
+ }
//polygonLines에서 divideLines를 제거하고 newLines를 추가한다.
- polygonLines = polygonLines.filter((line) => !divideLines.includes(line))
+ newLines = newLines.filter((line) => !(Math.abs(line.startPoint.x - line.endPoint.x) < 1 && Math.abs(line.startPoint.y - line.endPoint.y) < 1))
+
polygonLines = [...polygonLines, ...newLines]
- const allLines = [...polygonLines, ...innerLines]
+ let allLines = [...polygonLines, ...innerLines]
+
+ /*allLines.forEach((line) => {
+ const originColor = line.stroke
+
+ line.set('stroke', 'red')
+ canvas.renderAll()
+
+ line.set('stroke', originColor)
+ canvas.renderAll()
+ })*/
+
+ const allPoints = []
+
+ // test용 좌표
+ const polygonLinesPoints = polygonLines.map((line) => {
+ return { startPoint: line.startPoint, endPoint: line.endPoint }
+ })
+
+ const innerLinesPoints = innerLines.map((line) => {
+ return { startPoint: line.startPoint, endPoint: line.endPoint }
+ })
+
+ polygonLinesPoints.forEach(({ startPoint, endPoint }) => {
+ allPoints.push(startPoint)
+ allPoints.push(endPoint)
+ })
+
+ innerLinesPoints.forEach(({ startPoint, endPoint }) => {
+ allPoints.push(startPoint)
+ allPoints.push(endPoint)
+ })
+
+ // allPoints에서 중복을 제거한다.
+ const uniquePoints = allPoints.filter((point, index, self) => {
+ return (
+ index ===
+ self.findIndex((p) => {
+ return isSamePoint(p, point)
+ })
+ )
+ })
// 2025-02-19 대각선은 케라바, 직선은 용마루로 세팅
innerLines.forEach((innerLine) => {
@@ -956,6 +1025,13 @@ export const usePolygon = () => {
}
})
+ /*innerLines.forEach((line) => {
+ const startPoint = line.startPoint
+ const endPoint = line.endPoint
+ /!*canvas.add(new fabric.Circle({ left: startPoint.x, top: startPoint.y + 10, radius: 5, fill: 'red' }))
+ canvas.add(new fabric.Circle({ left: endPoint.x, top: endPoint.y - 10, radius: 5, fill: 'blue' }))*!/
+ })*/
+
/**
* 왼쪽 상단을 startPoint로 전부 변경
*/
@@ -982,102 +1058,18 @@ export const usePolygon = () => {
line.endPoint = endPoint
})
- // polygon line에서 각각 출발한다.
- polygonLines.forEach((line) => {
- /*line.set({ strokeWidth: 5, stroke: 'green' })
- canvas.add(line)
- canvas.renderAll()*/
- const startPoint = { ...line.startPoint } // 시작점
- let arrivalPoint = { ...line.endPoint } // 도착점
-
- let currentPoint = startPoint
- let roofPoints = [startPoint]
-
- let startLine = line
- let visitPoints = [startPoint]
- let visitLines = [startLine]
- let notVisitedLines = []
- let cnt = 0
-
- while (!isSamePoint(currentPoint, arrivalPoint)) {
- //현재 점으로 부터 갈 수 있는 다른 라인을 찾는다.
- let nextLines = allLines.filter(
- (line2) =>
- (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
- line !== line2 &&
- innerLines.includes(line2) &&
- !visitLines.includes(line2),
- )
-
- if (nextLines.length === 0) {
- nextLines = allLines.filter(
- (line2) =>
- (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
- line !== line2 &&
- !visitLines.includes(line2),
- )
- }
-
- if (nextLines.length === 0) {
- //아직 안갔던 line중 0번째를 선택한다.
- if (notVisitedLines.length === 0) {
- break
- } else {
- let notVisitedLine = notVisitedLines.shift()
- roofPoints = [...notVisitedLine.roofPoints]
- currentPoint = { ...notVisitedLine.currentPoint }
- continue
- }
- }
-
- let comparisonPoints = []
-
- nextLines.forEach((nextLine) => {
- if (isSamePoint(nextLine.startPoint, currentPoint)) {
- comparisonPoints.push(nextLine.endPoint)
- } else {
- comparisonPoints.push(nextLine.startPoint)
- }
+ //allLines에서 중복을 제거한다.
+ allLines = allLines.filter((line, index, self) => {
+ return (
+ index ===
+ self.findIndex((l) => {
+ return isSamePoint(l.startPoint, line.startPoint) && isSamePoint(l.endPoint, line.endPoint)
})
-
- comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
- comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
-
- const minDistancePoint = comparisonPoints.reduce((prev, current) => {
- const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
- const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
-
- return prevDistance < currentDistance ? prev : current
- }, comparisonPoints[0])
-
- nextLines.forEach((nextLine) => {
- if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
- visitLines.push(nextLine)
- } else {
- notVisitedLines.push({
- line: nextLine,
- endPoint: nextLine.endPoint,
- startPoint: nextLine.startPoint,
- currentPoint: { ...currentPoint },
- roofPoints: [...roofPoints],
- })
- }
- })
-
- currentPoint = { ...minDistancePoint }
- roofPoints.push(currentPoint)
- cnt++
-
- if (cnt > 100) {
- break
- }
- }
- roofs.push(roofPoints)
- canvas.remove(line)
- canvas.renderAll()
+ )
})
- const newRoofs = removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100))
+ // 나눠서 중복 제거된 roof return
+ const newRoofs = getSplitRoofsPoints(allLines)
newRoofs.forEach((roofPoint, index) => {
let defense, pitch
@@ -1108,7 +1100,7 @@ export const usePolygon = () => {
})
// blue로 생성된 것들은 대표라인이 될 수 없음.
- representLines = representLines.filter((line) => line.stroke !== 'blue')
+ // representLines = representLines.filter((line) => line.stroke !== 'blue')
// representLines중 가장 긴 line을 찾는다.
representLines.forEach((line) => {
if (!representLine) {
@@ -1119,7 +1111,7 @@ export const usePolygon = () => {
}
}
})
- const direction = polygon.direction ?? representLine.direction
+ const direction = polygon.direction ?? representLine?.direction
const polygonDirection = polygon.direction
switch (direction) {
@@ -1135,8 +1127,11 @@ export const usePolygon = () => {
case 'left':
defense = 'north'
break
+ default:
+ defense = 'south'
+ break
}
- pitch = polygon.lines[index].attributes?.pitch ?? 0
+ pitch = polygon.lines[index]?.attributes?.pitch ?? 0
const roof = new QPolygon(roofPoint, {
fontSize: polygon.fontSize,
@@ -1172,10 +1167,144 @@ export const usePolygon = () => {
})
canvas.add(roof)
- addLengthText(roof)
+ // addLengthText(roof)
canvas.remove(polygon)
canvas.renderAll()
})
+
+ //지붕 완료 후 보조선을 전부 제거한다.
+ const auxiliaryLines = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine')
+
+ auxiliaryLines.forEach((line) => {
+ canvas.remove(line)
+ })
+ canvas.renderAll()
+ canvas.discardActiveObject()
+ }
+
+ const getSplitRoofsPoints = (allLines) => {
+ // ==== Utility functions ====
+
+ function isSamePoint(p1, p2, epsilon = 1) {
+ return Math.abs(p1.x - p2.x) <= 2 && Math.abs(p1.y - p2.y) <= 2
+ }
+
+ function normalizePoint(p, epsilon = 1) {
+ return {
+ x: Math.round(p.x / epsilon) * epsilon,
+ y: Math.round(p.y / epsilon) * epsilon,
+ }
+ }
+
+ function pointToKey(p, epsilon = 1) {
+ const norm = normalizePoint(p, epsilon)
+ return `${norm.x},${norm.y}`
+ }
+
+ // 거리 계산
+ function calcDistance(p1, p2) {
+ return Math.hypot(p2.x - p1.x, p2.y - p1.y)
+ }
+
+ // ==== Direct edge check ====
+
+ function isDirectlyConnected(start, end, graph, epsilon = 1) {
+ const startKey = pointToKey(start, epsilon)
+ return (graph[startKey] || []).some((neighbor) => isSamePoint(neighbor.point, end, epsilon))
+ }
+ // ==== Dijkstra pathfinding ====
+
+ function findShortestPath(start, end, graph, epsilon = 1) {
+ const startKey = pointToKey(start, epsilon)
+ const endKey = pointToKey(end, epsilon)
+
+ const distances = {}
+ const previous = {}
+ const visited = new Set()
+ const queue = [{ key: startKey, dist: 0 }]
+
+ for (const key in graph) distances[key] = Infinity
+ distances[startKey] = 0
+
+ while (queue.length > 0) {
+ queue.sort((a, b) => a.dist - b.dist)
+ const { key } = queue.shift()
+ if (visited.has(key)) continue
+ visited.add(key)
+
+ for (const neighbor of graph[key] || []) {
+ const neighborKey = pointToKey(neighbor.point, epsilon)
+ const alt = distances[key] + neighbor.distance
+ if (alt < distances[neighborKey]) {
+ distances[neighborKey] = alt
+ previous[neighborKey] = key
+ queue.push({ key: neighborKey, dist: alt })
+ }
+ }
+ }
+
+ const path = []
+ let currentKey = endKey
+
+ if (!previous[currentKey]) return null
+
+ while (currentKey !== startKey) {
+ const [x, y] = currentKey.split(',').map(Number)
+ path.unshift({ x, y })
+ currentKey = previous[currentKey]
+ }
+
+ const [sx, sy] = startKey.split(',').map(Number)
+ path.unshift({ x: sx, y: sy })
+
+ return path
+ }
+
+ // 최종 함수
+ function getPath(start, end, graph, epsilon = 1) {
+ if (isDirectlyConnected(start, end, graph, epsilon)) {
+ console.log('직선 연결 있음. 무시.')
+ return []
+ }
+
+ const path = findShortestPath(start, end, graph, epsilon)
+ if (!path || path.length < 3) {
+ console.log('경로 존재하나 3개 미만 좌표. 무시.')
+ return []
+ }
+
+ return path
+ }
+
+ const roofs = []
+
+ allLines.forEach((line) => {
+ // 그래프 생성
+ const graph = {}
+ const edges = allLines
+ .filter((line2) => line !== line2)
+ .map((line) => {
+ return [line.startPoint, line.endPoint]
+ })
+
+ for (const [p1, p2] of edges) {
+ const key1 = pointToKey(p1)
+ const key2 = pointToKey(p2)
+ const distance = calcDistance(p1, p2)
+
+ if (!graph[key1]) graph[key1] = []
+ if (!graph[key2]) graph[key2] = []
+
+ graph[key1].push({ point: p2, distance })
+ graph[key2].push({ point: p1, distance })
+ }
+
+ const startPoint = { ...line.startPoint } // 시작점
+ let arrivalPoint = { ...line.endPoint } // 도착점
+ roofs.push(getPath(startPoint, arrivalPoint, graph))
+ })
+
+ return removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100))
}
const splitPolygonWithSeparate = (separates) => {
diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js
index 60725183..c7a1fc17 100644
--- a/src/util/canvas-util.js
+++ b/src/util/canvas-util.js
@@ -360,16 +360,16 @@ export const calculateIntersection = (line1, line2) => {
// Check if the intersection X and Y are within the range of both lines
if (
- result[0] >= line1MinX &&
- result[0] <= line1MaxX &&
- result[0] >= line2MinX &&
- result[0] <= line2MaxX &&
- result[1] >= line1MinY &&
- result[1] <= line1MaxY &&
- result[1] >= line2MinY &&
- result[1] <= line2MaxY
+ result[0] >= line1MinX - 1 &&
+ result[0] <= line1MaxX + 1 &&
+ result[0] >= line2MinX - 1 &&
+ result[0] <= line2MaxX + 1 &&
+ result[1] >= line1MinY - 1 &&
+ result[1] <= line1MaxY + 1 &&
+ result[1] >= line2MinY - 1 &&
+ result[1] <= line2MaxY + 1
) {
- return { x: Math.round(result[0]), y: Math.round(result[1]) }
+ return { x: result[0], y: result[1] }
} else {
return null // Intersection is out of range
}
@@ -515,24 +515,39 @@ export const sortedPointLessEightPoint = (points) => {
* @param line
* @param point
* @returns {boolean}
+ * @param epsilon
*/
// 직선의 방정식.
// 방정식은 ax + by + c = 0이며, 점의 좌표를 대입하여 계산된 값은 직선과 점 사이의 관계를 나타낸다.
-export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }) {
- /*const a = line.y2 - line.y1
+export function isPointOnLine({ x1, y1, x2, y2 }, { x, y }, epsilon = 2) {
+ /*const a = y2 - y1
+ const b = x1 - x2
+ const c = x2 * y1 - x1 * y2
+ return Math.abs(a * x + b * y + c) < 1000*/
+ /*/!*const a = line.y2 - line.y1
const b = line.x1 - line.x2
const c = line.x2 * line.y1 - line.x1 * line.y2
const result = Math.abs(a * point.x + b * point.y + c) / 100
// 점이 선 위에 있는지 확인
- return result <= 10*/
+ return result <= 10*!*/
// 직선 방정식 만족 여부 확인
const crossProduct = (y - y1) * (x2 - x1) - (x - x1) * (y2 - y1)
- if (Math.abs(crossProduct) > 5) return false // 작은 오차 허용
+ if (Math.abs(crossProduct) > 1000) return false // 작은 오차 허용
+
+ const isSameX = Math.abs(x1 - x2) < 2
+ const isSameY = Math.abs(y1 - y2) < 2
// 점이 선분의 범위 내에 있는지 확인
- const withinXRange = Math.min(x1, x2) <= x && x <= Math.max(x1, x2)
- const withinYRange = Math.min(y1, y2) <= y && y <= Math.max(y1, y2)
+ let withinXRange = Math.min(x1, x2) - x <= 2
+ if (!isSameX) {
+ withinXRange = withinXRange && 2 <= Math.max(x1, x2) - x
+ }
+
+ let withinYRange = Math.min(y1, y2) - y <= 2
+ if (!isSameY) {
+ withinYRange = withinYRange && 2 <= Math.max(y1, y2) - y
+ }
return withinXRange && withinYRange
}
diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js
index 2705b546..b780cdd9 100644
--- a/src/util/qpolygon-utils.js
+++ b/src/util/qpolygon-utils.js
@@ -1,6 +1,6 @@
import { fabric } from 'fabric'
import { QLine } from '@/components/fabric/QLine'
-import { getDegreeByChon } from '@/util/canvas-util'
+import { getDegreeByChon, isPointOnLine } from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon'
import * as turf from '@turf/turf'
@@ -23,14 +23,16 @@ export const defineQPloygon = () => {
* @returns {number}
*/
export const calculateAngle = (point1, point2) => {
- const deltaX = Big(point2?.x !== undefined ? point2.x : 0)
- .minus(point1?.x !== undefined ? point1.x : 0)
+ const deltaX = Big(point2.x ?? 0)
+ .minus(point1.x ?? 0)
.toNumber()
- const deltaY = Big(point2?.y !== undefined ? point2.y : 0)
- .minus(point1?.y !== undefined ? point1.y : 0)
+ const deltaY = Big(point2.y ?? 0)
+ .minus(point1.y ?? 0)
.toNumber()
const angleInRadians = Math.atan2(deltaY, deltaX)
- return angleInRadians * (180 / Math.PI)
+ return Big(angleInRadians * (180 / Math.PI))
+ .round()
+ .toNumber()
}
function inwardEdgeNormal(vertex1, vertex2) {
@@ -292,8 +294,9 @@ function arePolygonsEqual(polygon1, polygon2) {
}
export function removeDuplicatePolygons(polygons) {
- const uniquePolygons = []
+ let uniquePolygons = []
+ // x가 전부 같거나, y가 전부 같은 경우 제거
polygons.forEach((polygon) => {
const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon))
if (!isDuplicate) {
@@ -301,9 +304,55 @@ export function removeDuplicatePolygons(polygons) {
}
})
+ uniquePolygons = uniquePolygons.filter((polygon) => {
+ return isValidPoints(polygon)
+ })
+
return uniquePolygons
}
+// 같은 직선상에 있는지 확인 같은 직선이라면 polygon을 생성할 수 없으므로 false
+const isValidPoints = (points) => {
+ function isColinear(p1, p2, p3) {
+ return (p2.x - p1.x) * (p3.y - p1.y) === (p3.x - p1.x) * (p2.y - p1.y)
+ }
+
+ function segmentsOverlap(a1, a2, b1, b2) {
+ // 같은 직선 상에 있는가?
+ if (!isColinear(a1, a2, b1) || !isColinear(a1, a2, b2)) {
+ return false
+ }
+
+ const isHorizontal = a1.y === a2.y
+ if (isHorizontal) {
+ const aMin = Math.min(a1.x, a2.x),
+ aMax = Math.max(a1.x, a2.x)
+ const bMin = Math.min(b1.x, b2.x),
+ bMax = Math.max(b1.x, b2.x)
+ return Math.max(aMin, bMin) < Math.min(aMax, bMax)
+ } else {
+ const aMin = Math.min(a1.y, a2.y),
+ aMax = Math.max(a1.y, a2.y)
+ const bMin = Math.min(b1.y, b2.y),
+ bMax = Math.max(b1.y, b2.y)
+ return Math.max(aMin, bMin) < Math.min(aMax, bMax)
+ }
+ }
+
+ for (let i = 0; i < points.length - 2; i++) {
+ const a1 = points[i]
+ const a2 = points[i + 1]
+ const b1 = points[i + 1] // 연속되는 점
+ const b2 = points[i + 2]
+
+ if (segmentsOverlap(a1, a2, b1, b2)) {
+ return false
+ }
+ }
+
+ return true
+}
+
export const isSamePoint = (a, b) => {
if (!a || !b) {
return false
@@ -670,7 +719,16 @@ export const drawShedRoof = (roofId, canvas, textMode) => {
}
gables.forEach(
- (gable) => (gable.attributes.actualSize = calcLineActualSize({ x1: gable.x1, y1: gable.y1, x2: gable.x2, y2: gable.y2 }, shedDegree)),
+ (gable) =>
+ (gable.attributes.actualSize = calcLineActualSize(
+ {
+ x1: gable.x1,
+ y1: gable.y1,
+ x2: gable.x2,
+ y2: gable.y2,
+ },
+ shedDegree,
+ )),
)
const pitchSizeLines = []
@@ -738,374 +796,5251 @@ export const drawShedRoof = (roofId, canvas, textMode) => {
*/
export const drawRidgeRoof = (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)
- //Math.abs(line.x1 - line.x2) > 1 && Math.abs(line.y1 - line.y2) > 1
const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1))
if (hasNonParallelLines.length > 0) {
- // alert('대각선이 존재ㄴ합니다.')
return
}
- drawRidge(roof, canvas, textMode)
- drawHips(roof, canvas, textMode)
- connectLinePoint(roof, canvas, textMode)
- modifyRidge(roof, canvas, textMode)
- drawCenterLine(roof, canvas, textMode)
-}
+ const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
+ const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
-/**
- * 마루가 존재하면 그린다. 마루는 지붕의 중간에 위치한다.
- *
- * @param roof
- * @param canvas
- * @param textMode
- */
-const drawRidge = (roof, canvas, textMode) => {
- console.log('roof.lines : ', roof.lines)
- const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines // 외벽의 라인
- const roofLines = roof.lines // 지붕의 라인
- let ridgeRoof = []
+ /** 외벽선 */
+ const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0)
+ const baseLinePoints = baseLines.map((line) => ({ x: line.x1, y: line.y1 }))
- roofLines.forEach((currentRoof, index) => {
- let prevRoof,
- nextRoof,
- currentWall = wallLines[index]
+ /** 모양 판단을 위한 라인 처리.
+ * 평행한 라인이 나누어져 있는 경우 하나의 선으로 판단 한다.
+ */
+ const drawBaseLines = []
+ baseLines.forEach((currentLine, index) => {
+ let nextLine = baseLines[(index + 1) % baseLines.length]
+ let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
- prevRoof = index === 0 ? wallLines[wallLines.length - 1] : wallLines[index - 1]
- nextRoof = index === wallLines.length - 1 ? wallLines[0] : index === wallLines.length ? wallLines[1] : wallLines[index + 1]
+ let { x1, y1, x2, y2 } = currentLine
- const angle1 = Big(calculateAngle(prevRoof.startPoint, prevRoof.endPoint))
- const angle2 = Big(calculateAngle(nextRoof.startPoint, nextRoof.endPoint))
- if (
- angle1.minus(angle2).abs().round(Big.roundHalfUp).toNumber() === 180 &&
- currentWall.attributes.planeSize <= currentRoof.attributes.planeSize
- ) {
- ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize })
+ if (currentAngle !== prevAngle || (currentAngle === prevAngle && currentLine.attributes.type !== prevLine.attributes.type)) {
+ if (currentAngle === nextAngle && currentLine.attributes.type === nextLine.attributes.type) {
+ let nextIndex = baseLines.findIndex((line) => line === nextLine)
+ while (nextIndex !== index) {
+ const checkNextLine = baseLines[(nextIndex + 1 + baseLines.length) % baseLines.length]
+ const checkAngle = calculateAngle(checkNextLine.startPoint, checkNextLine.endPoint)
+ if (currentAngle !== checkAngle) {
+ x2 = checkNextLine.x1
+ y2 = checkNextLine.y1
+ break
+ } else {
+ nextIndex = nextIndex + 1
+ }
+ }
+ }
+ drawBaseLines.push({ x1, y1, x2, y2, line: currentLine, size: calcLinePlaneSize({ x1, y1, x2, y2 }) })
}
})
- // 지붕의 길이가 짧은 순으로 정렬
- ridgeRoof.sort((a, b) => a.length - b.length)
+ /** baseLine을 기준으로 확인용 polygon 작성 */
+ const checkWallPolygon = new QPolygon(baseLinePoints, {})
- ridgeRoof.forEach((item) => {
- if (getMaxRidge(roofLines.length) > roof.ridges.length) {
- const index = item.index,
- currentRoof = item.roof
- const prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1]
- const nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1]
- let startXPoint, startYPoint, endXPoint, endYPoint
+ /**
+ * 외벽선이 시계방향인지 시계반대 방향인지 확인
+ * @type {boolean}
+ */
+ let counterClockwise = true
+ let signedArea = 0
- let wallLine = wallLines.filter((w) => w.id === currentRoof.attributes.wallLine)
- if (wallLine.length > 0) {
- wallLine = wallLine[0]
+ baseLinePoints.forEach((point, index) => {
+ const nextPoint = baseLinePoints[(index + 1) % baseLinePoints.length]
+ signedArea += point.x * nextPoint.y - point.y * nextPoint.x
+ })
+
+ if (signedArea > 0) {
+ counterClockwise = false
+ }
+
+ const drawEavesFirstLines = []
+ const drawEavesSecondLines = []
+ const drawGableRidgeFirst = []
+ const drawGableRidgeSecond = []
+ const drawGablePolygonFirst = []
+ const drawGablePolygonSecond = []
+ /** 모양을 판단한다. */
+ drawBaseLines.forEach((currentBaseLine, index) => {
+ let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
+ let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+
+ const checkScale = Big(10)
+ const xVector = Big(nextLine.x2).minus(Big(nextLine.x1))
+ const yVector = Big(nextLine.y2).minus(Big(nextLine.y1))
+ const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
+ const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
+
+ const checkPoints = {
+ x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(),
+ y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(),
+ }
+
+ /** 현재 라인이 처마유형일 경우 */
+ if (eavesType.includes(currentLine.attributes?.type)) {
+ if (eavesType.includes(nextLine.attributes?.type)) {
+ /**
+ * 이전, 다음 라인이 처마일때 라인의 방향이 반대면 ㄷ 모양으로 판단한다.
+ */
+ if (
+ eavesType.includes(prevLine.attributes?.type) &&
+ eavesType.includes(nextLine.attributes?.type) &&
+ Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
+ ) {
+ /**
+ * 다음라인 방향에 포인트를 확인해서 역방향 ㄷ 모양인지 판단한다.
+ * @type {{x: *, y: *}}
+ */
+ if (checkWallPolygon.inPolygon(checkPoints)) {
+ drawEavesFirstLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ } else {
+ drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ }
+ } else {
+ drawEavesSecondLines.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ }
+ } else if (gableType.includes(nextLine.attributes?.type) && gableType.includes(prevLine.attributes?.type)) {
+ if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) {
+ const checkPoints = {
+ x: currentMidX.plus(checkScale.times(Math.sign(xVector.toNumber()))).toNumber(),
+ y: currentMidY.plus(checkScale.times(Math.sign(yVector.toNumber()))).toNumber(),
+ }
+
+ if (checkWallPolygon.inPolygon(checkPoints)) {
+ drawGablePolygonFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ } else {
+ drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ }
+ // if (!checkWallPolygon.inPolygon(checkPoints)) {
+ drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ // }
+ } else {
+ if (currentAngle !== prevAngle && currentAngle !== nextAngle) {
+ drawGablePolygonSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ }
+ }
+ }
+ }
+
+ if (gableType.includes(currentLine.attributes?.type)) {
+ if (eavesType.includes(nextLine.attributes?.type)) {
+ if (
+ eavesType.includes(prevLine.attributes?.type) &&
+ eavesType.includes(nextLine.attributes?.type) &&
+ Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
+ ) {
+ if (checkWallPolygon.inPolygon(checkPoints)) {
+ drawGableRidgeFirst.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ } else {
+ drawGableRidgeSecond.push({ currentBaseLine, prevBaseLine, nextBaseLine })
+ }
+ }
+ }
+ }
+ })
+
+ drawEavesFirstLines.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
+ drawGableRidgeFirst.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
+ drawGableRidgeSecond.sort((a, b) => a.currentBaseLine.size - b.currentBaseLine.size)
+
+ /** 추녀마루 */
+ let baseHipLines = []
+ /** 용마루 */
+ let baseRidgeLines = []
+ /** 박공지붕 마루*/
+ let baseGableRidgeLines = []
+
+ /** 박공지붕 라인*/
+ let baseGableLines = []
+ /** 용마루의 갯수*/
+ let baseRidgeCount = 0
+
+ // console.log('drawGableFirstLines :', drawGableFirstLines)
+ // console.log('drawGableSecondLines :', drawGableSecondLines)
+ // console.log('drawEavesFirstLines :', drawEavesFirstLines)
+ // console.log('drawEavesSecondLines :', drawEavesSecondLines)
+ // console.log('drawGableRidgeFirst : ', drawGableRidgeFirst)
+ // console.log('drawGableRidgeSecond : ', drawGableRidgeSecond)
+ // console.log('drawGablePolygonFirst :', drawGablePolygonFirst)
+ // console.log('drawGablePolygonSecond :', drawGablePolygonSecond)
+
+ /** 박공지붕에서 파생되는 마루를 그린다. ㄷ 형태*/
+ drawGableRidgeFirst.forEach((current) => {
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+
+ let { x1, x2, y1, y2, size } = currentBaseLine
+ let beforePrevBaseLine, afterNextBaseLine
+
+ /** 이전 라인의 경사 */
+ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ /** 다음 라인의 경사 */
+ const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
+ /** 현재 라인의 경사 */
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */
+ drawBaseLines.forEach((line, index) => {
+ if (line === prevBaseLine) {
+ beforePrevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
+ }
+ if (line === nextBaseLine) {
+ afterNextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
+ }
+ })
+
+ const beforePrevLine = beforePrevBaseLine?.line
+ const afterNextLine = afterNextBaseLine?.line
+
+ /** 각 라인의 흐름 방향을 확인한다. */
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ const beforePrevAngle = calculateAngle(beforePrevLine.startPoint, beforePrevLine.endPoint)
+ const afterNextAngle = calculateAngle(afterNextLine.startPoint, afterNextLine.endPoint)
+
+ /** 현재라인의 vector*/
+ const currentVectorX = Math.sign(Big(x2).minus(Big(x1)).toNumber())
+ const currentVectorY = Math.sign(Big(y2).minus(Big(y1)).toNumber())
+
+ /** 이전라인의 vector*/
+ const prevVectorX = Math.sign(Big(prevLine.x2).minus(Big(prevLine.x1)))
+ const prevVectorY = Math.sign(Big(prevLine.y2).minus(Big(prevLine.y1)))
+
+ /** 다음라인의 vector*/
+ const nextVectorX = Math.sign(Big(nextLine.x2).minus(Big(nextLine.x1)))
+ const nextVectorY = Math.sign(Big(nextLine.y2).minus(Big(nextLine.y1)))
+
+ /** 현재라인의 기준점*/
+ let currentMidX = Big(x1).plus(Big(x2)).div(2).plus(Big(prevVectorX).times(currentLine.attributes.offset))
+ let currentMidY = Big(y1).plus(Big(y2)).div(2).plus(Big(prevVectorY).times(currentLine.attributes.offset))
+
+ /** 마루 반대 좌표*/
+ let oppositeMidX = currentMidX,
+ oppositeMidY = currentMidY
+
+ /** 한개의 지붕선을 둘로 나누어서 처리 하는 경우 */
+ if (prevAngle === beforePrevAngle || nextAngle === afterNextAngle) {
+ if (currentVectorX === 0) {
+ const addLength = Big(currentLine.y1).minus(Big(currentLine.y2)).abs().div(2)
+ const ridgeVector = Math.sign(prevLine.x1 - currentLine.x1)
+ oppositeMidX = Big(prevLine.x1).plus(Big(addLength).times(ridgeVector))
+
+ const ridgeEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+
+ const ridgeVectorX = Math.sign(ridgeEdge.vertex1.x - ridgeEdge.vertex2.x)
+ roof.lines
+ .filter((line) => line.x1 === line.x2)
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(ridgeEdge, lineEdge)
+ if (is) {
+ const isVectorX = Math.sign(ridgeEdge.vertex1.x - is.x)
+ if (
+ isVectorX === ridgeVectorX &&
+ ((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX))
+ ) {
+ currentMidX = Big(is.x)
+ currentMidY = Big(is.y)
+ }
+ }
+ })
+ } else {
+ const addLength = Big(currentLine.x1).minus(Big(currentLine.x2)).abs().div(2)
+ const ridgeVector = Math.sign(prevLine.y1 - currentLine.y1)
+ oppositeMidY = Big(prevLine.y1).plus(addLength.times(ridgeVector))
+
+ const ridgeEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+ const ridgeVectorY = Math.sign(ridgeEdge.vertex1.y - ridgeEdge.vertex2.y)
+ roof.lines
+ .filter((line) => line.y1 === line.y2)
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(ridgeEdge, lineEdge)
+ if (is) {
+ const isVectorY = Math.sign(ridgeEdge.vertex1.y - is.y)
+ if (
+ isVectorY === ridgeVectorY &&
+ ((line.x1 <= currentMidX && line.x2 >= currentMidX) || (line.x2 <= currentMidX && line.x1 >= currentMidX))
+ ) {
+ currentMidX = Big(is.x)
+ currentMidY = Big(is.y)
+ }
+ }
+ })
}
- const anotherRoof = roofLines.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof)
-
- let currentX1 = Big(currentRoof.x1),
- currentY1 = Big(currentRoof.y1),
- currentX2 = Big(currentRoof.x2),
- currentY2 = Big(currentRoof.y2)
- let ridgeMaxLength = Big(Math.max(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize)).div(10)
- let ridgeMinLength = Big(Math.min(prevRoof.attributes.planeSize, nextRoof.attributes.planeSize)).div(10)
-
- const currentAngle = Math.atan2(currentY2.minus(currentY1).toNumber(), currentX2.minus(currentX1).toNumber()) * (180 / Math.PI)
- anotherRoof
- .filter((roof) => isInnerLine(prevRoof, currentRoof, nextRoof, roof))
- .forEach((innerRoof) => {
- const vector1 = { x: currentX2.minus(currentX1), y: currentY2.minus(currentY1) }
- const vector2 = {
- x: Big(innerRoof.x2).minus(Big(innerRoof.x1)),
- y: Big(innerRoof.y2).minus(Big(innerRoof.y1)),
- }
-
- const dotProduct = vector1.x.times(vector2.x).plus(vector1.y.times(vector2.y))
- const magnitude1 = vector1.x.pow(2).plus(vector1.y.pow(2)).sqrt()
- const magnitude2 = vector2.x.pow(2).plus(vector2.y.pow(2)).sqrt()
- const angle = (Math.acos(dotProduct.div(magnitude1.times(magnitude2).toNumber())) * 180) / Math.PI
-
- // 현재 지붕선과 직각인 선
- if (Math.abs(angle) === 90) {
- const innerBefore = roofLines.find((line) => innerRoof.x1 === line.x2 && innerRoof.y1 === line.y2)
- const ibAngle =
- Math.atan2(Big(innerBefore.y2).minus(Big(innerBefore.y1)).toNumber(), Big(innerBefore.x2).minus(Big(innerBefore.x1)).toNumber()) *
- (180 / Math.PI)
- if (Math.abs(Big(currentAngle).minus(Big(ibAngle))) === 180) {
- if (currentAngle === 0 || currentAngle === 180) {
- currentX2 = innerRoof.x1
- ridgeMinLength = Big(nextRoof.x1)
- .minus(Big(nextRoof.x2))
- .times(10)
- .round()
- .pow(2)
- .plus(Big(nextRoof.y1).minus(Big(nextRoof.y2)).times(10).round().pow(2))
- .sqrt()
- .div(10)
- }
- if (currentAngle === 90 || currentAngle === 270) {
- currentY2 = innerRoof.y1
- ridgeMinLength = Big(nextRoof.x1)
- .minus(Big(innerRoof.x2))
- .times(10)
- .round()
- .pow(2)
- .plus(Big(nextRoof.y1).minus(Big(nextRoof.y2)).times(10).round().pow(2))
- .sqrt()
- .div(10)
- }
- }
- if (Math.abs(currentAngle - ibAngle) === 0) {
- if (currentAngle === 0 || currentAngle === 180) {
- currentX1 = innerRoof.x2
- ridgeMinLength = Big(prevRoof.x1)
- .minus(Big(prevRoof.x2))
- .times(10)
- .round()
- .pow(2)
- .plus(Big(prevRoof.y1).minus(Big(innerRoof.y1)).times(10).round().pow(2))
- .sqrt()
- .div(10)
- }
- if (currentAngle === 90 || currentAngle === 270) {
- currentY1 = innerRoof.y2
- ridgeMinLength = Big(prevRoof.x1)
- .minus(innerRoof.x1)
- .times(10)
- .round()
- .pow(2)
- .plus(Big(prevRoof.y2).minus(prevRoof.y2).times(10).round().pow(2))
- .sqrt()
- .div(10)
- }
- }
- }
- // 현재 지붕선과 반대인 선
- if (Math.abs(angle) === 180) {
- if (currentAngle === 0 || currentAngle === 180) {
- }
- if (currentAngle === 90 || currentAngle === 270) {
+ const prevHipEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: prevLine.x1, y: prevLine.y1 },
+ }
+ const prevHipVectorX = Math.sign(prevHipEdge.vertex1.x - prevHipEdge.vertex2.x)
+ const prevHipVectorY = Math.sign(prevHipEdge.vertex1.y - prevHipEdge.vertex2.y)
+ const prevIsPoints = []
+ roof.lines
+ .filter((line) => (Math.sign(prevLine.x1 - prevLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(prevHipEdge, lineEdge)
+ if (is) {
+ const isVectorX = Math.sign(prevHipEdge.vertex1.x - is.x)
+ const isVectorY = Math.sign(prevHipEdge.vertex1.y - is.y)
+ if (isVectorX === prevHipVectorX && isVectorY === prevHipVectorY) {
+ const size = Big(prevHipEdge.vertex1.x)
+ .minus(Big(is.x))
+ .abs()
+ .pow(2)
+ .plus(Big(prevHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ prevIsPoints.push({ is, size })
}
}
})
- // 지붕선의 X 중심
- const midX = currentX1.plus(currentX2).div(2)
- // 지붕선의 Y 중심
- const midY = currentY1.plus(currentY2).div(2)
-
- // 외선벽과 지붕선의 X 거리
- const alpha = Big(currentRoof.x1)
- .plus(Big(currentRoof.x2))
- .div(2)
- .minus(Big(wallLine.x1).plus(Big(wallLine.x2)).div(2))
- // 외벽벽과 지붕선의 Y 거리
- const beta = Big(currentRoof.y1)
- .plus(Big(currentRoof.y2))
- .div(2)
- .minus(Big(wallLine.y1).plus(Big(wallLine.y2)).div(2))
- // 외벽벽과 지붕선의 거리
- const hypotenuse = alpha.pow(2).plus(beta.pow(2)).sqrt()
-
- // 지붕선의 평면길이
- const currentPlaneSize = Big(calcLinePlaneSize({ x1: currentX1, y1: currentY1, x2: currentX2, y2: currentY2 }))
- // 용마루의 기반길이(지붕선에서 떨어진 정도)
- let ridgeBaseLength = currentPlaneSize.div(2).round().div(10)
-
- // 시작점은 현재 지붕선을 대각선으로 한 직각이등변삼각형으로 판단하고 현재 지붕선의 가운데에서 지붕에서 90도방향으로 지붕선의 절반만큼 떨어진 방향으로 설정한다.
- startXPoint = midX.plus(alpha.div(hypotenuse).times(currentPlaneSize.div(2)).neg().div(10))
- startYPoint = midY.plus(beta.div(hypotenuse).times(currentPlaneSize.div(2)).neg().div(10))
-
- // 종료점은 시작점에서 용마루의 최대길이 + 기반길이 만큼 그려진것으로 판단하여 늘린다.
- const checkEndXPoint = startXPoint.plus(Big(Math.sign(alpha.toNumber())).times(ridgeMaxLength.plus(ridgeBaseLength)).neg())
- const checkEndYPoint = startYPoint.plus(Big(Math.sign(beta.toNumber())).times(ridgeMaxLength.plus(ridgeBaseLength)).neg())
-
- // 맞은편 외벽선을 확인하기 위한 라인을 생성
- const checkLine = new QLine([startXPoint.toNumber(), startYPoint.toNumber(), checkEndXPoint.toNumber(), checkEndYPoint.toNumber()], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: 'red',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.HIP,
- attributes: { roofId: roof.id, currentRoofId: currentRoof.id, actualSize: 0 },
- })
- // 디버그시 주석제거해서 라인 확인
- // canvas.add(checkLine)
- // canvas.renderAll()
-
- // checkLine 과 마주치는 지붕선을 모두 찾아낸다.
- const intersectLines = []
- roofLines.forEach((line) => {
- const intersection = edgesIntersection(
- { vertex1: { x: checkLine.x1, y: checkLine.y1 }, vertex2: { x: checkLine.x2, y: checkLine.y2 } },
- { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ if (prevIsPoints.length > 0) {
+ const prevIs = prevIsPoints.sort((a, b) => a.size - b.size)[0].is
+ const prevHipLine = drawHipLine(
+ [prevIs.x, prevIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ prevDegree,
)
- if (intersection && !intersection.isIntersectionOutside) {
- intersectLines.push({ x: intersection.x, y: intersection.y, line: line })
- }
- })
- // intersectLines 중 현재 지붕선과 가장 가까운 지붕선을 찾는다.
- if (intersectLines.length > 0) {
- intersectLines.reduce((prev, current) => {
- if (prev !== undefined) {
- const prevDistance = Big(prev.x).minus(startXPoint).pow(2).plus(Big(prev.y).minus(startYPoint).pow(2)).sqrt()
- const currentDistance = Big(current.x).minus(startXPoint).pow(2).plus(Big(current.y).minus(startYPoint).pow(2)).sqrt()
- return prevDistance > currentDistance ? current : prev
- } else {
- return current
- }
- }, undefined)
+ baseHipLines.push({
+ x1: prevLine.x1,
+ y1: prevLine.y1,
+ x2: oppositeMidX.toNumber(),
+ y2: oppositeMidY.toNumber(),
+ line: prevHipLine,
+ })
}
- // 현재 지붕선과 마주하는 지붕선이 있는 경우
- if (intersectLines.length > 0) {
- // 마주하는 지붕선
- const intersectLine = intersectLines[0]
- //지붕선에서 마주하는 지붕선까지의 x,y 좌표의 차이
+ const nextHipEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: nextLine.x2, y: nextLine.y2 },
+ }
+ const nextHipVectorX = Math.sign(nextHipEdge.vertex1.x - nextHipEdge.vertex2.x)
+ const nextHipVectorY = Math.sign(nextHipEdge.vertex1.y - nextHipEdge.vertex2.y)
+ const nextIsPoints = []
- const diffX = Big(intersectLine.x).minus(startXPoint).round(1).abs().lt(1) ? Big(0) : Big(intersectLine.x).minus(startXPoint).round(1)
- const diffY = Big(intersectLine.y).minus(startYPoint).round(1).abs().lt(1) ? Big(0) : Big(intersectLine.y).minus(startYPoint).round(1)
- // 시작점에서 ridgeBaseLength 만큼 떨어진 지점까지 용마루 길이로 본다.
- endXPoint = startXPoint.plus(Big(Math.sign(diffX.toNumber())).times(diffX.abs().minus(ridgeBaseLength)))
- endYPoint = startYPoint.plus(Big(Math.sign(diffY.toNumber())).times(diffY.abs().minus(ridgeBaseLength)))
+ roof.lines
+ .filter((line) => (Math.sign(nextLine.x1 - nextLine.x2) === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(nextHipEdge, lineEdge)
+ if (is) {
+ const isVectorX = Math.sign(nextHipEdge.vertex1.x - is.x)
+ const isVectorY = Math.sign(nextHipEdge.vertex1.y - is.y)
+ if (isVectorX === nextHipVectorX && isVectorY === nextHipVectorY) {
+ const size = Big(nextHipEdge.vertex1.x)
+ .minus(Big(is.x))
+ .abs()
+ .pow(2)
+ .plus(Big(nextHipEdge.vertex1.y).minus(Big(is.y)).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ nextIsPoints.push({ is, size })
+ }
+ }
+ })
+ if (nextIsPoints.length > 0) {
+ const nextIs = nextIsPoints.sort((a, b) => a.size - b.size)[0].is
+ const nextHipLine = drawHipLine(
+ [nextIs.x, nextIs.y, oppositeMidX.toNumber(), oppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ nextDegree,
+ nextDegree,
+ )
+ baseHipLines.push({
+ x1: nextLine.x2,
+ y1: nextLine.y2,
+ x2: oppositeMidX.toNumber(),
+ y2: oppositeMidY.toNumber(),
+ line: nextHipLine,
+ })
+ }
- // 시작점에서 종료점까지의 평면길이
- //startXPoint.minus(endXPoint).abs().pow(2).plus(startYPoint.minus(endYPoint).abs().pow(2)).sqrt()
- const hypo = Big(
- calcLinePlaneSize({
- x1: startXPoint.toNumber(),
- y1: startYPoint.toNumber(),
- x2: endXPoint.toNumber(),
- y2: endYPoint.toNumber(),
- }),
- ).div(10)
- // 현재 지붕선과 마주하는 지붕선을 잇는 선분의 교차점까지의 길이
- const intersectLength = Big(
- calcLinePlaneSize({
- x1: midX.toNumber(),
- y1: midY.toNumber(),
- x2: intersectLine.x,
- y2: intersectLine.y,
- }),
- ).div(10)
- //마주하는 지붕선까지의 길이가 현재 지붕선의 이전, 다음 지붕선의 길이보다 작을때
- if (intersectLength.lt(Big(prevRoof.attributes.planeSize).div(10)) && intersectLength.lt(Big(nextRoof.attributes.planeSize).div(10))) {
- endXPoint = startXPoint
- endYPoint = startYPoint
+ if (baseRidgeCount < getMaxRidge(baseLines.length)) {
+ const ridgeLine = drawRidgeLine(
+ [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ )
+ baseGableRidgeLines.push(ridgeLine)
+ baseRidgeCount++
+ }
+ } else {
+ if (beforePrevBaseLine === afterNextBaseLine) {
+ const afterNextMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2)
+ const afterNextMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2)
+ const vectorMidX = Math.sign(currentMidX.minus(afterNextMidX))
+ const vectorMidY = Math.sign(currentMidY.minus(afterNextMidY))
+
+ let oppositeMidX, oppositeMidY
+ if (eavesType.includes(afterNextLine.attributes?.type)) {
+ const checkSize = currentMidX
+ .minus(afterNextMidX)
+ .pow(2)
+ .plus(currentMidY.minus(afterNextMidY).pow(2))
+ .sqrt()
+ .minus(Big(afterNextLine.attributes.planeSize).div(20))
+ .round(1)
+ oppositeMidX = currentMidX.plus(checkSize.times(vectorMidX).neg())
+ oppositeMidY = currentMidY.plus(checkSize.times(vectorMidY).neg())
+
+ const xVector1 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x1)).neg().toNumber())
+ const yVector1 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y1)).neg().toNumber())
+ const xVector2 = Math.sign(Big(oppositeMidX).minus(Big(afterNextLine.x2)).neg().toNumber())
+ const yVector2 = Math.sign(Big(oppositeMidY).minus(Big(afterNextLine.y2)).neg().toNumber())
+
+ let addOppositeX1 = 0,
+ addOppositeY1 = 0,
+ addOppositeX2 = 0,
+ addOppositeY2 = 0
+
+ if (!checkWallPolygon.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) {
+ const checkScale = currentMidX.minus(oppositeMidX).pow(2).plus(currentMidY.minus(oppositeMidY).pow(2)).sqrt()
+ addOppositeX1 = checkScale.times(xVector1).toNumber()
+ addOppositeY1 = checkScale.times(yVector1).toNumber()
+ addOppositeX2 = checkScale.times(xVector2).toNumber()
+ addOppositeY2 = checkScale.times(yVector2).toNumber()
+ }
+
+ let scale1 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
+ scale1 = scale1.eq(0) ? Big(1) : scale1
+ let scale2 = Big(afterNextLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
+ scale2 = scale2.eq(0) ? Big(1) : scale2
+
+ const checkHip1 = {
+ x1: Big(afterNextLine.x1).plus(scale1.times(xVector1)).toNumber(),
+ y1: Big(afterNextLine.y1).plus(scale1.times(yVector1)).toNumber(),
+ x2: oppositeMidX.plus(addOppositeX1).toNumber(),
+ y2: oppositeMidY.plus(addOppositeY1).toNumber(),
+ }
+
+ const checkHip2 = {
+ x1: Big(afterNextLine.x2).plus(scale2.times(xVector2)).toNumber(),
+ y1: Big(afterNextLine.y2).plus(scale2.times(yVector2)).toNumber(),
+ x2: oppositeMidX.plus(addOppositeX2).toNumber(),
+ y2: oppositeMidY.plus(addOppositeY2).toNumber(),
+ }
+
+ const intersection1 = findRoofIntersection(roof, checkHip1, {
+ x: oppositeMidX.plus(addOppositeX1),
+ y: oppositeMidY.plus(addOppositeY1),
+ })
+ const intersection2 = findRoofIntersection(roof, checkHip2, {
+ x: oppositeMidX.plus(addOppositeX2),
+ y: oppositeMidY.plus(addOppositeY2),
+ })
+
+ const afterNextDegree =
+ afterNextLine.attributes.pitch > 0 ? getDegreeByChon(afterNextLine.attributes.pitch) : afterNextLine.attributes.degree
+
+ if (intersection1) {
+ const hipLine = drawHipLine(
+ [intersection1.intersection.x, intersection1.intersection.y, oppositeMidX.plus(addOppositeX1), oppositeMidY.plus(addOppositeY1)],
+ canvas,
+ roof,
+ textMode,
+ null,
+ nextDegree,
+ afterNextDegree,
+ )
+ baseHipLines.push({
+ x1: afterNextLine.x1,
+ y1: afterNextLine.y1,
+ x2: oppositeMidX.plus(addOppositeX1).toNumber(),
+ y2: oppositeMidY.plus(addOppositeY1).toNumber(),
+ line: hipLine,
+ })
+ }
+ if (intersection2) {
+ const hipLine = drawHipLine(
+ [intersection2.intersection.x, intersection2.intersection.y, oppositeMidX.plus(addOppositeX2), oppositeMidY.plus(addOppositeY2)],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ afterNextDegree,
+ )
+ baseHipLines.push({
+ x1: afterNextLine.x2,
+ y1: afterNextLine.y2,
+ x2: oppositeMidX.plus(addOppositeX2).toNumber(),
+ y2: oppositeMidY.plus(addOppositeY2).toNumber(),
+ line: hipLine,
+ })
+ }
} else {
- if (ridgeMinLength.lt(hypo)) {
- endXPoint = startXPoint.plus(Big(Math.sign(diffX.toNumber())).times(ridgeMinLength))
- endYPoint = startYPoint.plus(Big(Math.sign(diffY.toNumber())).times(ridgeMinLength))
+ oppositeMidX = Big(afterNextLine.x1).plus(Big(afterNextLine.x2)).div(2).plus(Big(prevVectorX).neg().times(afterNextLine.attributes.offset))
+ oppositeMidY = Big(afterNextLine.y1).plus(Big(afterNextLine.y2)).div(2).plus(Big(prevVectorY).neg().times(afterNextLine.attributes.offset))
+ }
+
+ const vectorOppositeX = Math.sign(currentMidX.minus(oppositeMidX))
+ const vectorOppositeY = Math.sign(currentMidY.minus(oppositeMidY))
+
+ if (vectorMidX === vectorOppositeX && vectorMidY === vectorOppositeY && baseRidgeCount < getMaxRidge(baseLines.length)) {
+ if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) {
+ const checkEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ }
+ const intersectionPoints = []
+
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
+ ) {
+ const size = Big(intersection.x)
+ .minus(currentMidX)
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ intersectionPoints.push({ intersection, size })
+ }
+ })
+
+ if (intersectionPoints.length > 0) {
+ const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
+ currentMidX = Big(intersection.x)
+ currentMidY = Big(intersection.y)
+ }
+ }
+
+ if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidX.toNumber() })) {
+ const checkEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+ const intersectionPoints = []
+
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
+ ) {
+ const size = Big(intersection.x)
+ .minus(oppositeMidX)
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ intersectionPoints.push({ intersection, size })
+ }
+ })
+
+ if (intersectionPoints.length > 0) {
+ const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
+ oppositeMidX = Big(intersection.x)
+ oppositeMidY = Big(intersection.y)
+ }
+ }
+
+ const ridge = drawRidgeLine(
+ [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ )
+ baseGableRidgeLines.push(ridge)
+ baseRidgeCount++
+ }
+ } else {
+ const vectorMidX = Math.sign(Big(nextLine.x2).minus(nextLine.x1))
+ const vectorMidY = Math.sign(Big(nextLine.y2).minus(nextLine.y1))
+
+ let prevOppositeMidX, prevOppositeMidY, nextOppositeMidX, nextOppositeMidY
+ const beforePrevOffset =
+ currentAngle === beforePrevAngle
+ ? Big(beforePrevLine.attributes.offset)
+ : Big(beforePrevLine.attributes.offset).plus(currentLine.attributes.offset)
+ const afterNextOffset =
+ currentAngle === afterNextAngle
+ ? Big(afterNextLine.attributes.offset)
+ : Big(afterNextLine.attributes.offset).plus(currentLine.attributes.offset)
+ const prevSize = Big(prevLine.attributes.planeSize).div(10)
+ const nextSize = Big(nextLine.attributes.planeSize).div(10)
+
+ let prevHipCoords, nextHipCoords
+
+ /** 다음 라인이 그 다음 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/
+ if (eavesType.includes(afterNextLine.attributes?.type)) {
+ /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
+ let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt()
+
+ const nextHalfVector = getHalfAngleVector(nextLine, afterNextLine)
+ let nextHipVector = { x: nextHalfVector.x, y: nextHalfVector.y }
+
+ /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const nextCheckPoint = {
+ x: Big(nextLine.x2).plus(Big(nextHalfVector.x).times(10)),
+ y: Big(nextLine.y2).plus(Big(nextHalfVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
+ nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() }
+ }
+
+ const nextEndPoint = {
+ x: Big(nextLine.x2).plus(Big(nextHipVector.x).times(hipLength)),
+ y: Big(nextLine.y2).plus(Big(nextHipVector.y).times(hipLength)),
+ }
+
+ let ridgeEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: {
+ x: currentMidX.plus(Big(nextVectorX).times(nextBaseLine.size)).toNumber(),
+ y: currentMidY.plus(Big(nextVectorY).times(nextBaseLine.size)).toNumber(),
+ },
+ }
+ let hipEdge = {
+ vertex1: { x: nextLine.x2, y: nextLine.y2 },
+ vertex2: { x: nextEndPoint.x, y: nextEndPoint.y },
+ }
+ let intersection = edgesIntersection(ridgeEdge, hipEdge)
+ if (intersection) {
+ nextHipCoords = { x1: nextLine.x2, y1: nextLine.y2, x2: intersection.x, y2: intersection.y }
+ nextOppositeMidY = Big(intersection.y)
+ nextOppositeMidX = Big(intersection.x)
+ }
+ } else {
+ if (vectorMidX === 0) {
+ nextOppositeMidY = currentMidY.plus(nextSize.plus(afterNextOffset).times(vectorMidY))
+ nextOppositeMidX = currentMidX
+ } else {
+ nextOppositeMidX = currentMidX.plus(nextSize.plus(afterNextOffset).times(vectorMidX))
+ nextOppositeMidY = currentMidY
+ }
+ }
+
+ /** 이전 라인이 그 이전 라인과의 사이에 추녀마루가 존재 하는지 확인. 처마-처마 인 경우 추녀마루*/
+ if (eavesType.includes(beforePrevLine.attributes?.type)) {
+ /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
+ let hipLength = Big(size).div(10).div(2).pow(2).plus(Big(size).div(10).div(2).pow(2)).sqrt()
+
+ const prevHalfVector = getHalfAngleVector(prevLine, beforePrevLine)
+ let prevHipVector = { x: prevHalfVector.x, y: prevHalfVector.y }
+
+ /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const prevCheckPoint = {
+ x: Big(prevLine.x1).plus(Big(prevHalfVector.x).times(10)),
+ y: Big(prevLine.y1).plus(Big(prevHalfVector.y).times(10)),
+ }
+
+ if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
+ prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() }
+ }
+
+ const prevEndPoint = {
+ x: Big(prevLine.x1).plus(Big(prevHipVector.x).times(hipLength)),
+ y: Big(prevLine.y1).plus(Big(prevHipVector.y).times(hipLength)),
+ }
+
+ let ridgeEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: {
+ x: currentMidX.plus(Big(prevVectorX).times(prevBaseLine.size)).toNumber(),
+ y: currentMidY.plus(Big(prevVectorY).times(prevBaseLine.size)).toNumber(),
+ },
+ }
+ let hipEdge = {
+ vertex1: { x: prevLine.x1, y: prevLine.y1 },
+ vertex2: { x: prevEndPoint.x, y: prevEndPoint.y },
+ }
+ let intersection = edgesIntersection(ridgeEdge, hipEdge)
+ if (intersection) {
+ prevHipCoords = { x1: prevLine.x1, y1: prevLine.y1, x2: intersection.x, y2: intersection.y }
+ prevOppositeMidY = Big(intersection.y)
+ prevOppositeMidX = Big(intersection.x)
+ }
+ } else {
+ if (vectorMidX === 0) {
+ prevOppositeMidY = currentMidY.plus(prevSize.plus(beforePrevOffset).times(vectorMidY))
+ prevOppositeMidX = currentMidX
+ } else {
+ prevOppositeMidX = currentMidX.plus(prevSize.plus(beforePrevOffset).times(vectorMidX))
+ prevOppositeMidY = currentMidY
+ }
+ }
+
+ const currentMidEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: {
+ x: currentVectorX === 0 ? nextLine.x2 : currentMidX.toNumber(),
+ y: currentVectorX === 0 ? currentMidY.toNumber() : nextLine.y2,
+ },
+ }
+
+ let oppositeLines = []
+ drawBaseLines
+ .filter((line, index) => {
+ const currentLine = line.line
+ const nextLine = drawBaseLines[(index + 1) % drawBaseLines.length].line
+ const prevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length].line
+ const angle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ if (angle === prevAngle || angle === nextAngle) {
+ const sameAngleLine = angle === prevAngle ? prevLine : nextLine
+ if (gableType.includes(currentLine.attributes.type) && !gableType.includes(sameAngleLine.attributes.type)) {
+ switch (currentAngle) {
+ case 90:
+ return angle === -90
+ case -90:
+ return angle === 90
+ case 0:
+ return angle === 180
+ case 180:
+ return angle === 0
+ }
+ }
+ }
+ return false
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(currentMidEdge, lineEdge)
+ if (intersection) {
+ if (line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) {
+ oppositeLines.push({
+ line,
+ intersection,
+ size: Big(intersection.x)
+ .minus(currentMidX)
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
+ .sqrt()
+ .toNumber(),
+ })
+ }
+ }
+ })
+
+ if (oppositeLines.length > 0) {
+ const oppositePoint = oppositeLines.sort((a, b) => a.size - b.size)[0].intersection
+ const checkEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: { x: oppositePoint.x, y: oppositePoint.y },
+ }
+ const oppositeRoofPoints = []
+ roof.lines
+ .filter((line) => {
+ const angle = calculateAngle(line.startPoint, line.endPoint)
+ switch (currentAngle) {
+ case 90:
+ return angle === -90
+ case -90:
+ return angle === 90
+ case 0:
+ return angle === 180
+ case 180:
+ return angle === 0
+ }
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y))
+ ) {
+ oppositeRoofPoints.push({
+ line,
+ intersection,
+ size: Big(intersection.x)
+ .minus(currentMidX.toNumber())
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2))
+ .sqrt()
+ .toNumber(),
+ })
+ }
+ })
+ const oppositeRoofPoint = oppositeRoofPoints.sort((a, b) => a.size - b.size)[0].intersection
+ oppositeMidX = Big(oppositeRoofPoint.x)
+ oppositeMidY = Big(oppositeRoofPoint.y)
+
+ const currentRoofPoints = []
+ roof.lines
+ .filter((line) => {
+ const angle = calculateAngle(line.startPoint, line.endPoint)
+ return currentAngle === angle
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x1 >= intersection.x && line.x2 <= intersection.x && line.y1 >= intersection.y && line.y2 <= intersection.y))
+ ) {
+ currentRoofPoints.push({
+ line,
+ intersection,
+ size: Big(intersection.x)
+ .minus(currentMidX.toNumber())
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(currentMidY.toNumber()).abs().pow(2))
+ .sqrt()
+ .toNumber(),
+ })
+ }
+ })
+ const currentRoofPoint = currentRoofPoints.sort((a, b) => a.size - b.size)[0].intersection
+ currentMidX = Big(currentRoofPoint.x)
+ currentMidY = Big(currentRoofPoint.y)
+ } else {
+ const checkPrevSize = currentMidX.minus(prevOppositeMidX).pow(2).plus(currentMidY.minus(prevOppositeMidY).pow(2)).sqrt()
+ const checkNextSize = currentMidX.minus(nextOppositeMidX).pow(2).plus(currentMidY.minus(nextOppositeMidY).pow(2)).sqrt()
+
+ /** 두 포인트 중에 current와 가까운 포인트를 사용*/
+ if (checkPrevSize.gt(checkNextSize)) {
+ if (nextHipCoords) {
+ let intersectPoints = []
+ const hipEdge = {
+ vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 },
+ vertex2: { x: nextHipCoords.x1, y: nextHipCoords.y1 },
+ }
+
+ /** 외벽선에서 라인 겹치는 경우에 대한 확인*/
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ Math.sign(nextHipCoords.x2 - nextHipCoords.x1) === Math.sign(nextHipCoords.x2 - intersection.x) &&
+ Math.sign(nextHipCoords.y2 - nextHipCoords.y1) === Math.sign(nextHipCoords.y2 - intersection.y)
+ ) {
+ const intersectEdge = {
+ vertex1: { x: nextHipCoords.x2, y: nextHipCoords.y2 },
+ vertex2: { x: intersection.x, y: intersection.y },
+ }
+ const is = edgesIntersection(intersectEdge, lineEdge)
+ if (!is.isIntersectionOutside) {
+ const intersectSize = Big(nextHipCoords.x2)
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(Big(nextHipCoords.y2).minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ line,
+ })
+ }
+ }
+ })
+ const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0]
+ if (intersect) {
+ const degree =
+ intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree
+ const hipLine = drawHipLine(
+ [intersect.intersection.x, intersect.intersection.y, nextOppositeMidX.toNumber(), nextOppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ degree,
+ degree,
+ )
+ baseHipLines.push({
+ x1: nextHipCoords.x1,
+ y1: nextHipCoords.y1,
+ x2: nextHipCoords.x2,
+ y2: nextHipCoords.y2,
+ line: hipLine,
+ })
+ }
+ }
+ oppositeMidY = nextOppositeMidY
+ oppositeMidX = nextOppositeMidX
+ } else {
+ if (prevHipCoords) {
+ let intersectPoints = []
+ const hipEdge = {
+ vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 },
+ vertex2: { x: prevHipCoords.x1, y: prevHipCoords.y1 },
+ }
+
+ /** 외벽선에서 라인 겹치는 경우에 대한 확인*/
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ Math.sign(prevHipCoords.x2 - prevHipCoords.x1) === Math.sign(prevHipCoords.x2 - intersection.x) &&
+ Math.sign(prevHipCoords.y2 - prevHipCoords.y1) === Math.sign(prevHipCoords.y2 - intersection.y)
+ ) {
+ const intersectEdge = {
+ vertex1: { x: prevHipCoords.x2, y: prevHipCoords.y2 },
+ vertex2: { x: intersection.x, y: intersection.y },
+ }
+ const is = edgesIntersection(intersectEdge, lineEdge)
+ if (!is.isIntersectionOutside) {
+ const intersectSize = Big(prevHipCoords.x2)
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(Big(prevHipCoords.y2).minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ line,
+ })
+ }
+ }
+ })
+ const intersect = intersectPoints.sort((a, b) => a.size - b.size)[0]
+
+ if (intersect) {
+ const degree =
+ intersect.line.attributes.pitch > 0 ? getDegreeByChon(intersect.line.attributes.pitch) : intersect.line.attributes.degree
+ const hipLine = drawHipLine(
+ [intersect.intersection.x, intersect.intersection.y, prevOppositeMidX.toNumber(), prevOppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ degree,
+ degree,
+ )
+ baseHipLines.push({
+ x1: prevHipCoords.x1,
+ y1: prevHipCoords.y1,
+ x2: prevHipCoords.x2,
+ y2: prevHipCoords.y2,
+ line: hipLine,
+ })
+ }
+ }
+ oppositeMidY = prevOppositeMidY
+ oppositeMidX = prevOppositeMidX
+ }
+ }
+ }
+ if (baseRidgeCount < getMaxRidge(baseLines.length)) {
+ /** 포인트가 지붕 밖에 있는 경우 조정 */
+ if (!roof.inPolygon({ x: currentMidX.toNumber(), y: currentMidY.toNumber() })) {
+ const checkEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ }
+ const intersectionPoints = []
+
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
+ ) {
+ const size = Big(intersection.x)
+ .minus(currentMidX)
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(currentMidY).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ intersectionPoints.push({ intersection, size })
+ }
+ })
+
+ if (intersectionPoints.length > 0) {
+ const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
+ currentMidX = Big(intersection.x)
+ currentMidY = Big(intersection.y)
+ }
+ }
+
+ if (!roof.inPolygon({ x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() })) {
+ const checkEdge = {
+ vertex1: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+ const intersectionPoints = []
+
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (
+ intersection &&
+ ((line.x1 <= intersection.x && line.x2 >= intersection.x && line.y1 <= intersection.y && line.y2 >= intersection.y) ||
+ (line.x2 <= intersection.x && line.x1 >= intersection.x && line.y2 <= intersection.y && line.y1 >= intersection.y))
+ ) {
+ const size = Big(intersection.x)
+ .minus(oppositeMidX)
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(oppositeMidY).abs().pow(2))
+ .sqrt()
+ .toNumber()
+ intersectionPoints.push({ intersection, size })
+ }
+ })
+
+ if (intersectionPoints.length > 0) {
+ const intersection = intersectionPoints.sort((a, b) => a.size - b.size)[0].intersection
+ oppositeMidX = Big(intersection.x)
+ oppositeMidY = Big(intersection.y)
+ }
+ }
+
+ /** 마루가 맞은편 외벽선에 닿는 경우 해당 부분까지로 한정한다. */
+ const ridgeEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: { x: oppositeMidX.toNumber(), y: oppositeMidY.toNumber() },
+ }
+ const ridgeVectorX = Math.sign(currentMidX.minus(oppositeMidX).toNumber())
+ const ridgeVectorY = Math.sign(currentMidY.minus(oppositeMidY).toNumber())
+
+ roof.lines
+ .filter((line) => {
+ const lineVectorX = Math.sign(Big(line.x2).minus(Big(line.x1)).toNumber())
+ const lineVectorY = Math.sign(Big(line.y2).minus(Big(line.y1)).toNumber())
+ return (
+ (lineVectorX === currentVectorX && lineVectorY !== currentVectorY) || (lineVectorX !== currentVectorX && lineVectorY === currentVectorY)
+ )
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(ridgeEdge, lineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(currentMidX).minus(intersection.x).toNumber())
+ const isVectorY = Math.sign(Big(currentMidY).minus(intersection.y).toNumber())
+ if (isVectorX === ridgeVectorX && isVectorY === ridgeVectorY) {
+ oppositeMidX = Big(intersection.x)
+ oppositeMidY = Big(intersection.y)
+ }
+ }
+ })
+ const ridgeLine = drawRidgeLine(
+ [currentMidX.toNumber(), currentMidY.toNumber(), oppositeMidX.toNumber(), oppositeMidY.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ )
+ baseGableRidgeLines.push(ridgeLine)
+ baseRidgeCount++
+ }
+ }
+ })
+
+ /** 박공지붕에서 파생되는 마루를 그린다. 첫번째에서 처리 하지 못한 라인이 있는 경우 */
+ drawGableRidgeSecond.forEach((current) => {
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+
+ /** 이전 라인의 경사 */
+ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ /** 다음 라인의 경사 */
+ const nextDegree = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
+ /** 현재 라인의 경사 */
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const currentVectorX = Big(currentLine.x2).minus(currentLine.x1)
+ const currentVectorY = Big(currentLine.y2).minus(currentLine.y1)
+ const checkVectorX = Big(nextLine.x2).minus(Big(nextLine.x1))
+ const checkVectorY = Big(nextLine.y2).minus(Big(nextLine.y1))
+ const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
+ const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
+ const checkSize = Big(10)
+
+ const checkPoints = {
+ x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.toNumber()))).toNumber(),
+ y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.toNumber()))).toNumber(),
+ }
+ if (!checkWallPolygon.inPolygon(checkPoints)) {
+ const currentMidEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: {
+ x: currentMidX.plus(checkSize.times(Math.sign(checkVectorX.neg().toNumber()))).toNumber(),
+ y: currentMidY.plus(checkSize.times(Math.sign(checkVectorY.neg().toNumber()))).toNumber(),
+ },
+ }
+
+ let oppositeLines = []
+ baseLines
+ .filter((line, index) => {
+ let nextLine = baseLines[(index + 1) % baseLines.length]
+ let prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ if (
+ (gableType.includes(nextLine.attributes.type) && gableType.includes(prevLine.attributes.type)) ||
+ (eavesType.includes(nextLine.attributes.type) && eavesType.includes(prevLine.attributes.type))
+ ) {
+ const angle = calculateAngle(line.startPoint, line.endPoint)
+ switch (currentAngle) {
+ case 90:
+ return angle === -90
+ case -90:
+ return angle === 90
+ case 0:
+ return angle === 180
+ case 180:
+ return angle === 0
+ }
+ }
+ return false
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(currentMidEdge, lineEdge)
+ if (intersection) {
+ oppositeLines.push({
+ line,
+ intersection,
+ size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
+ })
+ }
+ })
+
+ if (oppositeLines.length === 0) {
+ return
+ }
+ const oppositeLine = oppositeLines.sort((a, b) => a.size - b.size)[0]
+
+ let points = []
+ if (eavesType.includes(oppositeLine.line.attributes.type)) {
+ const oppositeCurrentLine = oppositeLine.line
+ let oppositePrevLine, oppositeNextLine
+ baseLines.forEach((line, index) => {
+ if (line === oppositeCurrentLine) {
+ oppositePrevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ oppositeNextLine = baseLines[(index + 1) % baseLines.length]
+ }
+ })
+ if (gableType.includes(oppositeNextLine.attributes.type) && gableType.includes(oppositePrevLine.attributes.type)) {
+ if (currentVectorX.eq(0)) {
+ const centerX = currentMidX.plus(oppositeLine.intersection.x).div(2).toNumber()
+ points = [centerX, currentLine.y1, centerX, currentLine.y2]
+ } else {
+ const centerY = currentMidY.plus(oppositeLine.intersection.y).div(2).toNumber()
+ points = [currentLine.x1, centerY, currentLine.x2, centerY]
+ }
+ }
+ if (eavesType.includes(oppositeNextLine.attributes.type) && eavesType.includes(oppositePrevLine.attributes.type)) {
+ /** 이전, 다음라인의 사잇각의 vector를 구한다. */
+ let prevVector = getHalfAngleVector(oppositePrevLine, oppositeCurrentLine)
+ let nextVector = getHalfAngleVector(oppositeCurrentLine, oppositeNextLine)
+
+ let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
+ let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
+
+ /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const prevCheckPoint = {
+ x: Big(oppositeCurrentLine.x1).plus(Big(prevHipVector.x).times(10)),
+ y: Big(oppositeCurrentLine.y1).plus(Big(prevHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
+ prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() }
+ }
+
+ /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const nextCheckPoint = {
+ x: Big(oppositeCurrentLine.x2).plus(Big(nextHipVector.x).times(10)),
+ y: Big(oppositeCurrentLine.y2).plus(Big(nextHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
+ nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() }
+ }
+
+ /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
+ let hipLength = Big(oppositeCurrentLine.attributes.planeSize)
+ .div(2)
+ .pow(2)
+ .plus(Big(oppositeCurrentLine.attributes.planeSize).div(2).pow(2))
+ .sqrt()
+ .div(10)
+ .round(2)
+
+ const ridgeEndPoint = {
+ x: Big(oppositeCurrentLine.x1).plus(hipLength.times(prevHipVector.x)).round(1),
+ y: Big(oppositeCurrentLine.y1).plus(hipLength.times(prevHipVector.y)).round(1),
+ }
+
+ const prevHypotenuse = Big(oppositePrevLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt()
+ const prevHipPoints = {
+ x1: Big(oppositeCurrentLine.x1).plus(prevHypotenuse.times(prevHipVector.x.neg())).round(1).toNumber(),
+ y1: Big(oppositeCurrentLine.y1).plus(prevHypotenuse.times(prevHipVector.y.neg())).round(1).toNumber(),
+ x2: ridgeEndPoint.x.toNumber(),
+ y2: ridgeEndPoint.y.toNumber(),
+ }
+
+ const nextHypotenuse = Big(oppositeNextLine.attributes.offset).pow(2).plus(Big(oppositeCurrentLine.attributes.offset).pow(2)).sqrt()
+ const nextHipPoints = {
+ x1: Big(oppositeCurrentLine.x2).plus(nextHypotenuse.times(nextHipVector.x.neg())).round(1).toNumber(),
+ y1: Big(oppositeCurrentLine.y2).plus(nextHypotenuse.times(nextHipVector.y.neg())).round(1).toNumber(),
+ x2: ridgeEndPoint.x.toNumber(),
+ y2: ridgeEndPoint.y.toNumber(),
+ }
+
+ const prevIntersection = findRoofIntersection(roof, prevHipPoints, ridgeEndPoint)
+ const nextIntersection = findRoofIntersection(roof, nextHipPoints, ridgeEndPoint)
+
+ if (prevIntersection) {
+ const prevHip = drawHipLine(
+ [prevIntersection.intersection.x, prevIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ prevDegree,
+ )
+ baseHipLines.push({
+ x1: oppositeCurrentLine.x1,
+ y1: oppositeCurrentLine.y1,
+ x2: ridgeEndPoint.x,
+ y2: ridgeEndPoint.y,
+ line: prevHip,
+ })
+ }
+ if (nextIntersection) {
+ const nextHip = drawHipLine(
+ [nextIntersection.intersection.x, nextIntersection.intersection.y, ridgeEndPoint.x.toNumber(), ridgeEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ nextDegree,
+ nextDegree,
+ )
+ baseHipLines.push({
+ x1: ridgeEndPoint.x,
+ y1: ridgeEndPoint.y,
+ x2: oppositeCurrentLine.x2,
+ y2: oppositeCurrentLine.y2,
+ line: nextHip,
+ })
+ }
+
+ const ridgeVectorX = Math.sign(currentMidX.minus(ridgeEndPoint.x).toNumber())
+ const ridgeVectorY = Math.sign(currentMidY.minus(ridgeEndPoint.y).toNumber())
+ const ridgePoints = {
+ x1: currentMidX.plus(Big(currentLine.attributes.offset).times(ridgeVectorX)).toNumber(),
+ y1: currentMidY.plus(Big(currentLine.attributes.offset).times(ridgeVectorY)).toNumber(),
+ x2: ridgeEndPoint.x.toNumber(),
+ y2: ridgeEndPoint.y.toNumber(),
+ }
+ const ridgeIntersection = findRoofIntersection(roof, ridgePoints, {
+ x: Big(ridgePoints.x2),
+ y: Big(ridgePoints.y2),
+ })
+ if (ridgeIntersection) {
+ points = [ridgeIntersection.intersection.x, ridgeIntersection.intersection.y, ridgeEndPoint.x, ridgeEndPoint.y]
}
}
} else {
- endXPoint = startXPoint.plus(Big(Math.sign(alpha.toNumber())).times(ridgeMinLength).neg())
- endYPoint = startYPoint.plus(Big(Math.sign(beta.toNumber())).times(ridgeMinLength).neg())
+ if (currentVectorX.eq(0)) {
+ points = [oppositeLine.intersection.x, currentLine.y1, oppositeLine.intersection.x, currentLine.y2]
+ } else {
+ points = [currentLine.x1, oppositeLine.intersection.y, currentLine.x2, oppositeLine.intersection.y]
+ }
}
- // 용마루 선을 그린다.
- const ridge = new QLine([startXPoint.toNumber(), startYPoint.toNumber(), endXPoint.toNumber(), endYPoint.toNumber()], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.RIDGE,
- textMode: textMode,
- attributes: {
- roofId: roof.id,
- planeSize: calcLinePlaneSize({
- x1: startXPoint.toNumber(),
- y1: startYPoint.toNumber(),
- x2: endXPoint.toNumber(),
- y2: endYPoint.toNumber(),
- }),
- actualSize: calcLinePlaneSize({
- x1: startXPoint.toNumber(),
- y1: startYPoint.toNumber(),
- x2: endXPoint.toNumber(),
- y2: endYPoint.toNumber(),
- }),
- },
+ if (baseRidgeCount < getMaxRidge(baseLines.length)) {
+ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
+ baseGableRidgeLines.push(ridgeLine)
+ baseRidgeCount++
+ }
+ } else {
+ const oppositeLines = baseLines.filter((line) => {
+ const lineAngle = calculateAngle(line.startPoint, line.endPoint)
+ switch (currentAngle) {
+ case 90:
+ return lineAngle === -90
+ case -90:
+ return lineAngle === 90
+ case 0:
+ return lineAngle === 180
+ case 180:
+ return lineAngle === 0
+ }
})
- // 용마루의 길이가 0보다 클때만 canvas 에 추가 한다.
- if (ridge.attributes.planeSize > 0) {
- canvas.add(ridge)
- roof.ridges.push(ridge)
- roof.innerLines.push(ridge)
+ if (oppositeLines.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
+ let ridgePoints = []
+ const oppositeLine = oppositeLines.sort((a, b) => {
+ let diffCurrentA, diffCurrentB
+ if (Math.sign(currentVectorX) === 0) {
+ diffCurrentA = currentMidY.minus(a.y1).abs()
+ diffCurrentB = currentMidY.minus(b.y1).abs()
+ } else {
+ diffCurrentA = currentMidX.minus(a.x1).abs()
+ diffCurrentB = currentMidX.minus(b.x1).abs()
+ }
+ return diffCurrentA.minus(diffCurrentB).toNumber()
+ })[0]
- const distance = (x1, y1, x2, y2) => x2.minus(x1).pow(2).plus(y2.minus(y1).pow(2)).sqrt()
- const dist1 = distance(startXPoint, startYPoint, Big(currentRoof.x1), Big(currentRoof.y1))
- const dist2 = distance(endXPoint, endYPoint, Big(currentRoof.x1), Big(currentRoof.y1))
+ const prevOffset = prevLine.attributes.offset
+ const nextOffset = nextLine.attributes.offset
+ if (Math.sign(currentVectorX) === 0) {
+ const prevY = Big(currentLine.y1)
+ .plus(Big(Math.sign(currentVectorY)).neg().times(prevOffset))
+ .toNumber()
+ const nextY = Big(currentLine.y2)
+ .plus(Big(Math.sign(currentVectorY)).times(nextOffset))
+ .toNumber()
+ const midX = Big(currentLine.x1).plus(oppositeLine.x1).div(2).toNumber()
+ ridgePoints = [midX, prevY, midX, nextY]
+ } else {
+ const prevX = Big(currentLine.x1)
+ .plus(Big(Math.sign(currentVectorX)).neg().times(prevOffset))
+ .toNumber()
+ const nextX = Big(currentLine.x2)
+ .plus(Big(Math.sign(currentVectorX)).times(nextOffset))
+ .toNumber()
+ const midY = Big(currentLine.y1).plus(oppositeLine.y1).div(2).toNumber()
+ ridgePoints = [prevX, midY, nextX, midY]
+ }
+ const ridge = drawRidgeLine(ridgePoints, canvas, roof, textMode)
+ baseGableRidgeLines.push(ridge)
+ baseRidgeCount++
+ }
+ }
+ })
- currentRoof.attributes.ridgeCoordinate = {
- x1: dist1 < dist2 ? startXPoint : endXPoint,
- y1: dist1 < dist2 ? startYPoint : endYPoint,
+ const uniqueRidgeLines = []
+ /** 중복제거 */
+ baseGableRidgeLines.forEach((currentLine, index) => {
+ if (index === 0) {
+ uniqueRidgeLines.push(currentLine)
+ } else {
+ const duplicateLines = uniqueRidgeLines.filter(
+ (line) =>
+ (currentLine.x1 === line.x1 && currentLine.y1 === line.y1 && currentLine.x2 === line.x2 && currentLine.y2 === line.y2) ||
+ (currentLine.x1 === line.x2 && currentLine.y1 === line.y2 && currentLine.x2 === line.x1 && currentLine.y2 === line.y1),
+ )
+ if (duplicateLines.length === 0) {
+ uniqueRidgeLines.push(currentLine)
+ }
+ }
+ })
+
+ baseGableRidgeLines = uniqueRidgeLines
+
+ /** 박공지붕 polygon 생성 */
+ drawGablePolygonFirst.forEach((current) => {
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ let { x1, x2, y1, y2 } = currentBaseLine
+ let prevLineRidges = [],
+ nextLineRidges = []
+
+ const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1)
+ const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1)
+ const nextVectorX = Math.sign(nextLine.x1 - nextLine.x2)
+ const nextVectorY = Math.sign(nextLine.y1 - nextLine.y2)
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ let roofX1, roofY1, roofX2, roofY2
+ const currentMidX = Big(currentLine.x1).plus(Big(currentLine.x2)).div(2)
+ const currentMidY = Big(currentLine.y1).plus(Big(currentLine.y2)).div(2)
+ const intersectionRoofs = []
+ if (currentVectorX === 0) {
+ const checkEdge = {
+ vertex1: { x: prevLine.x1, y: currentMidY.toNumber() },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+ roof.lines
+ .filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY)
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (intersection) {
+ intersection.x = Math.round(intersection.x)
+ intersection.y = Math.round(intersection.y)
+ if (
+ (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
+ (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
+ ) {
+ intersectionRoofs.push({
+ line,
+ intersection,
+ size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
+ })
+ }
+ }
+ })
+ } else {
+ const checkEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: prevLine.y1 },
+ vertex2: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ }
+ roof.lines
+ .filter((line) => Math.sign(line.x2 - line.x1) === currentVectorX && Math.sign(line.y2 - line.y1) === currentVectorY)
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdge, lineEdge)
+ if (intersection) {
+ intersection.x = Math.round(intersection.x)
+ intersection.y = Math.round(intersection.y)
+ if (
+ (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
+ (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
+ ) {
+ intersectionRoofs.push({
+ line,
+ intersection,
+ size: Big(intersection.x).minus(currentMidX).abs().pow(2).plus(Big(intersection.y).minus(currentMidY).abs().pow(2)).sqrt(),
+ })
+ }
+ }
+ })
+ }
+ if (intersectionRoofs.length > 0) {
+ const currentRoof = intersectionRoofs.sort((a, b) => a.size - b.size)[0].line
+ roofX1 = Big(currentRoof.x1)
+ roofY1 = Big(currentRoof.y1)
+ roofX2 = Big(currentRoof.x2)
+ roofY2 = Big(currentRoof.y2)
+ }
+
+ let prevRoofLine, nextRoofLine
+ const roofEdge = {
+ vertex1: { x: roofX1.toNumber(), y: roofY1.toNumber() },
+ vertex2: { x: roofX2.toNumber(), y: roofY2.toNumber() },
+ }
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.y1 === line.y2 : line.x1 === line.x2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(roofEdge, lineEdge)
+ if (intersection) {
+ intersection.x = Math.round(intersection.x)
+ intersection.y = Math.round(intersection.y)
+ if (
+ (line.x1 <= intersection.x && intersection.x <= line.x2 && line.y1 <= intersection.y && intersection.y <= line.y2) ||
+ (line.x2 <= intersection.x && intersection.x <= line.x1 && line.y2 <= intersection.y && intersection.y <= line.y1)
+ ) {
+ if (roofX1.eq(intersection.x) && roofY1.eq(intersection.y)) {
+ prevRoofLine = line
+ }
+ if (roofX2.eq(intersection.x) && roofY2.eq(intersection.y)) {
+ nextRoofLine = line
+ }
+ }
+ }
+ })
+ const prevRoofEdge = {
+ vertex1: { x: prevRoofLine.x2, y: prevRoofLine.y2 },
+ vertex2: { x: prevRoofLine.x1, y: prevRoofLine.y1 },
+ }
+ const nextRoofEdge = {
+ vertex1: { x: nextRoofLine.x1, y: nextRoofLine.y1 },
+ vertex2: { x: nextRoofLine.x2, y: nextRoofLine.y2 },
+ }
+
+ baseGableRidgeLines.forEach((ridge) => {
+ const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
+ const prevIs = edgesIntersection(prevRoofEdge, ridgeEdge)
+ const nextIs = edgesIntersection(nextRoofEdge, ridgeEdge)
+ if (
+ prevIs &&
+ ((ridgeEdge.vertex1.x === prevIs.x && ridgeEdge.vertex1.y === prevIs.y) ||
+ (ridgeEdge.vertex2.x === prevIs.x && ridgeEdge.vertex2.y === prevIs.y))
+ ) {
+ prevLineRidges.push({
+ ridge,
+ size: calcLinePlaneSize({ x1: roofX1, y1: roofY1, x2: prevIs.x, y2: prevIs.y }),
+ })
+ }
+ if (
+ nextIs &&
+ ((ridgeEdge.vertex1.x === nextIs.x && ridgeEdge.vertex1.y === nextIs.y) ||
+ (ridgeEdge.vertex2.x === nextIs.x && ridgeEdge.vertex2.y === nextIs.y))
+ ) {
+ nextLineRidges.push({
+ ridge,
+ size: calcLinePlaneSize({ x1: roofX2, y1: roofY2, x2: nextIs.x, y2: nextIs.y }),
+ })
+ }
+ })
+
+ const polygonPoints = []
+
+ let prevLineRidge, nextLineRidge
+ if (prevLineRidges.length > 0) {
+ prevLineRidges = prevLineRidges
+ .filter((line) => {
+ const ridge = line.ridge
+ if (currentVectorX === 0) {
+ return ridge.y1 === roofY1.toNumber() || ridge.y2 === roofY1.toNumber()
+ } else {
+ return ridge.x1 === roofX1.toNumber() || ridge.x2 === roofX1.toNumber()
+ }
+ })
+ .sort((a, b) => a.size - b.size)
+ prevLineRidge = prevLineRidges[0].ridge
+ }
+ if (nextLineRidges.length > 0) {
+ nextLineRidges = nextLineRidges
+ .filter((line) => {
+ const ridge = line.ridge
+ if (currentVectorX === 0) {
+ return ridge.y1 === roofY2.toNumber() || ridge.y2 === roofY2.toNumber()
+ } else {
+ return ridge.x1 === roofX2.toNumber() || ridge.x2 === roofX2.toNumber()
+ }
+ })
+ .sort((a, b) => a.size - b.size)
+ nextLineRidge = nextLineRidges[0].ridge
+ }
+ const ridgeLine = prevLineRidge === undefined ? nextLineRidge : prevLineRidge
+
+ /** 마루선이 한쪽만 존재 연결될 경우 다각형을 그린다.*/
+ if (prevLineRidge === undefined || nextLineRidge === undefined) {
+ const points = [
+ { x: roofX1.toNumber(), y: roofY1.toNumber() },
+ { x: roofX2.toNumber(), y: roofY2.toNumber() },
+ { x: ridgeLine.x1, y: ridgeLine.y1 },
+ { x: ridgeLine.x2, y: ridgeLine.y2 },
+ ]
+ const minX = Math.min(ridgeLine.x1, ridgeLine.x2, roofX1.toNumber(), roofX2.toNumber())
+ const minY = Math.min(ridgeLine.y1, ridgeLine.y2, roofY1.toNumber(), roofY2.toNumber())
+
+ let polygonPoints = []
+
+ polygonPoints.push(points.find((point) => point.x === minX && point.y === minY))
+
+ let i = 0
+ let length = points.length
+ while (i < length - 1) {
+ const currentPoint = polygonPoints[i]
+ if (i === 0) {
+ polygonPoints.push(points.find((point) => point.x === currentPoint.x && point.y > currentPoint.y))
+ i++
+ } else {
+ const prevPoint = polygonPoints[(i - 1 + polygonPoints.length) % polygonPoints.length]
+ const vectorX = Math.sign(prevPoint.x - currentPoint.x)
+ const vectorY = Math.sign(prevPoint.y - currentPoint.y)
+ if (i % 2 === 1) {
+ /** 가로선 찾기 */
+ const nextPoint = points.find((point) => point.y === currentPoint.y && point.x !== currentPoint.x)
+ if (nextPoint === undefined) {
+ const connectLine = roof.lines.find((line) => line.y1 === line.y2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y)
+ if (connectLine) {
+ polygonPoints.push(
+ connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y
+ ? { x: connectLine.x2, y: connectLine.y2 }
+ : { x: connectLine.x1, y: connectLine.y1 },
+ )
+ } else {
+ const intersectLine = roof.lines.find(
+ (line) =>
+ line.y1 === currentPoint.y &&
+ line.y2 === currentPoint.y &&
+ ((line.x1 <= currentPoint.x && line.x2 >= currentPoint.x) || (line.x2 <= currentPoint.x && line.x1 >= currentPoint.x)),
+ )
+ /** 이전 라인이 위에서 아래로 항하면 오른쪽 포인트 찾기*/
+ if (vectorY === 1) {
+ polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 })
+ }
+ /** 이전 라인이 아래에서 위로 항하면 왼쪽 포인트 찾기*/
+ if (vectorY === -1) {
+ polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 })
+ }
+ }
+ length = length + 1
+ } else {
+ polygonPoints.push(nextPoint)
+ }
+ i++
+ } else {
+ /** 세로선 찾기*/
+ const nextPoint = points.find((point) => point.x === currentPoint.x && point.y !== currentPoint.y)
+ if (nextPoint === undefined) {
+ const connectLine = roof.lines.find((line) => line.x1 === line.x2 && line.x1 === currentPoint.x && line.y1 === currentPoint.y)
+ if (connectLine) {
+ polygonPoints.push(
+ connectLine.x1 === currentPoint.x && connectLine.y1 === currentPoint.y
+ ? { x: connectLine.x2, y: connectLine.y2 }
+ : { x: connectLine.x1, y: connectLine.y1 },
+ )
+ } else {
+ const intersectLine = roof.lines.find(
+ (line) =>
+ line.x1 === currentPoint.x &&
+ line.x2 === currentPoint.x &&
+ ((line.y1 <= currentPoint.y && line.y2 >= currentPoint.y) || (line.y2 <= currentPoint.y && line.y1 >= currentPoint.y)),
+ )
+ /** 이전 라인이 왼쪽에서 오른쪽로 항하면 위쪽 포인트 찾기*/
+ if (vectorX === -1) {
+ polygonPoints.push({ x: intersectLine.x1, y: intersectLine.y1 })
+ }
+ /** 이전 라인이 오른에서 왼쪽으로 항하면 아래쪽 포인트 찾기*/
+ if (vectorX === 1) {
+ polygonPoints.push({ x: intersectLine.x2, y: intersectLine.y2 })
+ }
+ }
+ length = length + 1
+ } else {
+ polygonPoints.push(nextPoint)
+ }
+ i++
+ }
+ }
+ }
+ polygonPoints = getSortedPoint(polygonPoints)
+ polygonPoints.forEach((currentPoint, index) => {
+ const nextPoint = polygonPoints[(index + 1) % polygonPoints.length]
+ const points = [currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y]
+ const isParallel = ridgeLine.x1 === ridgeLine.x2 ? currentPoint.x === nextPoint.x : currentPoint.y === nextPoint.y
+
+ let line
+ if (isParallel) {
+ line = drawRoofLine(points, canvas, roof, textMode)
+ baseGableLines.push(line)
+ } else {
+ line = drawHipLine(points, canvas, roof, textMode, null, currentDegree, currentDegree)
+ baseHipLines.push({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2, line })
+ }
+ })
+ } else {
+ /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/
+ let checkEdge
+ if (currentVectorX === 0) {
+ checkEdge = {
+ vertex1: { x: ridgeLine.x1, y: roofY1.toNumber() },
+ vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() },
+ }
+ } else {
+ checkEdge = {
+ vertex1: { x: roofX1.toNumber(), y: ridgeLine.y1 },
+ vertex2: { x: roofX1.toNumber(), y: roofY1.toNumber() },
+ }
+ }
+ const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
+ const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
+ const intersectPoints = []
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(checkEdge, lineEdge)
+ if (is) {
+ const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
+ const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
+ const isLineOtherPoint =
+ is.x === line.x1 && is.y === line.y1
+ ? { x: line.x2, y: line.y2 }
+ : {
+ x: line.x1,
+ y: line.y1,
+ }
+ const lineVectorX = Math.sign(isLineOtherPoint.x - is.x)
+ const lineVectorY = Math.sign(isLineOtherPoint.y - is.y)
+ if (checkVectorX === isVectorX && checkVectorY === isVectorY && lineVectorX === currentVectorX && lineVectorY === currentVectorY) {
+ intersectPoints.push(is)
+ }
+ }
+ })
+
+ let intersect = intersectPoints[0]
+ if (currentVectorX === 0) {
+ polygonPoints.push({ x: intersect.x, y: roofY1.toNumber() }, { x: intersect.x, y: roofY2.toNumber() })
+ } else {
+ polygonPoints.push({ x: roofX1.toNumber(), y: intersect.y }, { x: roofX2.toNumber(), y: intersect.y })
+ }
+
+ if (prevLineRidge === nextLineRidge) {
+ /** 4각*/
+ polygonPoints.push({ x: prevLineRidge.x1, y: prevLineRidge.y1 }, { x: prevLineRidge.x2, y: prevLineRidge.y2 })
+ } else {
+ /** 6각이상*/
+ let isOverLap =
+ currentVectorX === 0
+ ? (prevLineRidge.y1 <= nextLineRidge.y1 && prevLineRidge.y2 >= nextLineRidge.y1) ||
+ (prevLineRidge.y1 >= nextLineRidge.y1 && prevLineRidge.y2 <= nextLineRidge.y1) ||
+ (prevLineRidge.y1 <= nextLineRidge.y2 && prevLineRidge.y2 >= nextLineRidge.y2) ||
+ (prevLineRidge.y1 >= nextLineRidge.y2 && prevLineRidge.y2 <= nextLineRidge.y2)
+ : (prevLineRidge.x1 <= nextLineRidge.x1 && prevLineRidge.x2 >= nextLineRidge.x1) ||
+ (prevLineRidge.x1 >= nextLineRidge.x1 && prevLineRidge.x2 <= nextLineRidge.x1) ||
+ (prevLineRidge.x1 <= nextLineRidge.x2 && prevLineRidge.x2 >= nextLineRidge.x2) ||
+ (prevLineRidge.x1 >= nextLineRidge.x2 && prevLineRidge.x2 <= nextLineRidge.x2)
+ if (isOverLap) {
+ const prevDistance = currentVectorX === 0 ? Math.abs(prevLineRidge.x1 - roofX1.toNumber()) : Math.abs(prevLineRidge.y1 - roofY1.toNumber())
+ const nextDistance = currentVectorX === 0 ? Math.abs(nextLineRidge.x1 - roofX1.toNumber()) : Math.abs(nextLineRidge.y1 - roofY1.toNumber())
+ /** 현재 지붕 라인과 먼 라인의 포인트를 온전히 사용한다. */
+ if (prevDistance <= nextDistance) {
+ polygonPoints.push(
+ { x: nextLineRidge.x1, y: nextLineRidge.y1 },
+ {
+ x: nextLineRidge.x2,
+ y: nextLineRidge.y2,
+ },
+ )
+ /** 이전라인과 교차한 마루의 포인트*/
+ const prevRidgePoint1 =
+ currentVectorX === 0
+ ? roofY1.toNumber() === prevLineRidge.y1
+ ? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
+ : { x: prevLineRidge.x2, y: prevLineRidge.y2 }
+ : roofX1.toNumber() === prevLineRidge.x1
+ ? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
+ : { x: prevLineRidge.x2, y: prevLineRidge.y2 }
+
+ polygonPoints.push(prevRidgePoint1)
+
+ /** 다음 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/
+ const checkRidgePoint =
+ currentVectorX === 0
+ ? roofY2.toNumber() !== nextLineRidge.y1
+ ? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
+ : { x: nextLineRidge.x2, y: nextLineRidge.y2 }
+ : roofX2.toNumber() !== nextLineRidge.x1
+ ? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
+ : { x: nextLineRidge.x2, y: nextLineRidge.y2 }
+
+ const prevRidgePoint2 =
+ currentVectorX === 0
+ ? { x: prevRidgePoint1.x, y: checkRidgePoint.y }
+ : {
+ x: checkRidgePoint.x,
+ y: prevRidgePoint1.y,
+ }
+ polygonPoints.push(prevRidgePoint2)
+ } else {
+ polygonPoints.push(
+ { x: prevLineRidge.x1, y: prevLineRidge.y1 },
+ {
+ x: prevLineRidge.x2,
+ y: prevLineRidge.y2,
+ },
+ )
+
+ /** 다음라인과 교차한 마루의 포인트*/
+ const nextRidgePoint1 =
+ currentVectorX === 0
+ ? roofY2.toNumber() === nextLineRidge.y1
+ ? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
+ : { x: nextLineRidge.x2, y: nextLineRidge.y2 }
+ : roofX2.toNumber() === nextLineRidge.x1
+ ? { x: nextLineRidge.x1, y: nextLineRidge.y1 }
+ : { x: nextLineRidge.x2, y: nextLineRidge.y2 }
+ polygonPoints.push(nextRidgePoint1)
+
+ /** 이전 라인과 교차한 마루의 포인트 중 라인과 접하지 않은 포인트*/
+ const checkRidgePoint =
+ currentVectorX === 0
+ ? roofY1.toNumber() !== prevLineRidge.y1
+ ? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
+ : { x: prevLineRidge.x2, y: prevLineRidge.y2 }
+ : roofX1.toNumber() !== prevLineRidge.x1
+ ? { x: prevLineRidge.x1, y: prevLineRidge.y1 }
+ : { x: prevLineRidge.x2, y: prevLineRidge.y2 }
+ const nextRidgePoint2 =
+ currentVectorX === 0
+ ? { x: nextRidgePoint1.x, y: checkRidgePoint.y }
+ : {
+ x: checkRidgePoint.x,
+ y: nextRidgePoint1.y,
+ }
+ polygonPoints.push(nextRidgePoint2)
+ }
+ } else {
+ /** 마루가 겹치지 않을때 */
+ const otherRidgeLines = []
+
+ baseGableRidgeLines
+ .filter((ridge) => ridge !== prevLineRidge && ridge !== nextLineRidge)
+ .filter((ridge) => (currentVectorX === 0 ? ridge.x1 === ridge.x2 : ridge.y1 === ridge.y2))
+ .filter((ridge) =>
+ currentVectorX === 0 ? nextVectorX === Math.sign(nextLine.x1 - ridge.x1) : nextVectorY === Math.sign(nextLine.y1 - ridge.y1),
+ )
+ .forEach((ridge) => {
+ const size = currentVectorX === 0 ? Math.abs(nextLine.x1 - ridge.x1) : Math.abs(nextLine.y1 - ridge.y1)
+ otherRidgeLines.push({ ridge, size })
+ })
+ if (otherRidgeLines.length > 0) {
+ const otherRidge = otherRidgeLines.sort((a, b) => a.size - b.size)[0].ridge
+ /**
+ * otherRidge이 prevRidgeLine, nextRidgeLine 과 currentLine의 사이에 있는지 확인해서 분할하여 작업
+ * 지붕의 덮힘이 다르기 때문
+ */
+ const isInside =
+ currentVectorX === 0
+ ? Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - prevLineRidge.x1) &&
+ Math.abs(currentLine.x1 - otherRidge.x1) < Math.abs(currentLine.x1 - nextLineRidge.x1)
+ : Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - prevLineRidge.y1) &&
+ Math.abs(currentLine.y1 - otherRidge.y1) < Math.abs(currentLine.y1 - nextLineRidge.y1)
+
+ if (isInside) {
+ polygonPoints.push(
+ { x: prevLineRidge.x1, y: prevLineRidge.y1 },
+ { x: prevLineRidge.x2, y: prevLineRidge.y2 },
+ { x: nextLineRidge.x1, y: nextLineRidge.y1 },
+ { x: nextLineRidge.x2, y: nextLineRidge.y2 },
+ )
+
+ let ridgeAllPoints = [
+ { x: prevLineRidge.x1, y: prevLineRidge.y1 },
+ { x: prevLineRidge.x2, y: prevLineRidge.y2 },
+ { x: nextLineRidge.x1, y: nextLineRidge.y1 },
+ { x: nextLineRidge.x2, y: nextLineRidge.y2 },
+ ]
+ let ridgePoints = []
+ ridgeAllPoints.forEach((point) => {
+ let isOnLine = false
+ roof.lines.forEach((line) => {
+ if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) {
+ isOnLine = true
+ }
+ })
+ if (!isOnLine) {
+ ridgePoints.push(point)
+ }
+ })
+ if (ridgePoints.length === 2) {
+ if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) {
+ polygonPoints.push(
+ { x: otherRidge.x1, y: ridgePoints[0].y },
+ {
+ x: otherRidge.x1,
+ y: ridgePoints[1].y,
+ },
+ )
+ } else {
+ polygonPoints.push(
+ { x: ridgePoints[0].x, y: otherRidge.y1 },
+ {
+ x: ridgePoints[1].x,
+ y: otherRidge.y1,
+ },
+ )
+ }
+ }
+ } else {
+ polygonPoints.push({ x: otherRidge.x1, y: otherRidge.y1 }, { x: otherRidge.x2, y: otherRidge.y2 })
+
+ let ridgePoints = [
+ { x: prevLineRidge.x1, y: prevLineRidge.y1 },
+ { x: prevLineRidge.x2, y: prevLineRidge.y2 },
+ { x: nextLineRidge.x1, y: nextLineRidge.y1 },
+ { x: nextLineRidge.x2, y: nextLineRidge.y2 },
+ ]
+
+ ridgePoints.forEach((point) => {
+ let isOnLine = false
+ roof.lines.forEach((line) => {
+ if (isPointOnLine({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }, point)) {
+ isOnLine = true
+ }
+ })
+ if (isOnLine) {
+ polygonPoints.push(point)
+ }
+ })
+
+ if (Math.sign(otherRidge.x1 - otherRidge.x2) === 0) {
+ const prevY =
+ (prevLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= prevLineRidge.y2) ||
+ (prevLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= prevLineRidge.y2)
+ ? otherRidge.y1
+ : otherRidge.y2
+ const nextY =
+ (nextLineRidge.y1 <= otherRidge.y1 && otherRidge.y1 <= nextLineRidge.y2) ||
+ (nextLineRidge.y1 >= otherRidge.y1 && otherRidge.y1 >= nextLineRidge.y2)
+ ? otherRidge.y1
+ : otherRidge.y2
+ polygonPoints.push({ x: prevLineRidge.x1, y: prevY }, { x: nextLineRidge.x1, y: nextY })
+ } else {
+ const prevX =
+ (prevLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= prevLineRidge.x2) ||
+ (prevLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= prevLineRidge.x2)
+ ? otherRidge.x1
+ : otherRidge.x2
+ const nextX =
+ (nextLineRidge.x1 <= otherRidge.x1 && otherRidge.x1 <= nextLineRidge.x2) ||
+ (nextLineRidge.x1 >= otherRidge.x1 && otherRidge.x1 >= nextLineRidge.x2)
+ ? otherRidge.x1
+ : otherRidge.x2
+ polygonPoints.push({ x: prevX, y: prevLineRidge.y1 }, { x: nextX, y: nextLineRidge.y1 })
+ }
+ }
+ }
+ }
+ }
+ const sortedPolygonPoints = getSortedPoint(polygonPoints)
+
+ /** 외벽선 밖으로 나가있는 포인트*/
+ const outsidePoints = polygonPoints.filter((point) => {
+ let isOutside = true
+ roof.lines.forEach((line) => {
+ if (
+ (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) ||
+ (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y)
+ ) {
+ isOutside = false
+ }
+ })
+ baseGableRidgeLines.forEach((line) => {
+ if (
+ (line.x1 <= point.x && line.x2 >= point.x && line.y1 <= point.y && line.y2 >= point.y) ||
+ (line.x1 >= point.x && line.x2 <= point.x && line.y1 >= point.y && line.y2 <= point.y)
+ ) {
+ isOutside = false
+ }
+ })
+ return isOutside
+ })
+
+ if (outsidePoints.length > 0) {
+ sortedPolygonPoints.forEach((currentPoint, index) => {
+ if (outsidePoints.includes(currentPoint)) {
+ const prevPoint = sortedPolygonPoints[(index - 1 + sortedPolygonPoints.length) % sortedPolygonPoints.length]
+ const nextPoint = sortedPolygonPoints[(index + 1) % sortedPolygonPoints.length]
+ const vectorX = Math.sign(currentPoint.x - prevPoint.x)
+ const vectorY = Math.sign(currentPoint.y - prevPoint.y)
+
+ const checkEdge = {
+ vertex1: { x: prevPoint.x, y: prevPoint.y },
+ vertex2: { x: currentPoint.x, y: currentPoint.y },
+ }
+ const intersectPoints = []
+ roof.lines
+ .filter((line) => (vectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(checkEdge, lineEdge)
+ if (is && !is.isIntersectionOutside) {
+ const isVectorX = Math.sign(is.x - prevPoint.x)
+ const isVectorY = Math.sign(is.y - prevPoint.y)
+ if ((vectorX === 0 && vectorY === isVectorY) || (vectorY === 0 && vectorX === isVectorX)) {
+ intersectPoints.push(is)
+ }
+ }
+ })
+ if (intersectPoints.length > 0) {
+ const intersection = intersectPoints[0]
+ if (vectorX === 0) {
+ currentPoint.y = intersection.y
+ nextPoint.y = intersection.y
+ } else {
+ currentPoint.x = intersection.x
+ nextPoint.x = intersection.x
+ }
+ }
+ }
+ })
+ }
+
+ sortedPolygonPoints.forEach((startPoint, index) => {
+ let endPoint
+ if (index === sortedPolygonPoints.length - 1) {
+ endPoint = sortedPolygonPoints[0]
+ } else {
+ endPoint = sortedPolygonPoints[index + 1]
+ }
+
+ const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree)
+ if (currentVectorX === 0) {
+ if (Math.sign(startPoint.x - endPoint.x) === 0) {
+ hipLine.attributes.actualSize = hipLine.attributes.planeSize
+ }
+ } else {
+ if (Math.sign(startPoint.y - endPoint.y) === 0) {
+ hipLine.attributes.actualSize = hipLine.attributes.planeSize
+ }
+ }
+ baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine })
+ })
+ }
+ })
+
+ drawGablePolygonSecond.forEach((current) => {
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ let { x1, x2, y1, y2 } = currentBaseLine
+
+ const currentVectorX = Math.sign(currentLine.x2 - currentLine.x1)
+ const currentVectorY = Math.sign(currentLine.y2 - currentLine.y1)
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+
+ const polygonPoints = []
+ if (Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)) {
+ const currentRidge = baseGableRidgeLines.find((line) =>
+ currentVectorX === 0
+ ? (line.y1 === y1 && line.y2 === y2) || (line.y1 === y2 && line.y2 === y1)
+ : (line.x1 === x1 && line.x2 === x2) || (line.x1 === x2 && line.x2 === x1),
+ )
+ if (currentRidge) {
+ const ridgeVectorX = Math.sign(currentRidge.x1 - currentRidge.x2)
+ const ridgeVectorY = Math.sign(currentRidge.y1 - currentRidge.y2)
+
+ let checkEdge
+ if (currentVectorX === 0) {
+ checkEdge = {
+ vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
+ vertex2: { x: currentLine.x1, y: currentRidge.y1 },
+ }
+ } else {
+ checkEdge = {
+ vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
+ vertex2: { x: currentRidge.x1, y: currentLine.y1 },
+ }
+ }
+ const isRoofLines = []
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(checkEdge, lineEdge)
+ if (is) {
+ const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
+ const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
+ const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
+ const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
+ if ((ridgeVectorX === 0 && checkVectorX === isVectorX) || (ridgeVectorY === 0 && checkVectorY === isVectorY)) {
+ const size = ridgeVectorX === 0 ? Math.abs(checkEdge.vertex1.x - is.x) : Math.abs(checkEdge.vertex1.y - is.y)
+ isRoofLines.push({ line, size })
+ }
+ }
+ })
+ isRoofLines.sort((a, b) => a.size - b.size)
+ const roofLine = isRoofLines[0].line
+ polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 })
+ if (ridgeVectorX === 0) {
+ polygonPoints.push({ x: roofLine.x1, y: currentRidge.y1 }, { x: roofLine.x1, y: currentRidge.y2 })
+ } else {
+ polygonPoints.push({ x: currentRidge.x1, y: roofLine.y1 }, { x: currentRidge.x2, y: roofLine.y1 })
+ }
+ }
+ } else {
+ const prevEdge = { vertex1: { x: prevLine.x1, y: prevLine.y1 }, vertex2: { x: prevLine.x2, y: prevLine.y2 } }
+ const prevRidge = baseGableRidgeLines.find((ridge) => {
+ const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
+ const is = edgesIntersection(prevEdge, ridgeEdge)
+ if (is && !is.isIntersectionOutside) {
+ return ridge
+ }
+ })
+
+ const nextEdge = { vertex1: { x: nextLine.x1, y: nextLine.y1 }, vertex2: { x: nextLine.x2, y: nextLine.y2 } }
+ const nextRidge = baseGableRidgeLines.find((ridge) => {
+ const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
+ const is = edgesIntersection(nextEdge, ridgeEdge)
+ if (is && !is.isIntersectionOutside) {
+ return ridge
+ }
+ })
+
+ let currentRidge
+
+ if (prevRidge) {
+ if (
+ currentVectorX === 0 &&
+ ((prevRidge.y1 <= currentLine.y1 && prevRidge.y2 >= currentLine.y1 && prevRidge.y1 <= currentLine.y2 && prevRidge.y2 >= currentLine.y2) ||
+ (prevRidge.y1 >= currentLine.y1 && prevRidge.y2 <= currentLine.y1 && prevRidge.y1 >= currentLine.y2 && prevRidge.y2 <= currentLine.y2))
+ ) {
+ currentRidge = prevRidge
+ }
+ if (
+ currentVectorY === 0 &&
+ ((prevRidge.x1 <= currentLine.x1 && prevRidge.x2 >= currentLine.x1 && prevRidge.x1 <= currentLine.x2 && prevRidge.x2 >= currentLine.x2) ||
+ (prevRidge.x1 >= currentLine.x1 && prevRidge.x2 <= currentLine.x1 && prevRidge.x1 >= currentLine.x2 && prevRidge.x2 <= currentLine.x2))
+ ) {
+ currentRidge = prevRidge
+ }
+ }
+ if (nextRidge) {
+ if (
+ currentVectorX === 0 &&
+ ((nextRidge.y1 <= currentLine.y1 && nextRidge.y2 >= currentLine.y1 && nextRidge.y1 <= currentLine.y2 && nextRidge.y2 >= currentLine.y2) ||
+ (nextRidge.y1 >= currentLine.y1 && nextRidge.y2 <= currentLine.y1 && nextRidge.y1 >= currentLine.y2 && nextRidge.y2 <= currentLine.y2))
+ ) {
+ currentRidge = nextRidge
+ }
+ if (
+ currentVectorY === 0 &&
+ ((nextRidge.x1 <= currentLine.x1 && nextRidge.x2 >= currentLine.x1 && nextRidge.x1 <= currentLine.x2 && nextRidge.x2 >= currentLine.x2) ||
+ (nextRidge.x1 >= currentLine.x1 && nextRidge.x2 <= currentLine.x1 && nextRidge.x1 >= currentLine.x2 && nextRidge.x2 <= currentLine.x2))
+ ) {
+ currentRidge = nextRidge
+ }
+ }
+ if (currentRidge) {
+ polygonPoints.push({ x: currentRidge.x1, y: currentRidge.y1 }, { x: currentRidge.x2, y: currentRidge.y2 })
+
+ /** 마루에서 현재라인으로 향하는 외벽선까지의 포인트를 확인 하여 처리*/
+ let checkEdge
+ if (currentVectorX === 0) {
+ checkEdge = {
+ vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
+ vertex2: { x: currentLine.x1, y: currentRidge.y1 },
+ }
+ } else {
+ checkEdge = {
+ vertex1: { x: currentRidge.x1, y: currentRidge.y1 },
+ vertex2: { x: currentRidge.x1, y: currentLine.y1 },
+ }
+ }
+
+ const checkVectorX = Math.sign(checkEdge.vertex1.x - checkEdge.vertex2.x)
+ const checkVectorY = Math.sign(checkEdge.vertex1.y - checkEdge.vertex2.y)
+ const intersectPoints = []
+ roof.lines
+ .filter((line) => (currentVectorX === 0 ? line.x1 === line.x2 : line.y1 === line.y2))
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const is = edgesIntersection(checkEdge, lineEdge)
+ if (is) {
+ const isVectorX = Math.sign(checkEdge.vertex1.x - is.x)
+ const isVectorY = Math.sign(checkEdge.vertex1.y - is.y)
+ const isLineOtherPoint =
+ is.x === line.x1 && is.y === line.y1
+ ? { x: line.x2, y: line.y2 }
+ : {
+ x: line.x1,
+ y: line.y1,
+ }
+ const isInPoint =
+ checkVectorX === 0
+ ? (currentRidge.x1 <= isLineOtherPoint.x && isLineOtherPoint.x <= currentRidge.x2) ||
+ (currentRidge.x1 >= isLineOtherPoint.x && isLineOtherPoint.x >= currentRidge.x2)
+ : (currentRidge.y1 <= isLineOtherPoint.y && isLineOtherPoint.y <= currentRidge.y2) ||
+ (currentRidge.y1 >= isLineOtherPoint.y && isLineOtherPoint.y >= currentRidge.y2)
+ if (checkVectorX === isVectorX && checkVectorY === isVectorY && isInPoint) {
+ const size = Big(checkEdge.vertex1.x).minus(is.x).abs().pow(2).plus(Big(checkEdge.vertex1.y).minus(is.y).abs().pow(2)).sqrt()
+ intersectPoints.push({ is, size })
+ }
+ }
+ })
+ intersectPoints.sort((a, b) => a.size - b.size)
+ let intersect = intersectPoints[0].is
+ if (currentVectorX === 0) {
+ polygonPoints.push({ x: intersect.x, y: currentRidge.y1 }, { x: intersect.x, y: currentRidge.y2 })
+ } else {
+ polygonPoints.push({ x: currentRidge.x1, y: intersect.y }, { x: currentRidge.x2, y: intersect.y })
+ }
+ }
+ }
+
+ const sortedPolygonPoints = getSortedPoint(polygonPoints)
+ sortedPolygonPoints.forEach((startPoint, index) => {
+ let endPoint
+ if (index === sortedPolygonPoints.length - 1) {
+ endPoint = sortedPolygonPoints[0]
+ } else {
+ endPoint = sortedPolygonPoints[index + 1]
+ }
+ const hipLine = drawHipLine([startPoint.x, startPoint.y, endPoint.x, endPoint.y], canvas, roof, textMode, null, currentDegree, currentDegree)
+ if (currentVectorX === 0) {
+ if (Math.sign(startPoint.x - endPoint.x) === 0) {
+ hipLine.attributes.actualSize = hipLine.attributes.planeSize
+ }
+ } else {
+ if (Math.sign(startPoint.y - endPoint.y) === 0) {
+ hipLine.attributes.actualSize = hipLine.attributes.planeSize
+ }
+ }
+ baseHipLines.push({ x1: hipLine.x1, y1: hipLine.y1, x2: hipLine.x2, y2: hipLine.y2, line: hipLine })
+ })
+ })
+
+ /** 케라바 지붕에 연결된 마루 중 처마라인이 그려지지 않은 경우 확*/
+ baseGableRidgeLines.forEach((ridge) => {
+ const ridgeVectorX = Math.sign(ridge.x1 - ridge.x2)
+ const ridgeVectorY = Math.sign(ridge.y1 - ridge.y2)
+
+ const firstGableLines = []
+ const secondGableLines = []
+
+ baseGableLines
+ .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
+ .filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
+ .forEach((line) => firstGableLines.push(line))
+ baseGableLines
+ .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
+ .filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
+ .forEach((line) => secondGableLines.push(line))
+
+ baseHipLines
+ .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
+ .filter((line) => (line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
+ .forEach((line) => firstGableLines.push(line))
+ baseHipLines
+ .filter((line) => (ridgeVectorX === 0 ? line.x1 !== line.x2 : line.y1 !== line.y2))
+ .filter((line) => (line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
+ .forEach((line) => secondGableLines.push(line))
+
+ let degree1, degree2
+ if (firstGableLines.length < 2 || secondGableLines.length < 2) {
+ drawBaseLines.forEach((currentBaseLine, index) => {
+ let prevBaseLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
+ let nextBaseLine = drawBaseLines[(index + 1) % drawBaseLines.length]
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+
+ if (
+ gableType.includes(currentLine.attributes?.type) &&
+ eavesType.includes(prevLine.attributes?.type) &&
+ eavesType.includes(nextLine.attributes?.type) &&
+ Big(prevAngle).minus(Big(nextAngle)).abs().eq(180)
+ ) {
+ if (
+ ridgeVectorX === 0 &&
+ currentLine.x1 !== currentLine.x2 &&
+ ((currentLine.x1 <= ridge.x1 && ridge.x1 <= currentLine.x2) || (currentLine.x2 <= ridge.x1 && ridge.x1 <= currentLine.x1))
+ ) {
+ degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
+ }
+
+ if (
+ ridgeVectorY === 0 &&
+ currentLine.y1 !== currentLine.y2 &&
+ ((currentLine.y1 <= ridge.y1 && ridge.y1 <= currentLine.y2) || (currentLine.y2 <= ridge.y1 && ridge.y1 <= currentLine.y1))
+ ) {
+ degree1 = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ degree2 = nextLine.attributes.pitch > 0 ? getDegreeByChon(nextLine.attributes.pitch) : nextLine.attributes.degree
+ }
+ }
+ })
+ }
+
+ if (firstGableLines.length < 2) {
+ const connectRoof = roof.lines.find(
+ (line) =>
+ (line.x1 <= ridge.x1 && line.x2 >= ridge.x1 && line.y1 <= ridge.y1 && line.y2 >= ridge.y1) ||
+ (line.x2 <= ridge.x1 && line.x1 >= ridge.x1 && line.y2 <= ridge.y1 && line.y1 >= ridge.y1),
+ )
+ if (connectRoof) {
+ let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x1, ridge.y1]
+ let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x1, ridge.y1]
+ let intersectPoints1
+ let intersectPoints2
+ baseHipLines
+ .filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
+ .forEach((hip) => {
+ const line = hip.line
+ if (
+ (hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) ||
+ (hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1])
+ ) {
+ intersectPoints1 = { x: line.x1, y: line.y1 }
+ }
+ if (
+ (hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) ||
+ (hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1])
+ ) {
+ intersectPoints1 = { x: line.x2, y: line.y2 }
+ }
+ if (
+ (hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) ||
+ (hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1])
+ ) {
+ intersectPoints2 = { x: line.x1, y: line.y1 }
+ }
+ if (
+ (hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) ||
+ (hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1])
+ ) {
+ intersectPoints2 = { x: line.x2, y: line.y2 }
+ }
+ })
+ if (intersectPoints1) {
+ hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x1, ridge.y1]
+ }
+ if (intersectPoints2) {
+ hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x1, ridge.y1]
+ }
+
+ if (hipPoint1) {
+ let alreadyHip = false
+ baseHipLines
+ .filter(
+ (line) =>
+ (line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) ||
+ (line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ baseGableLines
+ .filter(
+ (line) =>
+ (line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) ||
+ (line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+
+ if (!alreadyHip) {
+ const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1)
+ baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 })
+ }
+ }
+ if (hipPoint2) {
+ let alreadyHip = false
+ baseHipLines
+ .filter(
+ (line) =>
+ (line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) ||
+ (line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ baseGableLines
+ .filter(
+ (line) =>
+ (line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) ||
+ (line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ if (!alreadyHip) {
+ const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2)
+ baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 })
+ }
+ }
+ }
+ }
+ if (secondGableLines.length < 2) {
+ const connectRoof = roof.lines.find(
+ (line) =>
+ (line.x1 <= ridge.x2 && line.x2 >= ridge.x2 && line.y1 <= ridge.y2 && line.y2 >= ridge.y2) ||
+ (line.x2 <= ridge.x2 && line.x1 >= ridge.x2 && line.y2 <= ridge.y2 && line.y1 >= ridge.y2),
+ )
+ if (connectRoof) {
+ let hipPoint1 = [connectRoof.x1, connectRoof.y1, ridge.x2, ridge.y2]
+ let hipPoint2 = [connectRoof.x2, connectRoof.y2, ridge.x2, ridge.y2]
+ let intersectPoints1
+ let intersectPoints2
+
+ baseHipLines
+ .filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
+ .forEach((hip) => {
+ const line = hip.line
+ if (
+ (hipPoint1[0] <= line.x1 && line.x1 <= hipPoint1[2] && hipPoint1[1] <= line.y1 && line.y1 <= hipPoint1[3]) ||
+ (hipPoint1[2] <= line.x1 && line.x1 <= hipPoint1[0] && hipPoint1[3] <= line.y1 && line.y1 <= hipPoint1[1])
+ ) {
+ intersectPoints1 = { x: line.x1, y: line.y1 }
+ }
+ if (
+ (hipPoint1[0] <= line.x2 && line.x2 <= hipPoint1[2] && hipPoint1[1] <= line.y2 && line.y2 <= hipPoint1[3]) ||
+ (hipPoint1[2] <= line.x2 && line.x2 <= hipPoint1[0] && hipPoint1[3] <= line.y2 && line.y2 <= hipPoint1[1])
+ ) {
+ intersectPoints1 = { x: line.x2, y: line.y2 }
+ }
+ if (
+ (hipPoint2[0] <= line.x1 && line.x1 <= hipPoint2[2] && hipPoint2[1] <= line.y1 && line.y1 <= hipPoint2[3]) ||
+ (hipPoint2[2] <= line.x1 && line.x1 <= hipPoint2[0] && hipPoint2[3] <= line.y1 && line.y1 <= hipPoint2[1])
+ ) {
+ intersectPoints2 = { x: line.x1, y: line.y1 }
+ }
+ if (
+ (hipPoint2[0] <= line.x2 && line.x2 <= hipPoint2[2] && hipPoint2[1] <= line.y2 && line.y2 <= hipPoint2[3]) ||
+ (hipPoint2[2] <= line.x2 && line.x2 <= hipPoint2[0] && hipPoint2[3] <= line.y2 && line.y2 <= hipPoint2[1])
+ ) {
+ intersectPoints2 = { x: line.x2, y: line.y2 }
+ }
+ })
+
+ if (intersectPoints1) {
+ hipPoint1 = [intersectPoints1.x, intersectPoints1.y, ridge.x2, ridge.y2]
+ }
+ if (intersectPoints2) {
+ hipPoint2 = [intersectPoints2.x, intersectPoints2.y, ridge.x2, ridge.y2]
+ }
+
+ if (hipPoint1) {
+ let alreadyHip = false
+ baseHipLines
+ .filter(
+ (line) =>
+ (line.line.x1 === hipPoint1[0] && line.line.y1 === hipPoint1[1] && line.line.x2 === hipPoint1[2] && line.line.y2 === hipPoint1[3]) ||
+ (line.line.x2 === hipPoint1[0] && line.line.y2 === hipPoint1[1] && line.line.x1 === hipPoint1[2] && line.line.y1 === hipPoint1[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ baseGableLines
+ .filter(
+ (line) =>
+ (line.x1 === hipPoint1[0] && line.y1 === hipPoint1[1] && line.x2 === hipPoint1[2] && line.y2 === hipPoint1[3]) ||
+ (line.x2 === hipPoint1[0] && line.y2 === hipPoint1[1] && line.x1 === hipPoint1[2] && line.y1 === hipPoint1[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+
+ if (!alreadyHip) {
+ const hipLine1 = drawHipLine(hipPoint1, canvas, roof, textMode, null, degree1, degree1)
+ baseHipLines.push({ x1: hipLine1.x1, y1: hipLine1.y1, x2: hipLine1.x2, y2: hipLine1.y2, line: hipLine1 })
+ }
+ }
+ if (hipPoint2) {
+ let alreadyHip = false
+ baseHipLines
+ .filter(
+ (line) =>
+ (line.line.x1 === hipPoint2[0] && line.line.y1 === hipPoint2[1] && line.line.x2 === hipPoint2[2] && line.line.y2 === hipPoint2[3]) ||
+ (line.line.x2 === hipPoint2[0] && line.line.y2 === hipPoint2[1] && line.line.x1 === hipPoint2[2] && line.line.y1 === hipPoint2[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ baseGableLines
+ .filter(
+ (line) =>
+ (line.x1 === hipPoint2[0] && line.y1 === hipPoint2[1] && line.x2 === hipPoint2[2] && line.y2 === hipPoint2[3]) ||
+ (line.x2 === hipPoint2[0] && line.y2 === hipPoint2[1] && line.x1 === hipPoint2[2] && line.y1 === hipPoint2[3]),
+ )
+ .forEach((line) => {
+ alreadyHip = true
+ })
+ if (!alreadyHip) {
+ const hipLine2 = drawHipLine(hipPoint2, canvas, roof, textMode, null, degree2, degree2)
+ baseHipLines.push({ x1: hipLine2.x1, y1: hipLine2.y1, x2: hipLine2.x2, y2: hipLine2.y2, line: hipLine2 })
+ }
}
}
}
})
- //겹쳐지는 마루는 하나로 합침
- roof.ridges.forEach((ridge) => {
- roof.ridges
- .filter((ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2))
+ /** ⨆ 모양 처마에 추녀마루를 그린다. */
+ drawEavesFirstLines.forEach((current) => {
+ // 확인용 라인, 포인트 제거
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ let { x1, x2, y1, y2, size } = currentBaseLine
+ let beforePrevLine, afterNextLine
+
+ /** 이전 라인의 경사 */
+ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ /** 다음 라인의 경사 */
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ /** 이전 라인의 전라인, 다음 라인의 다음라인을 찾는다 */
+ drawBaseLines.forEach((line, index) => {
+ if (line === prevBaseLine) {
+ beforePrevLine = drawBaseLines[(index - 1 + drawBaseLines.length) % drawBaseLines.length]
+ }
+ if (line === nextBaseLine) {
+ afterNextLine = drawBaseLines[(index + 1) % drawBaseLines.length]
+ }
+ })
+
+ /** 이전, 다음라인의 사잇각의 vector를 구한다. */
+ let prevVector = getHalfAngleVector(prevLine, currentLine)
+ let nextVector = getHalfAngleVector(currentLine, nextLine)
+
+ let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
+ let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
+
+ /** 각 라인의 흐름 방향을 확인한다. */
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ const beforePrevAngle = calculateAngle(beforePrevLine.line.startPoint, beforePrevLine.line.endPoint)
+ const afterNextAngle = calculateAngle(afterNextLine.line.startPoint, afterNextLine.line.endPoint)
+
+ /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */
+ let currentSize = Big(size).div(10)
+
+ /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const prevCheckPoint = {
+ x: Big(x1).plus(Big(prevHipVector.x).times(10)),
+ y: Big(y1).plus(Big(prevHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
+ prevHipVector = { x: Big(prevHipVector.x).neg().toNumber(), y: Big(prevHipVector.y).neg().toNumber() }
+ }
+
+ /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const nextCheckPoint = {
+ x: Big(x2).plus(Big(nextHipVector.x).times(10)),
+ y: Big(y2).plus(Big(nextHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
+ nextHipVector = { x: Big(nextHipVector.x).neg().toNumber(), y: Big(nextHipVector.y).neg().toNumber() }
+ }
+
+ /** 현재 라인의 길이를 기준으로 추녀 마루의 길이를 삼각함수를 사용하여 판단한다.*/
+ let hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt()
+
+ /**
+ * 현재 라인에서 2번째 전 라인과 2번째 후 라인의 각도가 같을때 -_- 와 같은 형태로 판단하고
+ * 맞은 편 외벽선까지의 거리를 확인후 currentSize 를 조정한다.
+ */
+ if (beforePrevLine === afterNextLine || (currentAngle === beforePrevAngle && currentAngle === afterNextAngle)) {
+ const xVector = Big(nextLine.x2).minus(Big(nextLine.x1))
+ const yVector = Big(nextLine.y2).minus(Big(nextLine.y1))
+ const currentMidX = Big(x1).plus(Big(x2)).div(2)
+ const currentMidY = Big(y1).plus(Big(y2)).div(2)
+
+ const midLineEdge = {
+ vertex1: { x: currentMidX.toNumber(), y: currentMidY.toNumber() },
+ vertex2: {
+ x: currentMidX.plus(currentSize.times(Math.sign(xVector))).toNumber(),
+ y: currentMidY.plus(currentSize.times(Math.sign(yVector))).toNumber(),
+ },
+ }
+
+ /** 현재 라인의 중심 지점에서 현재라인의 길이만큼 다음라인의 방향만큼 거리를 확인한다*/
+ baseLines
+ .filter((line) => line !== currentLine)
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(midLineEdge, lineEdge)
+ /** 현재라인의 길이만큼 거리가 모자라면 해당 길이 만큼을 현재라인의 길이로 판단하고 나머지 계산을 진행한다.*/
+ if (intersection && !intersection.isIntersectionOutside) {
+ const intersectionSize = Big(intersection.x)
+ .minus(Big(currentMidX))
+ .plus(Big(intersection.y).minus(Big(currentMidY)))
+ if (intersectionSize.lt(currentSize)) {
+ currentSize = intersectionSize
+ }
+ }
+ })
+
+ hipLength = currentSize.div(2).pow(2).plus(currentSize.div(2).pow(2)).sqrt()
+ } else {
+ if (currentAngle !== beforePrevAngle && currentAngle !== afterNextAngle && beforePrevLine !== afterNextLine) {
+ const beforePrevX1 = beforePrevLine.x1,
+ beforePrevY1 = beforePrevLine.y1,
+ beforePrevX2 = beforePrevLine.x2,
+ beforePrevY2 = beforePrevLine.y2
+ const afterNextX1 = afterNextLine.x1,
+ afterNextY1 = afterNextLine.y1,
+ afterNextX2 = afterNextLine.x2,
+ afterNextY2 = afterNextLine.y2
+
+ /** beforePrevLine 과 afterNextLine 을 연결하는 사각형의 경우 6각으로 판단 */
+ const connectBAPoint = { x1: afterNextX2, y1: afterNextY2, x2: beforePrevX1, y2: beforePrevY1 }
+ const isConnect =
+ baseLines.filter(
+ (line) =>
+ line.x1 === connectBAPoint.x1 && line.y1 === connectBAPoint.y1 && line.x2 === connectBAPoint.x2 && line.y2 === connectBAPoint.y2,
+ ).length > 0
+
+ /** 6각 */
+ // console.log('isConnect :', isConnect)
+ if (isConnect) {
+ const checkScale = currentSize.pow(2).plus(currentSize.pow(2)).sqrt()
+ const intersectBaseLine = []
+ if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
+ const prevEndPoint = {
+ x: Big(x1).plus(Big(prevHipVector.x).times(checkScale)),
+ y: Big(y1).plus(Big(prevHipVector.y).times(checkScale)),
+ }
+ baseLines
+ .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
+ .forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectBaseLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x1))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y1)).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ }
+ if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
+ const nextEndPoint = {
+ x: Big(x2).plus(Big(nextHipVector.x).times(checkScale)),
+ y: Big(y2).plus(Big(nextHipVector.y).times(checkScale)),
+ }
+ baseLines
+ .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
+ .forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectBaseLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x2))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y2)).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ }
+ const intersection = intersectBaseLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectBaseLine[0])
+ if (intersection) {
+ hipLength = intersection.distance
+ }
+ } else {
+ const rightAngleLine = baseLines
+ .filter(
+ (line) =>
+ line !== prevLine &&
+ line !== nextLine &&
+ (prevAngle === calculateAngle(line.startPoint, line.endPoint) || nextAngle === calculateAngle(line.startPoint, line.endPoint)),
+ )
+ .filter((line) => {
+ const index = baseLines.indexOf(line)
+ const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ const nextLine = baseLines[(index + 1) % baseLines.length]
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ switch (prevAngle) {
+ case 90:
+ return nextAngle === -90
+ case -90:
+ return nextAngle === 90
+ case 0:
+ return nextAngle === 180
+ case 180:
+ return nextAngle === 0
+ }
+ })
+
+ const oppositeCurrentLine = baseLines
+ .filter((line) => {
+ const angle = calculateAngle(line.startPoint, line.endPoint)
+ switch (currentAngle) {
+ case 90:
+ return angle === -90
+ case -90:
+ return angle === 90
+ case 0:
+ return angle === 180
+ case 180:
+ return angle === 0
+ }
+ })
+ .filter((line) => {
+ const index = baseLines.indexOf(line)
+ const prevLine = baseLines[(index - 1 + baseLines.length) % baseLines.length]
+ const nextLine = baseLines[(index + 1) % baseLines.length]
+ const prevAngle = calculateAngle(prevLine.startPoint, prevLine.endPoint)
+ const nextAngle = calculateAngle(nextLine.startPoint, nextLine.endPoint)
+ switch (prevAngle) {
+ case 90:
+ return nextAngle === -90
+ case -90:
+ return nextAngle === 90
+ case 0:
+ return nextAngle === 180
+ case 180:
+ return nextAngle === 0
+ }
+ })
+
+ let checkHipPoints
+ if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
+ checkHipPoints = [
+ x1,
+ y1,
+ Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2),
+ Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2),
+ ]
+ }
+
+ if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
+ checkHipPoints = [
+ x2,
+ y2,
+ Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2),
+ Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2),
+ ]
+ }
+
+ if (checkHipPoints) {
+ const intersectPoints = []
+ rightAngleLine.forEach((line) => {
+ const intersection = edgesIntersection(
+ {
+ vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] },
+ vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] },
+ },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection) {
+ intersectPoints.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(checkHipPoints[0]))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+
+ oppositeCurrentLine.forEach((line) => {
+ const intersection = edgesIntersection(
+ {
+ vertex1: { x: checkHipPoints[0], y: checkHipPoints[1] },
+ vertex2: { x: checkHipPoints[2], y: checkHipPoints[3] },
+ },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection) {
+ intersectPoints.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(checkHipPoints[0]))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(checkHipPoints[1])).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+
+ const intersection = intersectPoints.reduce((prev, current) => (prev.distance.lt(current.distance) ? prev : current), intersectPoints[0])
+ if (intersection) {
+ hipLength = intersection.distance.div(2)
+ }
+ }
+ }
+ }
+ }
+
+ let prevHipLine, nextHipLine
+ /** 이전라인과의 연결지점에 추녀마루를 그린다. */
+ if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0) {
+ const prevEndPoint = {
+ x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(1),
+ y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(1),
+ }
+
+ const intersectRidgeLine = []
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectRidgeLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x1))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y1)).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
+
+ if (intersectRidge) {
+ prevEndPoint.x = Big(intersectRidge.intersection.x).round(1)
+ prevEndPoint.y = Big(intersectRidge.intersection.y).round(1)
+ }
+
+ const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber())
+ const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber())
+ /** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/
+ let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
+ scale = scale.eq(0) ? Big(1) : scale
+
+ /** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */
+ const hipEdge = {
+ vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() },
+ vertex2: prevEndPoint,
+ }
+
+ let intersectPoints = []
+ /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ !intersection.isIntersectionOutside &&
+ Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) &&
+ Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y)
+ ) {
+ const intersectSize = prevEndPoint.x
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ })
+ }
+ })
+
+ intersectPoints = intersectPoints.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectPoints[0])
+
+ /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/
+ if (intersectPoints && intersectPoints.intersection) {
+ prevHipLine = drawHipLine(
+ [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ currentDegree,
+ )
+ baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine })
+ }
+ }
+ if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0) {
+ const nextEndPoint = {
+ x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(1),
+ y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(1),
+ }
+
+ const intersectRidgeLine = []
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectRidgeLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x1))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y1)).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
+
+ if (intersectRidge) {
+ nextEndPoint.x = Big(intersectRidge.intersection.x).round(1)
+ nextEndPoint.y = Big(intersectRidge.intersection.y).round(1)
+ }
+
+ const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber())
+ const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber())
+ let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
+ scale = scale.eq(0) ? Big(1) : scale
+
+ const hipEdge = {
+ vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() },
+ vertex2: nextEndPoint,
+ }
+
+ let intersectPoints = []
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ !intersection.isIntersectionOutside &&
+ Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) &&
+ Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y)
+ ) {
+ const intersectSize = nextEndPoint.x
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ })
+ }
+ })
+
+ intersectPoints = intersectPoints.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectPoints[0])
+
+ if (intersectPoints && intersectPoints.intersection) {
+ nextHipLine = drawHipLine(
+ [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ currentDegree,
+ )
+ baseHipLines.push({
+ x1: x2,
+ y1: y2,
+ x2: nextEndPoint.x.toNumber(),
+ y2: nextEndPoint.y.toNumber(),
+ line: nextHipLine,
+ })
+ }
+ }
+
+ /** 두 선이 교차하면 해당 포인트까지로 선 조정*/
+ if (prevHipLine !== undefined && nextHipLine !== undefined) {
+ const prevEdge = {
+ vertex1: { x: prevHipLine.x1, y: prevHipLine.y1 },
+ vertex2: { x: prevHipLine.x2, y: prevHipLine.y2 },
+ }
+ const nextEdge = {
+ vertex1: { x: nextHipLine.x1, y: nextHipLine.y1 },
+ vertex2: { x: nextHipLine.x2, y: nextHipLine.y2 },
+ }
+ const intersection = edgesIntersection(prevEdge, nextEdge)
+ if (intersection) {
+ /** 포인트 조정*/
+ baseHipLines
+ .filter((line) => line.line === prevHipLine || line.line === nextHipLine)
+ .forEach((line) => {
+ line.x2 = intersection.x
+ line.y2 = intersection.y
+ line.line.set({ x2: intersection.x, y2: intersection.y })
+ })
+ prevHipLine.x2 = intersection.x
+ prevHipLine.y2 = intersection.y
+ const prevSize = reCalculateSize(prevHipLine)
+
+ prevHipLine.attributes.planeSize = prevSize.planeSize
+ prevHipLine.attributes.actualSize = prevSize.actualSize
+ prevHipLine.fire('modified')
+
+ nextHipLine.x2 = intersection.x
+ nextHipLine.y2 = intersection.y
+ const nextSize = reCalculateSize(nextHipLine)
+
+ nextHipLine.attributes.planeSize = nextSize.planeSize
+ nextHipLine.attributes.actualSize = nextSize.actualSize
+ nextHipLine.fire('modified')
+ canvas.renderAll()
+ }
+ }
+
+ /** 두 추녀마루가 한점에서 만나는 경우 해당 점을 기점으로 마루를 작성한다.*/
+ if (
+ prevHipLine !== undefined &&
+ nextHipLine !== undefined &&
+ Big(prevHipLine.x2).minus(Big(nextHipLine.x2)).abs().lte(1) &&
+ Big(prevHipLine.y2).minus(Big(nextHipLine.y2)).abs().lte(1)
+ ) {
+ const startPoint = { x: prevHipLine.x2, y: prevHipLine.y2 }
+ let ridgeSize = 0
+
+ const currentMidX = Big(currentLine.x2).plus(Big(currentLine.x1)).div(2)
+ const currentMidY = Big(currentLine.y2).plus(Big(currentLine.y1)).div(2)
+
+ const xVector = Big(currentMidX).minus(Big(startPoint.x)).round(0, Big.roundDown)
+ const yVector = Big(currentMidY).minus(Big(startPoint.y)).round(0, Big.roundDown)
+
+ if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
+ const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line
+ const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint)
+ let checkEdge
+ if (Math.sign(oppositeLine.x1 - oppositeLine.x2) === 0) {
+ checkEdge = { vertex1: startPoint, vertex2: { x: oppositeLine.x1, y: startPoint.y } }
+ } else {
+ checkEdge = { vertex1: startPoint, vertex2: { x: startPoint.x, y: oppositeLine.y1 } }
+ }
+ if (currentAngle === oppositeAngle) {
+ const oppositeEdge = {
+ vertex1: { x: oppositeLine.x1, y: oppositeLine.y1 },
+ vertex2: { x: oppositeLine.x2, y: oppositeLine.y2 },
+ }
+ const intersection = edgesIntersection(oppositeEdge, checkEdge)
+ if (intersection) {
+ ridgeSize = Big(intersection.x)
+ .minus(Big(startPoint.x))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(startPoint.y)).pow(2))
+ .sqrt()
+ }
+ } else {
+ const intersectPoints = []
+ roof.lines
+ .filter(
+ (line) =>
+ Math.sign(oppositeLine.x1 - oppositeLine.x2) === Math.sign(line.x1 - line.x2) &&
+ Math.sign(oppositeLine.y1 - oppositeLine.y2) === Math.sign(line.y1 - line.y2),
+ )
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(lineEdge, checkEdge)
+ if (intersection) {
+ const size = Big(startPoint.x)
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(Big(startPoint.y).minus(Big(intersection.y)).pow(2))
+ .sqrt()
+ .toNumber()
+ intersectPoints.push({ intersection, size })
+ }
+ })
+ intersectPoints.sort((a, b) => a.size - b.size)
+ if (intersectPoints.length > 0) {
+ ridgeSize = Big(intersectPoints[0].size)
+ }
+ }
+ } else {
+ /** baseLines에서 가장 작은 x1과 가장 큰 x1, 가장 작은 y1과 가장 큰 y1을 계산*/
+ let minX = Infinity
+ let maxX = -Infinity
+ let minY = Infinity
+ let maxY = -Infinity
+
+ baseLines.forEach((line) => {
+ if (line.x1 < minX) {
+ minX = line.x1
+ }
+ if (line.x1 > maxX) {
+ maxX = line.x1
+ }
+ if (line.y1 < minY) {
+ minY = line.y1
+ }
+ if (line.y1 > maxY) {
+ maxY = line.y1
+ }
+ })
+ const checkLength = Big(maxX)
+ .minus(Big(minX))
+ .pow(2)
+ .plus(Big(maxY).minus(Big(minY)).pow(2))
+ .sqrt()
+
+ const checkEdges = {
+ vertex1: { x: startPoint.x, y: startPoint.y },
+ vertex2: {
+ x: Big(startPoint.x).minus(checkLength.times(Math.sign(xVector))),
+ y: Big(startPoint.y).minus(checkLength.times(Math.sign(yVector))),
+ },
+ }
+
+ /** 맞은편 벽 까지의 길이 판단을 위한 교차되는 line*/
+ const intersectBaseLine = []
+ baseLines
+ .filter((line) => {
+ /** currentAngle 의 반대 각도인 라인 */
+ const angle = calculateAngle(line.startPoint, line.endPoint)
+ switch (currentAngle) {
+ case 90:
+ return angle === -90
+ case -90:
+ return angle === 90
+ case 0:
+ return angle === 180
+ case 180:
+ return angle === 0
+ }
+ })
+ .filter((line) => {
+ const currentMinX = Math.min(x1, x2)
+ const currentMaxX = Math.max(x1, x2)
+ const currentMinY = Math.min(y1, y2)
+ const currentMaxY = Math.max(y1, y2)
+ const lineMinX = Math.min(line.x1, line.x2)
+ const lineMaxX = Math.max(line.x1, line.x2)
+ const lineMinY = Math.min(line.y1, line.y2)
+ const lineMaxY = Math.max(line.y1, line.y2)
+
+ /** currentLine 의 안쪽에 있거나 currentLine이 line의 안쪽에 있는 라인 */
+ if (Big(currentLine.y1).minus(Big(currentLine.y2)).abs().lte(1)) {
+ return (
+ (currentMinX <= lineMinX && lineMinX <= currentMaxX) ||
+ (currentMinX <= lineMaxX && lineMaxX <= currentMaxX) ||
+ (lineMinX <= currentMinX && currentMinX <= lineMaxX) ||
+ (lineMinX <= currentMaxX && currentMaxX <= lineMaxX)
+ )
+ } else {
+ return (
+ (currentMinY <= lineMinY && lineMinY <= currentMaxY) ||
+ (currentMinY <= lineMaxY && lineMaxY <= currentMaxY) ||
+ (lineMinY <= currentMinY && currentMinY <= lineMaxY) ||
+ (lineMinY <= currentMaxY && currentMaxY <= lineMaxY)
+ )
+ }
+ })
+ .forEach((line, index) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(checkEdges, lineEdge)
+ if (intersection) {
+ intersectBaseLine.push({ intersection, line })
+ }
+ })
+
+ /** 맞은편 라인 */
+ const oppositeLine = intersectBaseLine.reduce((prev, current) => {
+ const prevDistance = Big(prev.intersection.x)
+ .minus(Big(startPoint.x))
+ .pow(2)
+ .plus(Big(prev.intersection.y).minus(Big(startPoint.y)).pow(2))
+ .sqrt()
+ const currentDistance = Big(current.intersection.x)
+ .minus(Big(startPoint.x))
+ .pow(2)
+ .plus(Big(current.intersection.y).minus(Big(startPoint.y)).pow(2))
+ .sqrt()
+ return prevDistance < currentDistance ? prev : current
+ }, intersectBaseLine[0])
+
+ /** 맞은편 라인까지의 길이 = 전체 길이 - 현재라인의 길이 */
+ const oppositeSize = oppositeLine
+ ? Big(oppositeLine.intersection.x)
+ .minus(Big(startPoint.x))
+ .pow(2)
+ .plus(Big(oppositeLine.intersection.y).minus(Big(startPoint.y)).pow(2))
+ .sqrt()
+ .minus(currentSize.div(2))
+ .round(1)
+ : Infinity
+
+ /** 이전, 다음 라인중 길이가 짧은 길이*/
+ const lineMinSize = prevBaseLine.size < nextBaseLine.size ? Big(prevBaseLine.size).div(10) : Big(nextBaseLine.size).div(10)
+
+ /** 마루의 길이는 이전 다음 라인중 짧은것의 길이와 현재라인부터 맞은편 라인까지의 길이에서 현재 라인의 길이를 뺀 것중 짧은 길이 */
+ ridgeSize = Big(Math.min(oppositeSize, lineMinSize))
+ }
+
+ if (ridgeSize.gt(0) && baseRidgeCount < getMaxRidge(baseLines.length)) {
+ const points = [
+ startPoint.x,
+ startPoint.y,
+ Big(startPoint.x).minus(ridgeSize.times(Math.sign(xVector))),
+ Big(startPoint.y).minus(ridgeSize.times(Math.sign(yVector))),
+ ]
+
+ /** 동일 라인이 있는지 확인. */
+ if (
+ baseRidgeLines.filter((line) => {
+ const ridgeMinX = Math.min(points[0], points[2])
+ const ridgeMaxX = Math.max(points[0], points[2])
+ const ridgeMinY = Math.min(points[1], points[3])
+ const ridgeMaxY = Math.max(points[1], points[3])
+ const lineMinX = Math.min(line.x1, line.x2)
+ const lineMaxX = Math.max(line.x1, line.x2)
+ const lineMinY = Math.min(line.y1, line.y2)
+ const lineMaxY = Math.max(line.y1, line.y2)
+
+ return ridgeMinX === lineMinX && ridgeMaxX === lineMaxX && ridgeMinY === lineMinY && ridgeMaxY === lineMaxY
+ }).length > 0
+ ) {
+ return
+ }
+
+ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
+ if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
+ baseGableRidgeLines.push(ridgeLine)
+ } else {
+ baseRidgeLines.push(ridgeLine)
+ }
+ baseRidgeCount = baseRidgeCount + 1
+
+ /** 포인트 조정*/
+ baseHipLines
+ .filter((line) => line.line === prevHipLine || line.line === nextHipLine)
+ .forEach((line) => {
+ line.x2 = points[0]
+ line.y2 = points[1]
+ line.line.set({ x2: points[0], y2: points[1] })
+ // line.line.fire('modified')
+ })
+ prevHipLine.x2 = points[0]
+ prevHipLine.y2 = points[1]
+ const prevSize = reCalculateSize(prevHipLine)
+ prevHipLine.attributes.planeSize = prevSize.planeSize
+ prevHipLine.attributes.actualSize = prevSize.actualSize
+ prevHipLine.fire('modified')
+ nextHipLine.x2 = points[0]
+ nextHipLine.y2 = points[1]
+ const nextSize = reCalculateSize(nextHipLine)
+ nextHipLine.attributes.planeSize = nextSize.planeSize
+ nextHipLine.attributes.actualSize = nextSize.actualSize
+ nextHipLine.fire('modified')
+ canvas.renderAll()
+
+ if (gableType.includes(beforePrevLine.line.attributes.type) || gableType.includes(afterNextLine.line.attributes.type)) {
+ const oppositeLine = gableType.includes(beforePrevLine.line.attributes.type) ? beforePrevLine.line : afterNextLine.line
+ const oppositeAngle = calculateAngle(oppositeLine.startPoint, oppositeLine.endPoint)
+ if (Math.sign(ridgeLine.x1 - ridgeLine.x2) === 0) {
+ const gableVector = Math.sign(ridgeLine.x1 - oppositeLine.x1)
+ const prevVector = ridgeLine.x1 === prevHipLine.x1 ? Math.sign(ridgeLine.x1 - prevHipLine.x2) : Math.sign(ridgeLine.x2 - prevHipLine.x1)
+ const nextVector = ridgeLine.x1 === nextHipLine.x1 ? Math.sign(ridgeLine.x1 - nextHipLine.x2) : Math.sign(ridgeLine.x2 - nextHipLine.x1)
+
+ const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine
+ const firstDegree =
+ gableVector === Math.sign(ridgeLine.x1 - prevLine.x1)
+ ? prevLine.attributes.pitch > 0
+ ? getDegreeByChon(prevLine.attributes.pitch)
+ : prevLine.attributes.degree
+ : nextLine.attributes.pitch > 0
+ ? getDegreeByChon(nextLine.attributes.pitch)
+ : nextLine.attributes.degree
+
+ const oppositeRoofPoints = [
+ ridgeLine.x2,
+ ridgeLine.y2,
+ ridgeLine.x1 === firstHipLine.x1 ? firstHipLine.x2 : firstHipLine.x1,
+ ridgeLine.y2,
+ ]
+ const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree)
+ baseHipLines.push({
+ x1: oppositeRoofLine.x1,
+ y1: oppositeRoofLine.y1,
+ x2: oppositeRoofLine.x2,
+ y2: oppositeRoofLine.y2,
+ line: oppositeRoofLine,
+ })
+
+ const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
+ if (ridgeLine.x1 === firstHipLine.x1) {
+ connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2)
+ } else {
+ connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1)
+ }
+ const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
+ baseHipLines.push({
+ x1: connectRoofLine.x1,
+ y1: connectRoofLine.y1,
+ x2: connectRoofLine.x2,
+ y2: connectRoofLine.y2,
+ line: connectRoofLine,
+ })
+
+ /** 다른 방향의 추녀마루 */
+ const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine
+ const secondDegree =
+ gableVector === Math.sign(ridgeLine.x1 - prevLine.x1)
+ ? nextLine.attributes.pitch > 0
+ ? getDegreeByChon(nextLine.attributes.pitch)
+ : nextLine.attributes.degree
+ : prevLine.attributes.pitch > 0
+ ? getDegreeByChon(prevLine.attributes.pitch)
+ : prevLine.attributes.degree
+
+ const intersections = []
+ const checkEdge = {
+ vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 },
+ vertex2: { x: ridgeLine.x1 === secondHipLine.x1 ? secondHipLine.x2 : secondHipLine.x1, y: ridgeLine.y2 },
+ }
+
+ baseGableRidgeLines
+ .filter((ridge) => ridge !== ridgeLine)
+ .forEach((ridge) => {
+ const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
+ const intersection = edgesIntersection(ridgeEdge, checkEdge)
+ if (intersection && !intersections.includes(intersection)) {
+ const size = Big(intersection.x)
+ .minus(Big(ridgeLine.x2))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2))
+ .sqrt()
+ .toNumber()
+ intersections.push({ intersection, size, ridge })
+ }
+ })
+ intersections.sort((a, b) => a.size - b.size)
+
+ if (intersections.length > 0) {
+ const intersection = intersections[0].intersection
+ const intersectRidge = intersections[0].ridge
+ const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y]
+ const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree)
+ baseHipLines.push({
+ x1: oppositeLine.x1,
+ y1: oppositeLine.y1,
+ x2: oppositeLine.x2,
+ y2: oppositeLine.y2,
+ line: oppositeRoofLine,
+ })
+
+ const ridgeVector = Math.sign(ridgeLine.y1 - ridgeLine.y2)
+ const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
+ if (ridgeVector === Math.sign(oppositeRoofLine.y1 - intersectRidge.y2)) {
+ connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2)
+ } else {
+ connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1)
+ }
+
+ const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
+ baseHipLines.push({
+ x1: connectRoofLine.x1,
+ y1: connectRoofLine.y1,
+ x2: connectRoofLine.x2,
+ y2: connectRoofLine.y2,
+ line: connectRoofLine,
+ })
+ }
+ } else {
+ const gableVector = Math.sign(ridgeLine.y1 - oppositeLine.y1)
+ const prevVector = ridgeLine.y1 === prevHipLine.y1 ? Math.sign(ridgeLine.y1 - prevHipLine.y2) : Math.sign(ridgeLine.y1 - prevHipLine.y1)
+ const nextVector = ridgeLine.y1 === nextHipLine.y1 ? Math.sign(ridgeLine.y1 - nextHipLine.y2) : Math.sign(ridgeLine.y1 - nextHipLine.y1)
+
+ /** 마루와 박공지붕을 연결하기위한 추녀마루 라인 */
+ const firstHipLine = gableVector === prevVector ? prevHipLine : nextHipLine
+ const firstDegree =
+ gableVector === Math.sign(ridgeLine.y1 - prevLine.y1)
+ ? prevLine.attributes.pitch > 0
+ ? getDegreeByChon(prevLine.attributes.pitch)
+ : prevLine.attributes.degree
+ : nextLine.attributes.pitch > 0
+ ? getDegreeByChon(nextLine.attributes.pitch)
+ : nextLine.attributes.degree
+
+ const oppositeRoofPoints = [
+ ridgeLine.x2,
+ ridgeLine.y2,
+ ridgeLine.x2,
+ ridgeLine.y1 === firstHipLine.y1 ? firstHipLine.y2 : firstHipLine.y1,
+ ]
+ const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, firstDegree, firstDegree)
+ baseHipLines.push({
+ x1: oppositeRoofLine.x1,
+ y1: oppositeRoofLine.y1,
+ x2: oppositeRoofLine.x2,
+ y2: oppositeRoofLine.y2,
+ line: oppositeRoofLine,
+ })
+
+ const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
+ if (ridgeLine.y1 === firstHipLine.y1) {
+ connectRoofPoints.push(firstHipLine.x2, firstHipLine.y2)
+ } else {
+ connectRoofPoints.push(firstHipLine.x1, firstHipLine.y1)
+ }
+
+ const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
+ baseHipLines.push({
+ x1: connectRoofLine.x1,
+ y1: connectRoofLine.y1,
+ x2: connectRoofLine.x2,
+ y2: connectRoofLine.y2,
+ line: connectRoofLine,
+ })
+
+ /** 다른 방향의 추녀마루 */
+ const secondHipLine = gableVector === prevVector ? nextHipLine : prevHipLine
+ const secondDegree =
+ gableVector === Math.sign(ridgeLine.y1 - prevLine.y1)
+ ? nextLine.attributes.pitch > 0
+ ? getDegreeByChon(nextLine.attributes.pitch)
+ : nextLine.attributes.degree
+ : prevLine.attributes.pitch > 0
+ ? getDegreeByChon(prevLine.attributes.pitch)
+ : prevLine.attributes.degree
+
+ const intersections = []
+ const checkEdge = {
+ vertex1: { x: ridgeLine.x2, y: ridgeLine.y2 },
+ vertex2: { x: ridgeLine.x2, y: ridgeLine.y1 === secondHipLine.y1 ? secondHipLine.y2 : secondHipLine.y1 },
+ }
+ baseGableRidgeLines
+ .filter((ridge) => ridge !== ridgeLine)
+ .forEach((ridge) => {
+ const ridgeEdge = { vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } }
+ const intersection = edgesIntersection(ridgeEdge, checkEdge)
+ if (intersection && !intersections.includes(intersection)) {
+ const size = Big(intersection.x)
+ .minus(Big(ridgeLine.x2))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(ridgeLine.y2)).pow(2))
+ .sqrt()
+ .toNumber()
+ intersections.push({ intersection, size, ridge })
+ }
+ })
+ intersections.sort((a, b) => a.size - b.size)
+ if (intersections.length > 0) {
+ const intersection = intersections[0].intersection
+ const intersectRidge = intersections[0].ridge
+ const oppositeRoofPoints = [ridgeLine.x2, ridgeLine.y2, intersection.x, intersection.y]
+ const oppositeRoofLine = drawHipLine(oppositeRoofPoints, canvas, roof, textMode, null, secondDegree, secondDegree)
+ baseHipLines.push({
+ x1: oppositeLine.x1,
+ y1: oppositeLine.y1,
+ x2: oppositeLine.x2,
+ y2: oppositeLine.y2,
+ line: oppositeRoofLine,
+ })
+
+ const ridgeVector = Math.sign(ridgeLine.x1 - ridgeLine.x2)
+ const connectRoofPoints = [oppositeRoofLine.x2, oppositeRoofLine.y2]
+ if (ridgeVector === Math.sign(oppositeRoofLine.x1 - intersectRidge.x2)) {
+ connectRoofPoints.push(intersectRidge.x2, intersectRidge.y2)
+ } else {
+ connectRoofPoints.push(intersectRidge.x1, intersectRidge.y1)
+ }
+
+ const connectRoofLine = drawRoofLine(connectRoofPoints, canvas, roof, textMode)
+ baseHipLines.push({
+ x1: connectRoofLine.x1,
+ y1: connectRoofLine.y1,
+ x2: connectRoofLine.x2,
+ y2: connectRoofLine.y2,
+ line: connectRoofLine,
+ })
+ }
+ }
+ }
+ }
+ }
+ })
+
+ /** 중복제거 */
+ baseRidgeLines.forEach((ridge) => {
+ baseRidgeLines
+ .filter((ridge2) => ridge !== ridge2)
.forEach((ridge2) => {
let overlap = segmentsOverlap(ridge, ridge2)
if (overlap) {
+ roof.canvas.remove(ridge)
+ roof.canvas.remove(ridge2)
+ baseRidgeLines = baseRidgeLines.filter((r) => r !== ridge && r !== ridge2)
+
+ baseRidgeCount = baseRidgeCount - 2
+
let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
- //겹치는 용마루의 좌표를 합친다.
- const newRidge = new QLine([x1, y1, x2, y2], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.RIDGE,
- textMode: textMode,
- attributes: {
- roofId: roof.id,
- planeSize: calcLinePlaneSize({ x1, y1, x2, y2 }),
- actualSize: calcLinePlaneSize({ x1, y1, x2, y2 }),
- },
- })
- //겹치는 용마루를 제거한다.
- roof.canvas.remove(ridge)
- roof.canvas.remove(ridge2)
- roof.ridges = roof.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
- roof.ridges = roof.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
- roof.innerLines = roof.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
- roof.innerLines = roof.innerLines.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
- canvas.add(newRidge)
- roof.ridges.push(newRidge)
- roof.innerLines.push(newRidge)
+ const newRidge = drawRidgeLine([x1, y1, x2, y2], canvas, roof, textMode)
+ baseRidgeLines.push(newRidge)
+ baseRidgeCount = baseRidgeCount + 1
}
})
})
- canvas?.renderAll()
+
+ /** ㄴ 모양 처마에 추녀마루를 그린다. */
+ drawEavesSecondLines.forEach((current) => {
+ const { currentBaseLine, prevBaseLine, nextBaseLine } = current
+ const currentLine = currentBaseLine.line
+ const prevLine = prevBaseLine.line
+ const nextLine = nextBaseLine.line
+ let { x1, x2, y1, y2, size } = currentBaseLine
+
+ /** 이전 라인의 경사 */
+ const prevDegree = prevLine.attributes.pitch > 0 ? getDegreeByChon(prevLine.attributes.pitch) : prevLine.attributes.degree
+ /** 다음 라인의 경사 */
+ const currentDegree = currentLine.attributes.pitch > 0 ? getDegreeByChon(currentLine.attributes.pitch) : currentLine.attributes.degree
+
+ /** 이전, 다음라인의 사잇각의 vector를 구한다. */
+ let prevVector = getHalfAngleVector(prevLine, currentLine)
+ let nextVector = getHalfAngleVector(currentLine, nextLine)
+
+ let prevHipVector = { x: Big(prevVector.x), y: Big(prevVector.y) }
+ let nextHipVector = { x: Big(nextVector.x), y: Big(nextVector.y) }
+
+ /** 각 라인의 흐름 방향을 확인한다. */
+ const currentAngle = calculateAngle(currentLine.startPoint, currentLine.endPoint)
+
+ /** 현재 라인의 길이 추녀마루 길이의 기준이 된다. */
+ let hipLength = Big(x2)
+ .minus(Big(x1))
+ .plus(Big(y2).minus(Big(y1)))
+ .abs()
+
+ /** 이전 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const prevCheckPoint = {
+ x: Big(x1).plus(Big(prevHipVector.x).times(10)),
+ y: Big(y1).plus(Big(prevHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(prevCheckPoint)) {
+ prevHipVector = { x: Big(prevHipVector.x).neg(), y: Big(prevHipVector.y).neg() }
+ }
+
+ /** 다음 라인과의 사이 추녀마루의 각도를 확인한다, 각도가 지붕안쪽으로 향하지 않을때 반대로 처리한다.*/
+ const nextCheckPoint = {
+ x: Big(x2).plus(Big(nextHipVector.x).times(10)),
+ y: Big(y2).plus(Big(nextHipVector.y).times(10)),
+ }
+ if (!checkWallPolygon.inPolygon(nextCheckPoint)) {
+ nextHipVector = { x: Big(nextHipVector.x).neg(), y: Big(nextHipVector.y).neg() }
+ }
+
+ let prevHipLine, nextHipLine
+ /** 이전라인과의 연결지점에 추녀마루를 그린다. */
+ if (baseHipLines.filter((line) => line.x1 === x1 && line.y1 === y1).length === 0 && eavesType.includes(prevLine.attributes.type)) {
+ let prevEndPoint = {
+ x: Big(x1).plus(Big(prevHipVector.x).times(hipLength)).round(2),
+ y: Big(y1).plus(Big(prevHipVector.y).times(hipLength)).round(2),
+ }
+
+ const prevEndEdge = { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint }
+
+ const intersectBaseLine = []
+ baseLines
+ .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
+ .filter((line) => {
+ if (currentAngle === 0 || currentAngle === 180) {
+ return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s
+ } else {
+ return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s
+ }
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(prevEndEdge, lineEdge)
+
+ if (intersection && Big(intersection.x - x1).s === nextHipVector.x.s && Big(intersection.y - y1).s === nextHipVector.y.s) {
+ const size = Big(intersection.x - x1)
+ .abs()
+ .pow(2)
+ .plus(
+ Big(intersection.y - y1)
+ .pow(2)
+ .abs(),
+ )
+ .sqrt()
+ if (size.gt(0)) {
+ intersectBaseLine.push({
+ intersection,
+ size,
+ })
+ }
+ }
+ })
+
+ const intersectBase = intersectBaseLine.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectBaseLine[0])
+
+ if (intersectBase) {
+ prevEndPoint = {
+ x: Big(x1)
+ .plus(Big(nextHipVector.x).times(intersectBase.size.div(2)))
+ .round(2),
+ y: Big(y1)
+ .plus(Big(nextHipVector.y).times(intersectBase.size.div(2)))
+ .round(2),
+ }
+ }
+
+ const intersectRidgeLine = []
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x1, y: y1 }, vertex2: prevEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectRidgeLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x1))
+ .abs()
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y1)).pow(2).abs())
+ .sqrt(),
+ })
+ }
+ })
+
+ const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
+
+ if (intersectRidge) {
+ prevEndPoint.x = Big(intersectRidge.intersection.x).round(1)
+ prevEndPoint.y = Big(intersectRidge.intersection.y).round(1)
+ }
+
+ const xVector = Math.sign(Big(prevEndPoint.x).minus(Big(x1)).neg().toNumber())
+ const yVector = Math.sign(Big(prevEndPoint.y).minus(Big(y1)).neg().toNumber())
+ /** 지붕 선까지의 곂침에 따른 길이를 파악하기 위한 scale*/
+ let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(prevLine.attributes.offset).pow(2)).sqrt()
+ scale = scale.eq(0) ? Big(1) : scale
+
+ /** scale 만큼 추녀마루를 늘려서 겹치는 포인트를 확인한다. */
+ const hipEdge = {
+ vertex1: { x: Big(x1).plus(scale.times(xVector)).toNumber(), y: Big(y1).plus(scale.times(yVector)).toNumber() },
+ vertex2: prevEndPoint,
+ }
+
+ let intersectPoints = []
+ /** 외벽선에서 추녀 마루가 겹치는 경우에 대한 확인*/
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ !intersection.isIntersectionOutside &&
+ Math.sign(prevEndPoint.x - x1) === Math.sign(prevEndPoint.x - intersection.x) &&
+ Math.sign(prevEndPoint.y - y1) === Math.sign(prevEndPoint.y - intersection.y)
+ ) {
+ const intersectSize = prevEndPoint.x
+ .minus(Big(intersection.x))
+ .abs()
+ .pow(2)
+ .plus(prevEndPoint.y.minus(Big(intersection.y)).pow(2).abs())
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ })
+ }
+ })
+
+ intersectPoints = intersectPoints.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectPoints[0])
+
+ /** 겹치는 외벽선이 있을때 추녀마루를 외벽선 까지 늘려서 수정*/
+ if (intersectPoints && intersectPoints.intersection) {
+ prevHipLine = drawHipLine(
+ [intersectPoints.intersection.x, intersectPoints.intersection.y, prevEndPoint.x.toNumber(), prevEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ currentDegree,
+ )
+ baseHipLines.push({ x1, y1, x2: prevEndPoint.x.toNumber(), y2: prevEndPoint.y.toNumber(), line: prevHipLine })
+ }
+ }
+ /** 다음라인과의 연결지점에 추녀마루를 그린다. */
+ if (baseHipLines.filter((line) => line.x1 === x2 && line.y1 === y2).length === 0 && eavesType.includes(nextLine.attributes.type)) {
+ let nextEndPoint = {
+ x: Big(x2).plus(Big(nextHipVector.x).times(hipLength)).round(2),
+ y: Big(y2).plus(Big(nextHipVector.y).times(hipLength)).round(2),
+ }
+
+ const nextEndEdge = {
+ vertex1: { x: x2, y: y2 },
+ vertex2: nextEndPoint,
+ }
+
+ const intersectBaseLine = []
+ baseLines
+ .filter((line) => line !== currentLine && line !== prevLine && line !== nextLine)
+ .filter((line) => {
+ if (currentAngle === 0 || currentAngle === 180) {
+ return Big(line.y1).minus(Big(y1)).s === nextHipVector.y.s || Big(line.y2).minus(Big(y1)).s === nextHipVector.y.s
+ } else {
+ return Big(line.x1).minus(Big(x1)).s === nextHipVector.x.s || Big(line.x2).minus(Big(x1)).s === nextHipVector.x.s
+ }
+ })
+ .forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(nextEndEdge, lineEdge)
+
+ if (intersection && Big(intersection.x - x2).s === nextHipVector.x.s && Big(intersection.y - y2).s === nextHipVector.y.s) {
+ const size = Big(intersection.x - x2)
+ .abs()
+ .pow(2)
+ .plus(
+ Big(intersection.y - y2)
+ .abs()
+ .pow(2),
+ )
+ .sqrt()
+ if (size.gt(0)) {
+ intersectBaseLine.push({
+ intersection,
+ size,
+ })
+ }
+ }
+ })
+
+ const intersectBase = intersectBaseLine.reduce((prev, current) => {
+ return prev.size.lt(current.size) ? prev : current
+ }, intersectBaseLine[0])
+
+ if (intersectBase) {
+ nextEndPoint = {
+ x: Big(x2)
+ .plus(Big(nextHipVector.x).times(intersectBase.size.div(2)))
+ .round(2),
+ y: Big(y2)
+ .plus(Big(nextHipVector.y).times(intersectBase.size.div(2)))
+ .round(2),
+ }
+ }
+
+ const intersectRidgeLine = []
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(
+ { vertex1: { x: x2, y: y2 }, vertex2: nextEndPoint },
+ { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
+ )
+
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectRidgeLine.push({
+ intersection,
+ distance: Big(intersection.x)
+ .minus(Big(x1))
+ .pow(2)
+ .plus(Big(intersection.y).minus(Big(y1)).pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ const intersectRidge = intersectRidgeLine.reduce((prev, current) => (prev.distance < current.distance ? prev : current), intersectRidgeLine[0])
+
+ if (intersectRidge) {
+ nextEndPoint.x = Big(intersectRidge.intersection.x).round(1)
+ nextEndPoint.y = Big(intersectRidge.intersection.y).round(1)
+ }
+
+ const xVector = Math.sign(Big(nextEndPoint.x).minus(Big(x2)).neg().toNumber())
+ const yVector = Math.sign(Big(nextEndPoint.y).minus(Big(y2)).neg().toNumber())
+ let scale = Big(currentLine.attributes.offset).pow(2).plus(Big(nextLine.attributes.offset).pow(2)).sqrt()
+ scale = scale.eq(0) ? Big(1) : scale
+
+ const hipEdge = {
+ vertex1: { x: Big(x2).plus(scale.times(xVector)).toNumber(), y: Big(y2).plus(scale.times(yVector)).toNumber() },
+ vertex2: nextEndPoint,
+ }
+
+ let intersectPoints = []
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(hipEdge, lineEdge)
+ if (
+ intersection &&
+ !intersection.isIntersectionOutside &&
+ Math.sign(nextEndPoint.x - x2) === Math.sign(nextEndPoint.x - intersection.x) &&
+ Math.sign(nextEndPoint.y - y2) === Math.sign(nextEndPoint.y - intersection.y)
+ ) {
+ const intersectSize = nextEndPoint.x
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(nextEndPoint.y.minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ })
+ }
+ })
+
+ intersectPoints = intersectPoints.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectPoints[0])
+
+ if (intersectPoints && intersectPoints.intersection) {
+ nextHipLine = drawHipLine(
+ [intersectPoints.intersection.x, intersectPoints.intersection.y, nextEndPoint.x.toNumber(), nextEndPoint.y.toNumber()],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ currentDegree,
+ )
+ baseHipLines.push({
+ x1: x2,
+ y1: y2,
+ x2: nextEndPoint.x.toNumber(),
+ y2: nextEndPoint.y.toNumber(),
+ line: nextHipLine,
+ })
+ }
+ }
+ })
+
+ /** baseHipLine 이 ridge에 붙지 않은 경우 확인 */
+ baseHipLines.forEach((hipLine) => {
+ const ridgeCount = baseRidgeLines.filter(
+ (ridgeLine) => (hipLine.x2 === ridgeLine.x1 && hipLine.y2 === ridgeLine.y1) || (hipLine.x2 === ridgeLine.x2 && hipLine.y2 === ridgeLine.y2),
+ ).length
+ if (ridgeCount === 0) {
+ const hipXVector = Big(hipLine.x2).minus(hipLine.x1)
+ const hipYVector = Big(hipLine.y2).minus(hipLine.y1)
+ const hipSize = hipXVector.abs().pow(2).plus(hipYVector.abs().pow(2)).sqrt()
+
+ const intersectRidgePoints = []
+
+ const hipLineEdge = { vertex1: { x: hipLine.x1, y: hipLine.y1 }, vertex2: { x: hipLine.x2, y: hipLine.y2 } }
+ baseRidgeLines.forEach((ridgeLine) => {
+ const ridgeLineEdge = {
+ vertex1: { x: ridgeLine.x1, y: ridgeLine.y1 },
+ vertex2: { x: ridgeLine.x2, y: ridgeLine.y2 },
+ }
+ const intersection = edgesIntersection(hipLineEdge, ridgeLineEdge)
+
+ if (intersection) {
+ const intersectXVector = Big(intersection.x).minus(Big(hipLine.x1))
+ const intersectYVector = Big(intersection.y).minus(Big(hipLine.y1))
+ const intersectSize = intersectXVector.pow(2).plus(intersectYVector.pow(2)).sqrt()
+
+ if (!intersection.isIntersectionOutside) {
+ intersectRidgePoints.push({
+ x: intersection.x,
+ y: intersection.y,
+ size: intersectSize,
+ })
+ } else if (
+ ((intersection.x === ridgeLine.x1 && intersection.y === ridgeLine.y1) ||
+ (intersection.x === ridgeLine.x2 && intersection.y === ridgeLine.y2)) &&
+ Math.sign(hipXVector.toNumber()) === Math.sign(intersectXVector.toNumber()) &&
+ Math.sign(hipYVector.toNumber()) === Math.sign(intersectYVector.toNumber()) &&
+ intersectSize.gt(0) &&
+ intersectSize.lt(hipSize)
+ ) {
+ intersectRidgePoints.push({
+ x: intersection.x,
+ y: intersection.y,
+ size: intersectSize,
+ })
+ }
+ }
+ })
+ intersectRidgePoints.sort((prev, current) => prev.size.minus(current.size).toNumber())
+ if (intersectRidgePoints.length > 0) {
+ const oldPlaneSize = hipLine.line.attributes.planeSize
+ const oldActualSize = hipLine.line.attributes.actualSize
+ const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize)))
+ .times(180)
+ .div(Math.PI)
+ const planeSize = calcLinePlaneSize({
+ x1: hipLine.line.x1,
+ y1: hipLine.line.y1,
+ x2: hipLine.line.x2,
+ y2: hipLine.line.y2,
+ })
+ hipLine.x2 = intersectRidgePoints[0].x
+ hipLine.y2 = intersectRidgePoints[0].y
+ hipLine.line.set({ x2: intersectRidgePoints[0].x, y2: intersectRidgePoints[0].y })
+ hipLine.line.attributes.planeSize = planeSize
+ hipLine.line.attributes.actualSize = planeSize === oldActualSize ? 0 : Big(planeSize).div(theta).round(1).toNumber()
+ hipLine.line.fire('modified')
+ }
+ }
+ })
+
+ /** 지붕선에 맞닫지 않은 부분을 확인하여 처리 한다.*/
+ /** 1. 그려진 마루 중 추녀마루가 부재하는 경우를 판단. 부재는 연결된 라인이 홀수인 경우로 판단한다.*/
+ let unFinishedRidge = []
+ baseRidgeLines.forEach((current) => {
+ let checkPoint = [
+ { x: current.x1, y: current.y1, line: current, cnt: 0, onRoofLine: false },
+ { x: current.x2, y: current.y2, line: current, cnt: 0, onRoofLine: false },
+ ]
+ baseHipLines.forEach((line) => {
+ if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) {
+ checkPoint[0].cnt = checkPoint[0].cnt + 1
+ }
+ if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) {
+ checkPoint[1].cnt = checkPoint[1].cnt + 1
+ }
+ })
+
+ /** 마루의 포인트가 지붕선 위에 있는경우는 제외 (케라바 등)*/
+ roof.lines.forEach((line) => {
+ if (
+ line.x1 === line.x2 &&
+ checkPoint[0].x === line.x1 &&
+ ((line.y1 <= checkPoint[0].y && line.y2 >= checkPoint[0].y) || (line.y1 >= checkPoint[0].y && line.y2 <= checkPoint[0].y))
+ ) {
+ checkPoint[0].onRoofLine = true
+ }
+ if (
+ line.y1 === line.y2 &&
+ checkPoint[0].y === line.y1 &&
+ ((line.x1 <= checkPoint[0].x && line.x2 >= checkPoint[0].x) || (line.x1 >= checkPoint[0].x && line.x2 <= checkPoint[0].x))
+ ) {
+ checkPoint[0].onRoofLine = true
+ }
+ if (
+ line.x1 === line.x2 &&
+ checkPoint[1].x === line.x1 &&
+ ((line.y1 <= checkPoint[1].y && line.y2 >= checkPoint[1].y) || (line.y1 >= checkPoint[1].y && line.y2 <= checkPoint[1].y))
+ ) {
+ checkPoint[1].onRoofLine = true
+ }
+ if (
+ line.y1 === line.y2 &&
+ checkPoint[1].y === line.y1 &&
+ ((line.x1 <= checkPoint[1].x && line.x2 >= checkPoint[1].x) || (line.x1 >= checkPoint[1].x && line.x2 <= checkPoint[1].x))
+ ) {
+ checkPoint[1].onRoofLine = true
+ }
+ })
+ // console.log('checkPoint : ', checkPoint)
+ if ((checkPoint[0].cnt === 0 || checkPoint[0].cnt % 2 !== 0) && !checkPoint[0].onRoofLine) {
+ unFinishedRidge.push(checkPoint[0])
+ }
+ if ((checkPoint[1].cnt === 0 || checkPoint[1].cnt % 2 !== 0) && !checkPoint[1].onRoofLine) {
+ unFinishedRidge.push(checkPoint[1])
+ }
+ })
+
+ /** 2. 그려진 추녀마루 중 완성되지 않은 것을 찾는다. 완성되지 않았다는 것은 연결된 포인트가 홀수인 경우로 판단한다.*/
+ const findUnFinishedPoints = (baseHipLines) => {
+ let unFinishedPoint = []
+ baseHipLines.forEach((current) => {
+ let checkPoint = [
+ { x: current.x1, y: current.y1, checked: true, line: current.line },
+ { x: current.x2, y: current.y2, checked: true, line: current.line },
+ ]
+ baseLines.forEach((line) => {
+ if ((line.x1 === current.x1 && line.y1 === current.y1) || (line.x2 === current.x1 && line.y2 === current.y1)) {
+ checkPoint[0].checked = false
+ }
+ if ((line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2)) {
+ checkPoint[1].checked = false
+ }
+ })
+
+ const samePoints = []
+ checkPoint
+ .filter((point) => point.checked)
+ .forEach((point) => {
+ baseHipLines.forEach((line) => {
+ if (line.x1 === point.x && line.y1 === point.y) {
+ samePoints.push({ x: point.x, y: point.y, line: line })
+ }
+ if (line.x2 === point.x && line.y2 === point.y) {
+ samePoints.push({ x: point.x, y: point.y, line: line })
+ }
+ })
+ })
+ if (samePoints.length > 0 && samePoints.length % 2 !== 0) {
+ unFinishedPoint.push(samePoints[0])
+ }
+ })
+ return unFinishedPoint
+ }
+
+ let unFinishedPoint = findUnFinishedPoints(baseHipLines)
+
+ /**3. 라인이 부재인 마루의 모자란 라인을 찾는다. 라인은 그려진 추녀마루 중에 완성되지 않은 추녀마루와 확인한다.*/
+ /**3-1 라인을 그릴때 각도가 필요하기 때문에 각도를 구한다. 각도는 전체 지부의 각도가 같다면 하나로 처리 */
+ let degreeAllLine = []
+ baseLines
+ .filter((line) => eavesType.includes(line.attributes.type))
+ .forEach((line) => {
+ const pitch = line.attributes.pitch
+ const degree = line.attributes.degree
+ degreeAllLine.push(pitch > 0 ? getDegreeByChon(pitch) : degree)
+ })
+
+ let currentDegree, prevDegree
+ degreeAllLine = [...new Set(degreeAllLine)]
+ currentDegree = degreeAllLine[0]
+ if (degreeAllLine.length === 1) {
+ prevDegree = currentDegree
+ } else {
+ prevDegree = degreeAllLine[1]
+ }
+
+ /** 라인이 부재한 마루에 라인을 찾아 그린다.*/
+ unFinishedRidge.forEach((current) => {
+ let checkPoints = []
+
+ unFinishedPoint
+ .filter(
+ (point) =>
+ point.x !== current.x &&
+ point.y !== current.y &&
+ Big(point.x)
+ .minus(Big(current.x))
+ .abs()
+ .minus(Big(point.y).minus(Big(current.y)).abs())
+ .abs()
+ .lt(1),
+ )
+ .forEach((point) => {
+ const pointEdge = { vertex1: { x: point.x, y: point.y }, vertex2: { x: current.x, y: current.y } }
+ let isIntersection = false
+ baseLines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(pointEdge, lineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ isIntersection = true
+ }
+ })
+ if (!isIntersection) {
+ checkPoints.push({
+ point,
+ size: Big(point.x)
+ .minus(Big(current.x))
+ .abs()
+ .pow(2)
+ .plus(Big(point.y).minus(Big(current.y)).abs().pow(2))
+ .sqrt(),
+ })
+ }
+ })
+ if (checkPoints.length > 0) {
+ checkPoints.sort((a, b) => a.size - b.size)
+ const maxCnt = Big(2).minus(Big(current.cnt)).toNumber()
+ for (let i = 0; i < maxCnt; i++) {
+ const checkPoint = checkPoints[i]
+ if (checkPoint) {
+ let point = [checkPoint.point.x, checkPoint.point.y, current.x, current.y]
+ let hipBasePoint
+
+ baseHipLines.forEach((line) => {
+ const checkAngel1 = calculateAngle({ x: point[0], y: point[1] }, { x: point[2], y: point[3] })
+ const checkAngel2 = calculateAngle(
+ { x: line.line.x1, y: line.line.y1 },
+ {
+ x: line.line.x2,
+ y: line.line.y2,
+ },
+ )
+
+ const isConnectLine =
+ ((line.line.x1 === point[0] && line.line.y1 === point[1]) || (line.line.x2 === point[0] && line.line.y2 === point[1])) &&
+ checkAngel1 === checkAngel2
+ const isOverlap = segmentsOverlap(line.line, { x1: point[0], y1: point[1], x2: point[2], y2: point[3] })
+ const isSameLine =
+ (point[0] === line.x2 && point[1] === line.y2 && point[2] === line.x1 && point[3] === line.y1) ||
+ (point[0] === line.x1 && point[1] === line.y1 && point[2] === line.x2 && point[3] === line.y2)
+
+ if (isConnectLine || isOverlap || isSameLine) {
+ /** 겹치는 추녀마루와 하나의 선으로 변경*/
+ const mergePoint = [
+ { x: point[0], y: point[1] },
+ { x: point[2], y: point[3] },
+ { x: line.line.x1, y: line.line.y1 },
+ { x: line.line.x2, y: line.line.y2 },
+ ]
+ /** baseHipLines도 조정*/
+ mergePoint.sort((a, b) => a.x - b.x)
+
+ hipBasePoint = { x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 }
+ point = [mergePoint[0].x, mergePoint[0].y, mergePoint[3].x, mergePoint[3].y]
+ const theta = Big(Math.acos(Big(line.line.attributes.planeSize).div(line.line.attributes.actualSize)))
+ .times(180)
+ .div(Math.PI)
+ .round(1)
+ prevDegree = theta.toNumber()
+ currentDegree = theta.toNumber()
+ canvas.remove(line.line)
+ baseHipLines = baseHipLines.filter((baseLine) => baseLine.line !== line.line)
+ }
+ })
+
+ const hipLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
+ if (hipBasePoint) {
+ baseHipLines.push({ x1: hipBasePoint.x1, y1: hipBasePoint.y1, x2: point[2], y2: point[3], line: hipLine })
+ } else {
+ baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: hipLine })
+ }
+ current.cnt = current.cnt + 1
+ }
+ }
+ }
+
+ /** 라인이 다 그려지지 않은 경우 */
+ if (current.cnt % 2 !== 0) {
+ let basePoints = baseLinePoints
+ .filter((point) =>
+ Big(point.x)
+ .minus(Big(current.x))
+ .abs()
+ .minus(Big(point.y).minus(Big(current.y)).abs())
+ .abs()
+ .lt(1),
+ )
+ .filter((point) => {
+ const pointEdge = { vertex1: { x: current.x, y: current.y }, vertex2: { x: point.x, y: point.y } }
+
+ const intersectPoints = []
+ baseLines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(pointEdge, lineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ intersectPoints.push(intersection)
+ }
+ })
+ return (
+ !intersectPoints.filter(
+ (intersect) => !(Big(intersect.x).minus(Big(point.x)).abs().lt(1) && Big(intersect.y).minus(Big(point.y)).abs().lt(1)),
+ ).length > 0
+ )
+ })
+
+ /** hip을 그리기 위한 기본 길이*/
+ let hipSize = Big(0)
+ if (basePoints.length > 0) {
+ basePoints.sort((a, b) => {
+ const aSize = Big(a.x)
+ .minus(Big(current.x))
+ .abs()
+ .pow(2)
+ .plus(Big(a.y).minus(Big(current.y)).abs().pow(2))
+ .sqrt()
+ const bSize = Big(b.x)
+ .minus(Big(current.x))
+ .abs()
+ .pow(2)
+ .plus(Big(b.y).minus(Big(current.y)).abs().pow(2))
+ .sqrt()
+ return aSize - bSize
+ })
+ const baseHips = baseHipLines.filter((line) => line.x1 === basePoints[0].x && line.y1 === basePoints[0].y)
+ if (baseHips.length > 0) {
+ hipSize = Big(baseHips[0].line.attributes.planeSize)
+ }
+ }
+
+ if (hipSize.eq(0)) {
+ const ridge = current.line
+ basePoints = baseHipLines
+ .filter((line) => (line.x2 === ridge.x1 && line.y2 === ridge.y1) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
+ .filter((line) => baseLines.filter((baseLine) => baseLine.x1 === line.x1 && baseLine.y1 === line.y1).length > 0)
+ basePoints.sort((a, b) => a.line.attributes.planeSize - b.line.attributes.planeSize)
+ hipSize = Big(basePoints[0].line.attributes.planeSize)
+ }
+ hipSize = hipSize.pow(2).div(2).sqrt().round().div(10).toNumber()
+
+ /** 현재 라인을 기준으로 45, 135, 225, 315 방향을 확인하기 위한 좌표, hip은 45도 방향으로만 그린다. */
+ const checkEdge45 = {
+ vertex1: { x: current.x, y: current.y },
+ vertex2: { x: current.x + hipSize, y: current.y - hipSize },
+ }
+ const checkEdge135 = {
+ vertex1: { x: current.x, y: current.y },
+ vertex2: { x: current.x + hipSize, y: current.y + hipSize },
+ }
+ const checkEdge225 = {
+ vertex1: { x: current.x, y: current.y },
+ vertex2: { x: current.x - hipSize, y: current.y + hipSize },
+ }
+ const checkEdge315 = {
+ vertex1: { x: current.x, y: current.y },
+ vertex2: { x: current.x - hipSize, y: current.y - hipSize },
+ }
+
+ let intersectPoints = []
+ let notIntersect45 = true,
+ notIntersect135 = true,
+ notIntersect225 = true,
+ notIntersect315 = true
+ baseLines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection45 = edgesIntersection(checkEdge45, lineEdge)
+ const intersection135 = edgesIntersection(checkEdge135, lineEdge)
+ const intersection225 = edgesIntersection(checkEdge225, lineEdge)
+ const intersection315 = edgesIntersection(checkEdge315, lineEdge)
+
+ if (intersection45 && !intersection45.isIntersectionOutside) {
+ intersectPoints.push(intersection45)
+ notIntersect45 = false
+ }
+ if (intersection135 && !intersection135.isIntersectionOutside) {
+ intersectPoints.push(intersection135)
+ notIntersect135 = false
+ }
+ if (intersection225 && !intersection225.isIntersectionOutside) {
+ intersectPoints.push(intersection225)
+ notIntersect225 = false
+ }
+ if (intersection315 && !intersection315.isIntersectionOutside) {
+ intersectPoints.push(intersection315)
+ notIntersect315 = false
+ }
+ })
+ /** baseLine과 교차하지 않는 포인트를 추가한다.*/
+ if (notIntersect45) {
+ intersectPoints.push(checkEdge45.vertex2)
+ }
+ if (notIntersect135) {
+ intersectPoints.push(checkEdge135.vertex2)
+ }
+ if (notIntersect225) {
+ intersectPoints.push(checkEdge225.vertex2)
+ }
+ if (notIntersect315) {
+ intersectPoints.push(checkEdge315.vertex2)
+ }
+ /** baseLine의 각 좌표와 교차하는 경우로 한정*/
+ intersectPoints = intersectPoints.filter((is) => baseLinePoints.filter((point) => point.x === is.x && point.y === is.y).length > 0)
+ /** baseLine과 교차하는 좌표의 경우 이미 그려진 추녀마루가 존재한다면 제외한다. */
+ intersectPoints = intersectPoints.filter((point) => baseHipLines.filter((hip) => hip.x1 === point.x && hip.y1 === point.y).length === 0)
+ /** 중복제거 */
+ intersectPoints = intersectPoints.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
+
+ intersectPoints.forEach((is) => {
+ const points = [current.x, current.y, is.x, is.y]
+ /** 추녀마루의 연결점이 처마라인이 아닌경우 return */
+ let hasGable = false
+ baseLines
+ .filter((line) => (line.x1 === points[2] && line.y1 === points[3]) || (line.x2 === points[2] && line.y2 === points[3]))
+ .forEach((line) => {
+ if (!eavesType.includes(line.attributes.type)) {
+ hasGable = true
+ }
+ })
+ if (hasGable) return
+ const pointEdge = { vertex1: { x: points[0], y: points[1] }, vertex2: { x: points[2], y: points[3] } }
+ const vectorX = Math.sign(Big(points[2]).minus(Big(points[0])).toNumber())
+ const vectorY = Math.sign(Big(points[3]).minus(Big(points[1])).toNumber())
+ const roofIntersections = []
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(pointEdge, lineEdge)
+ if (intersection) {
+ const vectorIntersectionX = Math.sign(Big(intersection.x).minus(Big(points[0])).toNumber())
+ const vectorIntersectionY = Math.sign(Big(intersection.y).minus(Big(points[1])).toNumber())
+ if (vectorIntersectionX === vectorX && vectorIntersectionY === vectorY) {
+ roofIntersections.push({
+ x: intersection.x,
+ y: intersection.y,
+ size: calcLinePlaneSize({ x1: points[0], y1: points[1], x2: intersection.x, y2: intersection.y }),
+ })
+ }
+ }
+ })
+ roofIntersections.sort((a, b) => a.size - b.size)
+
+ const hipLine = drawHipLine(
+ [points[0], points[1], roofIntersections[0].x, roofIntersections[0].y],
+ canvas,
+ roof,
+ textMode,
+ null,
+ prevDegree,
+ currentDegree,
+ )
+ baseHipLines.push({ x1: points[0], y1: points[1], x2: points[2], y2: points[3], line: hipLine })
+ current.cnt = current.cnt + 1
+ })
+ }
+ })
+
+ /** hip이 짝수개가 맞다아있는데 마루와 연결되지 않는 포인트를 찾는다. 그려지지 않은 마루를 찾기 위함.*/
+ let noRidgeHipPoints = baseHipLines
+ .filter((current) => current.x1 !== current.x2 && current.y1 !== current.y2)
+ .filter((current) => {
+ const lines = baseHipLines
+ .filter((line) => line !== current)
+ .filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
+
+ return lines.length !== 0 && lines.length % 2 !== 0
+ })
+ .filter(
+ (current) =>
+ baseRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
+ .length === 0 &&
+ baseGableRidgeLines.filter((line) => (line.x1 === current.x2 && line.y1 === current.y2) || (line.x2 === current.x2 && line.y2 === current.y2))
+ .length === 0,
+ )
+
+ noRidgeHipPoints.forEach((current) => {
+ const orthogonalPoints = noRidgeHipPoints.filter((point) => {
+ if (point !== current && ((current.x2 === point.x2 && current.y2 !== point.y2) || (current.x2 !== point.x2 && current.y2 === point.y2))) {
+ return true
+ }
+ })
+
+ /** 직교 하는 포인트가 존재 할때 마루를 그린다. */
+ if (orthogonalPoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
+ baseRidgeCount = baseRidgeCount + 1
+ const points = [current.x2, current.y2, orthogonalPoints[0].x2, orthogonalPoints[0].y2]
+ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
+ baseRidgeLines.push(ridgeLine)
+ }
+ })
+
+ /** 중복제거*/
+ baseRidgeLines.forEach((current) => {
+ const sameRidge = baseRidgeLines.filter(
+ (line) =>
+ line !== current &&
+ ((line.x1 === current.x1 && line.y1 === current.y1 && line.x2 === current.x2 && line.y2 === current.y2) ||
+ (line.x1 === current.x2 && line.y1 === current.y2 && line.x2 === current.x1 && line.y2 === current.y1)),
+ )
+ if (sameRidge.length > 0) {
+ sameRidge.forEach((duplicateLine) => {
+ const index = baseRidgeLines.indexOf(duplicateLine)
+ if (index !== -1) {
+ baseRidgeLines.splice(index, 1)
+ }
+ canvas.remove(duplicateLine)
+ })
+ }
+ })
+
+ /** 직교 하는 포인트가 없는 경우 남은 포인트 처리 */
+ const checkEdgeLines = []
+ noRidgeHipPoints.forEach((current) => {
+ noRidgeHipPoints.forEach((current) => {
+ noRidgeHipPoints
+ .filter((point) => point !== current)
+ .forEach((point) => {
+ checkEdgeLines.push(
+ { vertex1: { x: current.x2, y: current.y2 }, vertex2: { x: current.x2, y: point.y2 } },
+ { vertex1: { x: current.x2, y: point.y2 }, vertex2: { x: point.x2, y: point.y2 } },
+ { vertex1: { x: point.x2, y: point.y2 }, vertex2: { x: point.x2, y: current.y2 } },
+ { vertex1: { x: point.x2, y: current.y2 }, vertex2: { x: current.x2, y: current.y2 } },
+ )
+ })
+ })
+ })
+
+ /** 연결되지 않은 포인트를 찾아서 해당 포인트를 가지고 있는 라인을 찾는다. */
+ let unFinishedPoints = []
+ let unFinishedLines = []
+ let intersectPoints = []
+ baseHipLines.forEach((line) => {
+ if (baseLinePoints.filter((point) => point.x === line.x1 && point.y === line.y1).length === 0) {
+ unFinishedPoints.push({ x: line.x1, y: line.y1 })
+ }
+ if (baseLinePoints.filter((point) => point.x === line.x2 && point.y === line.y2).length === 0) {
+ unFinishedPoints.push({ x: line.x2, y: line.y2 })
+ }
+ })
+ unFinishedPoints
+ .filter((point) => unFinishedPoints.filter((p) => p !== point && p.x === point.x && p.y === point.y).length === 0)
+ .forEach((point) => {
+ baseHipLines
+ .filter((line) => (line.x1 === point.x && line.y1 === point.y) || (line.x2 === point.x && line.y2 === point.y))
+ .forEach((line) => unFinishedLines.push(line))
+ })
+
+ unFinishedLines.forEach((line) => {
+ const xVector = Math.sign(Big(line.x2).minus(Big(line.x1)))
+ const yVector = Math.sign(Big(line.y2).minus(Big(line.y1)))
+ let lineIntersectPoints = []
+ checkEdgeLines.forEach((edge) => {
+ const intersectEdge = edgesIntersection(edge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersectEdge) {
+ const isXVector = Math.sign(Big(intersectEdge.x).minus(Big(line.x1))) === xVector
+ const isYVector = Math.sign(Big(intersectEdge.y).minus(Big(line.y1))) === yVector
+ if (isXVector && isYVector) {
+ lineIntersectPoints.push({
+ intersection: intersectEdge,
+ size: Big(intersectEdge.x)
+ .minus(Big(line.x1))
+ .abs()
+ .pow(2)
+ .plus(Big(intersectEdge.y).minus(Big(line.y1)).abs().pow(2))
+ .sqrt()
+ .round(1)
+ .toNumber(),
+ })
+ }
+ }
+ })
+
+ lineIntersectPoints = lineIntersectPoints.filter(
+ (point, index, self) => index === self.findIndex((p) => p.intersection.x === point.intersection.x && p.intersection.y === point.intersection.y),
+ )
+ lineIntersectPoints.sort((a, b) => a.size - b.size)
+ if (lineIntersectPoints.length > 0) {
+ intersectPoints.push({ intersection: lineIntersectPoints[0].intersection, line })
+ }
+ })
+
+ /** 마루를 그릴수 있는지 찾는다. */
+ noRidgeHipPoints.forEach((hipPoint) => {
+ const ridgePoints = []
+ intersectPoints
+ .filter(
+ (intersectPoint) =>
+ (intersectPoint.intersection.x !== hipPoint.x2 && intersectPoint.intersection.y === hipPoint.y2) ||
+ (intersectPoint.intersection.x === hipPoint.x2 && intersectPoint.intersection.y !== hipPoint.y2),
+ )
+ .forEach((intersectPoint) => {
+ ridgePoints.push({
+ intersection: intersectPoint,
+ distance: Big(intersectPoint.intersection.x)
+ .minus(Big(hipPoint.x2))
+ .abs()
+ .pow(2)
+ .plus(Big(intersectPoint.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
+ .sqrt()
+ .round(1)
+ .toNumber(),
+ })
+ })
+ ridgePoints.sort((a, b) => a.distance - b.distance)
+
+ if (ridgePoints.length > 0 && baseRidgeCount < getMaxRidge(baseLines.length)) {
+ const intersection = ridgePoints[0].intersection
+ const isPoint = intersection.intersection
+ const points = [hipPoint.x2, hipPoint.y2, isPoint.x, isPoint.y]
+ const ridgeLine = drawRidgeLine(points, canvas, roof, textMode)
+ baseRidgeCount = baseRidgeCount + 1
+ baseRidgeLines.push(ridgeLine)
+
+ let baseHipLine = baseHipLines.filter((line) => line === intersection.line)[0]
+ baseHipLine.x2 = isPoint.x
+ baseHipLines.y2 = isPoint.y
+ /** 보조선 라인 조정*/
+ const hipLine = intersection.line.line
+ /** 평면길이 */
+ const planeSize = calcLinePlaneSize({
+ x1: hipLine.x1,
+ y1: hipLine.y1,
+ x2: isPoint.x,
+ y2: isPoint.y,
+ })
+ /** 실제길이 */
+ const actualSize =
+ prevDegree === currentDegree
+ ? calcLineActualSize(
+ {
+ x1: hipLine.x1,
+ y1: hipLine.y1,
+ x2: isPoint.x,
+ y2: isPoint.y,
+ },
+ currentDegree,
+ )
+ : 0
+ hipLine.set({
+ x2: isPoint.x,
+ y2: isPoint.y,
+ attributes: { roofId: roof.id, planeSize, actualSize },
+ })
+ hipLine.fire('modified')
+ intersectPoints = intersectPoints.filter((isp) => isp !== intersection)
+ noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
+ } else {
+ const linePoints = []
+ intersectPoints.forEach((intersectPoint) => {
+ const intersection = intersectPoint.intersection
+ const xVector = Math.sign(Big(intersection.x).minus(Big(hipPoint.x2)))
+ const yVector = Math.sign(Big(intersection.y).minus(Big(hipPoint.y2)))
+
+ const checkEdge = {
+ vertex1: { x: intersection.x, y: intersection.y },
+ vertex2: {
+ x: Big(intersection.x).plus(Big(xVector).times(10)).toNumber(),
+ y: Big(intersection.y).plus(Big(yVector).times(10)).toNumber(),
+ },
+ }
+ const intersectX = edgesIntersection(
+ {
+ vertex1: { x: hipPoint.x2, y: hipPoint.y2 },
+ vertex2: { x: Big(hipPoint.x2).plus(Big(xVector).neg().times(10)).toNumber(), y: hipPoint.y2 },
+ },
+ checkEdge,
+ )
+ const intersectY = edgesIntersection(
+ {
+ vertex1: { x: hipPoint.x2, y: hipPoint.y2 },
+ vertex2: { x: hipPoint.x2, y: Big(hipPoint.y2).plus(Big(yVector).neg().times(10)).toNumber() },
+ },
+ checkEdge,
+ )
+
+ let distanceX = Infinity,
+ distanceY = Infinity
+
+ if (intersectX) {
+ distanceX = Big(intersectX.x)
+ .minus(Big(intersection.x))
+ .abs()
+ .pow(2)
+ .plus(Big(intersectX.y).minus(Big(intersection.y)).abs().pow(2))
+ .sqrt()
+ .round(1)
+ .toNumber()
+ }
+ if (intersectY) {
+ distanceY = Big(intersectY.x)
+ .minus(Big(intersection.x))
+ .abs()
+ .pow(2)
+ .plus(Big(intersectY.y).minus(Big(intersection.y)).abs().pow(2))
+ .sqrt()
+ .round(1)
+ .toNumber()
+ }
+
+ if (distanceX < distanceY) {
+ linePoints.push({ intersection: intersectX, intersectPoint })
+ }
+ if (distanceX > distanceY) {
+ linePoints.push({ intersection: intersectY, intersectPoint })
+ }
+ })
+
+ const linePoint = linePoints.reduce((prev, current) => {
+ const prevDistance = Big(prev.intersection.x)
+ .minus(Big(hipPoint.x2))
+ .abs()
+ .pow(2)
+ .plus(Big(prev.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
+ .sqrt()
+ const currentDistance = Big(current.intersection.x)
+ .minus(Big(hipPoint.x2))
+ .abs()
+ .pow(2)
+ .plus(Big(current.intersection.y).minus(Big(hipPoint.y2)).abs().pow(2))
+ .sqrt()
+ if (prevDistance < currentDistance) {
+ return prev
+ } else {
+ return current
+ }
+ }, linePoints[0])
+
+ if (!linePoint) return
+ const hipStartPoint = [hipPoint.x2, hipPoint.y2, linePoint.intersection.x, linePoint.intersection.y]
+ /** 직선인 경우 마루를 그린다.*/
+ if (
+ ((hipStartPoint[0] === hipStartPoint[2] && hipStartPoint[1] !== hipStartPoint[3]) ||
+ (hipStartPoint[0] !== hipStartPoint[2] && hipStartPoint[1] === hipStartPoint[3])) &&
+ baseRidgeCount < getMaxRidge(baseLines.length)
+ ) {
+ const ridgeLine = drawRidgeLine(hipStartPoint, canvas, roof, textMode)
+ baseRidgeCount = baseRidgeCount + 1
+ baseRidgeLines.push(ridgeLine)
+ noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
+ }
+ /** 대각선인경우 hip을 그린다. */
+ if (
+ Big(hipStartPoint[0])
+ .minus(Big(hipStartPoint[2]))
+ .abs()
+ .minus(Big(hipStartPoint[1]).minus(Big(hipStartPoint[3])).abs())
+ .abs()
+ .lt(1)
+ ) {
+ // console.log('힙1')
+ const hipLine = drawHipLine(hipStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({
+ x1: hipStartPoint[0],
+ y1: hipStartPoint[1],
+ x2: hipStartPoint[2],
+ y2: hipStartPoint[3],
+ line: hipLine,
+ })
+ noRidgeHipPoints = noRidgeHipPoints.filter((hip) => hip !== hipPoint)
+ }
+
+ const isStartPoint = [
+ linePoint.intersection.x,
+ linePoint.intersection.y,
+ linePoint.intersectPoint.intersection.x,
+ linePoint.intersectPoint.intersection.y,
+ ]
+ if (
+ ((isStartPoint[0] === isStartPoint[2] && isStartPoint[1] !== isStartPoint[3]) ||
+ (isStartPoint[0] !== isStartPoint[2] && isStartPoint[1] === isStartPoint[3])) &&
+ baseRidgeCount < getMaxRidge(baseLines.length)
+ ) {
+ const ridgeLine = drawRidgeLine(isStartPoint, canvas, roof, textMode)
+ baseRidgeCount = baseRidgeCount + 1
+ baseRidgeLines.push(ridgeLine)
+ }
+ if (
+ Big(isStartPoint[0])
+ .minus(Big(isStartPoint[2]))
+ .abs()
+ .minus(Big(isStartPoint[1]).minus(Big(isStartPoint[3])).abs())
+ .abs()
+ .lt(1)
+ ) {
+ const hipLine = drawHipLine(isStartPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({
+ x1: isStartPoint[0],
+ y1: isStartPoint[1],
+ x2: isStartPoint[2],
+ y2: isStartPoint[3],
+ line: hipLine,
+ })
+ }
+ }
+ })
+
+ const ridgeAllPoints = []
+ baseRidgeLines.forEach((line) => ridgeAllPoints.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 }))
+
+ /** hip 중에 지붕의 라인과 만나지 않은 선을 확인.*/
+ baseHipLines
+ .filter(
+ (hip) => baseLinePoints.filter((point) => (point.x === hip.x1 && point.y === hip.y1) || (point.x === hip.x2 && point.y === hip.y2)).length > 0,
+ )
+ .filter((hip) => {
+ const hipEdge = { vertex1: { x: hip.line.x1, y: hip.line.y1 }, vertex2: { x: hip.line.x2, y: hip.line.y2 } }
+ const hipVectorX = Math.sign(Big(hip.x1).minus(Big(hip.x2)))
+ const hipVectorY = Math.sign(Big(hip.y1).minus(Big(hip.y2)))
+ let isIntersect = false
+ roof.lines.forEach((line) => {
+ const edge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(edge, hipEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(intersection.x).minus(Big(hip.x2)))
+ const isVectorY = Math.sign(Big(intersection.y).minus(Big(hip.y2)))
+ if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
+ isIntersect = true
+ }
+ }
+ })
+ return !isIntersect
+ })
+ .forEach((hip) => {
+ const hipLine = hip.line
+ if (hipLine) {
+ const hipVectorX = Big(hipLine.x2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.x1).minus(Big(hipLine.x2)).neg().s))
+ const hipVectorY = Big(hipLine.y2).plus(Big(hipLine.attributes.planeSize).times(Big(hipLine.y1).minus(Big(hipLine.y2)).neg().s))
+ const overlapLineX = roof.lines
+ .filter((roofLine) => roofLine.x1 !== roofLine.x2 && roofLine.y1 === hipLine.y2 && roofLine.y2 === hipLine.y2)
+ .filter((roofLine) => {
+ const minX = Math.min(roofLine.x1, roofLine.x2, hipLine.x2)
+ const maxX = Math.max(roofLine.x1, roofLine.x2, hipLine.x2)
+ const checkLineEdge = { vertex1: { x: minX, y: hipLine.y2 }, vertex2: { x: maxX, y: hipLine.y2 } }
+ let isIntersect = false
+ baseHipLines.forEach((baseHipLine) => {
+ const edge = {
+ vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 },
+ vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 },
+ }
+ const intersection = edgesIntersection(edge, checkLineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(intersection.x).minus(Big(baseHipLine.x2)))
+ const isVectorY = Math.sign(Big(intersection.y).minus(Big(baseHipLine.y2)))
+ if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
+ isIntersect = true
+ }
+ }
+ })
+ baseRidgeLines.forEach((baseRidgeLine) => {
+ const edge = {
+ vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 },
+ vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 },
+ }
+ const intersection = edgesIntersection(edge, checkLineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
+ const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
+ if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
+ isIntersect = true
+ }
+ }
+ })
+ return !isIntersect
+ })
+ const overlapLineY = roof.lines
+ .filter((roofLine) => roofLine.y1 !== roofLine.y2 && roofLine.x1 === hipLine.x2 && roofLine.x2 === hipLine.x2)
+ .filter((roofLine) => {
+ const minY = Math.min(roofLine.y1, roofLine.y2, hipLine.y2)
+ const maxY = Math.max(roofLine.y1, roofLine.y2, hipLine.y2)
+ const checkLineEdge = { vertex1: { x: hipLine.x2, y: minY }, vertex2: { x: hipLine.x2, y: maxY } }
+ let isIntersect = false
+ baseHipLines.forEach((baseHipLine) => {
+ const edge = {
+ vertex1: { x: baseHipLine.x1, y: baseHipLine.y1 },
+ vertex2: { x: baseHipLine.x2, y: baseHipLine.y2 },
+ }
+ const intersection = edgesIntersection(edge, checkLineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
+ const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
+ if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
+ isIntersect = true
+ }
+ }
+ })
+ baseRidgeLines.forEach((baseRidgeLine) => {
+ const edge = {
+ vertex1: { x: baseRidgeLine.x1, y: baseRidgeLine.y1 },
+ vertex2: { x: baseRidgeLine.x2, y: baseRidgeLine.y2 },
+ }
+ const intersection = edgesIntersection(edge, checkLineEdge)
+ if (intersection && !intersection.isIntersectionOutside) {
+ const isVectorX = Math.sign(Big(intersection.x).minus(Big(hipLine.x2)))
+ const isVectorY = Math.sign(Big(intersection.y).minus(Big(hipLine.y2)))
+ if (isVectorX === hipVectorX && isVectorY === hipVectorY) {
+ isIntersect = true
+ }
+ }
+ })
+ return !isIntersect
+ })
+
+ overlapLineX.reduce((prev, current) => {
+ const prevDistance = Big(prev.x1)
+ .minus(Big(hipLine.x2))
+ .abs()
+ .lt(Big(prev.x2).minus(Big(hipLine.x2)).abs())
+ ? Big(prev.x1).minus(Big(hipLine.x2)).abs()
+ : Big(prev.x2).minus(Big(hipLine.x2)).abs()
+ const currentDistance = Big(current.x1)
+ .minus(Big(hipLine.x2))
+ .abs()
+ .lt(Big(current.x2).minus(Big(hipLine.x2)).abs())
+ ? Big(current.x1).minus(Big(hipLine.x2)).abs()
+ : Big(current.x2).minus(Big(hipLine.x2)).abs()
+
+ return prevDistance.lt(currentDistance) ? prev : current
+ }, overlapLineX[0])
+
+ overlapLineY.reduce((prev, current) => {
+ const prevDistance = Big(prev.y1)
+ .minus(Big(hipLine.y2))
+ .abs()
+ .lt(Big(prev.y2).minus(Big(hipLine.y2)).abs())
+ ? Big(prev.y1).minus(Big(hipLine.y2)).abs()
+ : Big(prev.y2).minus(Big(hipLine.y2)).abs()
+ const currentDistance = Big(current.y1)
+ .minus(Big(hipLine.y2))
+ .abs()
+ .lt(Big(current.y2).minus(Big(hipLine.y2)).abs())
+ ? Big(current.y1).minus(Big(hipLine.y2)).abs()
+ : Big(current.y2).minus(Big(hipLine.y2)).abs()
+ return prevDistance.lt(currentDistance) ? prev : current
+ }, overlapLineY[0])
+
+ if (overlapLineX.length > 0) {
+ const overlapLine = overlapLineX[0]
+ const maxX = Math.max(overlapLine.x1, overlapLine.x2)
+ const point = [hipLine.x2, hipLine.y2, maxX, hipLine.y2]
+ const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine })
+ }
+ if (overlapLineY.length > 0) {
+ const overlapLine = overlapLineY[0]
+ const maxY = Math.max(overlapLine.y1, overlapLine.y2)
+ const point = [hipLine.x2, hipLine.y2, hipLine.x2, maxY]
+ const addLine = drawHipLine(point, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({ x1: point[0], y1: point[1], x2: point[2], y2: point[3], line: addLine })
+ }
+ }
+
+ const modifiedBaseLine = baseLines.filter((line) => (hip.x2 === line.x1 && hip.y2 === line.y1) || (hip.x2 === line.x2 && hip.y2 === line.y2))
+ if (modifiedBaseLine.length === 0) return
+ const verticalLine = modifiedBaseLine.find(
+ (line) =>
+ (hip.x2 === line.attributes.originPoint.x1 && hip.x2 === line.attributes.originPoint.x2) ||
+ (hip.y2 === line.attributes.originPoint.y1 && hip.y2 === line.attributes.originPoint.y2),
+ )
+ const horizonLine = modifiedBaseLine.find((line) => line !== verticalLine)
+
+ const horizonRoof = roof.lines.find((line) => {
+ const originPoint = horizonLine.attributes.originPoint
+ if (originPoint.y1 === originPoint.y2) {
+ return (
+ line.y1 === line.y2 &&
+ Math.sign(originPoint.x1 - originPoint.x2) === Math.sign(line.x1 - line.x2) &&
+ Big(originPoint.y1).minus(Big(line.y1)).abs().minus(horizonLine.attributes.offset).abs().lt(1)
+ )
+ } else {
+ return (
+ line.x1 === line.x2 &&
+ Math.sign(originPoint.y1 - originPoint.y2) === Math.sign(line.y1 - line.y2) &&
+ Big(originPoint.x1).minus(Big(line.x1)).abs().minus(horizonLine.attributes.offset).abs().lt(1)
+ )
+ }
+ })
+
+ if (horizonRoof) {
+ let horizonPoint
+ if (horizonRoof.y1 === horizonRoof.y2) {
+ const minX = Math.min(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2)
+ const maxX = Math.max(horizonRoof.x1, horizonRoof.x2, horizonLine.attributes.originPoint.x1, horizonLine.attributes.originPoint.x2)
+ horizonPoint = [minX, horizonRoof.y1, maxX, horizonRoof.y1]
+ } else {
+ const minY = Math.min(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2)
+ const maxY = Math.max(horizonRoof.y1, horizonRoof.y2, horizonLine.attributes.originPoint.y1, horizonLine.attributes.originPoint.y2)
+ horizonPoint = [horizonRoof.x1, minY, horizonRoof.x1, maxY]
+ }
+ let addLine
+ const alreadyHorizonLines = baseHipLines.find(
+ (hipLine) =>
+ hipLine.x1 === horizonPoint[0] && hipLine.y1 === horizonPoint[1] && hipLine.x2 === horizonPoint[2] && hipLine.y2 === horizonPoint[3],
+ )
+ if (!alreadyHorizonLines) {
+ addLine = drawHipLine(horizonPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({
+ x1: horizonPoint[0],
+ y1: horizonPoint[1],
+ x2: horizonPoint[2],
+ y2: horizonPoint[3],
+ line: addLine,
+ })
+ } else {
+ addLine = alreadyHorizonLines
+ }
+
+ let verticalPoint
+ if (addLine.y1 === addLine.y2) {
+ verticalPoint = [hip.x2, hip.y2, hip.x2, addLine.y1]
+ } else {
+ verticalPoint = [hip.x2, hip.y2, addLine.x1, hip.y2]
+ }
+ const alreadyVerticalLine = baseHipLines.find(
+ (hipLine) =>
+ hipLine.x1 === verticalPoint[0] && hipLine.y1 === verticalPoint[1] && hipLine.x2 === verticalPoint[2] && hipLine.y2 === verticalPoint[3],
+ )
+ if (!alreadyVerticalLine) {
+ addLine = drawHipLine(verticalPoint, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({
+ x1: verticalPoint[0],
+ y1: verticalPoint[1],
+ x2: verticalPoint[2],
+ y2: verticalPoint[3],
+ line: addLine,
+ })
+ }
+ }
+ })
+
+ ridgeAllPoints.forEach((current) => {
+ ridgeAllPoints
+ .filter((point) => point !== current)
+ .forEach((point) => {
+ let checkRidgeLine, checkHipLine
+ /** 직선인 경우 마루 확인*/
+ if (
+ baseRidgeCount < getMaxRidge(baseLines.length) &&
+ ((point.x === current.x && point.y !== current.y) || (point.x !== current.x && point.y === current.y))
+ ) {
+ checkRidgeLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y }
+ }
+ /** 대각선인 경우 hip확인*/
+ const hipX = Big(current.x).minus(Big(point.x)).abs()
+ const hipY = Big(current.y).minus(Big(point.y)).abs()
+ if (hipX.eq(hipY) && hipX.gt(0) && hipY.gt(0)) {
+ checkHipLine = { x1: current.x, y1: current.y, x2: point.x, y2: point.y }
+ }
+
+ if (checkRidgeLine) {
+ const ridgePoints = [checkRidgeLine.x1, checkRidgeLine.y1, checkRidgeLine.x2, checkRidgeLine.y2]
+
+ let baseIntersection = false
+ const ridgeInterSection = []
+ const ridgeEdge = {
+ vertex1: { x: ridgePoints[0], y: ridgePoints[1] },
+ vertex2: { x: ridgePoints[2], y: ridgePoints[3] },
+ }
+ baseLines.forEach((line) => {
+ const intersection = edgesIntersection(ridgeEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside) {
+ ridgeInterSection.push(intersection)
+ }
+ })
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(ridgeEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside) {
+ ridgeInterSection.push(intersection)
+ }
+ })
+ baseHipLines.forEach((line) => {
+ const intersection = edgesIntersection(ridgeEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside) {
+ ridgeInterSection.push(intersection)
+ }
+ })
+ const otherRidgeInterSection = ridgeInterSection.filter(
+ (intersection) =>
+ !(
+ (intersection.x === ridgePoints[0] && intersection.y === ridgePoints[1]) ||
+ (intersection.x === ridgePoints[2] && intersection.y === ridgePoints[3])
+ ),
+ )
+ const alreadyRidges = baseRidgeLines.filter(
+ (line) =>
+ (line.x1 === ridgePoints[0] && line.y1 === ridgePoints[1] && line.x2 === ridgePoints[2] && line.y2 === ridgePoints[3]) ||
+ (line.x1 === ridgePoints[2] && line.y1 === ridgePoints[3] && line.x2 === ridgePoints[0] && line.y2 === ridgePoints[1]),
+ )
+
+ if (
+ !baseIntersection &&
+ alreadyRidges.length === 0 &&
+ otherRidgeInterSection.length === 0 &&
+ baseRidgeCount < getMaxRidge(baseLines.length)
+ ) {
+ baseRidgeCount = baseRidgeCount + 1
+ const ridgeLine = drawRidgeLine(ridgePoints, canvas, roof, textMode)
+ baseRidgeLines.push(ridgeLine)
+ }
+ }
+ if (checkHipLine) {
+ const hipPoints = [checkHipLine.x1, checkHipLine.y1, checkHipLine.x2, checkHipLine.y2]
+
+ let baseIntersection = false
+ let hipInterSection = []
+ const hipEdge = {
+ vertex1: { x: hipPoints[0], y: hipPoints[1] },
+ vertex2: { x: hipPoints[2], y: hipPoints[3] },
+ }
+ baseLines.forEach((line) => {
+ const intersection = edgesIntersection(hipEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside && eavesType.includes(line.attributes.type)) {
+ baseIntersection = true
+ }
+ })
+ baseRidgeLines.forEach((line) => {
+ const intersection = edgesIntersection(hipEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside) {
+ hipInterSection.push(intersection)
+ }
+ })
+ baseHipLines.forEach((line) => {
+ const intersection = edgesIntersection(hipEdge, {
+ vertex1: { x: line.x1, y: line.y1 },
+ vertex2: { x: line.x2, y: line.y2 },
+ })
+ if (intersection && !intersection.isIntersectionOutside) {
+ hipInterSection.push(intersection)
+ }
+ })
+ const otherHipInterSection = hipInterSection.filter(
+ (intersection) =>
+ !(
+ (intersection.x === hipPoints[0] && intersection.y === hipPoints[1]) ||
+ (intersection.x === hipPoints[2] && intersection.y === hipPoints[3])
+ ),
+ )
+ const alreadyHips = baseHipLines.filter(
+ (line) =>
+ (line.x1 === hipPoints[0] && line.y1 === hipPoints[1] && line.x2 === hipPoints[2] && line.y2 === hipPoints[3]) ||
+ (line.x1 === hipPoints[2] && line.y1 === hipPoints[3] && line.x2 === hipPoints[0] && line.y2 === hipPoints[1]),
+ )
+
+ if (!baseIntersection && alreadyHips.length === 0 && otherHipInterSection.length === 0) {
+ const hipLine = drawHipLine(hipPoints, canvas, roof, textMode, null, prevDegree, currentDegree)
+ baseHipLines.push({ x1: hipPoints[0], y1: hipPoints[1], x2: hipPoints[2], y2: hipPoints[3], line: hipLine })
+ }
+ }
+ })
+ })
+
+ /** 중복 제거 */
+ baseHipLines.forEach((hipLine) => {
+ baseHipLines.filter((hipLine2) => hipLine !== hipLine2).forEach((hipLine2) => {})
+ })
+
+ const innerLines = [...baseRidgeLines, ...baseGableRidgeLines, ...baseGableLines, ...baseHipLines.map((line) => line.line)]
+ const uniqueInnerLines = []
+
+ innerLines.forEach((currentLine) => {
+ if (currentLine.length === 0) {
+ canvas.remove(currentLine)
+ } else {
+ const sameLines = uniqueInnerLines.filter(
+ (line) =>
+ line !== currentLine &&
+ ((line.x1 === currentLine.x1 && line.y1 === currentLine.y1 && line.x2 === currentLine.x2 && line.y2 === currentLine.y2) ||
+ (line.x1 === currentLine.x2 && line.y1 === currentLine.y2 && line.x2 === currentLine.x1 && line.y2 === currentLine.y1)),
+ )
+
+ if (sameLines.length === 0) {
+ uniqueInnerLines.push(currentLine)
+ } else {
+ canvas.remove(currentLine)
+ }
+ }
+ })
+ canvas.renderAll()
+ roof.innerLines = uniqueInnerLines
+
+ /*drawRidge(roof, canvas, textMode)
+ drawHips(roof, canvas, textMode)
+ connectLinePoint(roof, canvas, textMode)
+ modifyRidge(roof, canvas, textMode)
+ drawCenterLine(roof, canvas, textMode)*/
}
/**
- * line 이 세 라인 사이에 존재하는지 확인한다.
- * @param prevLine
- * @param currentLine
- * @param nextLine
- * @param line
+ * 추녀 마루를 그린다.
+ * @param points
+ * @param canvas
+ * @param roof
+ * @param textMode
+ * @param currentRoof
+ * @param prevDegree
+ * @param currentDegree
*/
-const isInnerLine = (prevLine, currentLine, nextLine, line) => {
- let inside = false
- let minX = Math.min(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
- let maxX = Math.max(currentLine.x1, currentLine.x2, prevLine.x1, nextLine.x2)
- let minY = Math.min(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
- let maxY = Math.max(currentLine.y1, currentLine.y2, prevLine.y1, nextLine.y2)
+const drawHipLine = (points, canvas, roof, textMode, currentRoof, prevDegree, currentDegree) => {
+ const hip = new QLine(points, {
+ parentId: roof.id,
+ fontSize: roof.fontSize,
+ stroke: '#1083E3',
+ strokeWidth: 2,
+ name: LINE_TYPE.SUBLINE.HIP,
+ textMode: textMode,
+ attributes: {
+ roofId: roof.id,
+ // currentRoofId: currentRoof.id,
+ planeSize: calcLinePlaneSize({
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ }),
+ actualSize:
+ prevDegree === currentDegree
+ ? calcLineActualSize(
+ {
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ },
+ currentDegree,
+ )
+ : 0,
+ },
+ })
- if (minX < line.x1 && line.x1 < maxX && minY < line.y1 && line.y1 < maxY && minX < line.x2 && line.x2 < maxX && minY < line.y2 && line.y2 < maxY) {
- inside = true
+ canvas.add(hip)
+ hip.bringToFront()
+ canvas.renderAll()
+ return hip
+}
+
+/**
+ * 라인의 흐름 방향에서 마주치는 지붕선의 포인트를 찾는다.
+ * @param roof
+ * @param baseLine
+ * @param endPoint
+ * @returns {*}
+ */
+const findRoofIntersection = (roof, baseLine, endPoint) => {
+ let intersectPoints = []
+ const { x1, y1, x2, y2 } = baseLine
+
+ const baseEdge = {
+ vertex1: { x: x1, y: y1 },
+ vertex2: { x: x2, y: y2 },
}
- return inside
+ /** 외벽선에서 라인 겹치는 경우에 대한 확인*/
+ roof.lines.forEach((line) => {
+ const lineEdge = { vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } }
+ const intersection = edgesIntersection(baseEdge, lineEdge)
+ if (
+ intersection &&
+ !intersection.isIntersectionOutside &&
+ Math.sign(endPoint.x - baseLine.x1) === Math.sign(endPoint.x - intersection.x) &&
+ Math.sign(endPoint.y - baseLine.y1) === Math.sign(endPoint.y - intersection.y)
+ ) {
+ const intersectSize = endPoint.x
+ .minus(Big(intersection.x))
+ .pow(2)
+ .plus(endPoint.y.minus(Big(intersection.y)).pow(2))
+ .abs()
+ .sqrt()
+ .toNumber()
+
+ intersectPoints.push({
+ intersection,
+ size: intersectSize,
+ })
+ }
+ })
+
+ return intersectPoints.reduce((prev, current) => {
+ return prev.size < current.size ? prev : current
+ }, intersectPoints[0])
+}
+
+/**
+ * 마루를 그린다.
+ * @param points
+ * @param canvas
+ * @param roof
+ * @param textMode
+ * @returns {*}
+ */
+const drawRidgeLine = (points, canvas, roof, textMode) => {
+ const ridge = new QLine(points, {
+ parentId: roof.id,
+ fontSize: roof.fontSize,
+ stroke: '#1083E3',
+ strokeWidth: 2,
+ name: LINE_TYPE.SUBLINE.RIDGE,
+ textMode: textMode,
+ attributes: {
+ roofId: roof.id,
+ planeSize: calcLinePlaneSize({
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ }),
+ actualSize: calcLinePlaneSize({
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ }),
+ },
+ })
+ canvas.add(ridge)
+ ridge.bringToFront()
+ canvas.renderAll()
+
+ return ridge
+}
+
+/**
+ * 지붕선을 그린다.
+ * @param points
+ * @param canvas
+ * @param roof
+ * @param textMode
+ * @returns {*}
+ */
+const drawRoofLine = (points, canvas, roof, textMode) => {
+ const ridge = new QLine(points, {
+ parentId: roof.id,
+ fontSize: roof.fontSize,
+ stroke: '#1083E3',
+ strokeWidth: 2,
+ name: LINE_TYPE.SUBLINE.HIP,
+ textMode: textMode,
+ attributes: {
+ roofId: roof.id,
+ planeSize: calcLinePlaneSize({
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ }),
+ actualSize: calcLinePlaneSize({
+ x1: points[0],
+ y1: points[1],
+ x2: points[2],
+ y2: points[3],
+ }),
+ },
+ })
+ canvas.add(ridge)
+ ridge.bringToFront()
+ canvas.renderAll()
+
+ return ridge
+}
+
+/**
+ * 벡터를 정규화(Normalization)하는 함수
+ * @param v
+ * @returns {{x: *, y: *}|{x: number, y: number}}
+ */
+const normalizeVector = (v) => {
+ /** 벡터의 크기(길이)*/
+ const magnitude = Big(v.x).pow(2).plus(Big(v.y).pow(2)).sqrt()
+ if (magnitude.eq(0)) return { x: 0, y: 0 } // 크기가 0일 경우 (예외 처리)
+ return { x: Big(v.x).div(magnitude).toNumber(), y: Big(v.y).div(magnitude).toNumber() }
+}
+
+/**
+ * 사잇각의 절반 방향 벡터 계산 함수
+ * @param line1
+ * @param line2
+ * @returns {{x: *, y: *}|{x: number, y: number}}
+ */
+const getHalfAngleVector = (line1, line2) => {
+ const v1 = { x: Big(line1.x2).minus(Big(line1.x1)).toNumber(), y: Big(line1.y1).minus(Big(line1.y2)).toNumber() }
+ const v2 = { x: Big(line2.x2).minus(Big(line2.x1)).toNumber(), y: Big(line2.y1).minus(Big(line2.y2)).toNumber() }
+
+ /**
+ * 벡터 정규화
+ * @type {{x: *, y: *}|{x: number, y: number}}
+ */
+ const unitV1 = normalizeVector(v1) // 첫 번째 벡터를 정규화
+ const unitV2 = normalizeVector(v2) // 두 번째 벡터를 정규화
+
+ /**
+ * 두 벡터를 더합니다
+ * @type {{x: *, y: *}}
+ */
+ const summedVector = {
+ x: Big(unitV1.x).plus(Big(unitV2.x)).toNumber(),
+ y: Big(unitV1.y).plus(Big(unitV2.y)).toNumber(),
+ }
+
+ /** 결과 벡터를 정규화하여 사잇각 벡터를 반환합니다 */
+ return normalizeVector(summedVector)
}
/**
@@ -1128,446 +6063,6 @@ export const segmentsOverlap = (line1, line2) => {
return false
}
-/**
- * 추녀마루를 그린다.
- * @param roof
- * @param canvas
- * @param textMode
- */
-const drawHips = (roof, canvas, textMode) => {
- const roofLines = roof.lines
- const ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roof.id)
-
- //마루에서 시작되는 hip 을 먼저 그립니다.
- roofLines
- .filter((roof) => roof.attributes.type === LINE_TYPE.WALLLINE.EAVES && roof.attributes.ridgeCoordinate !== undefined)
- .forEach((currentRoof, index) => {
- const prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
- const nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
- const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
- const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
- const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
-
- //용마루 작성시 입력된 용마루 좌표가 있는 경우 처리
- const ridgeCoordinate = currentRoof.attributes.ridgeCoordinate
-
- const vectorX1 = Big(ridgeCoordinate.x1).minus(Big(currentRoof.x1))
- const vectorY1 = Big(ridgeCoordinate.y1).minus(Big(currentRoof.y1))
- //현재 지붕선의 좌표와 용마루 좌표의 각도를 구한다.
- const angle1 = Big(Math.atan2(vectorY1.toNumber(), vectorX1.toNumber())).times(Big(180).div(Math.PI))
-
- // 용마루 까지의 각도가 45도 인 경우 작성.
- if (Big(angle1.abs().toNumber()).mod(45).eq(0)) {
- const hip1 = new QLine([currentRoof.x1, currentRoof.y1, ridgeCoordinate.x1, ridgeCoordinate.y1], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.HIP,
- attributes: {
- roofId: roof.id,
- currentRoofId: currentRoof.id,
- planeSize: calcLinePlaneSize({
- x1: currentRoof.x1,
- y1: currentRoof.y1,
- x2: ridgeCoordinate.x1,
- y2: ridgeCoordinate.y1,
- }),
- actualSize:
- prevDegree === currentDegree
- ? calcLineActualSize(
- {
- x1: currentRoof.x1,
- y1: currentRoof.y1,
- x2: ridgeCoordinate.x1,
- y2: ridgeCoordinate.y1,
- },
- currentDegree,
- )
- : 0,
- },
- })
- canvas.add(hip1)
- roof.hips.push(hip1)
- roof.innerLines.push(hip1)
- }
-
- const vectorX2 = Big(ridgeCoordinate.x1).minus(Big(currentRoof.x2))
- const vectorY2 = Big(ridgeCoordinate.y1).minus(Big(currentRoof.y2))
- const angle2 = Big(Math.atan2(vectorY2.toNumber(), vectorX2.toNumber())).times(Big(180).div(Math.PI))
- if (Big(angle2.abs().toNumber()).mod(45).eq(0)) {
- const hip2 = new QLine([currentRoof.x2, currentRoof.y2, ridgeCoordinate.x1, ridgeCoordinate.y1], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.HIP,
- attributes: {
- roofId: roof.id,
- currentRoofId: currentRoof.id,
- planeSize: calcLinePlaneSize({
- x1: currentRoof.x2,
- y1: currentRoof.y2,
- x2: ridgeCoordinate.x1,
- y2: ridgeCoordinate.y1,
- }),
- actualSize:
- prevDegree === currentDegree
- ? calcLineActualSize(
- {
- x1: currentRoof.x2,
- y1: currentRoof.y2,
- x2: ridgeCoordinate.x1,
- y2: ridgeCoordinate.y1,
- },
- currentDegree,
- )
- : 0,
- },
- })
- canvas.add(hip2)
- roof.hips.push(hip2)
- roof.innerLines.push(hip2)
- }
- })
-
- const hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roof.id)
-
- //마루에서 시작되지 않는 hip 을 그립니다.
- roofLines
- .filter((roof) => {
- let isHip = false
- if (hipLines.some((hip) => hip.x1 === roof.x1 && hip.y1 === roof.y1)) {
- isHip = true
- }
- return !isHip
- })
- .forEach((currentRoof) => {
- let prevRoof
- roofLines.forEach((roof, index) => {
- if (roof === currentRoof) {
- prevRoof = index === 0 ? roofLines[roofLines.length - 1] : roofLines[index - 1]
- }
- })
- const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
- const currentDegree = currentRoof.attributes.pitch > 0 ? getDegreeByChon(currentRoof.attributes.pitch) : currentRoof.attributes.degree
-
- let ridgePoints = []
- ridgeLines.forEach((ridge) => {
- const deltaX1 = Big(ridge.x1).minus(Big(currentRoof.x1))
- const deltaY1 = Big(ridge.y1).minus(Big(currentRoof.y1))
- const deltaX2 = Big(ridge.x2).minus(Big(currentRoof.x1))
- const deltaY2 = Big(ridge.y2).minus(Big(currentRoof.y1))
-
- if (deltaY1.div(deltaX1).abs().round(1).eq(1)) {
- ridgePoints.push({ x: ridge.x1, y: ridge.y1 })
- }
- if (deltaY2.div(deltaX2).abs().round(1).eq(1)) {
- ridgePoints.push({ x: ridge.x2, y: ridge.y2 })
- }
- })
-
- ridgePoints = ridgePoints.reduce((prev, current) => {
- if (prev !== undefined) {
- // Math.abs(prev.x - currentRoof.x1)
- const deltaPrevX = Big(prev.x).minus(Big(currentRoof.x1)).abs()
- const deltaPrevY = Big(prev.y).minus(Big(currentRoof.y1)).abs()
- const deltaCurrentX = Big(current.x).minus(Big(currentRoof.x1)).abs()
- const deltaCurrentY = Big(current.y).minus(Big(currentRoof.y1)).abs()
- if (deltaPrevX.lt(deltaCurrentX) && deltaPrevY.lt(deltaCurrentY)) {
- return prev
- } else {
- return current
- }
- } else {
- return current
- }
- }, undefined)
-
- if (ridgePoints !== undefined) {
- const hip = new QLine([currentRoof.x1, currentRoof.y1, ridgePoints.x, ridgePoints.y], {
- parentId: roof.id,
- fontSize: roof.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.HIP,
- attributes: {
- roofId: roof.id,
- currentRoofId: currentRoof.id,
- planeSize: calcLinePlaneSize({
- x1: currentRoof.x1,
- y1: currentRoof.y1,
- x2: ridgePoints.x,
- y2: ridgePoints.y,
- }),
- actualSize:
- prevDegree === currentDegree
- ? calcLineActualSize(
- {
- x1: currentRoof.x1,
- y1: currentRoof.y1,
- x2: ridgePoints.x,
- y2: ridgePoints.y,
- },
- currentDegree,
- )
- : 0,
- },
- })
- canvas.add(hip)
- roof.hips.push(hip)
- roof.innerLines.push(hip)
- }
- })
- canvas?.renderAll()
-}
-
-/**
- * 3개 이상 이어지지 않은 라인 포인트 계산
- * 모임지붕에서 point 는 3개 이상의 라인과 접해야 함.
- * @param polygon
- * @param canvas
- * @param textMode
- */
-const connectLinePoint = (polygon, canvas, textMode) => {
- // 연결되지 않은 모든 라인의 포인트를 구한다.
- let missedPoints = []
-
- const lineDegrees = [
- ...new Set(polygon.lines.map((line) => (line.attributes.pitch > 0 ? getDegreeByChon(line.attributes.pitch) : line.attributes.degree))),
- ]
-
- //마루
- polygon.ridges.forEach((ridge) => {
- if (ridge.x1 === ridge.x2) {
- if (
- polygon.lines
- .filter((roof) => roof.y1 === roof.y2)
- .filter((roof) => roof.y1 === ridge.y1 || roof.y1 === ridge.y2 || roof.y2 === ridge.y1 || roof.y2 === ridge.y2).length > 0
- ) {
- return
- }
- }
- if (ridge.y1 === ridge.y2) {
- if (
- polygon.lines
- .filter((roof) => roof.x1 === roof.x2)
- .filter((roof) => roof.x1 === ridge.x1 || roof.x1 === ridge.x2 || roof.x2 === ridge.x1 || roof.x2 === ridge.x2).length > 0
- ) {
- return
- }
- }
- if (polygon.hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1).length < 2) {
- missedPoints.push({ x: ridge.x1, y: ridge.y1 })
- }
- if (polygon.hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2).length < 2) {
- missedPoints.push({ x: ridge.x2, y: ridge.y2 })
- }
- })
-
- //추녀마루
- polygon.hips.forEach((hip) => {
- let count = 0
- count += polygon.ridges.filter((ridge) => (ridge.x1 === hip.x2 && ridge.y1 === hip.y2) || (ridge.x2 === hip.x2 && ridge.y2 === hip.y2)).length
-
- count += polygon.hips.filter((hip2) => (hip2.x1 === hip.x2 && hip2.y1 === hip.y2) || (hip2.x2 === hip.x2 && hip2.y2 === hip.y2)).length
- if (count < 3) {
- missedPoints.push({ x: hip.x2, y: hip.y2 })
- }
- })
-
- let missedLine = []
-
- //중복포인트제거
- missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
-
- missedPoints.forEach((p1) => {
- let p2 = missedPoints
- .filter((p) => p.x !== p1.x && p.y !== p1.y)
- .reduce((prev, current) => {
- if (prev !== undefined) {
- return Math.sqrt(Math.pow(Math.abs(current.x - p1.x), 2) + Math.pow(Math.abs(current.y - p1.y), 2)) <
- Math.sqrt(Math.pow(Math.abs(prev.x - p1.x), 2) + Math.pow(Math.abs(prev.y - p1.y), 2))
- ? current
- : prev
- } else {
- return current
- }
- }, undefined)
- if (p2 !== undefined) {
- if (p1.x < p2.x && p1.y < p2.y) {
- missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
- }
- if (p1.x > p2.x && p1.y < p2.y) {
- missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y })
- }
- if (p1.x > p2.x && p1.y > p2.y) {
- missedLine.push({ x1: p2.x, y1: p2.y, x2: p1.x, y2: p1.y })
- }
- if (p1.x < p2.x && p1.y > p2.y) {
- missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
- }
- }
- })
-
- //중복라인제거
- missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
-
- missedLine.forEach((p) => {
- const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
- parentId: polygon.id,
- attributes: {
- roofId: polygon.id,
- planeSize: calcLinePlaneSize(p),
- actualSize: lineDegrees.length === 0 ? 0 : calcLineActualSize(p, lineDegrees[0]),
- },
- fontSize: polygon.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- textMode: textMode,
- })
- polygon.canvas.add(line)
- polygon.innerLines.push(line)
- })
-
- missedPoints = []
- missedLine = []
-
- polygon.innerLines.forEach((line) => {
- if (
- polygon.innerLines.filter(
- (innerLine) => (line.x2 === innerLine.x1 && line.y2 === innerLine.y1) || (line.x2 === innerLine.x2 && line.y2 === innerLine.y2),
- ).length < 3
- ) {
- missedPoints.push({ x: line.x2, y: line.y2 })
- }
- })
-
- missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
-
- missedPoints.forEach((p1) => {
- let p2 = missedPoints
- .filter((p) => !(p.x === p1.x && p.y === p1.y))
- .reduce((prev, current) => {
- if (prev !== undefined) {
- return Math.abs(current.x - p1.x) + Math.abs(current.y - p1.y) < Math.abs(prev.x - p1.x) + Math.abs(prev.y - p1.y) ? current : prev
- } else {
- return current
- }
- }, undefined)
-
- if (p2 !== undefined) {
- if (p1.x === p2.x && p1.y < p2.y) {
- missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
- }
- if (p1.x === p2.x && p1.y > p2.y) {
- missedLine.push({ x1: p1.x, y1: p2.y, x2: p2.x, y2: p1.y })
- }
- if (p1.x < p2.x && p1.y === p2.y) {
- missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
- }
- if (p1.x > p2.x && p1.y === p2.y) {
- missedLine.push({ x1: p2.x, y1: p1.y, x2: p1.x, y2: p2.y })
- }
- }
- })
-
- //중복라인제거
- missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
-
- missedLine.forEach((p) => {
- const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
- attributes: {
- roofId: polygon.id,
- planeSize: calcLinePlaneSize(p),
- actualSize: lineDegrees.length === 0 ? 0 : calcLineActualSize(p, lineDegrees[0]),
- },
- fontSize: polygon.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- textMode: textMode,
- })
- line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10)
- line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10)
- polygon.canvas.add(line)
- polygon.innerLines.push(line)
- })
-
- //마지막으로 연결되지 않고 떨어져있는 마루를 확인한다.
- let missedRidge = []
- polygon.ridges.forEach((ridge) => {
- let lineCheck1 = polygon.innerLines.filter((line) => {
- if (
- !(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) &&
- ((line.x1 === ridge.x1 && line.y1 === ridge.y1) || (line.x2 === ridge.x1 && line.y2 === ridge.y1))
- ) {
- return line
- }
- })
-
- let lineCheck2 = polygon.innerLines.filter((line) => {
- if (
- !(line.x1 === ridge.x1 && line.y1 === ridge.y1 && line.x2 === ridge.x2 && line.y2 === ridge.y2) &&
- ((line.x1 === ridge.x2 && line.y1 === ridge.y2) || (line.x2 === ridge.x2 && line.y2 === ridge.y2))
- ) {
- return line
- }
- })
- if (lineCheck1.length === 0 || lineCheck2.length === 0) {
- missedRidge.push(ridge)
- }
- })
-
- missedRidge.forEach((ridge) => {
- let missedRidge2 = missedRidge.filter(
- (ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2),
- )
-
- missedRidge2.forEach((ridge2) => {
- let overlap = false
- if (ridge.x1 === ridge.x2 && ridge2.x1 === ridge2.x2 && ridge2.x1 === ridge.x1) {
- overlap = true
- }
- if (ridge.y1 === ridge.y2 && ridge2.y1 === ridge2.y2 && ridge2.y1 === ridge.y1) {
- overlap = true
- }
- if (overlap) {
- let x1 = Math.min(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
- let x2 = Math.max(ridge.x1, ridge2.x1, ridge.x2, ridge2.x2)
- let y1 = Math.min(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
- let y2 = Math.max(ridge.y1, ridge2.y1, ridge.y2, ridge2.y2)
- const newRidge = new QLine([x1, y1, x2, y2], {
- fontSize: polygon.fontSize,
- stroke: '#1083E3',
- strokeWidth: 2,
- name: LINE_TYPE.SUBLINE.RIDGE,
- textMode: textMode,
- attributes: {
- roofId: polygon.id,
- planeSize: calcLinePlaneSize(ridge),
- actualSize: lineDegrees.length > 0 ? 0 : calcLineActualSize(ridge, lineDegrees[0]),
- },
- })
- if (polygon.ridges.filter((r) => newRidge.x1 === r.x1 && newRidge.y1 === r.y1 && newRidge.x2 === r.x2 && newRidge.y2 === r.y2).length === 0) {
- polygon.canvas.remove(ridge)
- polygon.canvas.remove(ridge2)
- polygon.ridges = polygon.ridges.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
- polygon.ridges = polygon.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2))
- polygon.innerLines = polygon.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2))
- polygon.innerLines = polygon.innerLines.filter(
- (r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2),
- )
-
- polygon.canvas.add(newRidge)
- polygon.ridges.push(newRidge)
- polygon.innerLines.push(newRidge)
- }
- }
- })
- })
- polygon.canvas.renderAll()
-}
-
/**
* 외벽선 속성에 따라서 모양을 수정한다.
* @param roof
@@ -2063,7 +6558,15 @@ const changeGableRoof = (currentRoof, canvas, textMode) => {
x2: midX.toNumber(),
y2: midY.toNumber(),
}),
- actualSize: calcLineActualSize({ x1: currentRoof.x2, y1: currentRoof.y2, x2: midX.toNumber(), y2: midY.toNumber() }, nextDegree),
+ actualSize: calcLineActualSize(
+ {
+ x1: currentRoof.x2,
+ y1: currentRoof.y2,
+ x2: midX.toNumber(),
+ y2: midY.toNumber(),
+ },
+ nextDegree,
+ ),
},
})
canvas?.add(hip2)
@@ -3008,7 +7511,15 @@ const changeWallRoof = (currentRoof, canvas, textMode) => {
x2: wallMidX.toNumber(),
y2: wallMidY.toNumber(),
}),
- actualSize: calcLineActualSize({ x1: currentRoof.x1, y1: currentRoof.y1, x2: wallMidX.toNumber(), y2: wallMidY.toNumber() }, prevDegree),
+ actualSize: calcLineActualSize(
+ {
+ x1: currentRoof.x1,
+ y1: currentRoof.y1,
+ x2: wallMidX.toNumber(),
+ y2: wallMidY.toNumber(),
+ },
+ prevDegree,
+ ),
},
})
// const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
@@ -3031,7 +7542,15 @@ const changeWallRoof = (currentRoof, canvas, textMode) => {
x2: wallMidX.toNumber(),
y2: wallMidY.toNumber(),
}),
- actualSize: calcLineActualSize({ x1: currentRoof.x2, y1: currentRoof.y2, x2: wallMidX.toNumber(), y2: wallMidY.toNumber() }, nextDegree),
+ actualSize: calcLineActualSize(
+ {
+ x1: currentRoof.x2,
+ y1: currentRoof.y2,
+ x2: wallMidX.toNumber(),
+ y2: wallMidY.toNumber(),
+ },
+ nextDegree,
+ ),
},
})
// const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
@@ -3162,9 +7681,6 @@ const reDrawPolygon = (polygon, canvas) => {
line.attributes = l.attributes
}
})
- // const lineLength = Math.sqrt(
- // Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2),
- // )
const lineLength = calcLinePlaneSize({ x1: line.x1, y1: line.y1, x2: line.x2, y2: line.y2 })
if (line.attributes !== undefined) {
line.attributes.planeSize = lineLength
@@ -3530,14 +8046,8 @@ export const calcLinePlaneSize = (points) => {
export const calcLineActualSize = (points, degree = 0) => {
const { x1, y1, x2, y2 } = points
const planeSize = calcLinePlaneSize(points)
- let height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(planeSize)
- /**
- * 대각선일 경우 높이 계산 변경
- */
- if (x1 !== x2 && y1 !== y2) {
- height = Big(Math.tan(Big(degree).times(Math.PI / 180))).times(Big(x1).minus(x2).times(10).round())
- }
- return Big(planeSize).pow(2).plus(height.pow(2)).sqrt().abs().round().toNumber()
+ const theta = Big(Math.cos(Big(degree).times(Math.PI).div(180)))
+ return Big(planeSize).div(theta).round().toNumber()
}
export const createLinesFromPolygon = (points) => {
@@ -3554,3 +8064,62 @@ export const createLinesFromPolygon = (points) => {
}
return lines
}
+
+/** 포인트 정렬 가장왼쪽, 가장위 부터 */
+const getSortedPoint = (points) => {
+ const startPoint = points
+ .filter((point) => point.x === Math.min(...points.map((point) => point.x)))
+ .reduce((prev, curr) => {
+ return prev.y < curr.y ? prev : curr
+ })
+ const sortedPoints = []
+ sortedPoints.push(startPoint)
+
+ let prevPoint = startPoint
+
+ for (let i = 0; i < points.length - 1; i++) {
+ const samePoints = []
+ points
+ .filter((point) => !sortedPoints.includes(point))
+ .forEach((point) => {
+ if (i % 2 === 1 && prevPoint.y === point.y) {
+ samePoints.push({ point, size: Math.abs(point.x - prevPoint.x) })
+ }
+ if (i % 2 === 0 && prevPoint.x === point.x) {
+ samePoints.push({ point, size: Math.abs(point.y - prevPoint.y) })
+ }
+ })
+ const samePoint = samePoints.sort((a, b) => a.size - b.size)[0].point
+ sortedPoints.push(samePoint)
+ prevPoint = samePoint
+ }
+ return sortedPoints
+}
+
+const reCalculateSize = (line) => {
+ const oldPlaneSize = line.attributes.planeSize
+ const oldActualSize = line.attributes.actualSize
+ const theta = Big(Math.acos(Big(oldPlaneSize).div(oldActualSize)))
+ .times(180)
+ .div(Math.PI)
+ console.log('theta : ', theta.toNumber())
+ const planeSize = calcLinePlaneSize({
+ x1: line.x1,
+ y1: line.y1,
+ x2: line.x2,
+ y2: line.y2,
+ })
+ const actualSize =
+ planeSize === oldActualSize
+ ? 0
+ : calcLineActualSize(
+ {
+ x1: line.x1,
+ y1: line.y1,
+ x2: line.x2,
+ y2: line.y2,
+ },
+ theta,
+ )
+ return { planeSize, actualSize }
+}