758 lines
26 KiB
JavaScript
758 lines
26 KiB
JavaScript
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
|
import { canvasState, currentAngleTypeSelector, currentObjectState } from '@/store/canvasAtom'
|
|
import { useContext, useEffect, useState } from 'react'
|
|
import { useAxios } from '@/hooks/useAxios'
|
|
import { useSwal } from '@/hooks/useSwal'
|
|
import { usePolygon } from '@/hooks/usePolygon'
|
|
import {
|
|
addedRoofsState,
|
|
basicSettingState,
|
|
correntObjectNoState,
|
|
corridorDimensionSelector,
|
|
outlineDisplaySelector,
|
|
roofDisplaySelector,
|
|
roofMaterialsSelector,
|
|
selectedRoofMaterialSelector,
|
|
} from '@/store/settingAtom'
|
|
import { usePopup } from '@/hooks/usePopup'
|
|
import { POLYGON_TYPE } from '@/common/common'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
|
|
import { useMessage } from '@/hooks/useMessage'
|
|
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
|
|
import { useRoofFn } from '@/hooks/common/useRoofFn'
|
|
import { globalLocaleStore } from '@/store/localeAtom'
|
|
import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
|
|
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
|
|
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
|
|
import { outerLinePointsState } from '@/store/outerLineAtom'
|
|
import { QcastContext } from '@/app/QcastProvider'
|
|
import { usePlan } from '@/hooks/usePlan'
|
|
import { roofsState } from '@/store/roofAtom'
|
|
import { useText } from '@/hooks/useText'
|
|
import { QLine } from '@/components/fabric/QLine'
|
|
|
|
export function useRoofAllocationSetting(id) {
|
|
const canvas = useRecoilValue(canvasState)
|
|
const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
|
|
const roofDisplay = useRecoilValue(roofDisplaySelector)
|
|
const { drawDirectionArrow, addLengthText, splitPolygonWithLines, splitPolygonWithSeparate } = usePolygon()
|
|
const [popupId, setPopupId] = useState(uuidv4())
|
|
const { addPopup, closePopup, closeAll } = usePopup()
|
|
const currentObject = useRecoilValue(currentObjectState)
|
|
const { setSelectedMenu } = useCanvasMenu()
|
|
const roofMaterials = useRecoilValue(roofMaterialsSelector)
|
|
const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector)
|
|
const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState)
|
|
const [currentRoofMaterial, setCurrentRoofMaterial] = useState(roofMaterials[0])
|
|
/** 팝업 내 기준 지붕재 */
|
|
const [roofList, setRoofList] = useRecoilState(addedRoofsState)
|
|
/** 배치면 초기설정에서 선택한 지붕재 배열 */
|
|
const [editingLines, setEditingLines] = useState([])
|
|
const [currentRoofList, setCurrentRoofList] = useState([])
|
|
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
|
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
|
const [basicInfo, setBasicInfo] = useState(null)
|
|
const { get, post } = useAxios(globalLocaleState)
|
|
const { getMessage } = useMessage()
|
|
const { swalFire } = useSwal()
|
|
const { setIsGlobalLoading } = useContext(QcastContext)
|
|
const { setSurfaceShapePattern } = useRoofFn()
|
|
const { saveCanvas } = usePlan()
|
|
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
|
|
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
|
|
const outerLinePoints = useRecoilValue(outerLinePointsState)
|
|
const resetPoints = useResetRecoilState(outerLinePointsState)
|
|
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
|
|
const { changeCorridorDimensionText } = useText()
|
|
const outlineDisplay = useRecoilValue(outlineDisplaySelector)
|
|
|
|
useEffect(() => {
|
|
/** 배치면 초기설정에서 선택한 지붕재 배열 설정 */
|
|
setCurrentRoofList(roofList)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
/** 지붕면 조회 */
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) /** roofPolygon.innerLines */
|
|
|
|
roofBases.forEach((roof) => {
|
|
roof.innerLines.forEach((line) => {
|
|
/** 실측값이 없는 경우 라인 두께 4로 설정 */
|
|
if (!line.attributes.actualSize || line.attributes?.actualSize === 0) {
|
|
line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
|
|
}
|
|
|
|
/** 현재 선택된 라인인 경우 라인 두께 2로 설정 */
|
|
if (editingLines.includes(line)) {
|
|
line.set({ strokeWidth: 2, stroke: 'black', selectable: true })
|
|
}
|
|
})
|
|
})
|
|
|
|
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
|
|
if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
|
|
currentObject.set({ strokeWidth: 4, stroke: '#EA10AC' })
|
|
}
|
|
}, [currentObject])
|
|
|
|
useEffect(() => {
|
|
/** 현재 선택된 객체가 보조라인, 피라미드, 힙인 경우 두께 4로 설정 */
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
if (roofBases.length === 0) {
|
|
swalFire({ text: getMessage('roofAllocation.not.found'), icon: 'warning' })
|
|
closePopup(id)
|
|
}
|
|
|
|
/** 배치면 초기설정 조회 */
|
|
fetchBasicSettings(basicSetting.planNo)
|
|
}, [])
|
|
|
|
/**
|
|
* 배치면 초기설정 조회
|
|
*/
|
|
const fetchBasicSettings = async (planNo) => {
|
|
try {
|
|
const response = await get({ url: `/api/canvas-management/canvas-basic-settings/by-object/${correntObjectNo}/${planNo}` })
|
|
|
|
let roofsArray = []
|
|
|
|
// API에서 데이터를 성공적으로 가져온 경우
|
|
if (response && response.length > 0) {
|
|
roofsArray = response.map((item, index) => ({
|
|
planNo: item.planNo,
|
|
roofApply: item.roofApply,
|
|
roofSeq: item.roofSeq || index,
|
|
roofMatlCd: item.roofMatlCd,
|
|
roofWidth: item.roofWidth,
|
|
roofHeight: item.roofHeight,
|
|
roofHajebichi: item.roofHajebichi,
|
|
roofGap: item.roofGap,
|
|
roofLayout: item.roofLayout,
|
|
roofPitch: item.roofPitch,
|
|
roofAngle: item.roofAngle,
|
|
selected: index === 0, // 첫 번째 항목을 기본 선택으로 설정
|
|
index: index,
|
|
}))
|
|
}
|
|
// API에서 데이터가 없고 기존 roofList가 있는 경우
|
|
else if (roofList && roofList.length > 0) {
|
|
roofsArray = roofList.map((roof, index) => ({
|
|
...roof,
|
|
selected: index === 0, // 첫 번째 항목을 기본 선택으로 설정
|
|
}))
|
|
}
|
|
// 둘 다 없는 경우 기본값 설정
|
|
else {
|
|
roofsArray = [
|
|
{
|
|
planNo: planNo,
|
|
roofApply: true,
|
|
roofSeq: 0,
|
|
roofMatlCd: 'ROOF_ID_WA_53A',
|
|
roofWidth: 265,
|
|
roofHeight: 235,
|
|
roofHajebichi: 0,
|
|
roofGap: 'HEI_455',
|
|
roofLayout: 'P',
|
|
roofPitch: 4,
|
|
roofAngle: 21.8,
|
|
},
|
|
]
|
|
}
|
|
|
|
/**
|
|
* 데이터 설정
|
|
*/
|
|
const selectRoofs = []
|
|
for (let i = 0; i < roofsArray.length; i++) {
|
|
roofMaterials?.map((material) => {
|
|
if (material.roofMatlCd === roofsArray[i].roofMatlCd) {
|
|
selectRoofs.push({
|
|
...material,
|
|
selected: roofsArray[i].roofApply,
|
|
index: roofsArray[i].roofSeq,
|
|
id: roofsArray[i].roofMatlCd,
|
|
width: roofsArray[i].roofWidth,
|
|
length: roofsArray[i].roofHeight,
|
|
hajebichi: roofsArray[i].roofHajebichi,
|
|
raft: roofsArray[i].roofGap,
|
|
layout: roofsArray[i].roofLayout,
|
|
pitch: roofsArray[i].roofPitch,
|
|
angle: roofsArray[i].roofAngle,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
const firstRes = Array.isArray(response) && response.length > 0 ? response[0] : null
|
|
|
|
setBasicSetting({
|
|
...basicSetting,
|
|
planNo: firstRes?.planNo ?? planNo,
|
|
roofSizeSet: firstRes?.roofSizeSet ?? 0,
|
|
roofAngleSet: firstRes?.roofAngleSet ?? 0,
|
|
roofsData: roofsArray,
|
|
selectedRoofMaterial: selectRoofs.find((roof) => roof.selected),
|
|
})
|
|
|
|
setBasicInfo({
|
|
planNo: '' + (firstRes?.planNo ?? planNo),
|
|
roofSizeSet: '' + (firstRes?.roofSizeSet ?? 0),
|
|
roofAngleSet: '' + (firstRes?.roofAngleSet ?? 0),
|
|
})
|
|
// 데이터 동기화: 렌더링용 필드 기본값 보정
|
|
const normalizedRoofs = selectRoofs.map((roof) => ({
|
|
...roof,
|
|
width: roof.width ?? '',
|
|
length: roof.length ?? '',
|
|
hajebichi: roof.hajebichi ?? '',
|
|
pitch: roof.pitch ?? '',
|
|
angle: roof.angle ?? '',
|
|
}))
|
|
setCurrentRoofList(normalizedRoofs)
|
|
} catch (error) {
|
|
console.error('Data fetching error:', error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 지붕면 할당 저장
|
|
*/
|
|
const basicSettingSave = async () => {
|
|
try {
|
|
setIsGlobalLoading(true)
|
|
const patternData = {
|
|
objectNo: correntObjectNo,
|
|
planNo: Number(basicSetting.planNo),
|
|
roofSizeSet: Number(basicSetting.roofSizeSet),
|
|
roofAngleSet: basicSetting.roofAngleSet,
|
|
roofAllocationList: currentRoofList.map((item, index) => ({
|
|
planNo: Number(basicSetting.planNo),
|
|
roofApply: item.selected,
|
|
roofSeq: index,
|
|
roofMatlCd: item.roofMatlCd === null || item.roofMatlCd === undefined ? 'ROOF_ID_WA_53A' : item.roofMatlCd,
|
|
roofWidth: item.width === null || item.width === undefined ? 0 : Number(item.width),
|
|
roofHeight: item.length === null || item.length === undefined ? 0 : Number(item.length),
|
|
roofHajebichi: item.hajebichi === null || item.hajebichi === undefined ? 0 : Number(item.hajebichi),
|
|
roofGap: !item.raft ? item.raftBaseCd : item.raft,
|
|
roofLayout: item.layout === null || item.layout === undefined ? 'P' : item.layout,
|
|
roofPitch: item.pitch === null || item.pitch === undefined ? 4 : Number(item.pitch),
|
|
roofAngle: item.angle === null || item.angle === undefined ? 21.8 : Number(item.angle),
|
|
})),
|
|
}
|
|
|
|
await post({ url: `/api/canvas-management/roof-allocation-settings`, data: patternData }).then((res) => {
|
|
setIsGlobalLoading(false)
|
|
})
|
|
|
|
//Recoil 설정
|
|
//setCanvasSetting({ ...basicSetting })
|
|
|
|
/** 배치면 초기설정 조회 */
|
|
fetchBasicSettings(basicSetting.planNo)
|
|
} catch (error) {
|
|
swalFire({ text: error.message, icon: 'error' })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 지붕재 추가
|
|
*/
|
|
const onAddRoofMaterial = () => {
|
|
if (currentRoofList.length >= 4) {
|
|
swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.exceed.count') })
|
|
return
|
|
}
|
|
|
|
const originCurrentRoofList = currentRoofList.map((roof) => {
|
|
return { ...roof, selected: false }
|
|
})
|
|
originCurrentRoofList.push({
|
|
...currentRoofMaterial,
|
|
selected: true,
|
|
id: currentRoofMaterial.roofMatlCd,
|
|
name: currentRoofMaterial.roofMatlNm,
|
|
index: currentRoofList.length,
|
|
})
|
|
|
|
setCurrentRoofList(originCurrentRoofList)
|
|
}
|
|
|
|
/**
|
|
* 지붕재 삭제
|
|
*/
|
|
const onDeleteRoofMaterial = (idx) => {
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
|
|
for (let i = 0; i < roofs.length; i++) {
|
|
if (roofs[i].roofMaterial?.index === idx) {
|
|
swalFire({ type: 'alert', icon: 'error', text: getMessage('roof.material.can.not.delete') })
|
|
return
|
|
}
|
|
}
|
|
|
|
const isSelected = currentRoofList[idx].selected
|
|
const newRoofList = JSON.parse(JSON.stringify(currentRoofList)).filter((_, index) => index !== idx)
|
|
if (isSelected) {
|
|
newRoofList[0].selected = true
|
|
}
|
|
setCurrentRoofList(newRoofList)
|
|
setRoofsStore(newRoofList)
|
|
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
|
|
}
|
|
|
|
/**
|
|
* 선택한 지붕재로 할당
|
|
*/
|
|
const handleSave = () => {
|
|
/**
|
|
* 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
|
|
*/
|
|
if (checkInnerLines()) {
|
|
addPopup(popupId, 1, <ActualSizeSetting id={popupId} />)
|
|
} else {
|
|
apply()
|
|
|
|
resetPoints()
|
|
|
|
basicSettingSave()
|
|
}
|
|
//기존 지붕 선은 남겨둔다.
|
|
drawOriginRoofLine()
|
|
}
|
|
|
|
const drawOriginRoofLine = () => {
|
|
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
|
/** 벽면 삭제 */
|
|
wallLines.forEach((wallLine) => {
|
|
wallLine.set({
|
|
stroke: 'black',
|
|
strokeDashArray: [5, 2],
|
|
strokeWidth: 1,
|
|
selectable: false,
|
|
name: 'originRoofOuterLine',
|
|
visible: outlineDisplay,
|
|
})
|
|
})
|
|
canvas.renderAll()
|
|
}
|
|
|
|
/**
|
|
* 지붕재 오른쪽 마우스 클릭 후 단일로 지붕재 변경 필요한 경우
|
|
*/
|
|
const handleSaveContext = () => {
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (roof.index !== idx) {
|
|
// 기존 저장된 지붕재의 index 수정
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === roof.index)
|
|
roofs.forEach((roof) => {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, false, { ...roof, index: idx }, true)
|
|
})
|
|
}
|
|
|
|
return { ...roof, index: idx, raft: roof.raft ? roof.raft : roof.raftBaseCd }
|
|
})
|
|
|
|
setBasicSetting((prev) => {
|
|
return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
|
|
})
|
|
const selectedRoofMaterial = newRoofList.find((roof) => roof.selected)
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && obj.roofMaterial?.index === selectedRoofMaterial.index)
|
|
|
|
roofs.forEach((roof) => {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, false, { ...selectedRoofMaterial }, true)
|
|
drawDirectionArrow(roof)
|
|
})
|
|
|
|
setRoofList(newRoofList)
|
|
setRoofMaterials(newRoofList)
|
|
setRoofsStore(newRoofList)
|
|
|
|
setSurfaceShapePattern(currentObject, roofDisplay.column, false, selectedRoofMaterial, true)
|
|
drawDirectionArrow(currentObject)
|
|
modifyModuleSelectionData()
|
|
// closeAll()
|
|
closePopup(id)
|
|
basicSettingSave()
|
|
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
|
|
}
|
|
|
|
/**
|
|
* 기존 세팅된 지붕에 지붕재 내용을 바뀐 내용으로 수정
|
|
* @param newRoofMaterials
|
|
*/
|
|
const setRoofMaterials = (newRoofMaterials) => {
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
newRoofMaterials.forEach((roofMaterial) => {
|
|
const index = roofMaterial.index
|
|
const tempRoofs = roofs.filter((roof) => roof.roofMaterial?.index === index)
|
|
tempRoofs.forEach((roof) => {
|
|
setSurfaceShapePattern(roof, roofDisplay.column, false, roofMaterial)
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 지붕면 할당
|
|
*/
|
|
const handleAlloc = () => {
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
|
|
roofBases.forEach((roof) => {
|
|
if (roof.separatePolygon.length === 0) {
|
|
roof.innerLines.forEach((line) => {
|
|
if ((!line.attributes.actualSize || line.attributes?.actualSize === 0) && line.length > 1) {
|
|
line.set({ attributes: { ...line.attributes, actualSize: line.attributes.planeSize } })
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
apply()
|
|
}
|
|
|
|
/**
|
|
* 실측값 없는 경우 체크
|
|
*/
|
|
const checkInnerLines = () => {
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) // roofPolygon.innerLines
|
|
let result = false
|
|
|
|
roofBases.forEach((roof) => {
|
|
if (roof.separatePolygon.length === 0) {
|
|
roof.innerLines.forEach((line) => {
|
|
if ((!line.attributes.actualSize || line.attributes?.actualSize === 0) && line.length > 1) {
|
|
line.set({ strokeWidth: 4, stroke: 'black', selectable: true })
|
|
result = true
|
|
}
|
|
})
|
|
}
|
|
})
|
|
if (result) canvas?.renderAll()
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* 지붕면 할당
|
|
*/
|
|
const apply = () => {
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.roofMaterial)
|
|
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
|
|
roofBases.forEach((roofBase) => {
|
|
try {
|
|
const roofEaveHelpLines = canvas.getObjects().filter((obj) => obj.lineName === 'eaveHelpLine' && obj.roofId === roofBase.id)
|
|
if (roofEaveHelpLines.length > 0) {
|
|
if (roofBase.lines) {
|
|
// Filter out any eaveHelpLines that are already in lines to avoid duplicates
|
|
const existingEaveLineIds = new Set(roofBase.lines.map((line) => line.id))
|
|
const newEaveLines = roofEaveHelpLines.filter((line) => !existingEaveLineIds.has(line.id))
|
|
// Filter out lines from roofBase.lines that share any points with newEaveLines
|
|
const linesToKeep = roofBase.lines.filter(roofLine => {
|
|
const shouldRemove = newEaveLines.some(eaveLine => {
|
|
// 1. 기본적인 포인트 일치 확인
|
|
const rX1 = roofLine.x1, rY1 = roofLine.y1, rX2 = roofLine.x2, rY2 = roofLine.y2;
|
|
const eX1 = eaveLine.x1, eY1 = eaveLine.y1, eX2 = eaveLine.x2, eY2 = eaveLine.y2;
|
|
|
|
const isP1Matched = (Math.abs(rX1 - eX1) < 0.1 && Math.abs(rY1 - eY1) < 0.1) || (Math.abs(rX1 - eX2) < 0.1 && Math.abs(rY1 - eY2) < 0.1);
|
|
const isP2Matched = (Math.abs(rX2 - eX1) < 0.1 && Math.abs(rY2 - eY1) < 0.1) || (Math.abs(rX2 - eX2) < 0.1 && Math.abs(rY2 - eY2) < 0.1);
|
|
|
|
if (isP1Matched || isP2Matched) {
|
|
// 2. 일직선(평행)인지 확인
|
|
const dx1 = rX2 - rX1;
|
|
const dy1 = rY2 - rY1;
|
|
const dx2 = eX2 - eX1;
|
|
const dy2 = eY2 - eY1;
|
|
const crossProduct = Math.abs(dx1 * dy2 - dy1 * dx2);
|
|
const mag1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
|
const mag2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
const isStraight = (mag1 * mag2) === 0 ? true : (crossProduct / (mag1 * mag2) < 0.01);
|
|
|
|
if (isStraight) {
|
|
// 3. [핵심] 몸통이 포개지는지(Overlap) 확인
|
|
// 한 선의 끝점이 다른 선의 "내부"에 들어와 있는지 체크
|
|
const isPointInside = (x, y, x1, y1, x2, y2) => {
|
|
const dotProduct = (x - x1) * (x2 - x1) + (y - y1) * (y2 - y1);
|
|
if (dotProduct < 0.1) return false; // 시작점 바깥쪽
|
|
const squaredLength = (x2 - x1) ** 2 + (y2 - y1) ** 2;
|
|
if (dotProduct > squaredLength - 0.1) return false; // 끝점 바깥쪽
|
|
return true; // 선의 내부(몸통)에 있음
|
|
};
|
|
|
|
// roofLine의 끝점 중 하나가 eaveLine의 몸통 안에 있거나,
|
|
// eaveLine의 끝점 중 하나가 roofLine의 몸통 안에 있으면 "포개짐"으로 판단
|
|
const isOverlapping =
|
|
isPointInside(rX1, rY1, eX1, eY1, eX2, eY2) ||
|
|
isPointInside(rX2, rY2, eX1, eY1, eX2, eY2) ||
|
|
isPointInside(eX1, eY1, rX1, rY1, rX2, rY2) ||
|
|
isPointInside(eX2, eY2, rX1, rY1, rX2, rY2);
|
|
|
|
if (isOverlapping) {
|
|
console.log('Removing overlapping line:', roofLine);
|
|
return true; // 포개지는 경우에만 삭제
|
|
}
|
|
}
|
|
}
|
|
return false; // 끝점만 닿아 있거나 직각인 경우는 살림
|
|
});
|
|
return !shouldRemove;
|
|
});
|
|
// Combine remaining lines with newEaveLines
|
|
roofBase.lines = [...linesToKeep, ...newEaveLines];
|
|
} else {
|
|
roofBase.lines = [...roofEaveHelpLines]
|
|
}
|
|
if (!roofBase.innerLines) {
|
|
roofBase.innerLines = []
|
|
}
|
|
}
|
|
|
|
if (roofBase.adjustRoofLines.length > 0) {
|
|
const newRoofLines = []
|
|
let lineIndex = 1
|
|
roofBase.lines.forEach((line, idx) => {
|
|
const adjustLines = roofBase.adjustRoofLines.filter((adjustLine) => adjustLine.roofIdx === line.idx)
|
|
if (adjustLines.length === 0) {
|
|
line.idx = lineIndex
|
|
newRoofLines.push(line)
|
|
lineIndex++
|
|
} else {
|
|
adjustLines.forEach(({ point, roofIdx }) => {
|
|
const newLine = new QLine(point, {
|
|
idx: lineIndex,
|
|
selectable: false,
|
|
parentId: line.parentId,
|
|
parent: line.parent,
|
|
fontSize: line.fontSize,
|
|
stroke: line.stroke,
|
|
strokeWidth: line.strokeWidth,
|
|
attributes: line.attributes,
|
|
})
|
|
newRoofLines.push(newLine)
|
|
lineIndex++
|
|
})
|
|
}
|
|
})
|
|
roofBase.lines = newRoofLines
|
|
}
|
|
if (roofBase.separatePolygon.length > 0) {
|
|
splitPolygonWithSeparate(roofBase.separatePolygon)
|
|
} else {
|
|
splitPolygonWithLines(roofBase)
|
|
}
|
|
} catch (e) {
|
|
console.log(e)
|
|
canvas.discardActiveObject()
|
|
return
|
|
}
|
|
|
|
/** 라인 삭제 */
|
|
roofBase.innerLines.forEach((line) => {
|
|
canvas.remove(line)
|
|
})
|
|
|
|
canvas.remove(roofBase)
|
|
})
|
|
|
|
/** 데이터 설정 */
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
return { ...roof, index: idx, ...basicInfo, raft: roof.raft ? roof.raft : roof.raftBaseCd }
|
|
})
|
|
|
|
setBasicSetting((prev) => {
|
|
return { ...prev, selectedRoofMaterial: newRoofList.find((roof) => roof.selected) }
|
|
})
|
|
setRoofList(newRoofList)
|
|
|
|
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
|
|
|
|
roofs.forEach((roof) => {
|
|
if (roof.isFixed) return
|
|
roof.set({ isFixed: true })
|
|
|
|
/** 모양 패턴 설정 */
|
|
setSurfaceShapePattern(
|
|
roof,
|
|
roofDisplay.column,
|
|
false,
|
|
currentRoofList.find((roof) => roof.selected),
|
|
)
|
|
drawDirectionArrow(roof)
|
|
})
|
|
|
|
setRoofMaterials(newRoofList)
|
|
setRoofsStore(newRoofList)
|
|
/** 외곽선 삭제 */
|
|
const removeTargets = canvas.getObjects().filter((obj) => obj.name === 'outerLinePoint' || obj.name === 'outerLine' || obj.name === 'pitchText')
|
|
removeTargets.forEach((obj) => {
|
|
canvas.remove(obj)
|
|
})
|
|
setEditingLines([])
|
|
closeAll()
|
|
setSelectedMenu('surface')
|
|
//지붕면 완성 후 실측치 로 보이도록 수정
|
|
setCorridorDimension(1)
|
|
|
|
/** 모듈 선택 데이터 초기화 */
|
|
// modifyModuleSelectionData()
|
|
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList })
|
|
|
|
setTimeout(() => {
|
|
changeCorridorDimensionText('realDimension')
|
|
}, 500)
|
|
}
|
|
|
|
/**
|
|
* 라인 사이즈 설정
|
|
*/
|
|
const setLineSize = (id, size) => {
|
|
const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
|
roofBases.forEach((roof) => {
|
|
roof.innerLines.forEach((line) => {
|
|
if (id === line.id) {
|
|
setEditingLines([...editingLines.filter((editLine) => editLine.id !== line.id), line])
|
|
line.attributes.actualSize = size
|
|
line.set({ strokeWidth: 2, stroke: 'black' })
|
|
}
|
|
})
|
|
})
|
|
|
|
canvas?.renderAll()
|
|
}
|
|
|
|
/**
|
|
* 지붕재 변경
|
|
*/
|
|
const handleChangeRoofMaterial = (value, index) => {
|
|
const selectedRoofMaterial = roofMaterials.find((roof) => roof.roofMatlCd === value.id)
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (idx === index) {
|
|
return { ...selectedRoofMaterial, selected: roof.selected, index }
|
|
}
|
|
return roof
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 기본 지붕재 radio값 변경
|
|
*/
|
|
const handleDefaultRoofMaterial = (index) => {
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
return { ...roof, selected: idx === index }
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 서까래 변경
|
|
*/
|
|
const handleChangeRaft = (e, index) => {
|
|
const raftValue = e.clCode
|
|
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (idx === index) {
|
|
return { ...roof, raft: raftValue }
|
|
}
|
|
return roof
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 레이아웃 변경
|
|
*/
|
|
const handleChangeLayout = (layoutValue, index) => {
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (idx === index) {
|
|
return { ...roof, layout: layoutValue }
|
|
}
|
|
return roof
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 치수 입력방법(복시도입력/실측값입력/육지붕)
|
|
*/
|
|
const handleChangeInput = (e, type = '', index) => {
|
|
const value = e.target.value
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (idx === index) {
|
|
return { ...roof, [type]: value }
|
|
}
|
|
return roof
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 피치 변경
|
|
*/
|
|
const handleChangePitch = (e, index) => {
|
|
let value = e //e.target.value
|
|
|
|
const reg = /^[0-9]+(\.[0-9]{0,1})?$/
|
|
if (!reg.test(value)) {
|
|
value = value.substring(0, value.length - 1)
|
|
}
|
|
const newRoofList = currentRoofList.map((roof, idx) => {
|
|
if (idx === index) {
|
|
const result =
|
|
currentAngleType === 'slope'
|
|
? {
|
|
pitch: value,
|
|
angle: getDegreeByChon(value),
|
|
}
|
|
: { pitch: getChonByDegree(value), angle: value }
|
|
return { ...roof, ...result }
|
|
}
|
|
return roof
|
|
})
|
|
|
|
setCurrentRoofList(newRoofList)
|
|
}
|
|
|
|
/**
|
|
* 모듈 선택에서 선택한 데이터 초기화
|
|
*/
|
|
const modifyModuleSelectionData = () => {
|
|
if (moduleSelectionData.roofConstructions?.length > 0) {
|
|
setModuleSelectionData({ ...moduleSelectionData, roofConstructions: [] })
|
|
moduleSelectedDataTrigger({ ...moduleSelectionData, roofConstructions: [] })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 모듈 선택 데이터 트리거
|
|
*/
|
|
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
|
|
|
|
return {
|
|
handleSave,
|
|
onAddRoofMaterial,
|
|
onDeleteRoofMaterial,
|
|
handleAlloc,
|
|
setLineSize,
|
|
roofMaterials,
|
|
selectedRoofMaterial,
|
|
basicSetting,
|
|
setBasicSetting,
|
|
currentRoofMaterial,
|
|
setCurrentRoofMaterial,
|
|
handleDefaultRoofMaterial,
|
|
handleChangeRoofMaterial,
|
|
handleChangeRaft,
|
|
handleChangeLayout,
|
|
handleSaveContext,
|
|
currentRoofList,
|
|
handleChangeInput,
|
|
handleChangePitch,
|
|
}
|
|
}
|