import { useEffect, useRef, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, currentMenuState, currentObjectState, pitchTextSelector } from '@/store/canvasAtom' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' import { usePolygon } from '@/hooks/usePolygon' import { useMode } from '@/hooks/useMode' import { useLine } from '@/hooks/useLine' import { outerLineFixState } from '@/store/outerLineAtom' import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' import RoofAllocationSetting from '@/components/floor-plan/modal/roofAllocation/RoofAllocationSetting' import { addedRoofsState, basicSettingState, correntObjectNoState, settingModalFirstOptionsState } from '@/store/settingAtom' import { useAxios } from '@/hooks/useAxios' import { globalLocaleStore } from '@/store/localeAtom' // 지붕형상 설정 export function useRoofShapeSetting(id) { const [shapeNum, setShapeNum] = useState(1) const [buttonAct, setButtonAct] = useState(1) const { swalFire } = useSwal() const { getMessage } = useMessage() const canvas = useRecoilValue(canvasState) const currentAngleType = useRecoilValue(currentAngleTypeSelector) const pitchText = useRecoilValue(pitchTextSelector) const { addPolygonByLines } = usePolygon() const [pitch, setPitch] = useState(currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8) // 경사 const [eavesOffset, setEavesOffset] = useState(500) // 처마출폭 const [gableOffset, setGableOffset] = useState(300) // 케라바출폭 const [sleeveOffset, setSleeveOffset] = useState(300) // 소매출폭 const [jerkinHeadWidth, setJerkinHeadWidth] = useState(800) // 반절처 폭 const [jerkinHeadPitch, setJerkinHeadPitch] = useState(currentAngleType === ANGLE_TYPE.SLOPE ? 4.5 : 20) // 반절처 경사 const [hipAndGableWidth, setHipAndGableWidth] = useState(800) // 팔작지붕 폭 const [shedPitch, setShedPitch] = useState(currentAngleType === ANGLE_TYPE.SLOPE ? 4 : 21.8) // 팔작지붕 폭 const [shedWidth, setShedWidth] = useState(300) // 한쪽흐름 폭 const [hasSleeve, setHasSleeve] = useState('0') const currentObject = useRecoilValue(currentObjectState) const { drawRoofPolygon } = useMode() const { hideLine, showLine, removePitchText, addPitchText, addPitchTextsByOuterLines } = useLine() const setCurrentMenu = useSetRecoilState(currentMenuState) const outerLineFix = useRecoilValue(outerLineFixState) const isFixRef = useRef(false) const pitchRef = useRef(null) const shedPitchRef = useRef(null) const jerkinHeadPitchRef = useRef(null) const history = useRef([]) const { closePopup, addPopup } = usePopup() const settingModalFirstOptions = useRecoilValue(settingModalFirstOptionsState) const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) const [basicSetting, setBasicSetting] = useRecoilState(basicSettingState) const correntObjectNo = useRecoilValue(correntObjectNoState) const globalLocaleState = useRecoilValue(globalLocaleStore) const { post } = useAxios(globalLocaleState) useEffect(() => { pitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch) }, [pitch]) useEffect(() => { shedPitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? shedPitch : getChonByDegree(shedPitch) }, [shedPitch]) useEffect(() => { jerkinHeadPitchRef.current = currentAngleType === ANGLE_TYPE.SLOPE ? jerkinHeadPitch : getChonByDegree(jerkinHeadPitch) }, [jerkinHeadPitch]) useEffect(() => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') // if (!outerLineFix || outerLines.length === 0) { // swalFire({ text: '외벽선이 없습니다.' }) // // setShowRoofShapeSettingModal(false) // closePopup(id) // } return () => { if (!isFixRef.current) { return } const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const pitchTexts = canvas.getObjects().filter((obj) => obj.name === 'pitchText') canvas.remove(...pitchTexts) outerLines.forEach((line) => { let stroke, strokeWidth if (line.attributes) { if (line.attributes.type === LINE_TYPE.WALLLINE.EAVES || line.attributes.type === LINE_TYPE.WALLLINE.HIPANDGABLE) { stroke = '#45CD7D' strokeWidth = 4 } else if (line.attributes.type === LINE_TYPE.WALLLINE.GABLE || line.attributes.type === LINE_TYPE.WALLLINE.JERKINHEAD) { stroke = '#3FBAE6' strokeWidth = 4 } else { stroke = '#000000' strokeWidth = 4 } line.set({ stroke, strokeWidth, }) addPitchText(line) } }) canvas.renderAll() } }, []) useEffect(() => { if (shapeNum !== 4) { return } if (!currentObject) { return } if (currentObject.name !== 'outerLine') { return } /*const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((line) => { line.set({ stroke: '#000000', strokeWidth: 4, }) })*/ currentObject.set({ stroke: '#EA10AC', strokeWidth: 4, }) canvas.renderAll() }, [currentObject]) useEffect(() => { if (shapeNum === 4) { 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) line.bringToFront() }) canvas?.renderAll() } setEavesOffset(500) setGableOffset(300) setSleeveOffset(300) setJerkinHeadWidth(800) setHipAndGableWidth(800) setShedWidth(300) }, [shapeNum]) const shapeMenu = [ { id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, // 용마루 { id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // 패턴A { id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // 패턴B { id: 4, name: getMessage('modal.roof.shape.setting.side') }, // 변별로 설정 { id: 5, name: getMessage('commons.west') }, // 서 { id: 6, name: getMessage('commons.east') }, // 동 { id: 7, name: getMessage('commons.south') }, // 남 { id: 8, name: getMessage('commons.north') }, // 북 ] const buttonMenu = [ { id: 1, name: getMessage('eaves') }, { id: 2, name: getMessage('gable') }, { id: 3, name: getMessage('wall') }, { id: 4, name: getMessage('hipandgable') }, { id: 5, name: getMessage('jerkinhead') }, { id: 6, name: getMessage('shed') }, ] const handleSave = () => { let outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine' && obj.visible) let direction if (outerLines.length < 2) { swalFire({ text: '외벽선이 없습니다.', icon: 'error' }) return } if ([1, 2, 3, 5, 6, 7, 8].includes(shapeNum)) { // 변별로 설정이 아닌 경우 경사를 지붕재에 적용해주어야함 setRoofPitch() } switch (shapeNum) { case 1: { outerLines = saveRidge() break } case 2: { outerLines = saveAPattern() break } case 3: { outerLines = saveBPattern() break } case 4: { outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const pitch = outerLines.find((line) => line.attributes.type === LINE_TYPE.WALLLINE.SHED)?.attributes.pitch let isValid = outerLines.every((line) => line.attributes.isFixed) // 변별로 설정중 한쪽흐름일 경우 한쪽흐름의 pitch로 설정 if (pitch) { outerLines.forEach((line) => { if (line.attributes.type === LINE_TYPE.WALLLINE.SHED) { line.attributes = { ...line.attributes, pitch: pitchRef.current, onlyOffset: true, } } }) } if (!isValid) { swalFire({ text: '설정이 완료되지 않았습니다.', icon: 'error' }) return } break } case 5: { // 서쪽 initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') direction = 'west' outerLines.forEach((line) => { setWestAndEastRoof(line) if (outerLines[0].direction === 'bottom') { if (line.direction === 'bottom') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'top') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } else { if (line.direction === 'top') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'bottom') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } // hideLine(line) }) break } case 6: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') direction = 'east' outerLines.forEach((line) => { setWestAndEastRoof(line) if (outerLines[0].direction === 'bottom') { if (line.direction === 'top') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'bottom') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } else { if (line.direction === 'bottom') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'top') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } // hideLine(line) }) break } case 7: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') direction = 'south' outerLines.forEach((line) => { setSouthAndNorthRoof(line) if (outerLines[0].direction === 'bottom') { if (line.direction === 'right') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'left') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } else { if (line.direction === 'left') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'right') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } // hideLine(line) }) break } case 8: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') direction = 'north' outerLines.forEach((line) => { setSouthAndNorthRoof(line) if (outerLines[0].direction === 'bottom') { if (line.direction === 'left') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'right') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } else { if (line.direction === 'right') { line.attributes = { offset: eavesOffset / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.EAVES, } } if (line.direction === 'left') { line.attributes = { offset: shedWidth / 10, pitch: pitchRef.current, type: LINE_TYPE.WALLLINE.SHED, onlyOffset: true, } } } // hideLine(line) }) break } } // 기존 wallLine, roofBase 제거 canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.WALL) .forEach((line) => { canvas.remove(line) }) canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed) .forEach((obj) => { canvas.remove(...obj.innerLines) canvas.remove(obj) }) const removeTargets = canvas .getObjects() .filter( (obj) => (obj.name === 'pitchText' || obj.name === 'lengthText') && canvas.getObjects().find((parent) => parent.id === obj.parentId)?.name !== POLYGON_TYPE.ROOF, ) removeTargets.forEach((obj) => { canvas.remove(obj) }) const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction, originX: 'center', originY: 'center' }) polygon.lines = [...outerLines] addPitchTextsByOuterLines() const roof = drawRoofPolygon(polygon) canvas?.renderAll() roof.drawHelpLine(settingModalFirstOptions) isFixRef.current = true addPopup(id, 1, ) } const initLineSetting = () => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const tempPolygon = addPolygonByLines(outerLines) tempPolygon.lines.forEach((line) => { outerLines.forEach((outerLine) => { if ( (line.startPoint === outerLine.startPoint && line.endPoint === outerLine.endPoint) || (line.startPoint === outerLine.endPoint && line.endPoint === outerLine.startPoint) ) { outerLine.direction = line.direction outerLine.idx = line.idx } }) }) // 첫번째 line의 방향이 right일 경우 if (outerLines[0].direction === 'right') { // top과 bottom의 방향을 바꾼다. outerLines.forEach((line) => { if (line.direction === 'top') { line.direction = 'bottom' } else if (line.direction === 'bottom') { line.direction = 'top' } }) } canvas.remove(tempPolygon) } // 저장된 경사를 addedRoof 첫번째요소, basicSettings의 selectedRoofMaterial에 적용 const setRoofPitch = () => { const newAddedRoofs = addedRoofs.map((roof, index) => { if (index === 0) { return { ...roof, pitch: Number(pitchRef.current), angle: getDegreeByChon(pitchRef.current) } } else { return { ...roof } } }) const newBasicSetting = { ...basicSetting, selectedRoofMaterial: { ...newAddedRoofs[0] } } try { basicSettingSave(newAddedRoofs) setBasicSetting(newBasicSetting) setAddedRoofs(newAddedRoofs) } catch (error) { swalFire({ text: error.message, icon: 'error' }) } } /** * 지붕면 할당 저장 */ const basicSettingSave = async (newAddedRoofs) => { try { const patternData = { objectNo: correntObjectNo, planNo: Number(basicSetting.planNo), roofSizeSet: Number(basicSetting.roofSizeSet), roofAngleSet: basicSetting.roofAngleSet, roofAllocationList: newAddedRoofs.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 }) //Recoil 설정 //setCanvasSetting({ ...basicSetting }) } catch (error) { swalFire({ text: error.message, icon: 'error' }) } } // 동, 서 선택 시 가로라인을 케라바로 설정 const setWestAndEastRoof = (line) => { if (line.direction === 'left' || line.direction === 'right') { line.attributes = { offset: gableOffset / 10, type: LINE_TYPE.WALLLINE.GABLE, } } } // 남, 북 선택 시 세로라인을 케라바로 설정 const setSouthAndNorthRoof = (line) => { if (line.direction === 'top' || line.direction === 'bottom') { line.attributes = { offset: gableOffset / 10, type: LINE_TYPE.WALLLINE.GABLE, } } } const saveRidge = () => { // 용마루 저장 const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((line) => { line.attributes = { offset: eavesOffset / 10, pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch), type: LINE_TYPE.WALLLINE.EAVES, onlyOffset: false, } // hideLine(line) }) return outerLines } // 패턴 A : 가로선이 모두 케라바 const saveAPattern = () => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((line) => { if (line.direction === 'left' || line.direction === 'right') { line.attributes = { offset: gableOffset / 10, type: LINE_TYPE.WALLLINE.GABLE, } } else if (line.direction === 'top' || line.direction === 'bottom') { line.attributes = { offset: eavesOffset / 10, pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch), type: LINE_TYPE.WALLLINE.EAVES, } } // hideLine(line) }) return outerLines } // 패턴 B : 세로선이 모두 케라바 const saveBPattern = () => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((line) => { if (line.direction === 'top' || line.direction === 'bottom') { line.attributes = { offset: gableOffset / 10, type: LINE_TYPE.WALLLINE.GABLE, } } else if (line.direction === 'left' || line.direction === 'right') { line.attributes = { offset: eavesOffset / 10, pitch: currentAngleType === ANGLE_TYPE.SLOPE ? pitch : getChonByDegree(pitch), type: LINE_TYPE.WALLLINE.EAVES, } } // hideLine(line) }) return outerLines } // 변별로 설정 팝업 내 적용 const handleConfirm = () => { const selectedLine = canvas?.getActiveObject() if (!selectedLine) { return } let attributes switch (buttonAct) { case 1: { // 처마 attributes = { type: LINE_TYPE.WALLLINE.EAVES, pitch: pitchRef.current, offset: eavesOffset / 10, } selectedLine.attributes = { ...attributes, isFixed: true } addPitchText(currentObject) selectedLine.set({ strokeWidth: 4 }) selectedLine.set({ stroke: '#45CD7D' }) break } case 2: { // 케라바 attributes = { type: LINE_TYPE.WALLLINE.GABLE, offset: gableOffset / 10, } selectedLine.attributes = { ...attributes, isFixed: true } selectedLine.set({ strokeWidth: 4 }) selectedLine.set({ stroke: '#3FBAE6' }) break } case 3: { // 벽 attributes = { type: LINE_TYPE.WALLLINE.WALL, width: hasSleeve === '0' ? 0 : sleeveOffset / 10, sleeve: hasSleeve === '1', } selectedLine.attributes = { ...attributes, isFixed: true } break } case 4: { // 팔작지붕 attributes = { type: LINE_TYPE.WALLLINE.HIPANDGABLE, pitch: pitchRef.current, offset: eavesOffset / 10, width: hipAndGableWidth / 10, } selectedLine.attributes = { ...attributes, isFixed: true } addPitchText(currentObject) selectedLine.set({ strokeWidth: 4 }) selectedLine.set({ stroke: '#45CD7D' }) break } case 5: { // 반절처 attributes = { type: LINE_TYPE.WALLLINE.JERKINHEAD, offset: gableOffset / 10, width: jerkinHeadWidth / 10, pitch: jerkinHeadPitchRef.current, } selectedLine.attributes = { ...attributes, isFixed: true } addPitchText(currentObject) selectedLine.set({ strokeWidth: 4 }) selectedLine.set({ stroke: '#3FBAE6' }) break } case 6: { // 한쪽흐름 attributes = { type: LINE_TYPE.WALLLINE.SHED, pitch: shedPitchRef.current, width: shedWidth / 10, } selectedLine.attributes = { ...attributes, isFixed: true } addPitchText(currentObject) selectedLine.set({ strokeWidth: 4 }) selectedLine.set({ stroke: '#000000' }) break } } canvas.renderAll() nextLineFocus(selectedLine) } const nextLineFocus = (selectedLine) => { const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') const index = lines.findIndex((line) => line.idx === selectedLine.idx) const nextLine = lines[index + 1] || lines[0] if (nextLine.attributes?.isFixed) { canvas.discardActiveObject() return } history.current.push(selectedLine) canvas.setActiveObject(nextLine) } // 변별로 설정 내 일변 전으로 돌아가기 const handleRollBack = () => { if (history.current.length === 0) { return } const lastLine = history.current.pop() currentObject.set({ stroke: '#000000', strokeWidth: 4, }) lastLine.set({ stroke: '#000000', strokeWidth: 4, }) canvas.setActiveObject(lastLine) canvas.renderAll() } return { shapeNum, setShapeNum, shapeMenu, handleSave, buttonMenu, pitch, setPitch, eavesOffset, setEavesOffset, gableOffset, setGableOffset, sleeveOffset, setSleeveOffset, jerkinHeadWidth, setJerkinHeadWidth, jerkinHeadPitch, setJerkinHeadPitch, hipAndGableWidth, setHipAndGableWidth, shedWidth, setShedWidth, hasSleeve, setHasSleeve, buttonAct, setButtonAct, handleConfirm, handleRollBack, pitchText, shedPitch, setShedPitch, } }