diff --git a/src/common/common.js b/src/common/common.js index 61fdabd1..9d15e988 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -13,6 +13,7 @@ export const MENU = { MOVEMENT_SHAPE_UPDOWN: 'movementShapeUpdown', // 동선이동.형올림내림 OUTLINE_EDIT_OFFSET: 'outlineEditOffset', // 외벽선 편집 및 오프셋 ROOF_SHAPE_ALLOC: 'rootShapeAlloc', // 지붕면 항당 + ALL_REMOVE: 'allRemove', // 전체 삭제 DEFAULT: 'roofCoveringDefault', // 아무것도 선택 안할 경우 }, // 지붕덮개 BATCH_CANVAS: { diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 383c0ef9..4b718c05 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -66,12 +66,17 @@ export default function CanvasFrame() { canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () { canvasLoadInit() //config된 상태로 캔버스 객체를 그린다 canvas?.renderAll() // 캔버스를 다시 그립니다. + if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) { - setSelectedMenu('module') + setTimeout(() => { + setSelectedMenu('module') + }, 500) } else if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL).length > 0) { setSelectedMenu('outline') } else { - setSelectedMenu('surface') + setTimeout(() => { + setSelectedMenu('surface') + }, 500) } const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) diff --git a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx index a84efccf..4c1f0991 100644 --- a/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx +++ b/src/components/floor-plan/modal/flowDirection/FlowDirectionSetting.jsx @@ -8,6 +8,7 @@ import { usePopup } from '@/hooks/usePopup' import { canvasState } from '@/store/canvasAtom' import { usePolygon } from '@/hooks/usePolygon' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' +import { useRoofFn } from '@/hooks/common/useRoofFn' const FLOW_DIRECTION_TYPE = { EIGHT_AZIMUTH: 'eightAzimuth', @@ -19,6 +20,7 @@ export default function FlowDirectionSetting(props) { const { id, pos = contextPopupPosition, target } = props const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() + const { setSurfaceShapePattern } = useRoofFn() const { changeSurfaceLineType } = useSurfaceShapeBatch({}) @@ -79,6 +81,7 @@ export default function FlowDirectionSetting(props) { surfaceCompass: orientation, surfaceCompassType: type, }) + setSurfaceShapePattern(roof, null, null, roof.roofMaterial) drawDirectionArrow(roof) canvas?.renderAll() changeSurfaceLineType(roof) diff --git a/src/hooks/common/useMenu.js b/src/hooks/common/useMenu.js index 7836b73b..40c1b6a0 100644 --- a/src/hooks/common/useMenu.js +++ b/src/hooks/common/useMenu.js @@ -66,6 +66,9 @@ export default function useMenu() { case MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC: addPopup(popupId, 1, ) break + case MENU.ROOF_COVERING.ALL_REMOVE: + deleteAllSurfacesAndObjects() + break } } diff --git a/src/hooks/common/useRoofFn.js b/src/hooks/common/useRoofFn.js index fe3841f7..eb640d76 100644 --- a/src/hooks/common/useRoofFn.js +++ b/src/hooks/common/useRoofFn.js @@ -6,6 +6,8 @@ import { POLYGON_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' import { outerLinePointsState } from '@/store/outerLineAtom' +import { usePolygon } from '@/hooks/usePolygon' +import { useText } from '@/hooks/useText' const ROOF_COLOR = { 0: 'rgb(199,240,213)', @@ -13,6 +15,7 @@ const ROOF_COLOR = { 2: 'rgb(187,204,255)', 3: 'rgb(228,202,255)', } + export function useRoofFn() { const canvas = useRecoilValue(canvasState) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) @@ -20,6 +23,8 @@ export function useRoofFn() { const { addCanvasMouseEventListener, initEvent } = useEvent() const resetPoints = useResetRecoilState(outerLinePointsState) const { addPitchText } = useLine() + const { setPolygonLinesActualSize } = usePolygon() + const { changeCorridorDimensionText } = useText() //면형상 선택 클릭시 지붕 패턴 입히기 function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) { @@ -47,6 +52,7 @@ export function useRoofFn() { let width = (roofMaterial.width || 226) / 10 let height = (roofMaterial.length || 158) / 10 + const index = roofMaterial.index ?? 0 let roofStyle = 2 const inputPatternSize = { width: width, height: height } //임시 사이즈 @@ -172,6 +178,8 @@ export function useRoofFn() { polygon.set('fill', null) polygon.set('fill', pattern) polygon.roofMaterial = roofMaterial + setPolygonLinesActualSize(polygon) + changeCorridorDimensionText() polygon.canvas?.renderAll() } catch (e) { console.log(e) @@ -306,7 +314,15 @@ export function useRoofFn() { } function convertAbsolutePoint(area) { - return area.points.map((p) => fabric.util.transformPoint({ x: p.x - area.pathOffset.x, y: p.y - area.pathOffset.y }, area.calcTransformMatrix())) + return area.points.map((p) => + fabric.util.transformPoint( + { + x: p.x - area.pathOffset.x, + y: p.y - area.pathOffset.y, + }, + area.calcTransformMatrix(), + ), + ) } const removeOuterLines = (currentMousePos) => { diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index 7a700dc0..933a423e 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -63,6 +63,7 @@ export function useModuleBasicSetting(tabNum) { const { checkModuleDisjointSurface } = useTurf() useEffect(() => { + initEvent() return () => { //수동 설치시 초기화 removeMouseEvent('mouse:up') @@ -139,7 +140,7 @@ export function useModuleBasicSetting(tabNum) { roof.lines.forEach((line) => { line.attributes = { ...line.attributes, - offset: getOffset(offsetObjects.addRoof, line, roof.pitch, roof.from), + offset: getOffset(offsetObjects.addRoof, line, roof.roofMaterial.pitch), } }) //배치면 설치 영역 @@ -209,9 +210,9 @@ export function useModuleBasicSetting(tabNum) { const calculateHeightRate = 1 / Math.cos((degree * Math.PI) / 180) const calculateValue = calculateHeightRate / calculateExpression(degree) - const eavesResult = from === 'roofCover' ? (data.eavesMargin * Math.cos((degree * Math.PI) / 180)) / 10 : data.eavesMargin / 10 - const ridgeResult = from === 'roofCover' ? (data.ridgeMargin * Math.cos((degree * Math.PI) / 180)) / 10 : data.ridgeMargin / 10 - const kerabaMargin = from === 'roofCover' && isDiagonal ? data.kerabaMargin / calculateValue / 10 : data.kerabaMargin / 10 + const eavesResult = +roofSizeSet === 1 ? (data.eavesMargin * Math.cos((degree * Math.PI) / 180)) / 10 : data.eavesMargin / 10 + const ridgeResult = +roofSizeSet === 1 ? (data.ridgeMargin * Math.cos((degree * Math.PI) / 180)) / 10 : data.ridgeMargin / 10 + const kerabaMargin = +roofSizeSet === 1 && isDiagonal ? data.kerabaMargin / calculateValue / 10 : data.kerabaMargin / 10 switch (line.attributes.type) { case LINE_TYPE.WALLLINE.EAVES: @@ -232,7 +233,7 @@ export function useModuleBasicSetting(tabNum) { //가대 상세 데이터 기준으로 모듈 설치 배치면 생성 const makeModuleInstArea = (roof, trestleDetail) => { //지붕 객체 반환 - + if (tabNum == 3) { if (!roof) { return @@ -556,7 +557,7 @@ export function useModuleBasicSetting(tabNum) { let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth let { width, height } = - moduleSetupSurface.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection) : { width: tmpWidth, height: tmpHeight } @@ -1056,11 +1057,11 @@ export function useModuleBasicSetting(tabNum) { let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth width = - moduleSetupSurface.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection).width : tmpWidth height = - moduleSetupSurface.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection).height : tmpHeight @@ -1386,11 +1387,11 @@ export function useModuleBasicSetting(tabNum) { //복시도, 실치수에 따른 모듈 높이 조정 width = - trestlePolygon.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(trestlePolygon.roofMaterial.pitch), flowDirection).width : tmpWidth height = - trestlePolygon.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(trestlePolygon.roofMaterial.pitch), flowDirection).height : tmpHeight @@ -2974,11 +2975,11 @@ export function useModuleBasicSetting(tabNum) { const pointY2 = top //디버깅 - const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { - stroke: 'red', - strokeWidth: 1, - selectable: true, - }) + // const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { + // stroke: 'red', + // strokeWidth: 1, + // selectable: true, + // }) // canvas?.add(finalLine) // canvas?.renderAll() @@ -3106,11 +3107,11 @@ export function useModuleBasicSetting(tabNum) { const pointY2 = coords[2].y + ((coords[2].x - top) / (coords[2].x - coords[1].x)) * (coords[1].y - coords[2].y) //디버깅용 - const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { - stroke: 'red', - strokeWidth: 1, - selectable: true, - }) + // const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], { + // stroke: 'red', + // strokeWidth: 1, + // selectable: true, + // }) // canvas?.add(finalLine) // canvas?.renderAll() @@ -3308,7 +3309,7 @@ export function useModuleBasicSetting(tabNum) { let tmpHeight = flowDirection === 'south' || flowDirection === 'north' ? moduleHeight : moduleWidth let { width, height } = - moduleSetupSurface.from === 'roofCover' + +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurfaces[i].roofMaterial.pitch), flowDirection) : { width: tmpWidth, height: tmpHeight } @@ -4027,7 +4028,7 @@ export function useModuleBasicSetting(tabNum) { 10 } - return moduleSetupSurface.from === 'roofCover' + return +roofSizeSet === 1 ? calculateVisibleModuleHeight(tmpWidth, tmpHeight, getDegreeByChon(moduleSetupSurface.roofMaterial.pitch), moduleSetupSurface.direction) : { width: tmpWidth, height: tmpHeight } } diff --git a/src/hooks/module/useTrestle.js b/src/hooks/module/useTrestle.js index 05325822..73fb92f8 100644 --- a/src/hooks/module/useTrestle.js +++ b/src/hooks/module/useTrestle.js @@ -82,7 +82,6 @@ export const useTrestle = () => { } let rackInfos = [] - if (rack) { rackInfos = Object.keys(rack).map((key) => { return { key, value: rack[key] } @@ -2484,8 +2483,8 @@ export const useTrestle = () => { // 각도에 따른 길이 반환 function getTrestleLength(length, degree, surface) { - if (surface.from !== 'roofCover') { - // 지붕덮개로부터 온게 아니면 그냥 length 리턴 + if (+roofSizeSet !== 1) { + // 복시도 입력이 아닌경우 그냥 길이 return return length } const radians = (degree * Math.PI) / 180 diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js index e2742ff2..9b060fde 100644 --- a/src/hooks/option/useCanvasSetting.js +++ b/src/hooks/option/useCanvasSetting.js @@ -18,7 +18,6 @@ import { basicSettingState, correntObjectNoState, corridorDimensionSelector, - fetchRoofMaterialsState, roofMaterialsAtom, selectedRoofMaterialSelector, settingModalFirstOptionsState, @@ -41,6 +40,8 @@ import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupSta import { v4 as uuidv4 } from 'uuid' import { useEvent } from '@/hooks/useEvent' import { logger } from '@/util/logger' +import { useText } from '@/hooks/useText' +import { usePolygon } from '@/hooks/usePolygon' const defaultDotLineGridSetting = { INTERVAL: { @@ -118,7 +119,6 @@ export function useCanvasSetting(executeEffect = true) { const { getRoofMaterialList, getModuleTypeItemList } = useMasterController() const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom) const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) - const [fetchRoofMaterials, setFetchRoofMaterials] = useRecoilState(fetchRoofMaterialsState) const setCurrentMenu = useSetRecoilState(currentMenuState) const resetModuleSelectionData = useResetRecoilState(moduleSelectionDataState) /* 다음으로 넘어가는 최종 데이터 */ @@ -133,6 +133,9 @@ export function useCanvasSetting(executeEffect = true) { const { addPopup } = usePopup() const [popupId, setPopupId] = useState(uuidv4()) + const { changeCorridorDimensionText } = useText() + const { setPolygonLinesActualSize } = usePolygon() + const SelectOptions = [ { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, { id: 2, name: '1/2', value: 1 / 2 }, @@ -197,7 +200,7 @@ export function useCanvasSetting(executeEffect = true) { } }, [addedRoofs]) - useEffect(() => { + /*useEffect(() => { if (!executeEffect) { return } @@ -212,7 +215,7 @@ export function useCanvasSetting(executeEffect = true) { setAddedRoofs(newAddedRoofs) } setBasicSettings({ ...basicSetting, selectedRoofMaterial: selectedRoofMaterial }) - }, [roofMaterials]) + }, [roofMaterials])*/ useEffect(() => { if (!canvas) { @@ -221,39 +224,13 @@ export function useCanvasSetting(executeEffect = true) { if (!executeEffect) { return } - const { column } = corridorDimension - const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText') - const group = canvas.getObjects().filter((obj) => obj.type === 'group') - group.forEach((obj) => { - obj._objects - .filter((obj2) => obj2.name === 'lengthText') - .forEach((obj3) => { - lengthTexts.push(obj3) - }) - }) - - switch (column) { - case 'corridorDimension': - lengthTexts.forEach((obj) => { - if (obj.planeSize) { - obj.set({ text: obj.planeSize.toString() }) - } - }) - break - case 'realDimension': - lengthTexts.forEach((obj) => { - if (obj.actualSize) { - obj.set({ text: obj.actualSize.toString() }) - } - }) - break - case 'noneDimension': - lengthTexts.forEach((obj) => { - obj.set({ text: '' }) - }) - break + const roofs = canvasObjects.filter((obj) => obj.name === POLYGON_TYPE.ROOF) + if (roofs.length > 0) { + roofs.forEach((roof) => { + setPolygonLinesActualSize(roof) + }) + changeCorridorDimensionText() } - canvas?.renderAll() }, [corridorDimension]) useEffect(() => { @@ -448,17 +425,18 @@ export function useCanvasSetting(executeEffect = true) { } if (addRoofs.length > 0) { - setAddedRoofs(addRoofs) - - setBasicSettings({ - ...basicSetting, - roofMaterials: addRoofs[0], - planNo: roofsRow[0].planNo, - roofSizeSet: roofsRow[0].roofSizeSet, - roofAngleSet: roofsRow[0].roofAngleSet, - roofsData: roofsArray, - selectedRoofMaterial: addRoofs.find((roof) => roof.selected), + setBasicSettings((prev) => { + return { + ...basicSetting, + roofMaterials: addRoofs[0], + planNo: roofsRow[0].planNo, + roofSizeSet: roofsRow[0].roofSizeSet, + roofAngleSet: roofsRow[0].roofAngleSet, + roofsData: roofsArray, + selectedRoofMaterial: addRoofs.find((roof) => roof.selected), + } }) + setAddedRoofs(addRoofs) setCanvasSetting({ ...basicSetting, diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index 417286e4..3fef0ee9 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -28,6 +28,7 @@ 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' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) @@ -60,6 +61,7 @@ export function useRoofAllocationSetting(id) { const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState) const resetPoints = useResetRecoilState(outerLinePointsState) const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector) + const { changeCorridorDimensionText } = useText() useEffect(() => { /** 배치면 초기설정에서 선택한 지붕재 배열 설정 */ @@ -127,9 +129,9 @@ export function useRoofAllocationSetting(id) { } }) } else { - if(roofList.length > 0){ + if (roofList.length > 0) { roofsArray = roofList - }else{ + } else { roofsArray = [ { planNo: planNo, @@ -188,7 +190,6 @@ export function useRoofAllocationSetting(id) { }) //데이터 동기화 setCurrentRoofList(selectRoofs) - }) } catch (error) { console.error('Data fetching error:', error) @@ -459,6 +460,10 @@ export function useRoofAllocationSetting(id) { /** 모듈 선택 데이터 초기화 */ // modifyModuleSelectionData() setModuleSelectionData({ ...moduleSelectionData, roofConstructions: newRoofList }) + + setTimeout(() => { + changeCorridorDimensionText('realDimension') + }, 500) } /** diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js index 863de4bf..874526f8 100644 --- a/src/hooks/surface/useSurfaceShapeBatch.js +++ b/src/hooks/surface/useSurfaceShapeBatch.js @@ -1,9 +1,8 @@ 'use client' -import { useEffect } from 'react' -import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { useRecoilValue, useResetRecoilState } from 'recoil' import { canvasSettingState, canvasState, currentCanvasPlanState, globalPitchState } from '@/store/canvasAtom' -import { MENU, POLYGON_TYPE, LINE_TYPE } from '@/common/common' +import { LINE_TYPE, MENU, POLYGON_TYPE } from '@/common/common' import { getIntersectionPoint, toFixedWithoutRounding } from '@/util/canvas-util' import { degreesToRadians } from '@turf/turf' import { QPolygon } from '@/components/fabric/QPolygon' @@ -20,13 +19,12 @@ import { useRoofFn } from '@/hooks/common/useRoofFn' import { outerLinePointsState } from '@/store/outerLineAtom' import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' import { getBackGroundImage } from '@/lib/imageActions' -import PlacementSurfaceLineProperty from '@/components/floor-plan/modal/placementShape/PlacementSurfaceLineProperty' -import { v4 as uuidv4 } from 'uuid' import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' +import { useText } from '@/hooks/useText' export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { const { getMessage } = useMessage() - const { drawDirectionArrow, addPolygon } = usePolygon() + const { drawDirectionArrow, addPolygon, addLengthText } = usePolygon() const lengthTextFont = useRecoilValue(fontSelector('lengthText')) const resetOuterLinePoints = useResetRecoilState(outerLinePointsState) const resetPlacementShapeDrawingPoints = useResetRecoilState(placementShapeDrawingPointsState) @@ -40,6 +38,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext) const { addPopup, closePopup } = usePopup() const { setSurfaceShapePattern } = useRoofFn() + const { changeCorridorDimensionText } = useText() const currentCanvasPlan = useRecoilValue(currentCanvasPlanState) const { fetchSettings } = useCanvasSetting(false) @@ -848,7 +847,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { const selection = new fabric.ActiveSelection(selectionArray, { canvas: canvas, - draggable: true, + // draggable: true, lockMovementX: false, // X축 이동 허용 lockMovementY: false, // Y축 이동 허용 originX: 'center', @@ -858,7 +857,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { canvas.setActiveObject(selection) addCanvasMouseEventListener('mouse:up', (e) => { - canvas.selection = true + canvas.selection = false canvas.discardActiveObject() // 모든 선택 해제 canvas.requestRenderAll() // 화면 업데이트 @@ -875,10 +874,13 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) { } }) - canvas.renderAll() - roof.fire('polygonMoved') + // roof.fire('polygonMoved') + roof.fire('modified') drawDirectionArrow(roof) + changeCorridorDimensionText() + addLengthText(roof) initEvent() + canvas.renderAll() }) } } diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 2acc929f..d991a2cf 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -5,6 +5,8 @@ import { canvasSizeState, canvasState, canvasZoomState, currentMenuState, curren import { QPolygon } from '@/components/fabric/QPolygon' import { fontSelector } from '@/store/fontAtom' import { MENU, POLYGON_TYPE } from '@/common/common' +import { useText } from '@/hooks/useText' +import { usePolygon } from '@/hooks/usePolygon' // 캔버스에 필요한 이벤트 export function useCanvasEvent() { @@ -15,6 +17,8 @@ export function useCanvasEvent() { const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const lengthTextOption = useRecoilValue(fontSelector('lengthText')) const currentMenu = useRecoilValue(currentMenuState) + const { changeCorridorDimensionText } = useText() + const { setPolygonLinesActualSize } = usePolygon() useEffect(() => { canvas?.setZoom(canvasZoom / 100) @@ -63,6 +67,13 @@ export function useCanvasEvent() { textObjs.forEach((obj) => { obj.bringToFront() }) + + if (target.name === POLYGON_TYPE.ROOF) { + setTimeout(() => { + setPolygonLinesActualSize(target) + changeCorridorDimensionText() + }, 300) + } } if (target.name === 'cell') { @@ -116,48 +127,6 @@ export function useCanvasEvent() { target.setControlVisible(controlKey, false) }) }) - /*target.on('editing:exited', () => { - if (isNaN(target.text.trim())) { - target.set({ text: previousValue }) - canvas?.renderAll() - return - } - const updatedValue = parseFloat(target.text.trim()) - const targetParent = target.parent - const points = targetParent.getCurrentPoints() - const i = target.idx // Assuming target.index gives the index of the point - - const startPoint = points[i] - const endPoint = points[(i + 1) % points.length] - - const dx = endPoint.x - startPoint.x - const dy = endPoint.y - startPoint.y - - const currentLength = Math.sqrt(dx * dx + dy * dy) - const scaleFactor = updatedValue / currentLength - - const newEndPoint = { - x: startPoint.x + dx * scaleFactor, - y: startPoint.y + dy * scaleFactor, - } - - const newPoints = [...points] - newPoints[(i + 1) % points.length] = newEndPoint - - for (let idx = i + 1; idx < points.length; idx++) { - if (newPoints[idx].x === endPoint.x) { - newPoints[idx].x = newEndPoint.x - } else if (newPoints[idx].y === endPoint.y) { - newPoints[idx].y = newEndPoint.y - } - } - - const newPolygon = new QPolygon(newPoints, targetParent.initOptions) - canvas?.add(newPolygon) - canvas?.remove(targetParent) - canvas?.renderAll() - })*/ - target.on('moving', (e) => { target.uuid = uuidv4() diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index 9d1a48eb..58f87e7d 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -1,15 +1,18 @@ import { useRecoilValue } from 'recoil' import { - ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, + globalPitchState, pitchTextSelector, showAngleUnitSelector, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' -import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util' +import { basicSettingState } from '@/store/settingAtom' +import { calcLineActualSize } from '@/util/qpolygon-utils' +import { getDegreeByChon } from '@/util/canvas-util' +import { useText } from '@/hooks/useText' export const useLine = () => { const canvas = useRecoilValue(canvasState) @@ -18,6 +21,10 @@ export const useLine = () => { const currentAngleType = useRecoilValue(currentAngleTypeSelector) const pitchText = useRecoilValue(pitchTextSelector) const angleUnit = useRecoilValue(showAngleUnitSelector) + const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet + const globalPitch = useRecoilValue(globalPitchState) + + const { changeCorridorDimensionText } = useText() const addLine = (points = [], options) => { const line = new QLine(points, { @@ -151,6 +158,57 @@ export const useLine = () => { }) } + /** + * 복도치수, 실제치수에 따라 actualSize를 설정한다. + * @param line + * @param direction polygon의 방향 + * @param pitch + */ + const setActualSize = (line, direction, pitch = globalPitch) => { + const { x1, y1, x2, y2 } = line + + const isHorizontal = y1 === y2 + const isVertical = x1 === x2 + const isDiagonal = !isHorizontal && !isVertical + const lineLength = line.getLength() + + line.attributes = { ...line.attributes, planeSize: line.getLength(), actualSize: line.getLength() } + + if (+roofSizeSet === 1) { + if (direction === 'south' || direction === 'north') { + if (isVertical) { + line.attributes = { + ...line.attributes, + actualSize: calcLineActualSize(line, getDegreeByChon(pitch)), + } + } else if (isDiagonal) { + const yLength = Math.abs(y2 - y1) * 10 + + const h = yLength * Math.tan(getDegreeByChon(pitch) * (Math.PI / 180)) + + const actualSize = Math.sqrt(h ** 2 + lineLength ** 2) + line.attributes = { ...line.attributes, actualSize: actualSize } + } + } else if (direction === 'west' || direction === 'east') { + if (isHorizontal) { + line.attributes = { + ...line.attributes, + actualSize: calcLineActualSize(line, getDegreeByChon(pitch)), + } + } else if (isDiagonal) { + const xLength = Math.abs(x2 - x1) * 10 + + const h = xLength * Math.tan(getDegreeByChon(pitch) * (Math.PI / 180)) + + const actualSize = Math.sqrt(h ** 2 + lineLength ** 2) + line.attributes = { ...line.attributes, actualSize: actualSize } + } + } + } + + line.attributes = { ...line.attributes, actualSize: Number(line.attributes.actualSize.toFixed(0)) } + } + return { addLine, removeLine, @@ -160,5 +218,6 @@ export const useLine = () => { removePitchText, addPitchTextsByOuterLines, getLengthByLine, + setActualSize, } } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 12d7e188..a1cbc4f0 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -4,7 +4,7 @@ import { fabric } from 'fabric' import { calculateIntersection, findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' -import { flowDisplaySelector } from '@/store/settingAtom' +import { basicSettingState, flowDisplaySelector } from '@/store/settingAtom' import { fontSelector } from '@/store/fontAtom' import { QLine } from '@/components/fabric/QLine' import { LINE_TYPE, POLYGON_TYPE } from '@/common/common' @@ -18,6 +18,9 @@ export const usePolygon = () => { const currentAngleType = useRecoilValue(currentAngleTypeSelector) const pitchText = useRecoilValue(pitchTextSelector) const globalPitch = useRecoilValue(globalPitchState) + const roofSizeSet = useRecoilValue(basicSettingState).roofSizeSet + + const { setActualSize } = useLine() const { getLengthByLine } = useLine() @@ -86,27 +89,30 @@ export const usePolygon = () => { const maxY = line.top + line.length const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI - const text = new fabric.Textbox(planeSize ? planeSize.toString() : length.toString(), { - left: left, - top: top, - fontSize: lengthTextFontOptions.fontSize.value, - minX, - maxX, - minY, - maxY, - parentDirection: line.direction, - parentDegree: degree, - parentId: polygon.id, - planeSize: planeSize ?? length, - actualSize: actualSize ?? length, - editable: false, - selectable: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - parent: polygon, - name: 'lengthText', - }) + const text = new fabric.Textbox( + +roofSizeSet === 1 ? (actualSize ? actualSize.toString() : length.toString()) : planeSize ? planeSize.toString() : length.toString(), + { + left: left, + top: top, + fontSize: lengthTextFontOptions.fontSize.value, + minX, + maxX, + minY, + maxY, + parentDirection: line.direction, + parentDegree: degree, + parentId: polygon.id, + planeSize: planeSize ?? length, + actualSize: actualSize ?? length, + editable: false, + selectable: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + parent: polygon, + name: 'lengthText', + }, + ) polygon.texts.push(text) canvas.add(text) }) @@ -921,12 +927,69 @@ export const usePolygon = () => { } return !shouldRemove }) - + // 중복된 라인들을 canvas에서 제거 linesToRemove.forEach((line) => { canvas.remove(line) }) + // innerLines가 합쳐졌을 때 polygonLine과 같은 경우 그 polygonLine의 need를 false로 변경 + const mergeOverlappingInnerLines = (lines) => { + const mergedLines = [] + const processed = new Set() + + lines.forEach((line, index) => { + if (processed.has(index)) return + + let currentLine = { ...line } + processed.add(index) + + // 현재 라인과 겹치는 다른 라인들을 찾아서 합치기 + for (let i = index + 1; i < lines.length; i++) { + if (processed.has(i)) continue + + const otherLine = lines[i] + if (checkLineOverlap(currentLine, otherLine)) { + // 두 라인을 합치기 - 가장 긴 범위로 확장 + const isVertical = Math.abs(currentLine.x1 - currentLine.x2) < 1 + + if (isVertical) { + const allYPoints = [currentLine.y1, currentLine.y2, otherLine.y1, otherLine.y2] + currentLine.y1 = Math.min(...allYPoints) + currentLine.y2 = Math.max(...allYPoints) + currentLine.x1 = currentLine.x2 = (currentLine.x1 + otherLine.x1) / 2 + } else { + const allXPoints = [currentLine.x1, currentLine.x2, otherLine.x1, otherLine.x2] + currentLine.x1 = Math.min(...allXPoints) + currentLine.x2 = Math.max(...allXPoints) + currentLine.y1 = currentLine.y2 = (currentLine.y1 + otherLine.y1) / 2 + } + + processed.add(i) + } + } + + mergedLines.push(currentLine) + }) + + return mergedLines + } + + const mergedInnerLines = mergeOverlappingInnerLines(innerLines) + + // 합쳐진 innerLine과 동일한 polygonLine의 need를 false로 설정 + polygonLines.forEach((polygonLine) => { + mergedInnerLines.forEach((mergedInnerLine) => { + const isSameLine = + (isSamePoint(polygonLine.startPoint, mergedInnerLine.startPoint) && isSamePoint(polygonLine.endPoint, mergedInnerLine.endPoint)) || + (isSamePoint(polygonLine.startPoint, mergedInnerLine.endPoint) && isSamePoint(polygonLine.endPoint, mergedInnerLine.startPoint)) + + if (isSameLine) { + polygonLine.need = false + } + }) + }) + canvas.renderAll() /*polygonLines.forEach((line) => { @@ -934,6 +997,7 @@ export const usePolygon = () => { canvas.add(line) }) canvas.renderAll()*/ + polygonLines = polygonLines.filter((line) => line.need) polygonLines.forEach((line) => { /*const originStroke = line.stroke @@ -1589,8 +1653,8 @@ export const usePolygon = () => { const remainingLines = [...allLines] // 사용 가능한 line들의 복사본 // isStart가 true인 line들만 시작점으로 사용 - const startLines = remainingLines.filter(line => line.attributes?.isStart === true) - + const startLines = remainingLines.filter((line) => line.attributes?.isStart === true) + startLines.forEach((startLine) => { // 현재 남아있는 line들로 그래프 생성 const graph = {} @@ -1615,13 +1679,13 @@ export const usePolygon = () => { const startPoint = { ...startLine.startPoint } // 시작점 let arrivalPoint = { ...startLine.endPoint } // 도착점 - + const roof = getPath(startPoint, arrivalPoint, graph) if (roof.length > 0) { roofs.push(roof) - + // 사용된 startLine을 remainingLines에서 제거 - const startLineIndex = remainingLines.findIndex(line => line === startLine) + const startLineIndex = remainingLines.findIndex((line) => line === startLine) if (startLineIndex !== -1) { remainingLines.splice(startLineIndex, 1) } @@ -1675,6 +1739,22 @@ export const usePolygon = () => { canvas.renderAll() } + /** + * 폴리곤의 라인 속성을 복도치수, 실제치수에 따라 actualSize 설정 + * @param polygon + */ + const setPolygonLinesActualSize = (polygon) => { + if (!polygon.lines || polygon.lines.length === 0 || !polygon.roofMaterial) { + return + } + + polygon.lines.forEach((line) => { + setActualSize(line, polygon.direction, +polygon.roofMaterial?.pitch) + }) + + addLengthText(polygon) + } + return { addPolygon, addPolygonByLines, @@ -1683,5 +1763,6 @@ export const usePolygon = () => { addLengthText, splitPolygonWithLines, splitPolygonWithSeparate, + setPolygonLinesActualSize, } } diff --git a/src/hooks/useText.js b/src/hooks/useText.js new file mode 100644 index 00000000..134ade4a --- /dev/null +++ b/src/hooks/useText.js @@ -0,0 +1,51 @@ +import { useRecoilValue } from 'recoil' +import { corridorDimensionSelector } from '@/store/settingAtom' +import { canvasState } from '@/store/canvasAtom' + +export function useText() { + const canvas = useRecoilValue(canvasState) + const corridorDimension = useRecoilValue(corridorDimensionSelector) + + const changeCorridorDimensionText = (columnText) => { + let { column } = corridorDimension + if (columnText) { + column = columnText + } + const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText') + const group = canvas.getObjects().filter((obj) => obj.type === 'group') + group.forEach((obj) => { + obj._objects + .filter((obj2) => obj2.name === 'lengthText') + .forEach((obj3) => { + lengthTexts.push(obj3) + }) + }) + + switch (column) { + case 'corridorDimension': + lengthTexts.forEach((obj) => { + if (obj.planeSize) { + obj.set({ text: obj.planeSize.toString() }) + } + }) + break + case 'realDimension': + lengthTexts.forEach((obj) => { + if (obj.actualSize) { + obj.set({ text: obj.actualSize.toString() }) + } + }) + break + case 'noneDimension': + lengthTexts.forEach((obj) => { + obj.set({ text: '' }) + }) + break + } + canvas?.renderAll() + } + + return { + changeCorridorDimensionText, + } +} diff --git a/src/lib/skeletons/index.ts b/src/lib/skeletons/index.ts index 8244b4cd..b1f2cdb5 100644 --- a/src/lib/skeletons/index.ts +++ b/src/lib/skeletons/index.ts @@ -1,9 +1,12 @@ -import {GeoJSONMultipolygon, List} from "./Utils"; -import Vector2d from "./Primitives/Vector2d"; -import SkeletonBuilder from "./SkeletonBuilder"; -import { Skeleton } from "./Skeleton"; -import EdgeResult from "./EdgeResult"; -import Edge from "./Circular/Edge"; -import Vertex from "./Circular/Vertex"; +// Types +export type { GeoJSONMultipolygon, List } from "./Utils"; +export type { Skeleton } from "./Skeleton"; + +// Values +export { default as Vector2d } from "./Primitives/Vector2d"; +export { default as SkeletonBuilder } from "./SkeletonBuilder"; +export { default as EdgeResult } from "./EdgeResult"; +export { default as Edge } from "./Circular/Edge"; +export { default as Vertex } from "./Circular/Vertex"; + -export {SkeletonBuilder, List, Vector2d, GeoJSONMultipolygon, Skeleton, EdgeResult, Edge, Vertex}; \ No newline at end of file diff --git a/src/locales/ja.json b/src/locales/ja.json index 44f723fd..5aa83d3f 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -57,6 +57,7 @@ "modal.movement.flow.line.top.right": "高さ変更:上、右", "plan.menu.roof.cover.outline.edit.offset": "外壁の編集とオフセット", "plan.menu.roof.cover.roof.surface.alloc": "屋根面の割り当て", + "plan.menu.roof.cover.roof.surface.all.remove": "伏せ図全削除", "plan.menu.roof.cover.roof.shape.edit": "屋根形状編集", "plan.menu.roof.cover.auxiliary.line.drawing": "補助線の作成", "modal.cover.outline.drawing": "外壁線の作成", diff --git a/src/locales/ko.json b/src/locales/ko.json index 83ed3f2e..35caabfd 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -57,6 +57,7 @@ "modal.movement.flow.line.top.right": "높이변경 : 위, 오른쪽", "plan.menu.roof.cover.outline.edit.offset": "외벽선 편집 및 오프셋", "plan.menu.roof.cover.roof.surface.alloc": "지붕면 할당", + "plan.menu.roof.cover.roof.surface.all.remove": "배치면 전체 삭제", "plan.menu.roof.cover.roof.shape.edit": "지붕형상 편집", "plan.menu.roof.cover.auxiliary.line.drawing": "보조선 작성", "modal.cover.outline.drawing": "외벽선 작성", diff --git a/src/store/menuAtom.js b/src/store/menuAtom.js index fdb50461..007e7950 100644 --- a/src/store/menuAtom.js +++ b/src/store/menuAtom.js @@ -23,9 +23,19 @@ export const menusState = atom({ }, { type: 'outline', name: 'plan.menu.roof.cover', icon: 'con02', title: MENU.ROOF_COVERING.DEFAULT }, { type: 'surface', name: 'plan.menu.placement.surface', icon: 'con03', title: MENU.BATCH_CANVAS.DEFAULT }, - { type: 'module', name: 'plan.menu.module.circuit.setting', icon: 'con04', title: MENU.MODULE_CIRCUIT_SETTING.DEFAULT }, + { + type: 'module', + name: 'plan.menu.module.circuit.setting', + icon: 'con04', + title: MENU.MODULE_CIRCUIT_SETTING.DEFAULT, + }, { type: 'estimate', name: 'plan.menu.estimate', icon: 'con06', title: MENU.ESTIMATE.DEFAULT }, - { type: 'simulation', name: 'plan.menu.simulation', icon: 'con05', title: MENU.POWER_GENERATION_SIMULATION.DEFAULT }, + { + type: 'simulation', + name: 'plan.menu.simulation', + icon: 'con05', + title: MENU.POWER_GENERATION_SIMULATION.DEFAULT, + }, ], }) @@ -37,16 +47,17 @@ export const subMenusState = atom({ // 지붕덮개 { id: 0, name: 'plan.menu.roof.cover.outline.drawing', menu: MENU.ROOF_COVERING.EXTERIOR_WALL_LINE }, { id: 1, name: 'plan.menu.roof.cover.roof.shape.setting', menu: MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS }, - // { - // id: 2, - // name: 'plan.menu.roof.cover.roof.shape.passivity.setting', - // menu: MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS, - // }, + { + id: 2, + name: 'plan.menu.roof.cover.roof.shape.passivity.setting', + menu: MENU.ROOF_COVERING.ROOF_SHAPE_PASSIVITY_SETTINGS, + }, { id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing', menu: MENU.ROOF_COVERING.HELP_LINE_DRAWING }, { id: 4, name: 'plan.menu.roof.cover.eaves.kerava.edit', menu: MENU.ROOF_COVERING.EAVES_KERAVA_EDIT }, { id: 5, name: 'plan.menu.roof.cover.movement.shape.updown', menu: MENU.ROOF_COVERING.MOVEMENT_SHAPE_UPDOWN }, { id: 6, name: 'plan.menu.roof.cover.outline.edit.offset', menu: MENU.ROOF_COVERING.OUTLINE_EDIT_OFFSET }, { id: 7, name: 'plan.menu.roof.cover.roof.surface.alloc', menu: MENU.ROOF_COVERING.ROOF_SHAPE_ALLOC }, + { id: 8, name: 'plan.menu.roof.cover.roof.surface.all.remove', menu: MENU.ROOF_COVERING.ALL_REMOVE }, ], surface: [ // 배치면 diff --git a/tsconfig.json b/tsconfig.json index e2d762e2..2a8f023b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,12 +14,10 @@ "incremental": true, "module": "esnext", "esModuleInterop": true, - "allowSyntheticDefaultImports": true, // 추가 - "moduleResolution": "bundler", // 변경: "node" → "bundler" + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", - "baseUrl": ".", // 추가 "plugins": [ { "name": "next"