diff --git a/docs/dictionary.txt b/docs/dictionary.txt
index 6e81da01..268cde7a 100644
--- a/docs/dictionary.txt
+++ b/docs/dictionary.txt
@@ -19,9 +19,12 @@ Allpainted : allPainted
출폭: offset
폭: width
경사(구배): pitch
+각도: degree
이구배: doublePitch
소매: sleeve
개구: openSpace
도머: dormer
그림자: shadow
-치수선: dimensionLine
\ No newline at end of file
+치수선: dimensionLine
+복도치수: planeSize
+실제치수: actualSize
diff --git a/src/app/roof2/page.jsx b/src/app/roof2/page.jsx
index cccd7bb5..dce69910 100644
--- a/src/app/roof2/page.jsx
+++ b/src/app/roof2/page.jsx
@@ -1,16 +1,7 @@
import Roof2 from '@/components/Roof2'
-// import { initCheck } from '@/util/session-util'
import RoofSelect from '@/app/roof2/RoofSelect'
export default async function Roof2Page() {
- // const session = await initCheck()
- const roof2Props = {
- // name: session.name || '',
- // userId: session.userId || '',
- // email: session.email || '',
- // isLoggedIn: session.isLoggedIn,
- }
-
return (
<>
@@ -19,7 +10,7 @@ export default async function Roof2Page() {
-
+
>
)
diff --git a/src/common/common.js b/src/common/common.js
index 7ab733c4..cf60295b 100644
--- a/src/common/common.js
+++ b/src/common/common.js
@@ -65,6 +65,7 @@ export const LINE_TYPE = {
HIPANDGABLE: 'hipAndGable',
JERKINHEAD: 'jerkinhead',
SHED: 'shed',
+ ETC: 'etc',
},
SUBLINE: {
/**
@@ -116,6 +117,7 @@ export const INPUT_TYPE = {
export const POLYGON_TYPE = {
ROOF: 'roof',
+ WALL: 'wall',
TRESTLE: 'trestle',
}
diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx
index cc690ba9..fad075bd 100644
--- a/src/components/Roof2.jsx
+++ b/src/components/Roof2.jsx
@@ -4,7 +4,7 @@ import { useCanvas } from '@/hooks/useCanvas'
import { useEffect, useRef, useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useMode } from '@/hooks/useMode'
-import { Mode } from '@/common/common'
+import { LINE_TYPE, Mode } from '@/common/common'
import { Button, Input } from '@nextui-org/react'
import RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil'
@@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen
import InitSettingsModal from './InitSettingsModal'
import GridSettingsModal from './GridSettingsModal'
import { SurfaceShapeModal } from '@/components/ui/SurfaceShape'
-import { drawDirectionStringToArrow } from '@/util/qpolygon-utils'
+import { changeCurrentRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils'
import ThumbnailList from '@/components/ui/ThumbnailLIst'
import ObjectPlacement from '@/components/ui/ObjectPlacement'
import { globalLocaleStore } from '@/store/localeAtom'
@@ -409,6 +409,20 @@ export default function Roof2(props) {
{ x: 600, y: 100 },
]
+ const rectangleType1 = [
+ { x: 100, y: 100 },
+ { x: 100, y: 600 },
+ { x: 300, y: 600 },
+ { x: 300, y: 100 },
+ ]
+
+ const rectangleType2 = [
+ { x: 100, y: 100 },
+ { x: 100, y: 300 },
+ { x: 600, y: 300 },
+ { x: 600, y: 100 },
+ ]
+
const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint]
const newP = [
{ x: 450, y: 450 },
@@ -417,7 +431,7 @@ export default function Roof2(props) {
{ x: 450, y: 850 },
]
- const polygon = new QPolygon(twelvePoint, {
+ const polygon = new QPolygon(rectangleType2, {
fill: 'transparent',
stroke: 'green',
strokeWidth: 1,
@@ -658,17 +672,52 @@ export default function Roof2(props) {
canvas?.renderAll()
}
- const setAllGableRoof = () => {
- let offset = Number(prompt('gable roof offset', '50'))
+ const setHipRoof = () => {
+ const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
+ const currentRoof = polygon.lines[2]
+ currentRoof.attributes.type = LINE_TYPE.WALLLINE.EAVES
+ currentRoof.attributes.offset = 50
+ changeCurrentRoof(currentRoof, canvas)
+ }
+ const setGableRoof = () => {
+ const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
+ const currentRoof = polygon.lines[2]
+ currentRoof.attributes.type = LINE_TYPE.WALLLINE.GABLE
+ currentRoof.attributes.offset = 30
+ changeCurrentRoof(currentRoof, canvas)
+ }
+ const setHipAndGableRoof = () => {
+ let offset = Number(prompt('팔작지붕 폭', '50'))
if (!isNaN(offset) && offset > 0) {
- const polygon = canvas?.getObjects()
- console.log('gable roof offset : ', offset)
- console.log('polygon : ', polygon)
- changeAllGableRoof(polygon, offset, canvas)
+ const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
+ const currentRoof = polygon.lines[2]
+ currentRoof.attributes.type = LINE_TYPE.WALLLINE.HIPANDGABLE
+ currentRoof.attributes.width = offset
+ changeCurrentRoof(currentRoof, canvas)
} else {
- alert('offset 은 0 보다 커야 함')
+ alert('폭은 0 보다 커야 함')
}
}
+ const setJerkInHeadRoof = () => {
+ let offset = Number(prompt('팔작지붕 폭', '50'))
+ if (!isNaN(offset) && offset > 0) {
+ const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
+ const currentRoof = polygon.lines[2]
+ currentRoof.attributes.type = LINE_TYPE.WALLLINE.JERKINHEAD
+ currentRoof.attributes.width = offset
+ changeCurrentRoof(currentRoof, canvas)
+ } else {
+ alert('폭은 0 보다 커야 함')
+ }
+ }
+ const setWallRoof = () => {
+ let offset = Number(prompt('소매 폭', '0'))
+ const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof')
+ const currentRoof = polygon.lines[2]
+ currentRoof.attributes.type = LINE_TYPE.WALLLINE.WALL
+ currentRoof.attributes.width = offset
+ changeCurrentRoof(currentRoof, canvas)
+ }
return (
<>
{canvas && (
@@ -788,16 +837,28 @@ export default function Roof2(props) {
- {templateType === 0 && (
- <>
-
- >
- )}
-
-
+
{getMessage('plan.menu.estimate.save')}
diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js
new file mode 100644
index 00000000..280c70da
--- /dev/null
+++ b/src/hooks/floorPlan/estimate/useEstimateController.js
@@ -0,0 +1,60 @@
+import { useAxios } from '@/hooks/useAxios'
+import { useReducer } from 'react'
+
+const reducer = (prevState, nextState) => {
+ return { ...prevState, ...nextState }
+}
+
+// Constants
+const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
+
+const defaultEstimateData = {
+ name: '',
+ objectName: '',
+ estimateDate: '',
+ itemList: [{ id: 1, name: '' }],
+}
+
+// Helper functions
+const updateItemInList = (itemList, id, updates) => {
+ return itemList.map((item) => (item.id === id ? { ...item, ...updates } : item))
+}
+
+export const useEstimateController = () => {
+ const { promisePost } = useAxios()
+ const [state, setState] = useReducer(reducer, defaultEstimateData)
+
+ const updateItem = (id, updates) => {
+ setState({
+ itemList: updateItemInList(state.itemList, id, updates),
+ })
+ }
+
+ const addItem = () => {
+ const newId = Math.max(...state.itemList.map((item) => item.id)) + 1
+ setState({
+ itemList: [...state.itemList, { id: newId, name: '' }],
+ })
+ }
+
+ const handleEstimateSubmit = async () => {
+ try {
+ const result = await promisePost({
+ url: ESTIMATE_API_ENDPOINT,
+ data: state,
+ })
+ return result
+ } catch (error) {
+ console.error('Failed to submit estimate:', error)
+ throw error
+ }
+ }
+
+ return {
+ state,
+ setState,
+ updateItem,
+ addItem,
+ handleEstimateSubmit,
+ }
+}
diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js
index 0f87e156..2b2ea3c5 100644
--- a/src/hooks/option/useCanvasSetting.js
+++ b/src/hooks/option/useCanvasSetting.js
@@ -225,9 +225,9 @@ export function useCanvasSetting() {
const option1 = settingModalFirstOptions.option1
// 'allocDisplay' 할당 표시
- // 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
+ // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
- // 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
+ // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow'
@@ -244,13 +244,13 @@ export function useCanvasSetting() {
optionName = ['1']
break
case 'outlineDisplay': //외벽선 표시
- optionName = ['outerLine', 'wallLine']
+ optionName = ['outerLine', POLYGON_TYPE.WALL]
break
case 'gridDisplay': //그리드 표시
optionName = ['lindGrid', 'dotGrid']
break
case 'lineDisplay': //지붕선 표시
- optionName = ['roof', 'roofBase']
+ optionName = ['roof', POLYGON_TYPE.ROOF]
break
case 'wordDisplay': //문자 표시
optionName = ['6']
diff --git a/src/hooks/option/useFirstOption.js b/src/hooks/option/useFirstOption.js
index c3778587..4b85b4c4 100644
--- a/src/hooks/option/useFirstOption.js
+++ b/src/hooks/option/useFirstOption.js
@@ -12,9 +12,9 @@ export function useFirstOption() {
const option1 = settingModalFirstOptions.option1
// 'allocDisplay' 할당 표시
- // 'outlineDisplay' 외벽선 표시 'outerLine', 'wallLine'
+ // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
// 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
- // 'lineDisplay' 지붕선 표시 'roof', 'roofBase'
+ // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시
// 'flowDisplay' 흐름방향 표시 'arrow'
@@ -30,13 +30,13 @@ export function useFirstOption() {
optionName = ['1']
break
case 'outlineDisplay': //외벽선 표시
- optionName = ['outerLine', 'wallLine']
+ optionName = ['outerLine', POLYGON_TYPE.WALL]
break
case 'gridDisplay': //그리드 표시
optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
break
case 'lineDisplay': //지붕선 표시
- optionName = ['roof', 'roofBase']
+ optionName = ['roof', POLYGON_TYPE.ROOF]
break
case 'wordDisplay': //문자 표시
optionName = ['6']
diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js
index 956bcf8f..46311ad9 100644
--- a/src/hooks/roofcover/useAuxiliaryDrawing.js
+++ b/src/hooks/roofcover/useAuxiliaryDrawing.js
@@ -15,7 +15,7 @@ import {
outerLineLength2State,
outerLineTypeState,
} from '@/store/outerLineAtom'
-import { calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util'
+import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal'
@@ -23,6 +23,7 @@ import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup'
import { calculateAngle } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
+import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성
export function useAuxiliaryDrawing(id) {
@@ -80,7 +81,7 @@ export function useAuxiliaryDrawing(id) {
useEffect(() => {
// innerLines가 있을경우 삭제
- const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofs.length === 0) {
swalFire({ text: '지붕형상이 없습니다.' })
closePopup(id)
@@ -561,7 +562,7 @@ export function useAuxiliaryDrawing(id) {
return
}
- const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
/*const allLines = [...auxiliaryLines]
roofBases.forEach((roofBase) => {
@@ -611,9 +612,41 @@ export function useAuxiliaryDrawing(id) {
},
)
lineHistory.current.push(newLine)
+ lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1)
intersectionPoints.current.push(...interSectionPointsWithRoofLines)
return
+ } else if (interSectionPointsWithRoofLines.length === 1) {
+ //지붕선과 만나는 점이 하나일 경우
+ 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 >= distance2) {
+ const newLine = addLine([line1.x1, line1.y1, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
+ stroke: 'black',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'auxiliaryLine',
+ isFixed: true,
+ })
+ lineHistory.current.push(newLine)
+ lineHistory.current = lineHistory.current.filter((history) => history !== line1)
+ removeLine(line1)
+ } else {
+ const newLine = addLine([line1.x2, line1.y2, interSectionPointsWithRoofLines[0].x, interSectionPointsWithRoofLines[0].y], {
+ stroke: 'black',
+ strokeWidth: 1,
+ selectable: false,
+ name: 'auxiliaryLine',
+ isFixed: true,
+ })
+ lineHistory.current.push(newLine)
+ lineHistory.current = lineHistory.current.filter((history) => history !== line1)
+ removeLine(line1)
+ }
+ intersectionPoints.current.push(interSectionPointsWithRoofLines[0])
+ }
}
//보조선과 만나는 점을 찾는다.
@@ -659,6 +692,7 @@ export function useAuxiliaryDrawing(id) {
})
}
lineHistory.current.push(newLine)
+ lineHistory.current = lineHistory.current.filter((history) => history !== line1)
removeLine(line1)
})
@@ -742,7 +776,7 @@ export function useAuxiliaryDrawing(id) {
return
}
- const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
// 겹치는 선 하나는 canvas에서 제거한다.
@@ -772,9 +806,13 @@ export function useAuxiliaryDrawing(id) {
})
const roofInnerLines = innerLines.filter((line) => {
const inPolygon1 =
- tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) || roofBase.inPolygon({ x: line.x1, y: line.y1 })
+ tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
+ roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
+ roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
const inPolygon2 =
- tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) || roofBase.inPolygon({ x: line.x2, y: line.y2 })
+ tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
+ roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
+ roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
if (inPolygon1 && inPolygon2) {
line.attributes = { ...line.attributes, roofId: roofBase.id }
diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js
index 7696dc7a..5619abfb 100644
--- a/src/hooks/roofcover/useEavesGableEdit.js
+++ b/src/hooks/roofcover/useEavesGableEdit.js
@@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil'
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
-import { LINE_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useLine } from '@/hooks/useLine'
import { useMode } from '@/hooks/useMode'
import { outerLineFixState } from '@/store/outerLineAtom'
@@ -54,7 +54,7 @@ export function useEavesGableEdit(id) {
}, [])
useEffect(() => {
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
wallLines.forEach((wallLine) => {
convertPolygonToLines(wallLine)
})
@@ -160,7 +160,7 @@ export function useEavesGableEdit(id) {
attributes,
})
- const roofBases = canvas?.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofBases = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofBases.forEach((roof) => {
roof.innerLines.forEach((line) => {
@@ -169,7 +169,7 @@ export function useEavesGableEdit(id) {
canvas.remove(roof)
})
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'pitchText')
removeTargets.forEach((obj) => {
canvas.remove(obj)
diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js
index f0814a44..f09aee6b 100644
--- a/src/hooks/roofcover/useMovementSetting.js
+++ b/src/hooks/roofcover/useMovementSetting.js
@@ -4,6 +4,7 @@ import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage'
import { useEffect, useRef, useState } from 'react'
import { useEvent } from '@/hooks/useEvent'
+import { POLYGON_TYPE } from '@/common/common'
//동선이동 형 올림 내림
export function useMovementSetting(id) {
@@ -41,7 +42,7 @@ export function useMovementSetting(id) {
}, [type])
useEffect(() => {
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') // 기존 wallLine의 visible false
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) // 기존 wallLine의 visible false
wallLines.forEach((line) => {
line.set({ visible: false })
})
@@ -55,7 +56,7 @@ export function useMovementSetting(id) {
addCanvasMouseEventListener('mouse:move', mouseMoveEvent)
return () => {
initEvent()
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
wallLines.forEach((line) => {
line.set({ visible: true })
})
diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js
index b999c001..3a1535a3 100644
--- a/src/hooks/roofcover/usePropertiesSetting.js
+++ b/src/hooks/roofcover/usePropertiesSetting.js
@@ -1,5 +1,5 @@
import { useEffect, useRef } from 'react'
-import { LINE_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useMode } from '@/hooks/useMode'
@@ -135,7 +135,7 @@ export function usePropertiesSetting(id) {
hideLine(line)
})
- const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
+ const wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
wall.lines = [...lines]
diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js
index 3be745d6..c229549e 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -7,6 +7,7 @@ import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon'
import { roofDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup'
+import { POLYGON_TYPE } from '@/common/common'
// 지붕면 할당
export function useRoofAllocationSetting(id) {
@@ -81,12 +82,12 @@ export function useRoofAllocationSetting(id) {
const [selectedRoofMaterial, setSelectedRoofMaterial] = useState(roofMaterials[0])
useEffect(() => {
- const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
if (roofBases.length === 0) {
swalFire({ text: '할당할 지붕이 없습니다.' })
closePopup(id)
}
- // if (type === 'roofBase') {
+ // if (type === POLYGON_TYPE.ROOF) {
// // 지붕면 할당
//
// } else if ('roof') {
@@ -104,8 +105,8 @@ export function useRoofAllocationSetting(id) {
// 선택한 지붕재로 할당
const handleSave = () => {
- const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => {
try {
splitPolygonWithLines(roofBase)
@@ -117,7 +118,7 @@ export function useRoofAllocationSetting(id) {
canvas.remove(line)
})
- canvas.remove(roofBase)
+ // canvas.remove(roofBase)
})
wallLines.forEach((wallLine) => {
diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js
index ef7a4477..e0af3205 100644
--- a/src/hooks/roofcover/useRoofShapePassivitySetting.js
+++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react'
import { useLine } from '@/hooks/useLine'
import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
-import { LINE_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { useMode } from '@/hooks/useMode'
import { usePolygon } from '@/hooks/usePolygon'
import { outerLineFixState } from '@/store/outerLineAtom'
@@ -60,7 +60,7 @@ export function useRoofShapePassivitySetting(id) {
useEffect(() => {
if (!isLoading) return
addCanvasMouseEventListener('mouse:down', mouseDown)
- const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine')
+ const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
canvas?.remove(...wallLines)
@@ -185,7 +185,7 @@ export function useRoofShapePassivitySetting(id) {
}
const handleLineToPolygon = () => {
- const roofBases = canvas.getObjects().filter((obj) => obj.name === 'roofBase')
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine')
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
exceptObjs.forEach((obj) => {
@@ -199,10 +199,10 @@ export function useRoofShapePassivitySetting(id) {
let wall
if (isFix.current) {
- wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' })
+ wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
} else {
// 그냥 닫을 경우 처리
- wall = addPolygonByLines([...initLines.current], { name: 'wallLine', fill: 'transparent', stroke: 'black' })
+ wall = addPolygonByLines([...initLines.current], { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes
})
diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js
index ae886ce9..2823415d 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom'
-import { LINE_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import { usePolygon } from '@/hooks/usePolygon'
import { useMode } from '@/hooks/useMode'
import { useLine } from '@/hooks/useLine'
@@ -129,7 +129,7 @@ export function useRoofShapeSetting(id) {
useEffect(() => {
if (shapeNum === 4) {
- canvas?.remove(canvas.getObjects().find((obj) => obj.name === 'wallLine'))
+ canvas?.remove(canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL))
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
outerLines.forEach((line) => {
showLine(line)
@@ -376,20 +376,20 @@ export function useRoofShapeSetting(id) {
// 기존 wallLine, roofBase 제거
canvas
.getObjects()
- .filter((obj) => obj.name === 'wallLine')
+ .filter((obj) => obj.name === POLYGON_TYPE.WALL)
.forEach((line) => {
canvas.remove(line)
})
canvas
.getObjects()
- .filter((obj) => obj.name === 'roofBase')
+ .filter((obj) => obj.name === POLYGON_TYPE.ROOF)
.forEach((obj) => {
canvas.remove(...obj.innerLines)
canvas.remove(obj)
})
- const polygon = addPolygonByLines(outerLines, { name: 'wallLine' })
+ const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL })
polygon.lines = [...outerLines]
addPitchTextsByOuterLines()
diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js
index be42ef52..486bc8e9 100644
--- a/src/hooks/useMode.js
+++ b/src/hooks/useMode.js
@@ -36,10 +36,7 @@ import { QPolygon } from '@/components/fabric/QPolygon'
import offsetPolygon from '@/util/qpolygon-utils'
import { isObjectNotEmpty } from '@/util/common-utils'
import * as turf from '@turf/turf'
-import { INPUT_TYPE, Mode } from '@/common/common'
-import { m } from 'framer-motion'
-import { set } from 'react-hook-form'
-import { FaWineGlassEmpty } from 'react-icons/fa6'
+import { INPUT_TYPE, LINE_TYPE, Mode, POLYGON_TYPE } from '@/common/common'
export function useMode() {
const [mode, setMode] = useRecoilState(modeState)
@@ -1509,6 +1506,59 @@ export function useMode() {
*벽 지붕 외곽선 생성 polygon을 입력받아 만들기
*/
const handleOuterlinesTest2 = (polygon, offset = 50) => {
+ // TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
+ polygon.lines.forEach((line, index) => {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.EAVES,
+ offset: 40,
+ width: 50,
+ pitch: 4,
+ sleeve: true,
+ }
+ /*if (index === 1 || index === 3) {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.WALL,
+ offset: 50, //출폭
+ width: 30, //폭
+ pitch: 4, //구배
+ sleeve: true, //소매
+ }
+ } else {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.EAVES,
+ offset: 40,
+ width: 50,
+ pitch: 4,
+ sleeve: true,
+ }
+ }*/
+ /* if (index === 1) {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.SHED,
+ offset: 20, //출폭
+ width: 30, //폭
+ pitch: 4, //구배
+ sleeve: true, //소매
+ }
+ } else if (index === 3) {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.EAVES,
+ offset: 50, //출폭
+ width: 30, //폭
+ pitch: 4, //구배
+ sleeve: true, //소매
+ }
+ } else {
+ line.attributes = {
+ type: LINE_TYPE.WALLLINE.GABLE,
+ offset: 30,
+ width: 50,
+ pitch: 4,
+ sleeve: true,
+ }
+ }*/
+ })
+
const roof = drawRoofPolygon(polygon) //지붕 그리기
roof.drawHelpLine()
// roof.divideLine()
@@ -1677,21 +1727,6 @@ export function useMode() {
}
const drawRoofPolygon = (wall) => {
- // TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
- /*wall.lines.forEach((line, index) => {
- if (index === wall.lines.length - 1 || index === 3) {
- line.attributes = {
- type: 'gable',
- offset: 30,
- }
- } else {
- line.attributes = {
- type: 'hip',
- offset: 50,
- }
- }
- })*/
-
const polygon = createRoofPolygon(wall.points)
const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
originPolygon.setViewLengthText(false)
@@ -1711,13 +1746,33 @@ export function useMode() {
return { x1: point.x, y1: point.y }
}),
)
- roof.name = 'roofBase'
+ roof.name = POLYGON_TYPE.ROOF
roof.setWall(wall)
roof.lines.forEach((line, index) => {
- line.attributes = wall.lines[index].attributes
+ line.attributes = {
+ roofId: roof.id,
+ 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,
+ }
})
+ wall.attributes = {
+ roofId: roof.id,
+ }
+
+ wall.lines.forEach((line, index) => {
+ line.attributes.roofId = roof.id
+ line.attributes.currentRoof = roof.lines[index].id
+ })
+
+ console.log('drawRoofPolygon roof : ', roof)
+ console.log('drawRoofPolygon wall : ', wall)
+
setRoof(roof)
setWall(wall)
diff --git a/src/locales/ja.json b/src/locales/ja.json
index c57582b2..e4605537 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -814,7 +814,10 @@
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
"estimate.detail.header.showPrice": "価格表示",
"estimate.detail.showPrice.btn1": "Pricing",
- "estimate.detail.showPrice.description": "クリックして製品の特異性を確認する",
+ "estimate.detail.showPrice.description1": "製品価格 OPEN",
+ "estimate.detail.showPrice.description2": "追加, 変更資材",
+ "estimate.detail.showPrice.description3": "添付必須",
+ "estimate.detail.showPrice.description4": "クリックして製品の特異性を確認する",
"estimate.detail.showPrice.btn2": "製品を追加",
"estimate.detail.showPrice.btn3": "製品削除"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 1cb2c279..03443a0b 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -820,7 +820,10 @@
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
"estimate.detail.header.showPrice": "가격표시",
"estimate.detail.showPrice.btn1": "Pricing",
- "estimate.detail.showPrice.description": "클릭하여 제품 특이사항 확인",
+ "estimate.detail.showPrice.description1": "제품 가격 OPEN",
+ "estimate.detail.showPrice.description2": "추가, 변경 자재",
+ "estimate.detail.showPrice.description3": "첨부필수",
+ "estimate.detail.showPrice.description4": "클릭하여 제품 특이사항 확인",
"estimate.detail.showPrice.btn2": "제품추가",
"estimate.detail.showPrice.btn3": "제품삭제"
}
diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js
index 9679d2d7..fede515e 100644
--- a/src/store/settingAtom.js
+++ b/src/store/settingAtom.js
@@ -5,7 +5,7 @@ export const settingModalFirstOptionsState = atom({
default: {
option1: [
{ id: 1, column: 'allocDisplay', name: 'modal.canvas.setting.first.option.alloc', selected: false },
- { id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: false },
+ { id: 2, column: 'outlineDisplay', name: 'modal.canvas.setting.first.option.outline', selected: true },
{ id: 3, column: 'gridDisplay', name: 'modal.canvas.setting.first.option.grid', selected: false },
{ id: 4, column: 'lineDisplay', name: 'modal.canvas.setting.first.option.roof.line', selected: false },
{ id: 5, column: 'wordDisplay', name: 'modal.canvas.setting.first.option.word', selected: false },
diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js
index 74650e8d..1a78f991 100644
--- a/src/util/canvas-util.js
+++ b/src/util/canvas-util.js
@@ -263,12 +263,12 @@ export const getDegreeByChon = (chon) => {
}
/**
- *
+ * 각도를 입력받아 촌을 반환
+ * @param degree
+ * @returns {number}
*/
export const getChonByDegree = (degree) => {
- // tan(theta) = height / base
- const radians = (degree * Math.PI) / 180
- return Number(Number(Math.tan(radians) * 10).toFixed(2))
+ return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(2))
}
/**
@@ -521,9 +521,11 @@ export function isPointOnLine(line, point) {
const a = line.y2 - line.y1
const b = line.x1 - line.x2
const c = line.x2 * line.y1 - line.x1 * line.y2
- return a * point.x + b * point.y + c === 0
-}
+ const result = Math.abs(a * point.x + b * point.y + c) / 100
+ // 점이 선 위에 있는지 확인
+ return result <= 10
+}
/**
* 점과 가까운 line 찾기
* @param point
diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js
index 01140300..0e0ac586 100644
--- a/src/util/qpolygon-utils.js
+++ b/src/util/qpolygon-utils.js
@@ -1,9 +1,17 @@
import { fabric } from 'fabric'
import { QLine } from '@/components/fabric/QLine'
-import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util'
+import {
+ calculateIntersection,
+ distanceBetweenPoints,
+ findClosestPoint,
+ getDegreeByChon,
+ getDirectionByPoint,
+ isPointOnLine,
+} from '@/util/canvas-util'
+
import { QPolygon } from '@/components/fabric/QPolygon'
import * as turf from '@turf/turf'
-import { POLYGON_TYPE } from '@/common/common'
+import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
const TWO_PI = Math.PI * 2
@@ -13,7 +21,7 @@ export const defineQPloygon = () => {
}
}
-export const drawHelpLineInHexagon = (polygon, chon) => {
+export const drawHelpLineInHexagon = (polygon, pitch) => {
const centerLines = drawCenterLines(polygon)
let helpLines = []
@@ -957,10 +965,13 @@ export default function offsetPolygon(vertices, offset) {
}
}
-export const splitPolygonWithLines = (polygon) => {
+/*export const splitPolygonWithLines = (polygon) => {
const roofs = []
const allLines = [...polygon.innerLines]
+ const polygonLines = polygon.lines
+ const innerLines = polygon.innerLines
+
allLines.forEach((line) => {
line.startPoint = { x: line.x1, y: line.y1 }
line.endPoint = { x: line.x2, y: line.y2 }
@@ -983,10 +994,10 @@ export const splitPolygonWithLines = (polygon) => {
})
})
- /**
+ /!**
* 좌표 테스트용
- */
- /*allLines.forEach((line) => {
+ *!/
+ /!*allLines.forEach((line) => {
const text = new fabric.Text(`(${line.startPoint.x},${line.startPoint.y})`, {
left: line.startPoint.x,
top: line.startPoint.y,
@@ -1015,10 +1026,10 @@ export const splitPolygonWithLines = (polygon) => {
polygon.canvas.add(text)
polygon.canvas.renderAll()
- })*/
- /**
+ })*!/
+ /!**
* 좌표 테스트용 끝
- */
+ *!/
polygon.points.forEach((point, index) => {
allLines.forEach((line) => {
@@ -1164,6 +1175,273 @@ export const splitPolygonWithLines = (polygon) => {
polygon.canvas.add(roof)
polygon.canvas.renderAll()
})
+}*/
+export const splitPolygonWithLines = (polygon) => {
+ const canvas = polygon.canvas
+ polygon.set({ visible: false })
+ let innerLines = [...polygon.innerLines]
+ let polygonLines = [...polygon.lines]
+ const roofs = []
+
+ let delIndexs = []
+ let newLines = []
+
+ polygonLines.forEach((line, index) => {
+ line.tempIndex = index
+ innerLines.forEach((innerLine) => {
+ let newLine1, newLine2
+ if (isPointOnLine(line, innerLine.startPoint)) {
+ // 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
+ newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
+ fontSize: polygon.fontSize,
+ stroke: 'black',
+ strokeWidth: 3,
+ })
+
+ newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
+ fontSize: polygon.fontSize,
+ stroke: 'black',
+ strokeWidth: 3,
+ })
+ delIndexs.push(polygonLines.indexOf(line))
+ canvas.remove(polygonLines[polygonLines.indexOf(line)])
+ if (newLine1.length / 10 > 10) {
+ newLines.push(newLine1)
+ }
+ if (newLine2.length / 10 > 10) {
+ newLines.push(newLine2)
+ }
+ }
+ if (isPointOnLine(line, innerLine.endPoint)) {
+ newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
+ fontSize: polygon.fontSize,
+ stroke: 'black',
+ strokeWidth: 3,
+ })
+
+ newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
+ fontSize: polygon.fontSize,
+ stroke: 'black',
+ strokeWidth: 3,
+ })
+ delIndexs.push(polygonLines.indexOf(line))
+ canvas.remove(polygonLines[polygonLines.indexOf(line)])
+ if (newLine1.length / 10 > 10) {
+ newLines.push(newLine1)
+ }
+ if (newLine2.length / 10 > 10) {
+ newLines.push(newLine2)
+ }
+ }
+ })
+ })
+ polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
+ polygonLines = [...polygonLines, ...newLines]
+
+ const allLines = [...polygonLines, ...innerLines]
+
+ /**
+ * 왼쪽 상단을 startPoint로 전부 변경
+ */
+ allLines.forEach((line) => {
+ let startPoint // 시작점
+ let endPoint // 끝점
+ if (line.x1 < line.x2) {
+ startPoint = { x: line.x1, y: line.y1 }
+ endPoint = { x: line.x2, y: line.y2 }
+ } else if (line.x1 > line.x2) {
+ startPoint = { x: line.x2, y: line.y2 }
+ endPoint = { x: line.x1, y: line.y1 }
+ } else {
+ if (line.y1 < line.y2) {
+ startPoint = { x: line.x1, y: line.y1 }
+ endPoint = { x: line.x2, y: line.y2 }
+ } else {
+ startPoint = { x: line.x2, y: line.y2 }
+ endPoint = { x: line.x1, y: line.y1 }
+ }
+ }
+
+ line.startPoint = startPoint
+ line.endPoint = endPoint
+ })
+
+ polygonLines.forEach((line) => {
+ const startPoint = line.startPoint // 시작점
+ let arrivalPoint = line.endPoint // 도착점
+
+ let currentPoint = startPoint
+ const roofPoints = [startPoint]
+
+ const startLine = line
+ const visitPoints = [startPoint]
+ const visitLines = [startLine]
+ let cnt = 0
+
+ while (!isSamePoint(currentPoint, arrivalPoint)) {
+ line.set({ stroke: 'red' })
+ canvas.renderAll()
+ 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) {
+ break
+ }
+
+ let comparisonPoints = []
+
+ nextLines.forEach((nextLine) => {
+ if (isSamePoint(nextLine.startPoint, currentPoint)) {
+ comparisonPoints.push(nextLine.endPoint)
+ } else {
+ comparisonPoints.push(nextLine.startPoint)
+ }
+ })
+
+ 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)
+ }
+ })
+
+ currentPoint = { ...minDistancePoint }
+ roofPoints.push(currentPoint)
+ cnt++
+ if (cnt > 100) {
+ throw new Error('무한루프')
+ }
+ }
+ roofs.push(roofPoints)
+ })
+
+ const newRoofs = removeDuplicatePolygons(roofs)
+ newRoofs.forEach((roofPoint, index) => {
+ let defense, pitch
+ const polygonLines = [...polygon.lines]
+
+ let representLines = []
+ let representLine
+
+ // 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
+ polygonLines.forEach((line) => {
+ let startFlag = false
+ let endFlag = false
+ const startPoint = line.startPoint
+ const endPoint = line.endPoint
+ roofPoint.forEach((point, index) => {
+ if (isSamePoint(point, startPoint)) {
+ startFlag = true
+ }
+ if (isSamePoint(point, endPoint)) {
+ endFlag = true
+ }
+ })
+
+ if (startFlag && endFlag) {
+ representLines.push(line)
+ }
+ })
+
+ // representLines중 가장 긴 line을 찾는다.
+ representLines.forEach((line) => {
+ if (!representLine) {
+ representLine = line
+ } else {
+ if (representLine.length < line.length) {
+ representLine = line
+ }
+ }
+ })
+
+ const direction = representLine.direction
+
+ switch (direction) {
+ case 'top':
+ defense = 'east'
+ break
+ case 'right':
+ defense = 'south'
+ break
+ case 'bottom':
+ defense = 'west'
+ break
+ case 'left':
+ defense = 'north'
+ break
+ }
+ pitch = polygon.lines[index].attributes?.pitch ?? 0
+
+ const roof = new QPolygon(roofPoint, {
+ fontSize: polygon.fontSize,
+ stroke: 'black',
+ fill: 'transparent',
+ strokeWidth: 3,
+ name: POLYGON_TYPE.ROOF,
+ originX: 'center',
+ originY: 'center',
+ selectable: true,
+ defense: defense,
+ direction: defense,
+ pitch: pitch,
+ })
+
+ polygon.canvas.add(roof)
+ canvas.remove(polygon)
+ polygon.canvas.renderAll()
+ })
+}
+
+function normalizePoint(point) {
+ return {
+ x: Math.round(point.x),
+ y: Math.round(point.y),
+ }
+}
+
+function arePolygonsEqual(polygon1, polygon2) {
+ if (polygon1.length !== polygon2.length) return false
+
+ const normalizedPolygon1 = polygon1.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
+ const normalizedPolygon2 = polygon2.map(normalizePoint).sort((a, b) => a.x - b.x || a.y - b.y)
+
+ return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
+}
+
+function removeDuplicatePolygons(polygons) {
+ const uniquePolygons = []
+
+ polygons.forEach((polygon) => {
+ const isDuplicate = uniquePolygons.some((uniquePolygon) => arePolygonsEqual(polygon, uniquePolygon))
+ if (!isDuplicate) {
+ uniquePolygons.push(polygon)
+ }
+ })
+
+ return uniquePolygons
}
const isSamePoint = (a, b) => {
@@ -1198,35 +1476,37 @@ function calculateAngleBetweenLines(line1, line2) {
return (angleInRadians * 180) / Math.PI
}
-export const drawHippedRoof = (polygon, chon) => {
- const hasNonParallelLines = polygon.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
+export const drawRidgeRoof = (roofId, canvas) => {
+ const roof = canvas?.getObjects().find((object) => object.id === roofId)
+ const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
if (hasNonParallelLines.length > 0) {
alert('대각선이 존재합니다.')
return
}
-
- drawRidgeRoof(polygon, chon)
- drawHips(polygon)
- connectLinePoint(polygon)
+ drawRidge(roof, canvas)
+ drawHips(roof, canvas)
+ connectLinePoint(roof, canvas)
+ modifyRidge(roof, canvas)
}
/**
+ * 마루가 존재하면 그린다. 마루는 지붕의 중간에 위치한다.
*
- * @param polygon
- * @param chon
+ * @param roof
+ * @param canvas
*/
-const drawRidgeRoof = (polygon, chon) => {
- const walls = polygon.wall.lines // 외벽의 라인
- const roofs = polygon.lines // 지붕의 라인
+const drawRidge = (roof, canvas) => {
+ const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines // 외벽의 라인
+ const roofLines = roof.lines // 지붕의 라인
let ridgeRoof = []
- roofs.forEach((currentRoof, index) => {
+ roofLines.forEach((currentRoof, index) => {
let prevRoof,
nextRoof,
- currentWall = walls[index]
+ currentWall = wallLines[index]
- prevRoof = index === 0 ? walls[walls.length - 1] : walls[index - 1]
- nextRoof = index === walls.length - 1 ? walls[0] : index === walls.length ? walls[1] : walls[index + 1]
+ 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]
if (prevRoof.direction !== nextRoof.direction && currentWall.length <= currentRoof.length) {
ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.length })
@@ -1237,7 +1517,7 @@ const drawRidgeRoof = (polygon, chon) => {
ridgeRoof.sort((a, b) => a.length - b.length)
ridgeRoof.forEach((item) => {
- if (getMaxRidge(roofs.length) > polygon.ridges.length) {
+ if (getMaxRidge(roofLines.length) > roof.ridges.length) {
let index = item.index,
beforePrevRoof,
prevRoof,
@@ -1246,13 +1526,13 @@ const drawRidgeRoof = (polygon, chon) => {
afterNextRoof
let startXPoint, startYPoint, endXPoint, endYPoint
- prevRoof = index === 0 ? roofs[walls.length - 1] : roofs[index - 1]
- nextRoof = index === roofs.length - 1 ? roofs[0] : index === roofs.length ? roofs[1] : roofs[index + 1]
+ prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1]
+ nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1]
- beforePrevRoof = index <= 1 ? roofs[roofs.length - 2 + index] : roofs[index - 2]
- afterNextRoof = index >= roofs.length - 2 ? roofs[(index + 2) % roofs.length] : roofs[index + 2]
+ beforePrevRoof = index <= 1 ? roofLines[roofLines.length - 2 + index] : roofLines[index - 2]
+ afterNextRoof = index >= roofLines.length - 2 ? roofLines[(index + 2) % roofLines.length] : roofLines[index + 2]
- const anotherRoof = roofs.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof)
+ const anotherRoof = roofLines.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof)
let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선
@@ -1277,7 +1557,7 @@ const drawRidgeRoof = (polygon, chon) => {
.reduce((prev, current) => {
let hasBetweenRoof = false
if (current.x1 === current.x2) {
- hasBetweenRoof = roofs
+ hasBetweenRoof = roofLines
.filter((roof) => roof !== current && roof !== currentRoof)
.some((line) => {
let currentY2 = currentRoof.y2
@@ -1294,7 +1574,7 @@ const drawRidgeRoof = (polygon, chon) => {
})
}
if (current.y1 === current.y2) {
- hasBetweenRoof = walls
+ hasBetweenRoof = wallLines
.filter((roof) => roof !== current && roof !== currentRoof)
.some((line) => {
let currentX2 = currentRoof.x2
@@ -1471,37 +1751,38 @@ const drawRidgeRoof = (polygon, chon) => {
}
}
}
- const currentWall = walls[index]
- if (currentWall.attributes.type === 'gable') {
- if (currentRoof.x1 === currentRoof.x2) {
- startXPoint = currentRoof.x1
- }
- if (currentRoof.y1 === currentRoof.y2) {
- startYPoint = currentRoof.y1
- }
- }
// 마루 그리기
if (startXPoint !== undefined && startYPoint !== undefined && endXPoint !== undefined && endYPoint !== undefined) {
const ridge = new QLine(
[Math.min(startXPoint, endXPoint), Math.min(startYPoint, endYPoint), Math.max(startXPoint, endXPoint), Math.max(startYPoint, endYPoint)],
{
- fontSize: polygon.fontSize,
+ fontSize: roof.fontSize,
stroke: 'blue',
strokeWidth: 1,
- name: 'ridgeLine',
+ name: LINE_TYPE.SUBLINE.RIDGE,
+ attributes: { roofId: roof.id },
},
)
- polygon.canvas.add(ridge)
- polygon.ridges.push(ridge)
- polygon.innerLines.push(ridge)
+ canvas.add(ridge)
+ roof.ridges.push(ridge)
+ roof.innerLines.push(ridge)
+
+ const distance = (x1, y1, x2, y2) => Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
+ const dist1 = distance(startXPoint, startYPoint, currentRoof.x1, currentRoof.y1)
+ const dist2 = distance(endXPoint, endYPoint, currentRoof.x1, currentRoof.y1)
+
+ currentRoof.attributes.ridgeCoordinate = {
+ x1: dist1 < dist2 ? startXPoint : endXPoint,
+ y1: dist1 < dist2 ? startYPoint : endYPoint,
+ }
}
}
})
//겹쳐지는 마루는 하나로 합침
- polygon.ridges.forEach((ridge, index) => {
- polygon.ridges
+ roof.ridges.forEach((ridge, index) => {
+ roof.ridges
.filter((ridge2) => !(ridge.x1 === ridge2.x1 && ridge.y1 === ridge2.y1 && ridge.x2 === ridge2.x2 && ridge.y2 === ridge2.y2))
.forEach((ridge2) => {
let overlap = segmentsOverlap(ridge, ridge2)
@@ -1511,25 +1792,25 @@ const drawRidgeRoof = (polygon, chon) => {
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,
+ fontSize: roof.fontSize,
stroke: 'blue',
strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.RIDGE,
+ attributes: { roofId: roof.id },
})
- 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)
+ 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)
}
})
})
+ canvas?.renderAll()
}
/**
@@ -1573,720 +1854,185 @@ const segmentsOverlap = (line1, line2) => {
return false
}
-const drawHips = (polygon) => {
- /*
- 마루에서 시작되는 hip을 먼저 그립니다.
- */
- polygon.ridges.forEach((ridge) => {
- let leftTop, rightTop, leftBottom, rightBottom
- if (ridge.y1 === ridge.y2) {
- //왼쪽 좌표 기준 225, 315도 방향 라인확인
- leftTop = polygon.lines
- .filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(ridge.x1 - current.x1) < Math.min(ridge.x1 - prev.x1) ? current : prev
- }
- }, undefined)
+/**
+ * 추녀마루를 그린다.
+ * @param roof
+ * @param canvas
+ */
+const drawHips = (roof, canvas) => {
+ const roofLines = roof.lines
+ const ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roof.id)
- leftBottom = polygon.lines
- .filter((line) => line.x1 < ridge.x1 && line.y1 > ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(ridge.x1 - current.x1) < Math.min(ridge.x1 - prev.x1) ? current : prev
- }
- }, undefined)
+ //마루에서 시작되는 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
- //오른쪽 좌표 기준 45, 135도 방향 라인확인
- rightTop = polygon.lines
- .filter((line) => line.x1 > ridge.x2 && line.y1 < ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(current.x1 - ridge.x2) < Math.min(prev.x1 - ridge.x2) ? current : prev
- }
- }, undefined)
-
- rightBottom = polygon.lines
- .filter((line) => line.x1 > ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(current.x1 - ridge.x2) < Math.min(prev.x1 - ridge.x2) ? current : prev
- }
- }, undefined)
- if (leftTop !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 < ridge.x1 &&
- r.y1 < ridge.y1 &&
- (r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 < ridge.x1 &&
- r.y2 < ridge.y1 &&
- (r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([leftTop.x1, leftTop.y1, ridge.x1, ridge.y1], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (leftBottom !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 < ridge.x1 &&
- r.y1 > ridge.y1 &&
- (r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 < ridge.x1 &&
- r.y2 > ridge.y1 &&
- (r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([leftBottom.x1, leftBottom.y1, ridge.x1, ridge.y1], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (rightTop !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 > ridge.x2 &&
- r.y1 < ridge.y2 &&
- (r.y1 - rightTop.y1) * (ridge.x2 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y2 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 > ridge.x2 &&
- r.y2 < ridge.y2 &&
- (r.y2 - rightTop.y1) * (ridge.x2 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y2 - rightTop.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([rightTop.x1, rightTop.y1, ridge.x2, ridge.y2], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (rightBottom !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 > ridge.x2 &&
- r.y1 > ridge.y2 &&
- (r.y1 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x1 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 > ridge.x2 &&
- r.y2 > ridge.y2 &&
- (r.y2 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x2 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([rightBottom.x1, rightBottom.y1, ridge.x2, ridge.y2], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- }
- if (ridge.x1 === ridge.x2) {
- //위쪽 좌표 기준 45, 315도 방향 라인확인
- leftTop = polygon.lines
- .filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(ridge.y1 - current.y1) < Math.min(ridge.y1 - prev.y1) ? current : prev
- }
- }, undefined)
-
- rightTop = polygon.lines
- .filter((line) => line.x1 > ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(ridge.y1 - current.y1) < Math.min(ridge.y1 - prev.y1) ? current : prev
- }
- }, undefined)
-
- //아래쪽 좌표 기준 135, 225도 방향 라인확인
- leftBottom = polygon.lines
- .filter((line) => line.x1 < ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(current.y1 - ridge.y2) < Math.min(prev.y1 - ridge.y2) ? current : prev
- }
- }, undefined)
-
- rightBottom = polygon.lines
- .filter((line) => line.x1 > ridge.x2 && line.y1 > ridge.y2 && Math.abs(line.x1 - ridge.x2) === Math.abs(line.y1 - ridge.y2))
- .reduce((prev, current) => {
- if (prev === undefined) {
- return current
- } else {
- return Math.min(current.y1 - ridge.y2) < Math.min(prev.y1 - ridge.y2) ? current : prev
- }
- }, undefined)
-
- if (leftTop !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (r.x1 < ridge.x1 && r.y1 < ridge.y1) {
- if ((r.y1 - leftTop.y1) * (ridge.x1 - leftTop.x1) === (r.x1 - leftTop.x1) * (ridge.y1 - leftTop.y1)) {
- isRidgePointOnLine = true
- }
- }
- if (r.x2 < ridge.x1 && r.y2 < ridge.y1) {
- if ((r.y2 - leftTop.y1) * (ridge.x1 - leftTop.x1) === (r.x2 - leftTop.x1) * (ridge.y1 - leftTop.y1)) {
- isRidgePointOnLine = true
- }
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([leftTop.x1, leftTop.y1, ridge.x1, ridge.y1], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (rightTop !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (r.x1 > ridge.x1 && r.y1 < ridge.y1) {
- if ((r.y1 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x1 - rightTop.x1) * (ridge.y1 - rightTop.y1)) {
- isRidgePointOnLine = true
- }
- }
- if (r.x2 > ridge.x1 && r.y2 < ridge.y1) {
- if ((r.y2 - rightTop.y1) * (ridge.x1 - rightTop.x1) === (r.x2 - rightTop.x1) * (ridge.y1 - rightTop.y1)) {
- isRidgePointOnLine = true
- }
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([rightTop.x1, rightTop.y1, ridge.x1, ridge.y1], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (leftBottom !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 < ridge.x2 &&
- r.y1 > ridge.y2 &&
- (r.y1 - leftBottom.y1) * (ridge.x2 - leftBottom.x1) === (r.x1 - leftBottom.x1) * (ridge.y2 - leftBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 < ridge.x2 &&
- r.y2 > ridge.y2 &&
- (r.y2 - leftBottom.y1) * (ridge.x2 - leftBottom.x1) === (r.x2 - leftBottom.x1) * (ridge.y2 - leftBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([leftBottom.x1, leftBottom.y1, ridge.x2, ridge.y2], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- if (rightBottom !== undefined) {
- let isRidgePointOnLine = false
- polygon.ridges.forEach((r) => {
- if (
- r.x1 < ridge.x2 &&
- r.y1 > ridge.y2 &&
- (r.y1 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x1 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- if (
- r.x2 < ridge.x2 &&
- r.y2 > ridge.y2 &&
- (r.y2 - rightBottom.y1) * (ridge.x2 - rightBottom.x1) === (r.x2 - rightBottom.x1) * (ridge.y2 - rightBottom.y1)
- ) {
- isRidgePointOnLine = true
- }
- })
- if (!isRidgePointOnLine) {
- const hip = new QLine([rightBottom.x1, rightBottom.y1, ridge.x2, ridge.y2], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- }
- })
-
- // 가장 가까운 마루를 확인하여 그릴 수 있는 라인이 존재하면 먼저 그린다.
- let prevLine, currentLine, nextLine
- polygon.lines.forEach((value, index) => {
- if (index === 0) {
- prevLine = polygon.lines[polygon.lines.length - 1]
- } else {
- prevLine = polygon.lines[index - 1]
- }
- currentLine = polygon.lines[index]
-
- if (index === polygon.lines.length - 1) {
- nextLine = polygon.lines[0]
- } else if (index === polygon.lines.length) {
- nextLine = polygon.lines[1]
- } else {
- nextLine = polygon.lines[index + 1]
- }
-
- if (!isAlreadyHip(polygon, currentLine)) {
- let dVector = getDirectionForDegree(prevLine, currentLine)
- let nearRidge
-
- switch (dVector) {
- case 45:
- nearRidge = polygon.ridges
- .filter(
- (ridge) =>
- ((currentLine.x1 < ridge.x1 && currentLine.y1 > ridge.y1) || (currentLine.x1 < ridge.x2 && currentLine.y1 > ridge.y2)) &&
- (Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
- Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
- )
- .reduce((prev, current) => {
- if (prev !== undefined) {
- if (
- currentLine.x1 < current.x1 &&
- currentLine.y1 > current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x1,
- y: current.y1,
- }
- : prev
- } else if (
- currentLine.x1 < current.x2 &&
- currentLine.y1 > current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return Math.min(Math.abs(current.x2 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x2,
- y: current.y2,
- }
- : prev
- } else {
- return prev
- }
- } else {
- if (
- currentLine.x1 < current.x1 &&
- currentLine.y1 > current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return { x: current.x1, y: current.y1 }
- } else if (
- currentLine.x1 < current.x2 &&
- currentLine.y1 > current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return { x: current.x2, y: current.y2 }
- } else {
- return undefined
- }
- }
- }, undefined)
- break
- case 135:
- nearRidge = polygon.ridges
- .filter(
- (ridge) =>
- ((currentLine.x1 < ridge.x1 && currentLine.y1 < ridge.y1) || (currentLine.x1 < ridge.x2 && currentLine.y1 < ridge.y2)) &&
- (Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
- Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
- )
- .reduce((prev, current) => {
- if (prev !== undefined) {
- if (
- currentLine.x1 < current.x1 &&
- currentLine.y1 < current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x1,
- y: current.y1,
- }
- : prev
- } else if (
- currentLine.x1 < current.x2 &&
- currentLine.y1 < current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x2,
- y: current.y2,
- }
- : prev
- } else {
- return prev
- }
- } else {
- if (
- currentLine.x1 < current.x1 &&
- currentLine.y1 < current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return { x: current.x1, y: current.y1 }
- } else if (
- currentLine.x1 < current.x2 &&
- currentLine.y1 < current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return { x: current.x2, y: current.y2 }
- } else {
- return undefined
- }
- }
- }, undefined)
- break
- case 225:
- nearRidge = polygon.ridges
- .filter(
- (ridge) =>
- ((currentLine.x1 > ridge.x1 && currentLine.y1 < ridge.y1) || (currentLine.x1 > ridge.x2 && currentLine.y1 < ridge.y2)) &&
- (Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
- Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
- )
- .reduce((prev, current) => {
- if (prev !== undefined) {
- if (
- currentLine.x1 > current.x1 &&
- currentLine.y1 < current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x1,
- y: current.y1,
- }
- : prev
- } else if (
- currentLine.x1 > current.x2 &&
- currentLine.y1 < current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x2,
- y: current.y2,
- }
- : prev
- } else {
- return prev
- }
- } else {
- if (
- currentLine.x1 > current.x1 &&
- currentLine.y1 < current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return { x: current.x1, y: current.y1 }
- } else if (
- currentLine.x1 > current.x2 &&
- currentLine.y1 < current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return { x: current.x2, y: current.y2 }
- } else {
- return undefined
- }
- }
- }, undefined)
- break
- case 315:
- nearRidge = polygon.ridges
- .filter(
- (ridge) =>
- ((currentLine.x1 > ridge.x1 && currentLine.y1 > ridge.y1) || (currentLine.x1 > ridge.x2 && currentLine.y1 > ridge.y2)) &&
- (Math.abs(currentLine.x1 - ridge.x1) === Math.abs(currentLine.y1 - ridge.y1) ||
- Math.abs(currentLine.x1 - ridge.x2) === Math.abs(currentLine.y1 - ridge.y2)),
- )
- .reduce((prev, current) => {
- if (prev !== undefined) {
- if (
- currentLine.x1 > current.x1 &&
- currentLine.y1 > current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x1,
- y: current.y1,
- }
- : prev
- } else if (
- currentLine.x1 > current.x2 &&
- currentLine.y1 > current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return Math.min(Math.abs(current.x1 - currentLine.x1)) < Math.min(Math.abs(prev.x - currentLine.x1))
- ? {
- x: current.x2,
- y: current.y2,
- }
- : prev
- } else {
- return prev
- }
- } else {
- if (
- currentLine.x1 > current.x1 &&
- currentLine.y1 > current.y1 &&
- Math.abs(currentLine.x1 - current.x1) === Math.abs(currentLine.y1 - current.y1)
- ) {
- return { x: current.x1, y: current.y1 }
- } else if (
- currentLine.x1 > current.x2 &&
- currentLine.y1 > current.y2 &&
- Math.abs(currentLine.x1 - current.x2) === Math.abs(currentLine.y1 - current.y2)
- ) {
- return { x: current.x2, y: current.y2 }
- } else {
- return undefined
- }
- }
- }, undefined)
- break
- }
-
- if (nearRidge !== undefined) {
- let endXPoint, endYPoint
- let minX, maxX, minY, maxY
-
- switch (dVector) {
- case 45:
- endXPoint = nearRidge.x
- endYPoint = nearRidge.y
- minX = Math.min(currentLine.x1, nearRidge.x)
- minY = Math.min(currentLine.y1, nearRidge.y)
- maxX = Math.max(currentLine.x1, nearRidge.x)
- maxY = Math.max(currentLine.y1, nearRidge.y)
- break
- case 135:
- endXPoint = nearRidge.x
- endYPoint = nearRidge.y
- minX = Math.min(currentLine.x1, nearRidge.x)
- minY = Math.min(currentLine.y1, nearRidge.y)
- maxX = Math.max(currentLine.x1, nearRidge.x)
- maxY = Math.max(currentLine.y1, nearRidge.y)
- break
- case 225:
- endXPoint = nearRidge.x
- endYPoint = nearRidge.y
- minX = Math.min(currentLine.x1, nearRidge.x)
- minY = Math.min(currentLine.y1, nearRidge.y)
- maxX = Math.max(currentLine.x1, nearRidge.x)
- maxY = Math.max(currentLine.y1, nearRidge.y)
- break
- case 315:
- endXPoint = nearRidge.x
- endYPoint = nearRidge.y
- minX = Math.min(currentLine.x1, nearRidge.x)
- minY = Math.min(currentLine.y1, nearRidge.y)
- maxX = Math.max(currentLine.x1, nearRidge.x)
- maxY = Math.max(currentLine.y1, nearRidge.y)
- break
- }
-
- let lineCoordinate = [
- { x: minX, y: minY },
- { x: minX, y: maxY },
- { x: maxX, y: maxY },
- { x: maxX, y: minY },
- ]
-
- let innerPoint = polygon.lines.filter((line) => {
- if (getPointInPolygon(lineCoordinate, { x: line.x1, y: line.y1 })) {
- return line
- }
- })
-
- if (innerPoint <= 0) {
- const hip = new QLine([currentLine.x1, currentLine.y1, endXPoint, endYPoint], {
- fontSize: polygon.fontSize,
- stroke: 'red',
- strokeWidth: 1,
- name: 'hipLine',
- })
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
- }
- }
- }
- })
-
- // 마루와 연결되지 않은 hip을 그린다.
- /*polygon.lines.forEach((line, index) => {
- if (!isAlreadyHip(polygon, line)) {
- console.log(' 확인 : ', line)
- let prevLine, currentLine, nextLine
- if (index === 0) {
- prevLine = polygon.lines[polygon.lines.length - 1]
- } else {
- prevLine = polygon.lines[index - 1]
- }
- currentLine = polygon.lines[index]
-
- if (index === polygon.lines.length - 1) {
- nextLine = polygon.lines[0]
- } else if (index === polygon.lines.length) {
- nextLine = polygon.lines[1]
- } else {
- nextLine = polygon.lines[index + 1]
- }
-
- let endXPoint, endYPoint
- let dVector = getDirectionForDegree(prevLine, currentLine)
-
- 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)
-
- let lineCoordinate = [
- { x: minX, y: minY },
- { x: minX, y: maxY },
- { x: maxX, y: maxY },
- { x: maxX, y: minY },
- ]
-
- let acrossLine = getAcrossLine(polygon, currentLine, dVector)
- let hypotenuse, adjacent
- console.log(acrossLine)
-
- if (getLineDirection(prevLine) === getLineDirection(nextLine)) {
- hypotenuse = Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2))
- } else {
- hypotenuse = Math.min(
- Math.round(getRoofHypotenuse(currentLine.length / 2)),
- Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2)),
- )
- }
- adjacent = getAdjacent(hypotenuse)
-
- switch (dVector) {
- case 45:
- endXPoint = currentLine.x1 + adjacent
- endYPoint = currentLine.y1 - adjacent
- break
- case 135:
- endXPoint = currentLine.x1 + adjacent
- endYPoint = currentLine.y1 + adjacent
- break
- case 225:
- endXPoint = currentLine.x1 - adjacent
- endYPoint = currentLine.y1 + adjacent
- break
- case 315:
- endXPoint = currentLine.x1 - adjacent
- endYPoint = currentLine.y1 - adjacent
- break
- }
-
- const hip = new QLine([currentLine.x1, currentLine.y1, endXPoint, endYPoint], {
- fontSize: polygon.fontSize,
+ const ridgeCoordinate = currentRoof.attributes.ridgeCoordinate
+ const hip1 = new QLine([currentRoof.x1, currentRoof.y1, ridgeCoordinate.x1, ridgeCoordinate.y1], {
+ fontSize: roof.fontSize,
stroke: 'red',
strokeWidth: 1,
- name: 'hipLine',
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: { roofId: roof.id, currentRoof: currentRoof.id },
})
- polygon.canvas.add(hip)
- polygon.hips.push(hip)
- polygon.innerLines.push(hip)
+ canvas.add(hip1)
+ const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
+ const hip1Height = Math.round(hip1Base / Math.tan(((90 - currentDegree) * Math.PI) / 180))
+ hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
+ if (prevDegree === currentDegree) {
+ hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
+ }
+ roof.hips.push(hip1)
+ roof.innerLines.push(hip1)
+
+ const hip2 = new QLine([currentRoof.x2, currentRoof.y2, ridgeCoordinate.x1, ridgeCoordinate.y1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoof: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas.add(hip2)
+ const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
+ const hip2Height = Math.round(hip2Base / Math.tan(((90 - currentDegree) * Math.PI) / 180))
+ hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
+ if (nextDegree === currentDegree) {
+ hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
+ }
+ 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 = ridge.x1 - currentRoof.x1
+ const deltaY1 = ridge.y1 - currentRoof.y1
+ const deltaX2 = ridge.x2 - currentRoof.x1
+ const deltaY2 = ridge.y2 - currentRoof.y1
+
+ if (Math.abs(deltaY1 / deltaX1) === 1) {
+ ridgePoints.push({ x: ridge.x1, y: ridge.y1 })
+ }
+ if (Math.abs(deltaY2 / deltaX2) === 1) {
+ ridgePoints.push({ x: ridge.x2, y: ridge.y2 })
+ }
+ })
+
+ ridgePoints = ridgePoints.reduce((prev, current) => {
+ if (prev !== undefined) {
+ const deltaPrevX = Math.abs(prev.x - currentRoof.x1)
+ const deltaPrevY = Math.abs(prev.y - currentRoof.y1)
+ const deltaCurrentX = Math.abs(current.x - currentRoof.x1)
+ const deltaCurrentY = Math.abs(current.y - currentRoof.y1)
+ if (deltaPrevX < deltaCurrentX && deltaPrevY < 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], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoof: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas.add(hip)
+ const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10
+ const hipHeight = Math.round(hipBase / Math.tan(((90 - currentRoof.attributes.degree) * Math.PI) / 180))
+ hip.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip.x1 - hip.x2, 2) + Math.pow(hip.y1 - hip.y2, 2))) * 10
+ if (prevDegree === currentDegree) {
+ hip.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip.attributes.planeSize, 2) + Math.pow(hipHeight, 2)))
+ }
+ roof.hips.push(hip)
+ roof.innerLines.push(hip)
+ }
+ })
+ canvas?.renderAll()
+}
+
+/**
+ * 라인 사이가 지붕골 인지 확인.
+ * @param polygon
+ * @param line1
+ * @param line2
+ * @returns {boolean}
+ */
+const checkValley = (polygon, line1, line2) => {
+ let points = [
+ { x: line1.x1, y: line1.y1 },
+ { x: line1.x2, y: line1.y2 },
+ { x: line2.x1, y: line2.y1 },
+ { x: line2.x2, y: line2.y2 },
+ ]
+
+ const uniquePointsMap = new Map()
+ points.forEach((point) => {
+ const key = `${point.x},${point.y}`
+ if (!uniquePointsMap.has(key)) {
+ uniquePointsMap.set(key, point)
}
- })*/
+ })
+ points = Array.from(uniquePointsMap.values())
+
+ const centroidX = points.reduce((acc, point) => acc + point.x, 0) / points.length
+ const centroidY = points.reduce((acc, point) => acc + point.y, 0) / points.length
+
+ let isValley = false
+ const pPoints = polygon.points
+ pPoints.forEach((point, index) => {
+ let j = (index + 1) % pPoints.length
+ let xi = pPoints[index].x + polygon.left,
+ yi = pPoints[index].y + polygon.top
+ let xj = pPoints[j].x + polygon.left,
+ yj = pPoints[j].y + polygon.top
+
+ let intersect = yi > centroidY !== yj > centroidY && centroidX < ((xj - xi) * (centroidY - yi)) / (yj - yi) + xi
+ if (intersect) isValley = !isValley
+ })
+ return isValley
}
const getPointInPolygon = (polygon, point, isInclude = false) => {
@@ -2313,7 +2059,6 @@ const getPointInPolygon = (polygon, point, isInclude = false) => {
*/
const getAcrossLine = (polygon, currentLine, dVector) => {
let acrossLine
- console.log('dVector : ', dVector)
switch (dVector) {
case 45:
acrossLine = polygon.lines
@@ -2459,8 +2204,9 @@ const connectLinePoint = (polygon) => {
//중복라인제거
missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
- missedLine.forEach((p, index) => {
+ missedLine.forEach((p) => {
const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
+ attributes: { roofId: polygon.id },
fontSize: polygon.fontSize,
stroke: 'purple',
strokeWidth: 1,
@@ -2472,7 +2218,7 @@ const connectLinePoint = (polygon) => {
missedPoints = []
missedLine = []
- polygon.innerLines.forEach((line, index) => {
+ 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),
@@ -2514,8 +2260,9 @@ const connectLinePoint = (polygon) => {
//중복라인제거
missedLine = [...new Set(missedLine.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
- missedLine.forEach((p, index) => {
+ missedLine.forEach((p) => {
const line = new QLine([p.x1, p.y1, p.x2, p.y2], {
+ attributes: { roofId: polygon.id },
fontSize: polygon.fontSize,
stroke: 'purple',
strokeWidth: 1,
@@ -2571,7 +2318,8 @@ const connectLinePoint = (polygon) => {
fontSize: polygon.fontSize,
stroke: 'blue',
strokeWidth: 1,
- name: 'ridgeLine',
+ name: LINE_TYPE.SUBLINE.RIDGE,
+ attributes: { roofId: polygon.id },
})
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)
@@ -2590,8 +2338,97 @@ const connectLinePoint = (polygon) => {
}
})
})
+ polygon.canvas.renderAll()
+}
- console.log('polygon : ', polygon)
+const modifyRidge = (roof, canvas) => {
+ const ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roof.id)
+ const hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roof.id)
+
+ ridgeLines.forEach((ridge) => {
+ let ridgeHip1 = hipLines.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1)
+ let ridgeHip2 = hipLines.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2)
+ if (ridgeHip1.length >= 2) {
+ let currentRoof = roof.lines
+ .filter((roofLine) => roofLine.attributes !== undefined && roofLine.attributes.ridgeCoordinate !== undefined)
+ .find((roofLine) => roofLine.attributes.ridgeCoordinate.x1 === ridge.x1 && roofLine.attributes.ridgeCoordinate.y1 === ridge.y1)
+ if (currentRoof === undefined) {
+ currentRoof = roof.lines.find(
+ (roofLine) =>
+ (roofLine.x1 === ridgeHip1[0].x1 &&
+ roofLine.y1 === ridgeHip1[0].y1 &&
+ roofLine.x2 === ridgeHip1[1].x1 &&
+ roofLine.y2 === ridgeHip1[1].y1) ||
+ (roofLine.x1 === ridgeHip1[1].x1 &&
+ roofLine.y1 === ridgeHip1[1].y1 &&
+ roofLine.x2 === ridgeHip1[0].x1 &&
+ roofLine.y2 === ridgeHip1[0].y1),
+ )
+ if (currentRoof !== undefined) {
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
+ }
+ }
+ if (currentRoof !== undefined) {
+ switch (currentRoof.attributes.type) {
+ case LINE_TYPE.WALLLINE.EAVES:
+ changeEavesRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.GABLE:
+ changeGableRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.HIPANDGABLE:
+ changeHipAndGableRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.JERKINHEAD:
+ changeJerkInHeadRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.WALL:
+ changeWallRoof(currentRoof, canvas)
+ break
+ }
+ }
+ }
+ if (ridgeHip2.length >= 2) {
+ let currentRoof = roof.lines
+ .filter((roofLine) => roofLine.attributes !== undefined && roofLine.attributes.ridgeCoordinate !== undefined)
+ .find((roofLine) => roofLine.attributes.ridgeCoordinate.x1 === ridge.x2 && roofLine.attributes.ridgeCoordinate.y1 === ridge.y2)
+ if (currentRoof === undefined) {
+ currentRoof = roof.lines.find(
+ (roofLine) =>
+ (roofLine.x1 === ridgeHip2[0].x1 &&
+ roofLine.y1 === ridgeHip2[0].y1 &&
+ roofLine.x2 === ridgeHip2[1].x1 &&
+ roofLine.y2 === ridgeHip2[1].y1) ||
+ (roofLine.x1 === ridgeHip2[1].x1 &&
+ roofLine.y1 === ridgeHip2[1].y1 &&
+ roofLine.x2 === ridgeHip2[0].x1 &&
+ roofLine.y2 === ridgeHip2[0].y1),
+ )
+ if (currentRoof !== undefined) {
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
+ }
+ }
+ if (currentRoof !== undefined) {
+ switch (currentRoof.attributes.type) {
+ case LINE_TYPE.WALLLINE.EAVES:
+ changeEavesRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.GABLE:
+ changeGableRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.HIPANDGABLE:
+ changeHipAndGableRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.JERKINHEAD:
+ changeJerkInHeadRoof(currentRoof, canvas)
+ break
+ case LINE_TYPE.WALLLINE.WALL:
+ changeWallRoof(currentRoof, canvas)
+ break
+ }
+ }
+ }
+ })
}
/*
@@ -2601,398 +2438,1159 @@ const getMaxRidge = (length) => {
return (length - 4) / 2 + 1
}
-/*
- 두 라인의 사잇각 계산
+/**
+ * 처마지붕으로 변경
+ * @param currentRoof
+ * @param canvas
*/
-const getDirectionForDegree = (line1, line2) => {
- let degree = getLineDirection(line1) + getLineDirection(line2)
- let vector
-
- switch (degree) {
- case 'rb':
- vector = 45
- break
- case 'br':
- vector = 45
- break
- case 'lb':
- vector = 135
- break
- case 'bl':
- vector = 135
- break
- case 'lt':
- vector = 225
- break
- case 'tl':
- vector = 225
- break
- case 'rt':
- vector = 315
- break
- case 'tr':
- vector = 315
- break
- }
-
- return vector
-}
-
-/*
- 현재 라인의 방향을 계산
- */
-const getLineDirection = (line) => {
- let x1, x2, y1, y2, xp, yp
- x1 = Math.round(line.x1)
- x2 = Math.round(line.x2)
- y1 = Math.round(line.y1)
- y2 = Math.round(line.y2)
-
- xp = x1 - x2
- yp = y1 - y2
-
- if (xp === 0) {
- if (yp < 0) {
- return 'b'
- } else {
- return 't'
+const changeEavesRoof = (currentRoof, canvas) => {
+ if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
+ const roofId = currentRoof.attributes.roofId
+ const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
+ const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
+ let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
+ let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
+ let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
+ if (wallLine.length > 0) {
+ wallLine = wallLine[0]
}
- }
- if (yp === 0) {
- if (xp < 0) {
- return 'r'
- } else {
- return 'l'
- }
- }
-}
-
-export const changeAllHipAndGableRoof = (polygon, offset, canvas) => {
- const roof = polygon.filter((p) => p.name === 'roofBase')[0] // 지붕
- const roofLines = roof.lines // 지붕의 라인
- const ridges = roof.ridges // 마루의 라인
- const hips = roof.hips // 추녀마루의 라인
-
- console.log('roofLines : ', roofLines)
-
- ridges.forEach((ridge) => {
- let ridgeHip1 = hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1)
- let ridgeHip2 = hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2)
- let gableLines = []
- if (ridgeHip1.length > 1) {
- let x1 = ridgeHip1[0].x1,
- y1 = ridgeHip1[0].y1,
- x2 = ridgeHip1[1].x1,
- y2 = ridgeHip1[1].y1
- roofLines.filter((roofLine) => {
- if (
- (roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
- (roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
- ) {
- gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas))
- }
- })
- }
- if (ridgeHip2.length > 1) {
- let x1 = ridgeHip2[0].x1,
- y1 = ridgeHip2[0].y1,
- x2 = ridgeHip2[1].x1,
- y2 = ridgeHip2[1].y1
- roofLines.filter((roofLine) => {
- if (
- (roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
- (roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
- ) {
- gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas))
- }
- })
- }
- gableLines.forEach((gableLine) => {
- roof.innerLines.push(gableLine)
+ let prevRoof, nextRoof
+ roof.lines.forEach((r, index) => {
+ if (r.id === currentRoof.id) {
+ currentRoof = r
+ prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
+ nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
+ }
})
- })
- // splitPolygonWithLines(roof)
+ const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
+ const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
+ const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
+ const midWallY = (wallLine.y1 + wallLine.y2) / 2 // 벽의 Y 중심
+ const alpha = midX - midWallX // 벽과 지붕의 X 거리
+ const beta = midY - midWallY // 벽과 지붕의 Y 거리
+ const hypotenuse = Math.sqrt(Math.pow(alpha, 2) + Math.pow(beta, 2)) // 벽과 지붕의 거리
+ const addHipX2 = Math.sign(midX - midWallX) * (alpha / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 X 너비
+ const addHipY2 = Math.sign(midY - midWallY) * (beta / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 Y 너비
+ let hipX2 = 0
+ let hipY2 = 0
+
+ const innerLines = canvas
+ ?.getObjects()
+ .filter(
+ (object) =>
+ object.attributes !== undefined &&
+ object.attributes.roofId === roofId &&
+ object.attributes.currentRoof === currentRoof.id &&
+ object.x1 !== undefined &&
+ object.x2 !== undefined,
+ )
+ innerLines
+ .filter((line) => line.name !== LINE_TYPE.SUBLINE.RIDGE && line.name !== LINE_TYPE.SUBLINE.HIP && line.name !== LINE_TYPE.SUBLINE.VALLEY)
+ .forEach((line) => {
+ roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
+ canvas?.remove(line)
+ })
+
+ ridgeLines = ridgeLines.filter(
+ (ridge) =>
+ (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
+ (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
+ )
+ hipLines = hipLines.filter(
+ (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
+ )
+
+ if (hipLines === undefined || hipLines.length === 0) {
+ hipLines = innerLines.filter(
+ (line) =>
+ (line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
+ (line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
+ (line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
+ (line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
+ )
+ }
+
+ if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
+ let points = []
+ hipLines.forEach((hip) => {
+ points.push({ x: hip.x1, y: hip.y1 })
+ points.push({ x: hip.x2, y: hip.y2 })
+ })
+
+ const pointSet = new Set()
+ const duplicatePoints = []
+
+ points.forEach((point) => {
+ const pointKey = `${point.x},${point.y}`
+ if (pointSet.has(pointKey)) {
+ duplicatePoints.push(point)
+ } else {
+ pointSet.add(pointKey)
+ }
+ })
+
+ ridgeLines = innerLines
+ .filter((r) => r !== hipLines[0] && r !== hipLines[1])
+ .filter(
+ (r) => (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
+ )
+ if (ridgeLines.length > 0) {
+ currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
+ }
+ }
+
+ if (ridgeLines.length > 0) {
+ const ridge = ridgeLines[0]
+ if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: midX + addHipX2,
+ y1: midY + addHipY2,
+ x2: ridge.x2,
+ y2: ridge.y2,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
+ hipX2 = midX + addHipX2
+ hipY2 = midY + addHipY2
+ }
+ if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: ridge.x1,
+ y1: ridge.y1,
+ x2: midX - addHipX2,
+ y2: midY - addHipY2,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
+ hipX2 = midX - addHipX2
+ hipY2 = midY - addHipY2
+ }
+ }
+
+ hipLines.forEach((hip) => {
+ roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
+ canvas.remove(hip)
+ })
+
+ 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 hip1 = new QLine([currentRoof.x1, currentRoof.y1, hipX2, hipY2], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ },
+ })
+ canvas?.add(hip1)
+ roof.innerLines.push(hip1)
+ const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
+ const hip1Height = Math.round(hip1Base / Math.tan(((90 - currentDegree) * Math.PI) / 180))
+ hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
+ if (prevDegree === currentDegree) {
+ hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
+ }
+
+ const hip2 = new QLine([currentRoof.x2, currentRoof.y2, hipX2, hipY2], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(hip2)
+ roof.innerLines.push(hip2)
+ const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
+ const hip2Height = Math.round(hip2Base / Math.tan(((90 - currentDegree) * Math.PI) / 180))
+ hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
+ if (currentDegree === nextDegree) {
+ hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
+ }
+ }
}
/**
- * 모임지붕 -> 팔작지붕 변경
- * @param roof
- * @param ridge
- * @param hip1
- * @param hip2
- * @param offset
+ * 박공지붕으로 변경
+ * @param currentRoof
* @param canvas
- * @returns {*}
*/
-const setHipAndGableRoof = (roof, ridge, hip1, hip2, offset, canvas) => {
- let x1 = hip1.x1,
- y1 = hip1.y1
- let gableLine, diffOffset
- if (ridge.direction === 'top') {
- if (ridge.y1 > y1 && ridge.y2 > y1) {
- offset = Math.abs(ridge.y1 - y1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1,
- x2: ridge.x2,
- y2: ridge.y2 + offset,
+const changeGableRoof = (currentRoof, canvas) => {
+ if (currentRoof.attributes.type === LINE_TYPE.WALLLINE.GABLE) {
+ const roofId = currentRoof.attributes.roofId
+ const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
+ let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
+ let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
+
+ const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
+ const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
+
+ const innerLines = canvas
+ ?.getObjects()
+ .filter(
+ (object) =>
+ object.attributes !== undefined &&
+ object.attributes.roofId === roofId &&
+ object.attributes.currentRoof === currentRoof.id &&
+ object.x1 !== undefined &&
+ object.x2 !== undefined,
+ )
+
+ let prevRoof, nextRoof
+ roof.lines.forEach((r, index) => {
+ if (r.id === currentRoof.id) {
+ currentRoof = r
+ prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1]
+ nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1]
+ }
+ })
+
+ innerLines
+ .filter((line) => line.name !== LINE_TYPE.SUBLINE.RIDGE && line.name !== LINE_TYPE.SUBLINE.HIP && line.name !== LINE_TYPE.SUBLINE.VALLEY)
+ .forEach((line) => {
+ roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
+ canvas?.remove(line)
})
- gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
- fontSize: roof.fontSize,
- stroke: 'blue',
- strokeWidth: 1,
- name: 'gableLine',
- })
- canvas?.add(gableLine)
+ ridgeLines = ridgeLines.filter(
+ (ridge) =>
+ (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
+ (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
+ )
+ hipLines = hipLines.filter(
+ (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
+ )
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
- y2: hip1.y2 + offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
- y2: hip2.y2 + offset,
- })
+ if (hipLines === undefined || hipLines.length === 0) {
+ hipLines = innerLines.filter(
+ (line) =>
+ (line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
+ (line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
+ (line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
+ (line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
+ )
}
- if (ridge.y1 < y1 && ridge.y2 < y1) {
- offset = Math.abs(ridge.y2 - y1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1 - offset,
- x2: ridge.x2,
- y2: ridge.y2,
+
+ hipLines.forEach((hip) => {
+ roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
+ canvas.remove(hip)
+ })
+
+ if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
+ let points = []
+ hipLines.forEach((hip) => {
+ points.push({ x: hip.x1, y: hip.y1 })
+ points.push({ x: hip.x2, y: hip.y2 })
})
- gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
+
+ const pointSet = new Set()
+ const duplicatePoints = []
+
+ points.forEach((point) => {
+ const pointKey = `${point.x},${point.y}`
+ if (pointSet.has(pointKey)) {
+ duplicatePoints.push(point)
+ } else {
+ pointSet.add(pointKey)
+ }
+ })
+
+ ridgeLines = innerLines
+ .filter((r) => r !== hipLines[0] && r !== hipLines[1])
+ .filter(
+ (r) => (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
+ )
+ if (ridgeLines.length > 0) {
+ currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
+ }
+ }
+
+ if (ridgeLines !== undefined && ridgeLines.length > 0) {
+ const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree
+ const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree
+
+ const ridge = ridgeLines[0]
+ if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: midX,
+ y1: midY,
+ x2: ridge.x2,
+ y2: ridge.y2,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
+ }
+ if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: ridge.x1,
+ y1: ridge.y1,
+ x2: midX,
+ y2: midY,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
+ }
+
+ let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX, midY], {
fontSize: roof.fontSize,
- stroke: 'blue',
+ stroke: 'red',
strokeWidth: 1,
- name: 'gableLine',
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roofId,
+ currentRoofId: currentRoof.id,
+ },
})
- canvas?.add(gableLine)
+ canvas?.add(hip1)
+ const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10
+ const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180))
+ hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10
+ hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2)))
+ roof.innerLines.push(hip1)
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
- y2: hip1.y2 - offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
- y2: hip2.y2 - offset,
+ let hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX, midY], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roofId,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
})
+ canvas?.add(hip2)
+ const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10
+ const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180))
+ hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10
+ hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2)))
+ roof.innerLines.push(hip2)
+ canvas?.renderAll()
}
}
- if (ridge.direction === 'bottom') {
- if (ridge.y1 > y1 && ridge.y2 > y1) {
- offset = Math.abs(ridge.y1 - y1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1 - offset,
- x2: ridge.x2,
- y2: ridge.y2,
- })
- gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
- fontSize: roof.fontSize,
- stroke: 'blue',
- strokeWidth: 1,
- name: 'gableLine',
- })
- canvas?.add(gableLine)
+}
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
- y2: hip1.y2 - offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
- y2: hip2.y2 - offset,
- })
+/**
+ * 팔작지붕으로 변경
+ * @param currentRoof
+ * @param canvas
+ */
+const changeHipAndGableRoof = (currentRoof, canvas) => {
+ if (
+ currentRoof.attributes.type === LINE_TYPE.WALLLINE.HIPANDGABLE &&
+ currentRoof.attributes.width !== undefined &&
+ currentRoof.attributes.width > 0
+ ) {
+ const roofId = currentRoof.attributes.roofId
+ const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
+ const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
+ let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
+ let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
+ let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
+ if (wallLine.length > 0) {
+ wallLine = wallLine[0]
}
- if (ridge.y1 < y1 && ridge.y2 < y1) {
- offset = Math.abs(ridge.y2 - y1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1,
- x2: ridge.x2,
- y2: ridge.y2 + offset,
- })
- gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
+ const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
+ const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
+ const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
+ const midWallY = (wallLine.y1 + wallLine.y2) / 2 // 벽의 Y 중심
+ const alpha = midX - midWallX // 벽과 지붕의 X 거리
+ const beta = midY - midWallY // 벽과 지붕의 Y 거리
+ const hypotenuse = Math.sqrt(Math.pow(alpha, 2) + Math.pow(beta, 2)) // 벽과 지붕의 거리
+ const xWidth = Math.sign(midX - midWallX) * (alpha / hypotenuse) * currentRoof.attributes.width // 지붕의 X 너비
+ const yWidth = Math.sign(midY - midWallY) * (beta / hypotenuse) * currentRoof.attributes.width // 지붕의 Y 너비
+ const hipX2 = Math.sign(midX - midWallX) * (alpha / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 X 너비
+ const hipY2 = Math.sign(midY - midWallY) * (beta / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 Y 너비
+
+ if (Math.sqrt(Math.pow(xWidth, 2) + Math.pow(yWidth, 2)) < Math.sqrt(Math.pow(hipX2, 2) + Math.pow(hipY2, 2))) {
+ const innerLines = canvas
+ ?.getObjects()
+ .filter(
+ (object) =>
+ object.attributes !== undefined &&
+ object.attributes.roofId === roofId &&
+ object.attributes.currentRoof === currentRoof.id &&
+ object.x1 !== undefined &&
+ object.x2 !== undefined,
+ )
+
+ innerLines
+ .filter((line) => line.name !== LINE_TYPE.SUBLINE.RIDGE && line.name !== LINE_TYPE.SUBLINE.HIP && line.name !== LINE_TYPE.SUBLINE.VALLEY)
+ .forEach((line) => {
+ roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
+ canvas?.remove(line)
+ })
+
+ ridgeLines = ridgeLines.filter(
+ (ridge) =>
+ (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
+ (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
+ )
+ hipLines = hipLines.filter(
+ (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
+ )
+
+ if (hipLines === undefined || hipLines.length === 0) {
+ hipLines = innerLines.filter(
+ (line) =>
+ (line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
+ (line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
+ (line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
+ (line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
+ )
+ }
+
+ if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
+ let points = []
+ hipLines.forEach((hip) => {
+ points.push({ x: hip.x1, y: hip.y1 })
+ points.push({ x: hip.x2, y: hip.y2 })
+ })
+
+ const pointSet = new Set()
+ const duplicatePoints = []
+
+ points.forEach((point) => {
+ const pointKey = `${point.x},${point.y}`
+ if (pointSet.has(pointKey)) {
+ duplicatePoints.push(point)
+ } else {
+ pointSet.add(pointKey)
+ }
+ })
+
+ ridgeLines = innerLines
+ .filter((r) => r !== hipLines[0] && r !== hipLines[1])
+ .filter(
+ (r) =>
+ (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
+ )
+ if (ridgeLines.length > 0) {
+ currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
+ }
+ }
+
+ hipLines.forEach((hip) => {
+ roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
+ canvas.remove(hip)
+ })
+ hipLines = []
+
+ if (ridgeLines.length > 0) {
+ const ridge = ridgeLines[0]
+ if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: midX + xWidth,
+ y1: midY + yWidth,
+ x2: ridge.x2,
+ y2: ridge.y2,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
+ }
+ if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: ridge.x1,
+ y1: ridge.y1,
+ x2: midX - xWidth,
+ y2: midY - yWidth,
+ })
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
+ }
+ }
+
+ const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX + hipX2, midY + hipY2], {
fontSize: roof.fontSize,
- stroke: 'blue',
+ stroke: 'red',
strokeWidth: 1,
- name: 'gableLine',
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoof: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
})
- canvas?.add(gableLine)
+ canvas?.add(hip1)
+ roof.innerLines.push(hip1)
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x1 < hip1.x2 ? hip1.x2 - offset : hip1.x2 + offset,
- y2: hip1.y2 + offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x1 < hip2.x2 ? hip2.x2 - offset : hip2.x2 + offset,
- y2: hip2.y2 + offset,
- })
- }
- }
- if (ridge.direction === 'right') {
- if (ridge.x1 > x1 && ridge.x2 > x1) {
- offset = Math.abs(ridge.x1 - x1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1 - offset,
- y1: ridge.y1,
- x2: ridge.x2,
- y2: ridge.y2,
- })
- gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
+ const hip2 = new QLine([currentRoof.x2, currentRoof.y2, midX + hipX2, midY + hipY2], {
fontSize: roof.fontSize,
- stroke: 'blue',
+ stroke: 'red',
strokeWidth: 1,
- name: 'gableLine',
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoof: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
})
- canvas?.add(gableLine)
+ canvas?.add(hip2)
+ roof.innerLines.push(hip2)
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x2 - offset,
- y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
+ hipLines.push(hip1)
+ hipLines.push(hip2)
+
+ hipLines.forEach((hip) => {
+ const singHipX = Math.sign(hip.x1 - midWallX)
+ const singHipY = Math.sign(hip.y1 - midWallY)
+
+ hip.set({
+ x1: hip.x1,
+ y1: hip.y1,
+ x2: hip.x1 - singHipX * currentRoof.attributes.width,
+ y2: hip.y1 - singHipY * currentRoof.attributes.width,
+ })
})
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x2 - offset,
- y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
- })
- }
- if (ridge.x1 < x1 && ridge.x2 < x1) {
- offset = Math.abs(ridge.x2 - x1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1,
- x2: ridge.x2 + offset,
- y2: ridge.y2,
- })
- gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
- fontSize: roof.fontSize,
- stroke: 'blue',
- strokeWidth: 1,
- name: 'gableLine',
- })
- canvas?.add(gableLine)
-
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x2 + offset,
- y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x2 + offset,
- y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
- })
- }
- }
- if (ridge.direction === 'left') {
- if (ridge.x1 > x1 && ridge.x2 > x1) {
- offset = Math.abs(ridge.x1 - x1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1,
- y1: ridge.y1,
- x2: ridge.x2 + offset,
- y2: ridge.y2,
- })
- gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
- fontSize: roof.fontSize,
- stroke: 'blue',
- strokeWidth: 1,
- name: 'gableLine',
- })
- canvas?.add(gableLine)
-
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x2 + offset,
- y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x2 + offset,
- y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
- })
- }
- if (ridge.x1 < x1 && ridge.x2 < x1) {
- offset = Math.abs(ridge.x2 - x1) - offset
- offset = offset < 0 ? 0 : offset
- ridge.set({
- x1: ridge.x1 - offset,
- y1: ridge.y1,
- x2: ridge.x2,
- y2: ridge.y2,
- })
- gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
- fontSize: roof.fontSize,
- stroke: 'blue',
- strokeWidth: 1,
- name: 'gableLine',
- })
- canvas?.add(gableLine)
-
- hip1.set({
- x1: hip1.x1,
- y1: hip1.y1,
- x2: hip1.x2 - offset,
- y2: hip1.y1 < hip1.y2 ? hip1.y2 - offset : hip1.y2 + offset,
- })
-
- hip2.set({
- x1: hip2.x1,
- y1: hip2.y1,
- x2: hip2.x2 - offset,
- y2: hip2.y1 < hip2.y2 ? hip2.y2 - offset : hip2.y2 + offset,
+ hipLines.forEach((hip) => {
+ const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.GABLE,
+ attributes: {
+ roofId: roof.id,
+ currentRoof: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(gableLine)
+ roof.innerLines.push(gableLine)
})
}
}
canvas?.renderAll()
- return gableLine
+}
+
+/**
+ * 반절처 지붕으로 변경
+ * @param currentRoof
+ * @param canvas
+ */
+const changeJerkInHeadRoof = (currentRoof, canvas) => {
+ if (
+ currentRoof.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD &&
+ currentRoof.attributes.width !== undefined &&
+ currentRoof.attributes.width > 0
+ ) {
+ const roofId = currentRoof.attributes.roofId
+ const wall = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
+ const roof = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.ROOF && object.id === roofId)
+ let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
+ let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
+ let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
+ if (wallLine.length > 0) {
+ wallLine = wallLine[0]
+ }
+
+ const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심
+ const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심
+ const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심
+ const midWallY = (wallLine.y1 + wallLine.y2) / 2 // 벽의 Y 중심
+ const alpha = midX - midWallX // 벽과 지붕의 X 거리
+ const beta = midY - midWallY // 벽과 지붕의 Y 거리
+ const hypotenuse = Math.sqrt(Math.pow(alpha, 2) + Math.pow(beta, 2)) // 벽과 지붕의 거리
+ const xWidth = Math.sign(midX - midWallX) * (alpha / hypotenuse) * (currentRoof.attributes.width / 2) // 지붕의 X 너비
+ const yWidth = Math.sign(midY - midWallY) * (beta / hypotenuse) * (currentRoof.attributes.width / 2) // 지붕의 Y 너비
+ const addHipX2 = Math.sign(midX - midWallX) * (alpha / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 X 너비
+ const addHipY2 = Math.sign(midY - midWallY) * (beta / hypotenuse) * (currentRoof.length / 2) // 추녀마루의 Y 너비
+ let hipX2 = 0
+ let hipY2 = 0
+
+ if (Math.sqrt(Math.pow(xWidth, 2) + Math.pow(yWidth, 2)) < Math.sqrt(Math.pow(addHipX2, 2) + Math.pow(addHipY2, 2))) {
+ // reDrawPolygon(roof, canvas)
+
+ const innerLines = canvas
+ ?.getObjects()
+ .filter(
+ (object) =>
+ object.attributes !== undefined &&
+ object.attributes.roofId === roofId &&
+ object.attributes.currentRoof === currentRoof.id &&
+ object.x1 !== undefined &&
+ object.x2 !== undefined,
+ )
+
+ innerLines
+ .filter((line) => line.name !== LINE_TYPE.SUBLINE.RIDGE && line.name !== LINE_TYPE.SUBLINE.HIP && line.name !== LINE_TYPE.SUBLINE.VALLEY)
+ .forEach((line) => {
+ roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
+ canvas?.remove(line)
+ })
+
+ ridgeLines = ridgeLines.filter(
+ (ridge) =>
+ (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
+ (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
+ )
+ hipLines = hipLines.filter(
+ (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
+ )
+
+ if (hipLines === undefined || hipLines.length === 0) {
+ hipLines = innerLines.filter(
+ (line) =>
+ (line.x1 === currentRoof.x1 && line.y1 === currentRoof.y1) ||
+ (line.x2 === currentRoof.x1 && line.y2 === currentRoof.y1) ||
+ (line.x1 === currentRoof.x2 && line.y1 === currentRoof.y2) ||
+ (line.x2 === currentRoof.x2 && line.y2 === currentRoof.y2),
+ )
+ }
+
+ if ((ridgeLines === undefined || ridgeLines.length === 0) && hipLines.length >= 2) {
+ let points = []
+ hipLines.forEach((hip) => {
+ points.push({ x: hip.x1, y: hip.y1 })
+ points.push({ x: hip.x2, y: hip.y2 })
+ })
+
+ const pointSet = new Set()
+ const duplicatePoints = []
+
+ points.forEach((point) => {
+ const pointKey = `${point.x},${point.y}`
+ if (pointSet.has(pointKey)) {
+ duplicatePoints.push(point)
+ } else {
+ pointSet.add(pointKey)
+ }
+ })
+
+ ridgeLines = innerLines
+ .filter((r) => r !== hipLines[0] && r !== hipLines[1])
+ .filter(
+ (r) =>
+ (r.x1 === duplicatePoints[0].x && r.y1 === duplicatePoints[0].y) || (r.x2 === duplicatePoints[0].x && r.y2 === duplicatePoints[0].y),
+ )
+ if (ridgeLines.length > 0) {
+ currentRoof.attributes.ridgeCoordinate = { x1: duplicatePoints[0].x, y1: duplicatePoints[0].y }
+ }
+ }
+
+ hipLines.forEach((hip) => {
+ roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
+ canvas.remove(hip)
+ })
+
+ if (ridgeLines.length > 0) {
+ const ridge = ridgeLines[0]
+ if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: midX + xWidth,
+ y1: midY + yWidth,
+ x2: ridge.x2,
+ y2: ridge.y2,
+ })
+
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x1, y1: ridge.y1 }
+ hipX2 = midX + xWidth
+ hipY2 = midY + yWidth
+ }
+ if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
+ ridge.set({
+ x1: ridge.x1,
+ y1: ridge.y1,
+ x2: midX - xWidth,
+ y2: midY - yWidth,
+ })
+
+ currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 }
+ hipX2 = midX - xWidth
+ hipY2 = midY - yWidth
+ }
+ }
+
+ let hipX1 = (Math.sign(currentRoof.x1 - midX) * currentRoof.attributes.width) / 2
+ let hipY1 = (Math.sign(currentRoof.y1 - midY) * currentRoof.attributes.width) / 2
+
+ const gable1 = new QLine([midX + hipX1, midY + hipY1, hipX2, hipY2], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.GABLE,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(gable1)
+ roof.innerLines.push(gable1)
+
+ hipX1 = (Math.sign(currentRoof.x2 - midX) * currentRoof.attributes.width) / 2
+ hipY1 = (Math.sign(currentRoof.y2 - midY) * currentRoof.attributes.width) / 2
+
+ const gable2 = new QLine([midX + hipX1, midY + hipY1, hipX2, hipY2], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.GABLE,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(gable2)
+ roof.innerLines.push(gable2)
+
+ const gable3 = new QLine([gable1.x1, gable1.y1, gable2.x1, gable2.y1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.GABLE,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(gable3)
+ roof.innerLines.push(gable3)
+
+ const hip1 = new QLine([currentRoof.x1, currentRoof.y1, gable1.x1, gable1.y1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.GABLE,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(hip1)
+ roof.innerLines.push(hip1)
+
+ const hip2 = new QLine([currentRoof.x2, currentRoof.y2, gable2.x1, gable2.y1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(hip2)
+ roof.innerLines.push(hip2)
+ }
+ }
+}
+
+/**
+ * 벽지붕으로 변경
+ * @param currentRoof
+ * @param canvas
+ */
+const changeWallRoof = (currentRoof, canvas) => {
+ const roofId = currentRoof.attributes.roofId
+ const roof = canvas?.getObjects().find((object) => object.name === 'roof' && object.id === roofId)
+ const roofLines = roof.lines
+ let prevRoof, nextRoof
+ roofLines.forEach((r, index) => {
+ if (r.id === currentRoof.id) {
+ currentRoof = r
+ prevRoof = roofLines[index === 0 ? roofLines.length - 1 : index - 1]
+ nextRoof = roofLines[index === roofLines.length - 1 ? 0 : index + 1]
+ }
+ })
+
+ const wall = canvas?.getObjects().find((object) => object.name === 'wall' && object.attributes.roofId === roofId)
+ let wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)
+ let hipLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.HIP && object.attributes.roofId === roofId)
+ let ridgeLines = canvas?.getObjects().filter((object) => object.name === LINE_TYPE.SUBLINE.RIDGE && object.attributes.roofId === roofId)
+
+ if (wallLine.length > 0) {
+ wallLine = wallLine[0]
+ }
+
+ ridgeLines = ridgeLines.filter(
+ (ridge) =>
+ (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) ||
+ (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1),
+ )
+ hipLines = hipLines.filter(
+ (hip) => (hip.x1 === currentRoof.x1 && hip.y1 === currentRoof.y1) || (hip.x1 === currentRoof.x2 && hip.y1 === currentRoof.y2),
+ )
+
+ const wallMidX = (wallLine.x1 + wallLine.x2) / 2
+ const wallMidY = (wallLine.y1 + wallLine.y2) / 2
+ const roofMidX = (currentRoof.x1 + currentRoof.x2) / 2
+ const roofMidY = (currentRoof.y1 + currentRoof.y2) / 2
+
+ const alpha = wallMidX - roofMidX === 0 ? 0 : wallMidX - roofMidX
+ const beta = wallMidY - roofMidY === 0 ? 0 : wallMidY - roofMidY
+
+ currentRoof.set({
+ x1: currentRoof.x1 + alpha,
+ y1: currentRoof.y1 + beta,
+ x2: currentRoof.x2 + alpha,
+ y2: currentRoof.y2 + beta,
+ })
+
+ prevRoof.set({
+ x1: prevRoof.x1,
+ y1: prevRoof.y1,
+ x2: prevRoof.x2 + alpha,
+ y2: prevRoof.y2 + beta,
+ })
+
+ nextRoof.set({
+ x1: nextRoof.x1 + alpha,
+ y1: nextRoof.y1 + beta,
+ x2: nextRoof.x2,
+ y2: nextRoof.y2,
+ })
+
+ const innerLines = canvas
+ ?.getObjects()
+ .filter(
+ (object) =>
+ object.attributes !== undefined &&
+ object.attributes.roofId === roofId &&
+ object.attributes.currentRoof === currentRoof.id &&
+ object.x1 !== undefined &&
+ object.x2 !== undefined,
+ )
+
+ innerLines
+ .filter((line) => line.name !== LINE_TYPE.SUBLINE.RIDGE && line.name !== LINE_TYPE.SUBLINE.HIP && line.name !== LINE_TYPE.SUBLINE.VALLEY)
+ .forEach((line) => {
+ roof.innerLines = roof.innerLines.filter((l) => l.id !== line.id)
+ canvas?.remove(line)
+ })
+
+ if (currentRoof.attributes.sleeve && currentRoof.attributes.width > 0 && prevRoof.attributes.offset > 0 && nextRoof.attributes.offset > 0) {
+ const prevSignX = Math.sign(prevRoof.x1 - prevRoof.x2)
+ const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2)
+ const nextSignX = Math.sign(nextRoof.x1 - nextRoof.x2)
+ const nextSignY = Math.sign(nextRoof.y1 - nextRoof.y2)
+
+ const prevWidthX = prevSignX === 0 ? 0 : prevSignX * currentRoof.attributes.width
+ const prevWidthY = prevSignY === 0 ? 0 : prevSignY * currentRoof.attributes.width
+ const nextWidthX = nextSignX === 0 ? 0 : nextSignX * currentRoof.attributes.width
+ const nextWidthY = nextSignY === 0 ? 0 : nextSignY * currentRoof.attributes.width
+ const prevX2 = prevRoof.x2 - prevWidthX
+ const prevY2 = prevRoof.y2 - prevWidthY
+ const nextX1 = nextRoof.x1 + nextWidthX
+ const nextY1 = nextRoof.y1 + nextWidthY
+
+ currentRoof.set({
+ x1: wallLine.x1,
+ y1: wallLine.y1,
+ x2: wallLine.x2,
+ y2: wallLine.y2,
+ })
+
+ prevRoof.set({
+ x1: prevRoof.x1,
+ y1: prevRoof.y1,
+ x2: prevX2,
+ y2: prevY2,
+ })
+
+ nextRoof.set({
+ x1: nextX1,
+ y1: nextY1,
+ x2: nextRoof.x2,
+ y2: nextRoof.y2,
+ })
+
+ const addPrevWallLine1 = new QLine([prevX2, prevY2, wallLine.x1 - prevWidthX, wallLine.y1 - prevWidthY], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: 'roofLine',
+ attributes: { roofId: roofId, type: LINE_TYPE.WALLLINE.ETC },
+ })
+
+ const addPrevWallLine2 = new QLine(
+ [addPrevWallLine1.x2, addPrevWallLine1.y2, addPrevWallLine1.x2 + prevWidthX, addPrevWallLine1.y2 + prevWidthY],
+ {
+ fontSize: roof.fontSize,
+ stroke: 'cyan',
+ strokeWidth: 1,
+ name: 'roofLine',
+ attributes: { roofId: roofId, type: LINE_TYPE.WALLLINE.ETC },
+ },
+ )
+
+ const addNextWallLine1 = new QLine([wallLine.x2, wallLine.y2, wallLine.x2 + nextWidthX, wallLine.y2 + nextWidthY], {
+ fontSize: roof.fontSize,
+ stroke: 'green',
+ strokeWidth: 1,
+ name: 'roofLine',
+ attributes: { roofId: roofId, type: LINE_TYPE.WALLLINE.ETC },
+ })
+
+ const addNextWallLine2 = new QLine([addNextWallLine1.x2, addNextWallLine1.y2, nextX1, nextY1], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: 'roofLine',
+ attributes: { roofId: roofId, type: LINE_TYPE.WALLLINE.ETC },
+ })
+
+ canvas?.renderAll()
+ const prevIndex = roof.lines.indexOf(prevRoof) + 1
+ roof.lines.splice(prevIndex, 0, addPrevWallLine1, addPrevWallLine2)
+
+ const nextIndex = roof.lines.indexOf(currentRoof) + 1
+ roof.lines.splice(nextIndex, 0, addNextWallLine1, addNextWallLine2)
+ }
+
+ reDrawPolygon(roof, canvas)
+
+ if (ridgeLines.length > 0) {
+ const ridge = ridgeLines[0]
+ if (ridge.x1 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y1 === currentRoof.attributes.ridgeCoordinate.y1) {
+ const diffX = ridge.x1 - wallMidX === 0 ? 0 : ridge.x1 - wallMidX
+ const diffY = ridge.y1 - wallMidY === 0 ? 0 : ridge.y1 - wallMidY
+
+ ridge.set({
+ x1: ridge.x1 - diffX,
+ y1: ridge.y1 - diffY,
+ x2: ridge.x2,
+ y2: ridge.y2,
+ })
+ }
+ if (ridge.x2 === currentRoof.attributes.ridgeCoordinate.x1 && ridge.y2 === currentRoof.attributes.ridgeCoordinate.y1) {
+ const diffX = ridge.x2 - wallMidX === 0 ? 0 : ridge.x2 - wallMidX
+ const diffY = ridge.y2 - wallMidY === 0 ? 0 : ridge.y2 - wallMidY
+
+ ridge.set({
+ x1: ridge.x1,
+ y1: ridge.y1,
+ x2: ridge.x2 - diffX,
+ y2: ridge.y2 - diffY,
+ })
+ }
+
+ let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX, wallMidY], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX, wallMidY], {
+ fontSize: roof.fontSize,
+ stroke: 'red',
+ strokeWidth: 1,
+ name: LINE_TYPE.SUBLINE.HIP,
+ attributes: {
+ roofId: roof.id,
+ currentRoofId: currentRoof.id,
+ planeSize: currentRoof.length,
+ actualSize: currentRoof.length,
+ },
+ })
+ canvas?.add(hip1)
+ canvas?.add(hip2)
+ roof.innerLines.push(hip1)
+ roof.innerLines.push(hip2)
+ }
+ if (hipLines.length > 0) {
+ hipLines.forEach((hip) => {
+ roof.innerLines = roof.innerLines.filter((h) => h.id !== hip.id)
+ canvas?.remove(hip)
+ })
+ }
+}
+
+/**
+ * 지붕을 변경한다.
+ * @param currentRoof
+ * @param canvas
+ */
+export const changeCurrentRoof = (currentRoof, canvas) => {
+ const roofId = currentRoof.attributes.roofId
+ const originRoof = canvas?.getObjects().find((object) => object.name === 'roof' && object.id === roofId)
+ const wall = canvas?.getObjects().find((object) => object.name === 'wall' && object.attributes.roofId === roofId)
+ const wallLine = wall.lines.filter((w) => w.id === currentRoof.attributes.wallLine)[0]
+ const innerLines = canvas
+ ?.getObjects()
+ .filter((object) => object.attributes !== undefined && object.attributes.roofId === roofId && object.x1 !== undefined && object.x2 !== undefined)
+
+ wallLine.attributes.type = currentRoof.attributes.type
+ wallLine.attributes.offset = currentRoof.attributes.offset
+ wallLine.attributes.width = currentRoof.attributes.width
+ wallLine.attributes.pitch = currentRoof.attributes.pitch
+ wallLine.attributes.sleeve = currentRoof.attributes.sleeve
+
+ canvas?.remove(originRoof)
+
+ innerLines.forEach((line) => canvas?.remove(line))
+
+ const polygon = createPolygon(wall.points)
+ const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
+ originPolygon.setViewLengthText(false)
+ let offsetPolygon
+
+ let result = createRoofMarginPolygon(polygon, wall.lines).vertices
+ const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
+
+ if (allPointsOutside) {
+ offsetPolygon = createRoofMarginPolygon(polygon, wall.lines).vertices
+ } else {
+ offsetPolygon = createRoofPaddingPolygon(polygon, wall.lines).vertices
+ }
+
+ const newRoof = new QPolygon(offsetPolygon, {
+ fill: originRoof.fill,
+ stroke: originRoof.stroke,
+ strokeWidth: originRoof.strokeWidth,
+ selectable: originRoof.selectable,
+ fontSize: originRoof.fontSize,
+ })
+
+ newRoof.name = POLYGON_TYPE.ROOF
+ newRoof.setWall(wall)
+
+ newRoof.lines.forEach((line, index) => {
+ line.attributes = {
+ roofId: newRoof.id,
+ 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,
+ }
+ })
+ wall.attributes = {
+ roofId: newRoof.id,
+ }
+
+ wall.lines.forEach((line, index) => {
+ line.attributes.roofId = newRoof.id
+ line.attributes.currentRoof = newRoof.lines[index].id
+ })
+ canvas?.add(newRoof)
+ canvas?.renderAll()
+
+ newRoof.drawHelpLine()
+}
+
+/**
+ * 지붕을 변경한다.
+ * @param polygon
+ * @param canvas
+ */
+const reDrawPolygon = (polygon, canvas) => {
+ const lines = polygon.lines
+ let point = []
+ lines.forEach((line) => point.push({ x: line.x1, y: line.y1 }))
+
+ canvas?.remove(polygon)
+
+ const newPolygon = new QPolygon(point, {
+ id: polygon.id,
+ name: polygon.name,
+ fill: polygon.fill,
+ stroke: polygon.stroke,
+ strokeWidth: polygon.strokeWidth,
+ selectable: polygon.selectable,
+ fontSize: polygon.fontSize,
+ wall: polygon.wall !== undefined ? polygon.wall : null,
+ })
+
+ const newLines = newPolygon.lines
+
+ newLines.forEach((line, index) => {
+ lines.forEach((l, i) => {
+ if (line.x1 === l.x1 && line.y1 === l.y1) {
+ line.id = l.id
+ line.attributes = l.attributes
+ }
+ })
+ })
+
+ canvas?.add(newPolygon)
+ canvas?.renderAll()
+
+ return newPolygon
+}
+
+function createRoofMarginPolygon(polygon, lines, arcSegments = 0) {
+ const offsetEdges = []
+
+ polygon.edges.forEach((edge, i) => {
+ 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))
+ })
+
+ const vertices = []
+
+ offsetEdges.forEach((thisEdge, i) => {
+ const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
+ const vertex = edgesIntersection(prevEdge, thisEdge)
+ if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
+ vertices.push({
+ x: vertex.x,
+ y: vertex.y,
+ })
+ }
+ })
+
+ const marginPolygon = createPolygon(vertices)
+ marginPolygon.offsetEdges = offsetEdges
+ return marginPolygon
+}
+
+function createRoofPaddingPolygon(polygon, lines, arcSegments = 0) {
+ const offsetEdges = []
+
+ polygon.edges.forEach((edge, i) => {
+ 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))
+ })
+
+ const vertices = []
+
+ offsetEdges.forEach((thisEdge, i) => {
+ const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
+ const vertex = edgesIntersection(prevEdge, thisEdge)
+ if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
+ vertices.push({
+ x: vertex.x,
+ y: vertex.y,
+ })
+ }
+ })
+
+ const paddingPolygon = createPolygon(vertices)
+ paddingPolygon.offsetEdges = offsetEdges
+ return paddingPolygon
}
function arePointsEqual(point1, point2) {
- return point1.x === point2.x && point1.y === point2.y
+ return Math.abs(point1.x - point2.x) <= 1 && Math.abs(point1.y - point2.y) <= 1
}
function arraysHaveSamePoints(array1, array2) {