diff --git a/src/common/common.js b/src/common/common.js index ca472a11..0fa678db 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -198,6 +198,10 @@ export const SAVE_KEY = [ 'isChidory', 'textVisible', 'groupPoints', + 'fontSize', + 'fontStyle', + 'fontWeight', + 'dormerAttributes', ] -export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] +export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] diff --git a/src/components/floor-plan/modal/object/ObjectSetting.jsx b/src/components/floor-plan/modal/object/ObjectSetting.jsx index 909aad99..b602f163 100644 --- a/src/components/floor-plan/modal/object/ObjectSetting.jsx +++ b/src/components/floor-plan/modal/object/ObjectSetting.jsx @@ -24,7 +24,6 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) { const { closePopup } = usePopup() const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) - //오브젝트 배치로 넘어오면 면형상 선택 불가 useEffect(() => { canvas.discardActiveObject() @@ -54,15 +53,41 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) { const applyObject = () => { if (surfaceShapePolygons.length === 0) { - swalFire({ text: '지붕이 없어요 지붕부터 그리세요', icon: 'error' }) + swalFire({ text: getMessage('batch.object.outside.roof'), icon: 'error' }) return } //개구배치, 그림자배치 if (buttonAct === 1 || buttonAct === 2) { - applyOpeningAndShadow(objectPlacement, buttonAct, surfaceShapePolygons) + applyOpeningAndShadow(objectPlacement, buttonAct) } else { - applyDormers(dormerPlacement, buttonAct, surfaceShapePolygons) + const height = dormerPlacement.heightRef.current !== null ? dormerPlacement.heightRef.current.value / 10 : 0 + const width = dormerPlacement.widthRef.current !== null ? dormerPlacement.widthRef.current.value / 10 : 0 //triangle일때는 없음 + const pitch = dormerPlacement.pitchRef.current !== null ? Number(dormerPlacement.pitchRef.current.value) : 0 + const offsetRef = + dormerPlacement.offsetRef.current !== null + ? dormerPlacement.offsetRef.current.value === '' + ? 0 + : parseInt(dormerPlacement.offsetRef.current.value) / 10 + : 0 + const offsetWidthRef = + dormerPlacement.offsetWidthRef.current !== null + ? dormerPlacement.offsetWidthRef.current.value === '' + ? 0 + : parseInt(dormerPlacement.offsetWidthRef.current.value) / 10 + : 0 + const directionRef = dormerPlacement.directionRef.current + + const dormerParams = { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + } + + applyDormers(dormerParams, buttonAct) } setIsHidden(true) } diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js index 089aeea9..19736e81 100644 --- a/src/hooks/common/useCanvasConfigInitialize.js +++ b/src/hooks/common/useCanvasConfigInitialize.js @@ -23,7 +23,7 @@ export function useCanvasConfigInitialize() { const {} = useFont() const {} = useGrid() const {} = useRoof() - const { drawDirectionArrow } = usePolygon() + const { drawDirectionArrow, addLengthText } = usePolygon() useEffect(() => { if (!canvas) return @@ -160,11 +160,15 @@ export function useCanvasConfigInitialize() { const objectsParentId = canvas.getObjects().filter((obj) => obj.groupId === id || obj.id === id)[0].parentId let objectArray = [] + let groupPoints = [] //그룹객체가 있으면 배열에 추가함 if (groupObjects) { groupObjects.forEach((obj) => { objectArray.push(obj) + if (obj.groupPoints) { + groupPoints = obj.groupPoints + } }) } @@ -187,9 +191,21 @@ export function useCanvasConfigInitialize() { }) canvas.add(group) + //도머등 그룹에 포인트가 있는애들한테 다시 넣어준다 + if (groupPoints.length > 0) { + group.set({ + groupPoints: groupPoints, + }) + } + //그룹 객체 재그룹 완료 group.getObjects().forEach((obj) => { obj.fire('modified') + if (obj.texts) { + obj.texts.forEach((text) => { + text.bringToFront() + }) + } }) }) } diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js index dbad7a88..85cbc2bd 100644 --- a/src/hooks/common/useCommonUtils.js +++ b/src/hooks/common/useCommonUtils.js @@ -10,6 +10,7 @@ import { v4 as uuidv4 } from 'uuid' import { usePopup } from '@/hooks/usePopup' import Distance from '@/components/floor-plan/modal/distance/Distance' import { usePolygon } from '@/hooks/usePolygon' +import { useObjectBatch } from '@/hooks/object/useObjectBatch' export function useCommonUtils() { const canvas = useRecoilValue(canvasState) @@ -21,6 +22,7 @@ export function useCommonUtils() { const [commonUtils, setCommonUtilsState] = useRecoilState(commonUtilsState) const { addPopup } = usePopup() const { drawDirectionArrow, addLengthText } = usePolygon() + const { applyDormers } = useObjectBatch({}) useEffect(() => { if (commonUtils.text) { @@ -557,58 +559,76 @@ export function useCommonUtils() { const commonCopyObject = (obj) => { if (obj) { - let clonedObj = null + if (obj.name.indexOf('triangleDormer') || obj.name.indexOf('pentagonDormer')) { + const dormerAttributes = obj._objects[0].dormerAttributes + const dormerName = obj._objects[0].name - obj.clone((cloned) => { - clonedObj = cloned - }) - - addCanvasMouseEventListener('mouse:move', (e) => { - const pointer = canvas?.getPointer(e.e) - if (!clonedObj) return - - canvas - .getObjects() - .filter((obj) => obj.name === 'clonedObj') - .forEach((obj) => canvas?.remove(obj)) - - clonedObj.set({ - left: pointer.x, - top: pointer.y, - name: 'clonedObj', - }) - canvas.add(clonedObj) - }) - - addCanvasMouseEventListener('mouse:down', (e) => { - clonedObj.set({ - lockMovementX: true, - lockMovementY: true, - name: obj.name, - editable: false, - id: uuidv4(), //복사된 객체라 새로 따준다 - }) - - //객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다 - if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() }) - - //배치면일 경우 - if (obj.name === 'roof') { - clonedObj.setCoords() - clonedObj.fire('polygonMoved') - clonedObj.set({ direction: obj.direction, directionText: obj.directionText, roofMaterial: obj.roofMaterial }) - - obj.lines.forEach((line, index) => { - clonedObj.lines[index].set({ attributes: line.attributes }) - }) - - canvas.renderAll() - addLengthText(clonedObj) //수치 추가 - drawDirectionArrow(clonedObj) //방향 화살표 추가 + const dormerParams = { + height: dormerAttributes.height, + width: dormerAttributes.width, + pitch: dormerAttributes.pitch, + offsetRef: dormerAttributes.offsetRef, + offsetWidthRef: dormerAttributes.offsetWidthRef, + directionRef: dormerAttributes.directionRef, } - initEvent() - }) + const buttonAct = dormerName == 'triangleDormer' ? 3 : 4 + + applyDormers(dormerParams, buttonAct) + } else { + let clonedObj = null + + obj.clone((cloned) => { + clonedObj = cloned + }) + + addCanvasMouseEventListener('mouse:move', (e) => { + const pointer = canvas?.getPointer(e.e) + if (!clonedObj) return + + canvas + .getObjects() + .filter((obj) => obj.name === 'clonedObj') + .forEach((obj) => canvas?.remove(obj)) + + clonedObj.set({ + left: pointer.x, + top: pointer.y, + name: 'clonedObj', + }) + canvas.add(clonedObj) + }) + + addCanvasMouseEventListener('mouse:down', (e) => { + clonedObj.set({ + lockMovementX: true, + lockMovementY: true, + name: obj.name, + editable: false, + id: uuidv4(), //복사된 객체라 새로 따준다 + }) + + //객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다 + if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() }) + + //배치면일 경우 + if (obj.name === 'roof') { + clonedObj.setCoords() + clonedObj.fire('polygonMoved') + clonedObj.set({ direction: obj.direction, directionText: obj.directionText, roofMaterial: obj.roofMaterial }) + + obj.lines.forEach((line, index) => { + clonedObj.lines[index].set({ attributes: line.attributes }) + }) + + canvas.renderAll() + addLengthText(clonedObj) //수치 추가 + drawDirectionArrow(clonedObj) //방향 화살표 추가 + } + + initEvent() + }) + } } } diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js index a7a03c42..37d8dc1d 100644 --- a/src/hooks/module/useModuleBasicSetting.js +++ b/src/hooks/module/useModuleBasicSetting.js @@ -391,11 +391,11 @@ export function useModuleBasicSetting(tabNum) { //모듈,회로에서 다른메뉴 -> 배치면으로 갈 경수 초기화 const restoreModuleInstArea = () => { //설치면 삭제 - const setupArea = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) + const setupArea = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE || obj.name === POLYGON_TYPE.OBJECT_SURFACE) //모듈 삭제 및 초기화 setupArea.forEach((obj) => { - if (obj.modules.length > 0) { + if (isObjectNotEmpty(obj.modules) && obj.modules.length > 0) { obj.modules.forEach((module) => { canvas.remove(module) }) diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js index 231f422c..ff9bf24d 100644 --- a/src/hooks/object/useObjectBatch.js +++ b/src/hooks/object/useObjectBatch.js @@ -1,11 +1,11 @@ 'use client' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { useMessage } from '@/hooks/useMessage' import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { BATCH_TYPE, INPUT_TYPE, POLYGON_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' -import { pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, triangleToPolygon } from '@/util/canvas-util' +import { pointsToTurfPolygon, polygonToTurfPolygon, rectToPolygon, triangleToPolygon, toFixedWithoutRounding } from '@/util/canvas-util' import { useSwal } from '@/hooks/useSwal' import * as turf from '@turf/turf' import { usePolygon } from '@/hooks/usePolygon' @@ -71,13 +71,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { } } - const applyOpeningAndShadow = (objectPlacement, buttonAct, surfaceShapePolygons) => { + const applyOpeningAndShadow = (objectPlacement, buttonAct) => { const selectedType = objectPlacement.typeRef.current.find((radio) => radio.checked).value const isCrossChecked = buttonAct === 1 ? objectPlacement.isCrossRef.current.checked : false const objName = buttonAct === 1 ? BATCH_TYPE.OPENING : BATCH_TYPE.SHADOW + const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) const objTempName = buttonAct === 1 ? BATCH_TYPE.OPENING_TEMP : BATCH_TYPE.SHADOW_TEMP let rect, isDown, origX, origY + let selectedSurface //프리입력 @@ -267,26 +269,14 @@ export function useObjectBatch({ isHidden, setIsHidden }) { * @param {*} surfaceShapePolygons * @returns */ - const applyDormers = (dormerPlacement, buttonAct, surfaceShapePolygons) => { + const applyDormers = ({ height, width, pitch, offsetRef, offsetWidthRef, directionRef }, buttonAct) => { const dormerName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER : BATCH_TYPE.PENTAGON_DORMER const dormerTempName = buttonAct === 3 ? BATCH_TYPE.TRIANGLE_DORMER_TEMP : BATCH_TYPE.PENTAGON_DORMER_TEMP - const height = dormerPlacement.heightRef.current !== null ? dormerPlacement.heightRef.current.value / 10 : 0 - const width = dormerPlacement.widthRef.current !== null ? dormerPlacement.widthRef.current.value / 10 : 0 //triangle일때는 없음 - const pitch = dormerPlacement.pitchRef.current !== null ? dormerPlacement.pitchRef.current.value : 0 - const offsetRef = - dormerPlacement.offsetRef.current !== null - ? dormerPlacement.offsetRef.current.value === '' - ? 0 - : parseInt(dormerPlacement.offsetRef.current.value) / 10 - : 0 - const offsetWidthRef = - dormerPlacement.offsetWidthRef.current !== null - ? dormerPlacement.offsetWidthRef.current.value === '' - ? 0 - : parseInt(dormerPlacement.offsetWidthRef.current.value) / 10 - : 0 - const directionRef = dormerPlacement.directionRef.current + let dormer, dormerOffset, isDown, selectedSurface, pentagonPoints, pentagonOffsetPoints + + const surfaceShapePolygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) + const id = uuidv4() if (height === '' || pitch === '' || height <= 0 || pitch <= 0) { @@ -296,8 +286,9 @@ export function useObjectBatch({ isHidden, setIsHidden }) { //삼각형 도머 if (buttonAct === 3) { - const bottomLength = height / (pitch * 0.25) - const bottomOffsetLength = (height + offsetRef) / (pitch * 0.25) + const bottomLength = Math.floor((height / Math.cos(Math.atan(pitch / 10))) * 10) / 10 + const bottomOffsetLength = Math.floor(((height + offsetRef) / Math.cos(Math.atan(pitch / 10))) * 10) / 10 + let groupDormerPoints = [] //나중에 offset을 위한 포인트 저장용 addCanvasMouseEventListener('mouse:move', (e) => { @@ -313,6 +304,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) { } }) + console.log('selectedSurface', selectedSurface) + let angle = 0 if (directionRef === 'left') { //서 @@ -433,6 +426,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, + groupPoints: groupPoints, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) const rightTriangle = new QPolygon(rightPoints, { @@ -452,6 +454,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { fontSize: lengthTextFont.fontSize.value, fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, + groupPoints: groupPoints, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) // canvas?.add(leftTriangle) @@ -489,6 +500,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, angle: originAngle, + pitch: pitch, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) } @@ -528,8 +548,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) { //(동의길이 깊이)+출폭(깊이)-[(입력한 폭값)/2+출폭(폭)]*(0.25*입력한 寸) const heightOffsetLength = height + offsetRef - (width / 2 + offsetWidthRef) * (pitch * 0.25) - let groupDormerPoints = [] - addCanvasMouseEventListener('mouse:move', (e) => { isDown = true if (!isDown) return @@ -672,6 +690,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { originY: 'center', name: dormerName, pitch: pitch, + groupPoints: groupPoints, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) const rightPentagon = new QPolygon(rightPoints, { @@ -689,6 +716,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { originY: 'center', name: dormerName, pitch: pitch, + groupPoints: groupPoints, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) // canvas?.add(leftPentagon) @@ -703,8 +739,6 @@ export function useObjectBatch({ isHidden, setIsHidden }) { let offsetPolygon - groupDormerPoints = groupPoints - if (offsetRef > 0) { canvas?.remove(dormer) @@ -725,6 +759,15 @@ export function useObjectBatch({ isHidden, setIsHidden }) { fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal', fontWeight: lengthTextFont.fontWeight.value, angle: originAngle, + groupPoints: groupPoints, + dormerAttributes: { + height: height, + width: width, + pitch: pitch, + offsetRef: offsetRef, + offsetWidthRef: offsetWidthRef, + directionRef: directionRef, + }, }) } @@ -738,7 +781,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) { groupYn: true, originX: 'center', originY: 'center', - groupPoints: groupDormerPoints, + groupPoints: groupPoints, }) canvas?.add(objectGroup) @@ -1191,6 +1234,26 @@ export function useObjectBatch({ isHidden, setIsHidden }) { } } + const dormerPlacement = { + widthRef: useRef(null), + heightRef: useRef(null), + pitchRef: useRef(null), + offsetRef: useRef(null), + offsetWidthRef: useRef(null), + directionRef: useRef(null), + } + + const copyOjbectDormer = () => { + const obj = canvas.getActiveObject() + if (obj) { + if (obj.name === 'triangleDormer') { + const offset = obj._objects.filter((item) => item.name === 'triangleDormerOffset') + } else { + //오각도머 + } + } + } + const dormerOffsetKeyEvent = (setArrow1, setArrow2) => { addDocumentEventListener('keydown', document, (e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 83f604ec..66116af8 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -1035,3 +1035,8 @@ export function calculateVisibleModuleHeight(sourceWidth, sourceHeight, angle, d height: Number(visibleHeight.toFixed(1)), // 소수점 두 자리로 고정 } } + +//숫자, 몇자리수 +export function toFixedWithoutRounding(number, decimals) { + return Math.floor(number * Math.pow(10, decimals)) / Math.pow(10, decimals) +}