+ {/* chart */}
+
+
+
@@ -237,7 +300,7 @@ export default function Simulator() {
{chartData.length > 0 ? (
{chartData.map((data) => (
- | {convertNumberToPriceDecimal(data)} |
+ {data} |
))}
) : (
diff --git a/src/hooks/common/useCanvasConfigInitialize.js b/src/hooks/common/useCanvasConfigInitialize.js
index a07d4a00..18d1a052 100644
--- a/src/hooks/common/useCanvasConfigInitialize.js
+++ b/src/hooks/common/useCanvasConfigInitialize.js
@@ -40,19 +40,19 @@ export function useCanvasConfigInitialize() {
const flowTexts = canvas.getObjects().filter((obj) => obj.name === 'flowText')
if (basicSetting.roofAngleSet === 'slope') {
offsetTexts.forEach((obj) => {
- obj.set({ text: `${obj.originText}-∠${obj.pitch}${angleUnit}` })
+ obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${angleUnit}` })
})
flowTexts.forEach((obj) => {
- obj.set({ text: `${obj.originText}-∠${obj.pitch}${pitchText}` })
+ obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${obj.pitch}${pitchText}` })
})
}
if (basicSetting.roofAngleSet === 'flat') {
offsetTexts.forEach((obj) => {
- obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${angleUnit}` })
+ obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${angleUnit}` })
})
flowTexts.forEach((obj) => {
- obj.set({ text: `${obj.originText}-∠${getDegreeByChon(obj.pitch)}${pitchText}` })
+ obj.set({ text: `${!obj.originText ? '' : obj.originText + '-'}∠${getDegreeByChon(obj.pitch)}${pitchText}` })
})
}
diff --git a/src/hooks/common/useCanvasMenu.js b/src/hooks/common/useCanvasMenu.js
index f8758e3e..7a883e55 100644
--- a/src/hooks/common/useCanvasMenu.js
+++ b/src/hooks/common/useCanvasMenu.js
@@ -1,8 +1,30 @@
import { menuNumberState } from '@/store/menuAtom'
-import { useRecoilState } from 'recoil'
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { useEffect } from 'react'
+import { canvasState } from '@/store/canvasAtom'
+import { usePolygon } from '@/hooks/usePolygon'
+import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => {
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
+ const canvas = useRecoilValue(canvasState)
+ const { drawDirectionArrow } = usePolygon()
+
+ useEffect(() => {
+ /*
+ * 모듈,회로 구성을 벗어나면 방향 표시 초기화 필요
+ * */
+ if (!canvas) return
+ if (![4, 5].includes(menuNumber)) {
+ canvas
+ .getObjects()
+ .filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ .forEach((obj) => {
+ obj.set('moduleCompass', null)
+ drawDirectionArrow(obj)
+ })
+ }
+ }, [menuNumber])
return {
menuNumber,
diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js
index 2902e0c3..1f9c2063 100644
--- a/src/hooks/common/useCommonUtils.js
+++ b/src/hooks/common/useCommonUtils.js
@@ -1,21 +1,20 @@
import { useEffect } from 'react'
-import { useRecoilValue, useRecoilState } from 'recoil'
+import { useRecoilState, useRecoilValue } from 'recoil'
import { wordDisplaySelector } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent'
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
-import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
+import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { fontSelector } from '@/store/fontAtom'
import { canvasState } from '@/store/canvasAtom'
import { v4 as uuidv4 } from 'uuid'
import { usePopup } from '@/hooks/usePopup'
import Distance from '@/components/floor-plan/modal/distance/Distance'
-import { commonUtilsState } from '@/store/commonUtilsAtom'
-import { center, point } from '@turf/turf'
export function useCommonUtils() {
const canvas = useRecoilValue(canvasState)
const wordDisplay = useRecoilValue(wordDisplaySelector)
const { addCanvasMouseEventListener, addDocumentEventListener, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, addDocumentEventListener, initEvent } = useContext(EventContext)
const dimensionSettings = useRecoilValue(dimensionLineSettingsState)
const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText'))
const commonTextFont = useRecoilValue(fontSelector('commonText'))
@@ -23,7 +22,7 @@ export function useCommonUtils() {
const { addPopup } = usePopup()
useEffect(() => {
- initEvent()
+ // initEvent()
if (commonUtils.text) {
commonTextMode()
} else if (commonUtils.dimension) {
@@ -39,6 +38,7 @@ export function useCommonUtils() {
commonTextKeyEvent()
addCanvasMouseEventListener('mouse:down', (event) => {
const pointer = canvas?.getPointer(event.e)
+
textbox = new fabric.Textbox('', {
left: pointer.x,
top: pointer.y,
@@ -49,7 +49,8 @@ export function useCommonUtils() {
fill: commonTextFont.fontColor.value,
fontFamily: commonTextFont.fontFamily.value,
fontSize: commonTextFont.fontSize.value,
- fontStyle: commonTextFont.fontWeight.value,
+ fontStyle: commonTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
+ fontWeight: commonTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
selectable: true,
lockMovementX: true,
lockMovementY: true,
@@ -262,7 +263,8 @@ export function useCommonUtils() {
fill: dimensionLineTextFont.fontColor.value,
fontSize: dimensionLineTextFont.fontSize.value,
fontFamily: dimensionLineTextFont.fontFamily.value,
- fontStyle: dimensionLineTextFont.fontWeight.value,
+ fontStyle: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
+ fontWeight: dimensionLineTextFont.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
selectable: true,
textAlign: 'center',
originX: 'center',
@@ -710,7 +712,7 @@ export function useCommonUtils() {
canvas?.remove(centerLine, ...extendLine, ...arrows, textObj)
const reGroup = new fabric.Group(reGroupObj, {
- name: 'dimensionLine',
+ name: 'dimensionGroup',
selectable: true,
lineDirection: originLineDirection,
groupId: id,
diff --git a/src/hooks/common/useFont.js b/src/hooks/common/useFont.js
index 595ec74c..cb305565 100644
--- a/src/hooks/common/useFont.js
+++ b/src/hooks/common/useFont.js
@@ -12,13 +12,13 @@ export function useFont() {
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
useEffect(() => {
- if (canvas) {
+ if (canvas && commonText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: commonText.fontFamily.value,
- fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
- fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
+ fontWeight: commonText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
+ fontStyle: commonText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: commonText.fontSize.value,
fill: commonText.fontColor.value,
})
@@ -28,13 +28,13 @@ export function useFont() {
}, [commonText])
useEffect(() => {
- if (canvas) {
+ if (canvas && dimensionLineText.fontWeight.value) {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'dimensionLineText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: dimensionLineText.fontFamily.value,
- fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
- fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
+ fontWeight: dimensionLineText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
+ fontStyle: dimensionLineText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: dimensionLineText.fontSize.value,
fill: dimensionLineText.fontColor.value,
})
@@ -44,13 +44,13 @@ export function useFont() {
}, [dimensionLineText])
useEffect(() => {
- if (canvas) {
- const textObjs = canvas.getObjects().filter((obj) => obj.name === 'flowText')
+ if (canvas && flowText.fontWeight.value) {
+ const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'flowText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: flowText.fontFamily.value,
- fontWeight: lengthText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
- fontStyle: lengthText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
+ fontWeight: flowText.fontWeight.value.toLowerCase().includes('bold') ? 'bold' : 'normal',
+ fontStyle: flowText.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontSize: flowText.fontSize.value,
fill: flowText.fontColor.value,
})
@@ -60,8 +60,8 @@ export function useFont() {
}, [flowText])
useEffect(() => {
- if (canvas) {
- const textObjs = canvas.getObjects().filter((obj) => obj.name === 'lengthText')
+ if (canvas && lengthText.fontWeight.value) {
+ const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText')
textObjs.forEach((obj) => {
obj.set({
fontFamily: lengthText.fontFamily.value,
diff --git a/src/hooks/common/useRefFiles.js b/src/hooks/common/useRefFiles.js
index a5d085cc..05f60afe 100644
--- a/src/hooks/common/useRefFiles.js
+++ b/src/hooks/common/useRefFiles.js
@@ -1,13 +1,13 @@
-import { useRef, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
import { useRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import { useSwal } from '@/hooks/useSwal'
-import { convertDwgToPng } from '@/lib/cadAction'
import { useAxios } from '../useAxios'
import { currentCanvasPlanState } from '@/store/canvasAtom'
+import { convertDwgToPng, writeImageBuffer } from '@/lib/fileAction'
-export default function useRefFiles() {
+export function useRefFiles() {
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const [refImage, setRefImage] = useState(null)
const [refFileMethod, setRefFileMethod] = useState('1')
@@ -16,7 +16,7 @@ export default function useRefFiles() {
const queryRef = useRef(null)
const { swalFire } = useSwal()
- const { get, promisePut } = useAxios()
+ const { get, promisePut, promisePost } = useAxios()
// const { currentCanvasPlan, setCurrentCanvasPlan } = usePlan()
/**
@@ -25,7 +25,11 @@ export default function useRefFiles() {
*/
const handleRefFile = (file) => {
setRefImage(file)
- file.name.split('.').pop() === 'dwg' ? handleUploadRefFile(file) : () => {}
+ /**
+ * 파일 확장자가 dwg일 경우 변환하여 이미지로 저장
+ * 파일 확장자가 이미지일 경우 이미지 저장
+ */
+ file.name.split('.').pop() === 'dwg' ? handleUploadConvertRefFile(file) : handleUploadImageRefFile(file)
// handleUploadRefFile(file)
}
@@ -58,22 +62,29 @@ export default function useRefFiles() {
}
/**
- * 현재 플랜이 변경되면 플랜 상태 저장
+ * 이미지 파일 업로드
+ * @param {*} file
*/
- // useEffect(() => {
- // const handleCurrentPlan = async () => {
- // await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
- // console.log('🚀 ~ awaitpromisePut ~ res:', res)
- // })
- // }
- // handleCurrentPlan()
- // }, [currentCanvasPlan])
+ const handleUploadImageRefFile = async (file) => {
+ console.log('🚀 ~ handleUploadImageRefFile ~ file:', file)
+ const formData = new FormData()
+ formData.append('file', file)
+
+ const response = await fetch('http://localhost:3000/api/image-upload', {
+ method: 'POST',
+ body: formData,
+ })
+
+ const result = await response.json()
+ console.log('🚀 ~ handleUploadImageRefFile ~ res:', result)
+ // writeImageBuffer(file)
+ }
/**
* RefFile이 캐드 도면 파일일 경우 변환하여 이미지로 저장
* @param {*} file
*/
- const handleUploadRefFile = async (file) => {
+ const handleUploadConvertRefFile = async (file) => {
const formData = new FormData()
formData.append('file', file)
@@ -95,6 +106,19 @@ export default function useRefFiles() {
setRefFileMethod(e.target.value)
}
+ /**
+ * 현재 플랜이 변경되면 플랜 상태 저장
+ */
+ useEffect(() => {
+ console.log('🚀 ~ useRefFiles ~ currentCanvasPlan:', currentCanvasPlan)
+ // const handleCurrentPlan = async () => {
+ // await promisePut({ url: '/api/canvas-management/canvas-statuses', data: currentCanvasPlan }).then((res) => {
+ // console.log('🚀 ~ awaitpromisePut ~ res:', res)
+ // })
+ // }
+ // handleCurrentPlan()
+ }, [currentCanvasPlan])
+
return {
refImage,
queryRef,
diff --git a/src/hooks/contextpopup/useFlowDirectionSetting.js b/src/hooks/contextpopup/useFlowDirectionSetting.js
new file mode 100644
index 00000000..9e77aa4a
--- /dev/null
+++ b/src/hooks/contextpopup/useFlowDirectionSetting.js
@@ -0,0 +1,30 @@
+import { canvasState } from '@/store/canvasAtom'
+import { useRecoilValue } from 'recoil'
+import { usePolygon } from '@/hooks/usePolygon'
+import { useState } from 'react'
+import { usePopup } from '@/hooks/usePopup'
+
+export const FLOW_DIRECTION_TYPE = {
+ EIGHT_AZIMUTH: 'eightAzimuth',
+ TWENTY_FOUR_AZIMUTH: 'twentyFourAzimuth',
+}
+
+export function useFlowDirectionSetting(id) {
+ const canvas = useRecoilValue(canvasState)
+ const { drawDirectionArrow } = usePolygon()
+ const { closePopup } = usePopup()
+
+ const [type, setType] = useState(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
+
+ const changeSurfaceFlowDirection = (roof, direction, orientation) => {
+ roof.set({
+ direction: direction,
+ surfaceCompass: orientation,
+ })
+ drawDirectionArrow(roof)
+ canvas?.renderAll()
+ closePopup(id)
+ }
+
+ return { changeSurfaceFlowDirection, type, setType }
+}
diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js
index 05d10554..ddfa2e68 100644
--- a/src/hooks/floorPlan/estimate/useEstimateController.js
+++ b/src/hooks/floorPlan/estimate/useEstimateController.js
@@ -6,34 +6,19 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage'
-
-const reducer = (prevState, nextState) => {
- return { ...prevState, ...nextState }
-}
+import { useRouter } from 'next/navigation'
+import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
// Constants
-const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의
-
-const defaultEstimateData = {
- estimateDate: new Date(), //견적일
- charger: '', //담당자
- objectName: '', //안건명
- objectNameOmit: '', //경칭코드
- estimateType: 'YJOD', //주문분류
- remarks: '', //비고
- estimateOption: '', //견적특이사항
- itemList: [],
- fileList: [],
- fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
- priceCd: '',
-}
+const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
// Helper functions
-const updateItemInList = (itemList, itemId, updates) => {
- return itemList.map((item) => (item.itemId === itemId ? { ...item, ...updates } : item))
+const updateItemInList = (itemList, dispOrder, updates) => {
+ return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item))
}
export const useEstimateController = (planNo) => {
+ const router = useRouter()
const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const objectRecoil = useRecoilValue(floorPlanObjectState)
@@ -41,25 +26,32 @@ export const useEstimateController = (planNo) => {
const { getMessage } = useMessage()
- const { get, post, promisePost } = useAxios(globalLocaleState)
+ const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
const [isLoading, setIsLoading] = useState(false)
- const [state, setState] = useReducer(reducer, defaultEstimateData)
+ const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
useEffect(() => {
- if (!isLoading) {
+ if (planNo && !isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) {
- fetchSetting()
+ fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
}
}
}, [])
// 상세 조회
- const fetchSetting = async () => {
+ const fetchSetting = async (objectNo, planNo) => {
try {
- await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => {
- if (isObjectNotEmpty(res)) {
- setState(res)
+ await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
+ if (res.status === 200) {
+ if (isObjectNotEmpty(res.data)) {
+ if (res.data.itemList.length > 0) {
+ res.data.itemList.map((item) => {
+ item.delFlg = '0'
+ })
+ }
+ setEstimateContextState(res.data)
+ }
}
})
setIsLoading(true)
@@ -69,95 +61,239 @@ export const useEstimateController = (planNo) => {
}
}
- const updateItem = (itemId, updates) => {
- setState({
- itemList: updateItemInList(state.itemList, itemId, updates),
+ const updateItem = (dispOrder, updates) => {
+ setEstimateContextState({
+ itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
})
}
const addItem = () => {
- const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) + 1) * 100
- setState({
+ let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
+ newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
+ setEstimateContextState({
itemList: [
- ...state.itemList,
+ ...estimateContextState.itemList,
{
- dispOrder: newItemDispOrder,
+ objectNo: objectRecoil.floorPlanObjectNo,
+ planNo: planNo,
+ dispOrder: newItemDispOrder.toString(),
itemId: '', //제품번호
- itemNo: '', //형명
- itemName: '',
+ itemNo: '',
+ itemName: '', //형명
amount: '', //수량
unitPrice: '0',
unit: '', //단위
- salePrice: '0', //단가
- saleTotPrice: '0', //금액(부가세별도)
+ salePrice: '', //단가
+ saleTotPrice: '', //금액(부가세별도)
+ itemChangeFlg: '1', //추가시 체인지플래그 1로
+ partAdd: '1', //NEW 체인지 플래그
+ delFlg: '0', //삭제 플래그 0 삭제하면 1
+ addFlg: true,
},
],
})
}
useEffect(() => {
- setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd })
- }, [state])
+ setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
+ }, [estimateContextState])
+
+ // 첨부파일 다운로드
+ const handleEstimateFileDownload = async (originFile) => {
+ const options = { responseType: 'blob' }
+
+ await promisePost({ url: `/api/file/fileDownload`, data: originFile, option: options })
+ .then((resultData) => {
+ if (resultData) {
+ const blob = new Blob([resultData.data], { type: resultData.headers['content-type'] || 'application/octet-stream' })
+ const fileUrl = window.URL.createObjectURL(blob)
+ const link = document.createElement('a')
+
+ link.href = fileUrl
+ link.download = originFile.faileName
+ document.body.appendChild(link)
+ link.click()
+ link.remove()
+ window.URL.revokeObjectURL(fileUrl)
+ }
+ })
+ .catch((error) => {
+ console.log('::FileDownLoad Error::', error)
+ alert('File does not exist.')
+ })
+ }
//견적서 저장
const handleEstimateSubmit = async () => {
//0. 필수체크
let flag = true
- console.log('::담긴 estimateData:::', estimateData)
+ let fileFlg = true
+ let itemFlg = true
+ if (estimateData.charger.trim().length === 0) {
+ flag = false
+ return alert(getMessage('estimate.detail.save.requiredCharger'))
+ }
+ if (estimateData.objectName.trim().length === 0) {
+ flag = false
+ return alert(getMessage('estimate.detail.save.requiredObjectName'))
+ }
+
+ if (isNaN(Date.parse(estimateData.estimateDate))) {
+ flag = false
+ return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
+ }
+
+ //첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
- if (estimateData.itemList.length > 1) {
- estimateData.itemList.map((row) => {
- if (row.fileUploadFlg === '1') {
- if (estimateData.fileFlg === '0') {
- alert(getMessage('estimate.detail.save.requiredMsg'))
- flag = false
+ if (flag) {
+ if (estimateData.fileList.length < 1) {
+ if (estimateData.itemList.length > 1) {
+ estimateData.itemList.map((row) => {
+ if (row.delFlg === '0') {
+ if (row.fileUploadFlg === '1') {
+ if (fileFlg) {
+ if (estimateData.fileFlg === '0') {
+ fileFlg = false
+ return alert(getMessage('estimate.detail.save.requiredFileUpload'))
+ }
+ }
+ }
+ }
+ })
+ }
+ }
+ }
+
+ if (fileFlg) {
+ estimateData.itemList.map((item) => {
+ if (item.delFlg === '0') {
+ item.amount = item.amount?.replaceAll(',', '')
+ item.salePrice = parseFloat(item.salePrice?.replaceAll(',', '')).toFixed(2)
+ item.saleTotPrice = parseFloat(item.saleTotPrice?.replaceAll(',', '')).toFixed(2)
+
+ if (!item.paDispOrder) {
+ if (itemFlg) {
+ if (isNaN(item.amount)) {
+ item.amount = '0'
+ }
+
+ if (item.amount < 1) {
+ itemFlg = false
+ return alert(getMessage('estimate.detail.save.requiredAmount'))
+ }
+
+ if (estimateData.estimateType !== 'YJSS') {
+ if (isNaN(item.salePrice)) {
+ item.salePrice = '0'
+ }
+
+ if (item.salePrice < 1) {
+ itemFlg = false
+ return alert(getMessage('estimate.detail.save.requiredSalePrice'))
+ }
+ }
+ }
}
}
})
}
- if (flag) {
- //1. 첨부파일 저장
+
+ if (flag && fileFlg && itemFlg) {
+ //1. 첨부파일 저장시작
const formData = new FormData()
formData.append('file', estimateData.fileList)
formData.append('objectNo', estimateData.objectNo)
formData.append('planNo', estimateData.planNo)
formData.append('category', '10')
formData.append('userId', estimateData.userId)
- // for (const value of formData.values()) {
- // console.log('formData::', value)
- // }
await post({ url: '/api/file/fileUpload', data: formData })
+ //첨부파일저장끝
- //2. 상세데이터 저장
- return
- await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
- if (res) {
- alert(getMessage('estimate.detail.save.alertMsg'))
+ //제품라인 추가했는데 아이템 안고르고 저장하면itemId=''은 날리고 나머지 저장하기
+ estimateData.itemList = estimateData.itemList.filter((item) => item.itemId !== '')
+
+ let delCnt = 0
+ estimateData.itemList.map((item) => {
+ if (item.delFlg === '1') {
+ delCnt++
+ }
+ })
+ if (delCnt === estimateData.itemList.length) {
+ return alert(getMessage('estimate.detail.save.requiredItem'))
+ }
+
+ // console.log('최종 아이템 정보::;', estimateData.itemList)
+ let option = []
+ estimateData.itemList.forEach((item) => {
+ if (item.specialNoteCd) {
+ let split2 = item.specialNoteCd.split('、')
+ option = option.concat(split2)
}
})
- // try {
- // const result = await promisePost({
- // url: ESTIMATE_API_ENDPOINT,
- // data: estimateData,
- // })
- // alert(getMessage('estimate.detail.save.alertMsg'))
- // return result
- // } catch (error) {
- // console.error('Failed to submit estimate:', error)
- // throw error
- // }
+ // console.log('아이템리스트::', estimateData.itemList)
+ // console.log('최종 정보::;', estimateData)
+ //2. 상세데이터 저장
+ // return
+ try {
+ await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
+ if (res.status === 201) {
+ alert(getMessage('estimate.detail.save.alertMsg'))
+ //어디로 보낼지
+ fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
+ }
+ })
+ } catch (e) {
+ console.log('error::::::::::::', e.response.data.message)
+ }
}
}
+ /**
+ * 견적서 복사버튼
+ * (견적서 번호(estimateData.docNo)가 생성된 이후 버튼 활성화 )
+ * T01관리자 계정 및 1차판매점에게만 제공
+ */
+ const handleEstimateCopy = async (sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId) => {
+ if (saleStoreId === '') {
+ return alert(getMessage('estimate.detail.productFeaturesPopup.requiredStoreId'))
+ }
+
+ if (copyReceiveUser.trim().length === 0) {
+ return alert(getMessage('estimate.detail.productFeaturesPopup.requiredReceiveUser'))
+ }
+ const params = {
+ saleStoreId: session.storeId,
+ sapSalesStoreCd: session.custCd,
+ objectNo: objectRecoil.floorPlanObjectNo,
+ planNo: sendPlanNo,
+ copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
+ copyReceiveUser: copyReceiveUser,
+ userId: session.userId,
+ }
+
+ // return
+ await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
+ if (res.status === 201) {
+ if (isObjectNotEmpty(res.data)) {
+ let newObjectNo = res.data.objectNo
+ alert(getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'))
+ router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
+ }
+ }
+ })
+ }
+
return {
- state,
- setState,
+ estimateContextState,
+ setEstimateContextState,
updateItem,
addItem,
handleEstimateSubmit,
fetchSetting,
+ handleEstimateFileDownload,
+ handleEstimateCopy,
}
}
diff --git a/src/hooks/main/useMainContentsController.js b/src/hooks/main/useMainContentsController.js
new file mode 100644
index 00000000..01f75658
--- /dev/null
+++ b/src/hooks/main/useMainContentsController.js
@@ -0,0 +1,94 @@
+'use client'
+
+import { useContext, useEffect } from 'react'
+import { useAxios } from '../useAxios'
+import { SessionContext } from '@/app/SessionProvider'
+import { QcastContext } from '@/app/QcastProvider'
+
+export const useMainContentsController = () => {
+ const { session } = useContext(SessionContext)
+ const { promiseGet } = useAxios()
+ const { setQcastState, setIsGlobalLoading } = useContext(QcastContext)
+
+ useEffect(() => {
+ setIsGlobalLoading(true)
+ }, [])
+
+ /**
+ * main search area
+ */
+ // const [saleStoreId, setSaleStoreId] = useState('')
+ // const [saleStoreName, setSaleStoreName] = useState('')
+
+ /**
+ * main contents area
+ */
+ // const [objectList, setObjectList] = useState([])
+ // const [businessCharger, setBusinessCharger] = useState(null)
+ // const [businessChargerMail, setBusinessChargerMail] = useState(null)
+
+ /**
+ * 최근 물건 목록 조회
+ */
+ const fetchObjectList = async () => {
+ try {
+ const apiUrl = `/api/main-page/object/${session?.storeId}/list`
+ await promiseGet({
+ url: apiUrl,
+ }).then((res) => {
+ if (res.status === 200) {
+ // setSaleStoreId(res.data.saleStoreId)
+ // setSaleStoreName(res.data.saleStoreName)
+
+ // setObjectList(res.data.objectList)
+ // setBusinessCharger(res.data.businessCharger)
+ // setBusinessChargerMail(res.data.businessChargerMail)
+ setQcastState({
+ saleStoreId: res.data.saleStoreId,
+ saleStoreName: res.data.saleStoreName,
+ objectList: res.data.objectList,
+ businessCharger: res.data.businessCharger,
+ businessChargerMail: res.data.businessChargerMail,
+ })
+ setIsGlobalLoading(false)
+ } else {
+ // setSaleStoreId('')
+ // setSaleStoreName('')
+
+ // setObjectList([])
+ // setBusinessCharger(null)
+ // setBusinessChargerMail(null)
+ setQcastState({
+ saleStoreId: '',
+ saleStoreName: '',
+ objectList: [],
+ businessCharger: null,
+ businessChargerMail: null,
+ })
+ }
+ })
+ } catch (error) {
+ console.error('MAIN API fetching error:', error)
+ }
+ }
+
+ const initObjectList = () => {
+ setQcastState({
+ saleStoreId: '',
+ saleStoreName: '',
+ objectList: [],
+ businessCharger: null,
+ businessChargerMail: null,
+ })
+ }
+
+ return {
+ // saleStoreId,
+ // saleStoreName,
+ // objectList,
+ // businessCharger,
+ // businessChargerMail,
+ fetchObjectList,
+ initObjectList,
+ }
+}
diff --git a/src/hooks/module/useModuleBasicSetting.js b/src/hooks/module/useModuleBasicSetting.js
new file mode 100644
index 00000000..067d9f8a
--- /dev/null
+++ b/src/hooks/module/useModuleBasicSetting.js
@@ -0,0 +1,963 @@
+import { useContext, useEffect, useState } from 'react'
+import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
+import { canvasState } from '@/store/canvasAtom'
+import { rectToPolygon, setSurfaceShapePattern } from '@/util/canvas-util'
+import { roofDisplaySelector } from '@/store/settingAtom'
+import offsetPolygon from '@/util/qpolygon-utils'
+import { QPolygon } from '@/components/fabric/QPolygon'
+import { QLine } from '@/components/fabric/QLine'
+import { moduleSetupSurfaceState, moduleIsSetupState } from '@/store/canvasAtom'
+import { useEvent } from '@/hooks/useEvent'
+import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
+
+import * as turf from '@turf/turf'
+import { EventContext } from '@/app/floor-plan/EventProvider'
+
+export function useModuleBasicSetting() {
+ const canvas = useRecoilValue(canvasState)
+ const roofDisplay = useRecoilValue(roofDisplaySelector)
+ const [moduleSetupSurface, setModuleSetupSurface] = useRecoilState(moduleSetupSurfaceState)
+ const [moduleIsSetup, setModuleIsSetup] = useRecoilState(moduleIsSetupState)
+ const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useEvent()
+ // const { addTargetMouseEventListener, addCanvasMouseEventListener, initEvent } = useContext(EventContext)
+ const [flowModuleLine, setFlowModuleLine] = useState({})
+ let selectedModuleInstSurfaceArray = []
+
+ const makeModuleInstArea = () => {
+ //지붕 객체 반환
+ const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
+
+ if (!roofs) {
+ return
+ }
+
+ roofs.forEach((roof) => {
+ setSurfaceShapePattern(roof, roofDisplay.column, true) //패턴 변경
+ const offsetPoints = offsetPolygon(roof.points, -20) //안쪽 offset
+ //모듈설치영역?? 생성
+ let setupSurface = new QPolygon(offsetPoints, {
+ stroke: 'red',
+ fill: 'transparent',
+ strokeDashArray: [10, 4],
+ strokeWidth: 1,
+ lockMovementX: true,
+ lockMovementY: true,
+ lockRotation: true,
+ lockScalingX: true,
+ lockScalingY: true,
+ selectable: true,
+ parentId: roof.id, //가대 폴리곤의 임시 인덱스를 넣어줌
+ name: POLYGON_TYPE.MODULE_SETUP_SURFACE,
+ flowDirection: roof.direction,
+ flipX: roof.flipX,
+ flipY: roof.flipY,
+ })
+
+ setupSurface.setViewLengthText(false)
+
+ canvas.add(setupSurface)
+
+ if (setupSurface.flowDirection === 'south' || setupSurface.flowDirection === 'north') {
+ setFlowModuleLine(bottomTopFlowLine(setupSurface))
+ } else {
+ setFlowModuleLine(leftRightFlowLine(setupSurface))
+ }
+
+ //지붕면 선택 금지
+ roof.set({
+ selectable: false,
+ })
+
+ //모듈설치면 클릭이벤트
+ addTargetMouseEventListener('mousedown', setupSurface, function () {
+ toggleSelection(setupSurface)
+ })
+ })
+ }
+
+ //설치 범위 지정 클릭 이벤트
+ const toggleSelection = (setupSurface) => {
+ const isExist = selectedModuleInstSurfaceArray.some((obj) => obj.parentId === setupSurface.parentId)
+ //최초 선택일때
+ if (!isExist) {
+ //기본 선택이랑 스트로크 굵기가 같으면 선택 안됨으로 봄
+ setupSurface.set({
+ ...setupSurface,
+ strokeWidth: 3,
+ strokeDashArray: [0],
+ fill: 'transparent',
+ })
+ canvas.discardActiveObject() // 객체의 활성 상태 해제
+ //중복으로 들어가는걸 방지하기 위한 코드
+
+ canvas?.renderAll()
+ selectedModuleInstSurfaceArray.push(setupSurface)
+ } else {
+ //선택후 재선택하면 선택안됨으로 변경
+ setupSurface.set({
+ ...setupSurface,
+ fill: 'transparent',
+ strokeDashArray: [10, 4],
+ strokeWidth: 1,
+ })
+ canvas.discardActiveObject() // 객체의 활성 상태 해제
+
+ //폴리곤에 커스텀 인덱스를 가지고 해당 배열 인덱스를 찾아 삭제함
+ const removeIndex = setupSurface.parentId
+ const removeArrayIndex = selectedModuleInstSurfaceArray.findIndex((obj) => obj.parentId === removeIndex)
+ selectedModuleInstSurfaceArray.splice(removeArrayIndex, 1)
+ }
+
+ canvas?.renderAll()
+ setModuleSetupSurface([...selectedModuleInstSurfaceArray])
+ }
+
+ /**
+ * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
+ * 확인 후 셀을 이동시킴
+ */
+ const manualModuleSetup = () => {
+ const moduleSetupSurfaces = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE) //모듈설치면를 가져옴
+ const batchObjects = canvas
+ ?.getObjects()
+ .filter(
+ (obj) =>
+ obj.name === BATCH_TYPE.OPENING ||
+ obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
+ obj.name === BATCH_TYPE.PENTAGON_DORMER ||
+ obj.name === BATCH_TYPE.SHADOW,
+ ) //도머s 객체
+
+ if (moduleSetupSurfaces.length !== 0) {
+ let tempModule
+ let manualDrawModules = moduleIsSetup // 앞에서 자동으로 했을때 추가됨
+ let inside = false
+ let turfPolygon
+ let flowDirection
+ let trestlePolygon
+ addCanvasMouseEventListener('mouse:move', (e) => {
+ //마우스 이벤트 삭제 후 재추가
+ const mousePoint = canvas.getPointer(e.e)
+
+ for (let i = 0; i < moduleSetupSurfaces.length; i++) {
+ turfPolygon = polygonToTurfPolygon(moduleSetupSurfaces[i])
+ trestlePolygon = moduleSetupSurfaces[i]
+ flowDirection = moduleSetupSurfaces[i].flowDirection //도형의 방향
+ let width = flowDirection === 'south' || flowDirection === 'north' ? 172 : 113
+ let height = flowDirection === 'south' || flowDirection === 'north' ? 113 : 172
+
+ const points = [
+ { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
+ { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
+ { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
+ { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
+ ]
+
+ const turfPoints = coordToTurfPolygon(points)
+
+ if (turf.booleanWithin(turfPoints, turfPolygon)) {
+ let isDrawing = false
+
+ if (isDrawing) return
+ canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule')) //움직일때 일단 지워가면서 움직임
+
+ tempModule = new fabric.Rect({
+ fill: 'white',
+ stroke: 'black',
+ strokeWidth: 1,
+ width: width,
+ height: height,
+ left: mousePoint.x - width / 2,
+ top: mousePoint.y - height / 2,
+ selectable: false,
+ lockMovementX: true,
+ lockMovementY: true,
+ lockRotation: true,
+ lockScalingX: true,
+ lockScalingY: true,
+ opacity: 0.8,
+ name: 'tempModule',
+ parentId: moduleSetupSurfaces[i].parentId,
+ })
+
+ canvas?.add(tempModule) //움직여가면서 추가됨
+
+ /**
+ * 스냅기능
+ */
+ let snapDistance = 10
+ let cellSnapDistance = 20
+
+ const trestleLeft = moduleSetupSurfaces[i].left
+ const trestleTop = moduleSetupSurfaces[i].top
+ const trestleRight = trestleLeft + moduleSetupSurfaces[i].width * moduleSetupSurfaces[i].scaleX
+ const trestleBottom = trestleTop + moduleSetupSurfaces[i].height * moduleSetupSurfaces[i].scaleY
+ const bigCenterY = (trestleTop + trestleTop + moduleSetupSurfaces[i].height) / 2
+
+ // 작은 폴리곤의 경계 좌표 계산
+ const smallLeft = tempModule.left
+ const smallTop = tempModule.top
+ const smallRight = smallLeft + tempModule.width * tempModule.scaleX
+ const smallBottom = smallTop + tempModule.height * tempModule.scaleY
+ const smallCenterX = smallLeft + (tempModule.width * tempModule.scaleX) / 2
+ const smallCenterY = smallTop + (tempModule.height * tempModule.scaleX) / 2
+
+ /**
+ * 미리 깔아놓은 셀이 있을때 셀에 흡착됨
+ */
+ if (manualDrawModules) {
+ manualDrawModules.forEach((cell) => {
+ const holdCellLeft = cell.left
+ const holdCellTop = cell.top
+ const holdCellRight = holdCellLeft + cell.width * cell.scaleX
+ const holdCellBottom = holdCellTop + cell.height * cell.scaleY
+ const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2
+ const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2
+
+ //설치된 셀에 좌측에 스냅
+ if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
+ tempModule.left = holdCellLeft - width - 0.5
+ }
+
+ //설치된 셀에 우측에 스냅
+ if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
+ tempModule.left = holdCellRight + 0.5
+ }
+
+ //설치된 셀에 위쪽에 스냅
+ if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
+ tempModule.top = holdCellTop - height - 0.5
+ }
+
+ //설치된 셀에 밑쪽에 스냅
+ if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
+ tempModule.top = holdCellBottom + 0.5
+ }
+ //가운데 -> 가운데
+ if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
+ tempModule.left = holdCellCenterX - width / 2
+ }
+ //왼쪽 -> 가운데
+ if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
+ tempModule.left = holdCellCenterX
+ }
+ // 오른쪽 -> 가운데
+ if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
+ tempModule.left = holdCellCenterX - width
+ }
+ //세로 가운데 -> 가운데
+ if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
+ tempModule.top = holdCellCenterY - height / 2
+ }
+ //위쪽 -> 가운데
+ if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
+ tempModule.top = holdCellCenterY
+ }
+ //아랫쪽 -> 가운데
+ if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
+ tempModule.top = holdCellCenterY - height
+ }
+ })
+ }
+
+ // 위쪽 변에 스냅
+ if (Math.abs(smallTop - trestleTop) < snapDistance) {
+ tempModule.top = trestleTop
+ }
+
+ // 아래쪽 변에 스냅
+ if (Math.abs(smallTop + tempModule.height * tempModule.scaleY - (trestleTop + moduleSetupSurfaces[i].height)) < snapDistance) {
+ tempModule.top = trestleTop + moduleSetupSurfaces[i].height - tempModule.height * tempModule.scaleY
+ }
+
+ // 왼쪽변에 스냅
+ if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
+ tempModule.left = trestleLeft
+ }
+ //오른쪽 변에 스냅
+ if (Math.abs(smallRight - trestleRight) < snapDistance) {
+ tempModule.left = trestleRight - tempModule.width * tempModule.scaleX
+ }
+
+ if (flowDirection === 'south' || flowDirection === 'north') {
+ // 모듈왼쪽이 세로중앙선에 붙게 스냅
+ if (Math.abs(smallLeft - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
+ tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2
+ }
+
+ // 모듈이 가운데가 세로중앙선에 붙게 스냅
+ if (Math.abs(smallCenterX - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
+ tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - (tempModule.width * tempModule.scaleX) / 2
+ }
+
+ // 모듈오른쪽이 세로중앙선에 붙게 스냅
+ if (Math.abs(smallRight - (trestleLeft + moduleSetupSurfaces[i].width / 2)) < snapDistance) {
+ tempModule.left = trestleLeft + moduleSetupSurfaces[i].width / 2 - tempModule.width * tempModule.scaleX
+ }
+ } else {
+ // 모듈이 가로중앙선에 스냅
+ if (Math.abs(smallTop + tempModule.height / 2 - bigCenterY) < snapDistance) {
+ tempModule.top = bigCenterY - tempModule.height / 2
+ }
+
+ if (Math.abs(smallTop - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
+ tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2
+ }
+ // 모듈 밑면이 가로중앙선에 스냅
+ if (Math.abs(smallBottom - (trestleTop + moduleSetupSurfaces[i].height / 2)) < snapDistance) {
+ tempModule.top = trestleTop + moduleSetupSurfaces[i].height / 2 - tempModule.height * tempModule.scaleY
+ }
+ }
+
+ tempModule.setCoords()
+ canvas?.renderAll()
+ inside = true
+ break
+ } else {
+ inside = false
+ }
+ }
+
+ if (!inside) {
+ canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tempModule'))
+ canvas?.renderAll()
+ }
+ })
+
+ addCanvasMouseEventListener('mouse:up', (e) => {
+ let isIntersection = true
+ if (!inside) return
+ if (tempModule) {
+ const rectPoints = [
+ { x: tempModule.left + 0.5, y: tempModule.top + 0.5 },
+ { x: tempModule.left + 0.5 + tempModule.width * tempModule.scaleX, y: tempModule.top + 0.5 },
+ {
+ x: tempModule.left + tempModule.width * tempModule.scaleX + 0.5,
+ y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5,
+ },
+ { x: tempModule.left + 0.5, y: tempModule.top + tempModule.height * tempModule.scaleY + 0.5 },
+ ]
+
+ tempModule.set({ points: rectPoints })
+ const tempTurfModule = polygonToTurfPolygon(tempModule)
+
+ //도머 객체를 가져옴
+ if (batchObjects) {
+ batchObjects.forEach((object) => {
+ let dormerTurfPolygon
+
+ if (object.type === 'group') {
+ //도머는 그룹형태임
+ dormerTurfPolygon = batchObjectGroupToTurfPolygon(object)
+ } else {
+ //개구, 그림자
+ dormerTurfPolygon = polygonToTurfPolygon(rectToPolygon(object))
+ }
+
+ const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
+ //겹치면 안됨
+ if (intersection) {
+ alert('도머위에 모듈을 올릴 수 없습니다.')
+ isIntersection = false
+ }
+ })
+ }
+
+ if (!isIntersection) return
+
+ tempModule.setCoords() //좌표 재정렬
+
+ if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
+ //마우스 클릭시 set으로 해당 위치에 셀을 넣음
+ const isOverlap = manualDrawModules.some((module) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(module))) //겹치는지 확인
+ if (!isOverlap) {
+ //안겹치면 넣는다
+ tempModule.setCoords()
+ tempModule.set({ name: 'module', fill: '#BFFD9F' })
+ manualDrawModules.push(tempModule) //모듈배열에 추가
+ //해당 모듈에 프로퍼티로 넣는다
+ trestlePolygon.set({
+ modules: manualDrawModules,
+ })
+ } else {
+ alert('셀끼리 겹치면 안되죠?')
+ }
+ } else {
+ alert('나갔죠?!!')
+ }
+ }
+ })
+ }
+ }
+
+ //자동 모듈 설치(그리드 방식)
+ const autoModuleSetup = (placementRef) => {
+ const isChidori = placementRef.isChidori.current
+ const setupLocation = placementRef.setupLocation.current
+ const isMaxSetup = placementRef.isMaxSetup.current
+
+ initEvent()
+ const moduleSetupSurfaces = moduleSetupSurface //선택 설치면
+
+ const notSelectedTrestlePolygons = canvas
+ ?.getObjects()
+ .filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && !moduleSetupSurfaces.includes(obj)) //설치면이 아닌것
+
+ const batchObjects = canvas
+ ?.getObjects()
+ .filter(
+ (obj) =>
+ obj.name === BATCH_TYPE.OPENING ||
+ obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
+ obj.name === BATCH_TYPE.PENTAGON_DORMER ||
+ obj.name === BATCH_TYPE.SHADOW,
+ ) //도머s 객체
+
+ if (moduleSetupSurfaces.length === 0) {
+ alert('선택된 모듈 설치면이 없습니다.')
+ return
+ }
+
+ if (moduleIsSetup.length > 0) {
+ alert('기존 모듈은 제거됩니다.')
+ moduleIsSetup.forEach((module) => {
+ canvas?.remove(module)
+ })
+ }
+
+ notSelectedTrestlePolygons.forEach((obj) => {
+ if (obj.modules) {
+ obj.modules.forEach((module) => {
+ canvas?.remove(module)
+ })
+ obj.modules = []
+ }
+ })
+
+ const moduleSetupArray = []
+ moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
+ moduleSetupSurface.fire('mousedown')
+
+ const surfaceMaxLines = findSetupSurfaceMaxLines(moduleSetupSurface)
+
+ let maxLengthLine = moduleSetupSurface.lines.reduce((acc, cur) => {
+ return acc.length > cur.length ? acc : cur
+ })
+
+ const turfModuleSetupSurface = polygonToTurfPolygon(moduleSetupSurface) //폴리곤을 turf 객체로 변환
+
+ const containsBatchObjects = batchObjects.filter((batchObject) => {
+ let convertBatchObject
+
+ if (batchObject.type === 'group') {
+ //도머는 그룹형태임
+ convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
+ } else {
+ //개구, 그림자
+ batchObject.set({
+ points: rectToPolygon(batchObject),
+ })
+ canvas?.renderAll() // set된걸 바로 적용하기 위해
+ convertBatchObject = polygonToTurfPolygon(batchObject) //rect를 폴리곤으로 변환 -> turf 폴리곤으로 변환
+ }
+
+ // 폴리곤 안에 도머 폴리곤이 포함되어있는지 확인해서 반환하는 로직
+ return turf.booleanContains(turfModuleSetupSurface, convertBatchObject) || turf.booleanWithin(convertBatchObject, turfModuleSetupSurface)
+ })
+
+ let difference = turfModuleSetupSurface //기본 객체(면형상)
+
+ if (containsBatchObjects.length > 0) {
+ //turf로 도머를 제외시키는 로직
+ for (let i = 0; i < containsBatchObjects.length; i++) {
+ let convertBatchObject
+ if (containsBatchObjects[i].type === 'group') {
+ convertBatchObject = batchObjectGroupToTurfPolygon(containsBatchObjects[i])
+ } else {
+ convertBatchObject = polygonToTurfPolygon(containsBatchObjects[i])
+ }
+ }
+ }
+
+ let width = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 172.2 : 113.4
+ let height = maxLengthLine.flowDirection === 'east' || maxLengthLine.flowDirection === 'west' ? 113.4 : 172.2
+
+ //배치면때는 방향쪽으로 패널이 넓게 누워져야함
+ if (moduleSetupSurface.flowDirection !== undefined) {
+ width = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 172.2 : 113.4
+ height = moduleSetupSurface.flowDirection === 'south' || moduleSetupSurface.flowDirection === 'north' ? 113.4 : 172.2
+ }
+
+ let square
+ let startPoint, endPoint
+
+ if (setupLocation === 'eaves') {
+ if (moduleSetupSurface.flowDirection === 'south') {
+ startPoint = flowModuleLine.find((obj) => obj.target === 'bottom')
+ endPoint = flowModuleLine.find((obj) => obj.target === 'top')
+ const totalHeight = endPoint.y1 - startPoint.y1
+ const diffHeight = Math.abs(totalHeight / height)
+ let leftMargin = 0
+ let bottomMargin = 0
+
+ for (let i = 0; i < diffHeight; i++) {
+ leftMargin = i === 0 ? 1 : 0
+ bottomMargin = i === 0 ? 0 : 1
+
+ square = [
+ [startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
+ [startPoint.x1 + leftMargin, startPoint.y1 - bottomMargin],
+ [startPoint.x1 + leftMargin + width, startPoint.y1 - bottomMargin],
+ [startPoint.x1 + leftMargin + width, startPoint.y1 - height - bottomMargin],
+ [startPoint.x1 + leftMargin, startPoint.y1 - height - bottomMargin],
+ ]
+
+ const squarePolygon = turf.polygon([square])
+
+ //설치면 안에 있는지 확인
+ const disjointFromTrestle =
+ turf.booleanContains(turfModuleSetupSurface, squarePolygon) || turf.booleanWithin(squarePolygon, turfModuleSetupSurface)
+
+ if (disjointFromTrestle) {
+ let turfCoordnates = squarePolygon.geometry.coordinates[0].slice(0, -1)
+ const points = turfCoordnates.map((coord) => ({ x: coord[0], y: coord[1] }))
+
+ if (containsBatchObjects.length > 0) {
+ let convertBatchObject
+ //도머가 있으면 적용되는 로직
+ const isDisjoint = containsBatchObjects.every((batchObject) => {
+ if (batchObject.type === 'group') {
+ convertBatchObject = batchObjectGroupToTurfPolygon(batchObject)
+ } else {
+ convertBatchObject = polygonToTurfPolygon(batchObject)
+ }
+ return turf.booleanDisjoint(squarePolygon, convertBatchObject) //도머가 여러개일수있으므로 겹치는게 있다면...
+ })
+ if (isDisjoint) {
+ const tempModule = new QPolygon(points, {
+ fill: '#BFFD9F',
+ stroke: 'black',
+ strokeWidth: 0.1,
+ selectable: true, // 선택 가능하게 설정
+ lockMovementX: false, // X 축 이동 잠금
+ lockMovementY: false, // Y 축 이동 잠금
+ lockRotation: false, // 회전 잠금
+ lockScalingX: false, // X 축 크기 조정 잠금
+ lockScalingY: false, // Y 축 크기 조정 잠금
+ opacity: 0.8,
+ parentId: moduleSetupSurface.parentId,
+ name: 'module',
+ })
+ tempModule.setViewLengthText(false)
+ canvas?.add(tempModule)
+ moduleSetupArray.push(tempModule)
+ }
+ } else {
+ //도머가 없을땐 그냥 그림
+ const tempModule = new QPolygon(points, {
+ fill: '#BFFD9F',
+ stroke: 'black',
+ selectable: true, // 선택 가능하게 설정
+ lockMovementX: true, // X 축 이동 잠금
+ lockMovementY: true, // Y 축 이동 잠금
+ lockRotation: true, // 회전 잠금
+ lockScalingX: true, // X 축 크기 조정 잠금
+ lockScalingY: true, // Y 축 크기 조정 잠금
+ opacity: 0.8,
+ parentId: moduleSetupSurface.parentId,
+ name: 'module',
+ })
+ canvas?.add(tempModule)
+ moduleSetupArray.push(tempModule)
+ }
+ startPoint = { x1: points[0].x, y1: points[0].y, x2: points[3].x, y2: points[3].y }
+ }
+ }
+ }
+ } else if (setupLocation === 'ridge') {
+ } else {
+ }
+
+ moduleSetupSurface.set({ modules: moduleSetupArray })
+ })
+
+ setModuleIsSetup(moduleSetupArray)
+
+ console.log(calculateForApi(moduleSetupArray))
+ }
+
+ const calculateForApi = (moduleSetupArray) => {
+ const centerPoints = []
+ moduleSetupArray.forEach((module, index) => {
+ module.tempIndex = index
+ const { x, y } = module.getCenterPoint()
+ const { width, height } = module
+ centerPoints.push({ x, y, width, height, index })
+ const circle = new fabric.Circle({
+ radius: 5,
+ fill: 'red',
+ name: 'redCircle',
+ left: x - 5,
+ top: y - 5,
+ index: index,
+ selectable: false,
+ })
+ canvas.add(circle)
+ })
+
+ //완전 노출 하면
+ let exposedBottom = 0
+ // 반 노출 하면
+ let exposedHalfBottom = 0
+ // 완전 노출 상면
+ let exposedTop = 0
+ //반 노출 상면
+ let exposedHalfTop = 0
+ // 완전 접면
+ let touchDimension = 0
+ //반접면
+ let halfTouchDimension = 0
+ // 노출하면 체크
+ centerPoints.forEach((centerPoint, index) => {
+ const { x, y, width, height } = centerPoint
+ // centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
+ const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
+ if (bottomCell.length === 1) {
+ touchDimension++
+ return
+ }
+
+ const bottomLeftPoint = { x: x - width / 2, y: y + height }
+ const bottomRightPoint = { x: x + width / 2, y: y + height }
+
+ // 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
+ const leftBottomCnt = centerPoints.filter(
+ (centerPoint) => Math.abs(centerPoint.x - bottomLeftPoint.x) < 2 && Math.abs(centerPoint.y - bottomLeftPoint.y) < 2,
+ ).length
+ const rightBottomCnt = centerPoints.filter(
+ (centerPoint) => Math.abs(centerPoint.x - bottomRightPoint.x) < 2 && Math.abs(centerPoint.y - bottomRightPoint.y) < 2,
+ ).length
+ if (leftBottomCnt + rightBottomCnt === 2) {
+ touchDimension++
+ return
+ }
+ if (leftBottomCnt + rightBottomCnt === 1) {
+ halfTouchDimension++
+ exposedHalfBottom++
+ return
+ }
+ if (leftBottomCnt + rightBottomCnt === 0) {
+ exposedBottom++
+ return
+ }
+ })
+ // 노출상면 체크
+
+ centerPoints.forEach((centerPoint, index) => {
+ const { x, y, width, height } = centerPoint
+ const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
+ if (topCell.length === 1) {
+ return
+ }
+
+ const topLeftPoint = { x: x - width / 2, y: y - height }
+ const topRightPoint = { x: x + width / 2, y: y - height }
+
+ const leftTopCnt = centerPoints.filter(
+ (centerPoint) => Math.abs(centerPoint.x - topLeftPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
+ ).length
+ const rightTopCnt = centerPoints.filter(
+ (centerPoint) => Math.abs(centerPoint.x - topRightPoint.x) < 2 && Math.abs(centerPoint.y - topRightPoint.y) < 2,
+ ).length
+
+ if (leftTopCnt + rightTopCnt === 1) {
+ exposedHalfTop++
+ return
+ }
+ if (leftTopCnt + rightTopCnt === 0) {
+ exposedTop++
+ return
+ }
+ })
+ return {
+ exposedBottom,
+ exposedHalfBottom,
+ exposedTop,
+ exposedHalfTop,
+ touchDimension,
+ halfTouchDimension,
+ }
+ }
+
+ const coordToTurfPolygon = (points) => {
+ const coordinates = points.map((point) => [point.x, point.y])
+ coordinates.push(coordinates[0])
+ return turf.polygon([coordinates])
+ }
+
+ const polygonToTurfPolygon = (object) => {
+ let coordinates
+ coordinates = object.points.map((point) => [point.x, point.y])
+ coordinates.push(coordinates[0])
+ return turf.polygon(
+ [coordinates],
+ {},
+ {
+ parentId: object.parentId,
+ },
+ )
+ }
+
+ const batchObjectGroupToTurfPolygon = (group) => {
+ const polygons = group.getObjects().filter((obj) => obj.type === 'QPolygon')
+ let allPoints = []
+
+ polygons.forEach((obj) => allPoints.push(...obj.get('points')))
+
+ const points = turf.featureCollection(allPoints.map((point) => turf.point([point.x, point.y])))
+ const hull = turf.concave(points, { tolerance: 0.1 })
+
+ return hull
+ }
+
+ const bottomTopFlowLine = (surface) => {
+ const flowArray = []
+ const bottomFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
+ return { x1: line.x1, y1: line.y1, index: index }
+ }
+ return acc
+ },
+ { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+ flowArray.push(bottomFlow)
+
+ const topFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
+ return { x1: line.x1, y1: line.y1, index: index }
+ }
+ return acc
+ },
+ { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+ flowArray.push(topFlow)
+
+ let idx = 0
+ let rtnObjArray = []
+ flowArray.forEach((center) => {
+ const linesArray = new Array()
+
+ surface.lines.filter((line) => {
+ if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
+ linesArray.push(line)
+ }
+ })
+
+ let coords = []
+ if (center.index === 0) {
+ coords = [
+ { x: linesArray[0].x2, y: linesArray[0].y2 },
+ { x: center.x1, y: center.y1 },
+ { x: linesArray[1].x1, y: linesArray[1].y1 },
+ ]
+ } else {
+ coords = [
+ { x: linesArray[0].x1, y: linesArray[0].y1 },
+ { x: center.x1, y: center.y1 },
+ { x: linesArray[1].x2, y: linesArray[1].y2 },
+ ]
+ }
+
+ const adjust1 = coords[0].x - coords[1].x
+ const height1 = coords[1].y - coords[0].y
+ const angle1 = Math.abs(Math.round(Math.atan(height1 / adjust1) * (180 / Math.PI) * 1000) / 1000)
+
+ const adjust2 = coords[2].x - coords[1].x
+ const height2 = coords[2].y - coords[1].y
+ const angle2 = Math.abs(Math.round(Math.atan(height2 / adjust2) * (180 / Math.PI) * 1000) / 1000)
+ const angle3 = 180 - (angle1 + angle2)
+
+ const charlie = 173.3 + 3 // 평행선길이 약간 여유를 줌
+ const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
+ const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
+ const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
+ const sign = Math.sign(coords[0].y - coords[1].y) // 진행방향
+ const top = coords[1].y + sign * h // 변경되는 높이 좌표 값
+ // const line3 = new QLine([coords[1].x, coords[1].y, coords[1].x, top], {
+ // stroke: 'blue',
+ // strokeWidth: 1,
+ // selectable: true,
+ // })
+ // // canvas?.add(line3)
+
+ const pointX1 = coords[0].x + ((coords[0].y - top) / (coords[0].y - coords[1].y)) * (coords[1].x - coords[0].x)
+ const pointY1 = top
+ const pointX2 = coords[2].x + ((coords[2].y - top) / (coords[2].y - coords[1].y)) * (coords[1].x - coords[2].x)
+ const pointY2 = top
+
+ const finalLine = new QLine([pointX1, pointY1, pointX2, pointY2], {
+ stroke: 'red',
+ strokeWidth: 1,
+ selectable: true,
+ })
+ canvas?.add(finalLine)
+ canvas?.renderAll()
+
+ const rtnObj = { target: idx === 0 ? 'bottom' : 'top', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
+ rtnObjArray.push(rtnObj)
+ ++idx
+ })
+
+ return rtnObjArray
+ }
+
+ const leftRightFlowLine = (surface) => {
+ const flowArray = []
+ const leftFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
+ return { x1: line.x1, y1: line.y1, index: index }
+ }
+ return acc
+ },
+ { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+ flowArray.push(leftFlow)
+
+ const rightFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
+ return { x1: line.x1, y1: line.y1, index: index }
+ }
+ return acc
+ },
+ { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+ flowArray.push(rightFlow)
+
+ let idx = 0
+ let rtnObjArray = []
+ flowArray.forEach((center) => {
+ const linesArray = surface.lines.filter((line) => {
+ if ((center.x1 === line.x1 && center.y1 === line.y1) || (center.x1 === line.x2 && center.y1 === line.y2)) {
+ return line
+ }
+ })
+
+ let coords = []
+ if (center.index === 0) {
+ coords = [
+ { x: linesArray[1].x1, y: linesArray[1].y1 },
+ { x: center.x1, y: center.y1 },
+ { x: linesArray[0].x2, y: linesArray[0].y2 },
+ ]
+ } else {
+ coords = [
+ { x: linesArray[0].x1, y: linesArray[0].y1 },
+ { x: center.x1, y: center.y1 },
+ { x: linesArray[1].x2, y: linesArray[1].y2 },
+ ]
+ }
+
+ const adjust1 = coords[0].x - coords[1].x
+ const height1 = coords[1].y - coords[0].y
+ const angle1 = Math.abs(Math.round(Math.atan(adjust1 / height1) * (180 / Math.PI) * 1000) / 1000)
+
+ const adjust2 = coords[2].x - coords[1].x
+ const height2 = coords[2].y - coords[1].y
+ const angle2 = Math.abs(Math.round(Math.atan(adjust2 / height2) * (180 / Math.PI) * 1000) / 1000)
+ const angle3 = 180 - (angle1 + angle2)
+
+ const charlie = 173.3 + 3 // 평행선길이 약간 여유를줌
+ const alpha = (charlie * Math.sin((angle1 * Math.PI) / 180)) / Math.sin((angle3 * Math.PI) / 180)
+ const beta = Math.sqrt(alpha ** 2 + charlie ** 2 - 2 * alpha * charlie * Math.cos((angle2 * Math.PI) / 180))
+
+ const h = beta * Math.sin((angle1 * Math.PI) / 180) // 높이
+ const sign = Math.sign(coords[0].x - coords[1].x) // 진행방향
+ const top = coords[1].x + sign * h // 변경되는 높이 좌표 값
+
+ // const line3 = new QLine([coords[1].x, coords[1].y, top, coords[1].y], {
+ // stroke: 'blue',
+ // strokeWidth: 1,
+ // selectable: true,
+ // })
+ // canvas?.add(line3)
+
+ const pointX1 = top
+ const pointY1 = coords[0].y + ((coords[0].x - top) / (coords[0].x - coords[1].x)) * (coords[1].y - coords[0].y)
+ const pointX2 = top
+ 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,
+ })
+ canvas?.add(finalLine)
+ canvas?.renderAll()
+
+ const rtnObj = { target: idx === 0 ? 'left' : 'right', x1: pointX1, y1: pointY1, x2: pointX2, y2: pointY2 }
+ rtnObjArray.push(rtnObj)
+ ++idx
+ })
+ }
+
+ const findSetupSurfaceMaxLines = (surface) => {
+ const leftFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.x1 < acc.x1 || (line.x1 === acc.x1 && line.y1 < acc.y1)) {
+ return { x1: line.x1, y1: line.y1 }
+ }
+ return acc
+ },
+ { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+
+ const rightFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.x1 > acc.x1 || (line.x1 === acc.x1 && line.y1 > acc.y1)) {
+ return { x1: line.x1, y1: line.y1 }
+ }
+ return acc
+ },
+ { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+
+ const topFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.y1 < acc.y1 || (line.y1 === acc.y1 && line.x1 < acc.x1)) {
+ return { x1: line.x1, y1: line.y1 }
+ }
+ return acc
+ },
+ { x1: Infinity, y1: Infinity, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+
+ const bottomFlow = surface.lines.reduce(
+ (acc, line, index) => {
+ if (line.y1 > acc.y1 || (line.y1 === acc.y1 && line.x1 > acc.x1)) {
+ return { x1: line.x1, y1: line.y1 }
+ }
+ return acc
+ },
+ { x1: 0, y1: 0, index: -1 }, // 초기값: 무한대와 유효하지 않은 인덱스
+ )
+
+ const obj = {
+ left: leftFlow,
+ right: rightFlow,
+ top: topFlow,
+ bottom: bottomFlow,
+ }
+
+ return obj
+ }
+
+ return {
+ makeModuleInstArea,
+ manualModuleSetup,
+ autoModuleSetup,
+ }
+}
diff --git a/src/hooks/module/useOrientation.js b/src/hooks/module/useOrientation.js
new file mode 100644
index 00000000..b64fc171
--- /dev/null
+++ b/src/hooks/module/useOrientation.js
@@ -0,0 +1,36 @@
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { canvasState } from '@/store/canvasAtom'
+import { usePolygon } from '@/hooks/usePolygon'
+import { POLYGON_TYPE } from '@/common/common'
+import { compasDegAtom } from '@/store/orientationAtom'
+import { useEffect } from 'react'
+
+// 모듈,회로 구성 탭 기본설정 > 방위설정 탭
+export function useOrientation() {
+ const canvas = useRecoilValue(canvasState)
+ const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
+
+ const { drawDirectionArrow } = usePolygon()
+
+ useEffect(() => {
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ roofs.forEach((roof) => {
+ roof.set({
+ moduleCompass: null,
+ })
+ drawDirectionArrow(roof)
+ })
+ }, [])
+
+ const nextStep = () => {
+ const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ roofs.forEach((roof) => {
+ roof.set({
+ moduleCompass: compasDeg,
+ })
+ drawDirectionArrow(roof)
+ })
+ }
+
+ return { nextStep, compasDeg, setCompasDeg }
+}
diff --git a/src/hooks/object/useObjectBatch.js b/src/hooks/object/useObjectBatch.js
index 1820599b..dd67b017 100644
--- a/src/hooks/object/useObjectBatch.js
+++ b/src/hooks/object/useObjectBatch.js
@@ -17,6 +17,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useEvent()
+ // const { addCanvasMouseEventListener, initEvent, addDocumentEventListener } = useContext(EventContext)
const { swalFire } = useSwal()
const { drawDirectionArrow } = usePolygon()
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
diff --git a/src/hooks/option/useCanvasSetting.js b/src/hooks/option/useCanvasSetting.js
index 549cc9a4..c3155512 100644
--- a/src/hooks/option/useCanvasSetting.js
+++ b/src/hooks/option/useCanvasSetting.js
@@ -1,16 +1,22 @@
-import { useEffect, useState } from 'react'
-import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
-import { adsorptionPointModeState, adsorptionRangeState, canvasState } from '@/store/canvasAtom'
+import { useCallback, useEffect, useState } from 'react'
+import { useRecoilState, useRecoilValue } from 'recoil'
+import { adsorptionPointModeState, adsorptionRangeState, canvasState, planSizeSettingState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import { useSwal } from '@/hooks/useSwal'
-import { corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
-import { setSurfaceShapePattern } from '@/util/canvas-util'
+import { correntObjectNoState, corridorDimensionSelector, settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common'
+import { globalFontAtom } from '@/store/fontAtom'
+import { dimensionLineSettingsState } from '@/store/commonUtilsAtom'
+
+let objectNo
export function useCanvasSetting() {
const canvas = useRecoilValue(canvasState)
+ // canvas가 null이 아닐 때에만 getObjects 호출
+ const canvasObjects = canvas ? canvas.getObjects() : []
+ const [correntObjectNo, setCorrentObjectNo] = useRecoilState(correntObjectNoState)
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const [settingModalSecondOptions, setSettingModalSecondOptions] = useRecoilState(settingModalSecondOptionsState)
@@ -25,9 +31,16 @@ export function useCanvasSetting() {
const { swalFire } = useSwal()
const [adsorptionPointMode, setAdsorptionPointMode] = useRecoilState(adsorptionPointModeState)
- const setAdsorptionRange = useSetRecoilState(adsorptionRangeState)
+ const [adsorptionRange, setAdsorptionRange] = useRecoilState(adsorptionRangeState)
+ const [planSizeSettingMode, setPlanSizeSettingMode] = useRecoilState(planSizeSettingState)
+ //const setAdsorptionRange = useSetRecoilState(adsorptionRangeState)
- const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
+ const [selectedFont, setSelectedFont] = useState()
+ const [selectedFontWeight, setSelectedFontWeight] = useState()
+ const [selectedFontSize, setSelectedFontSize] = useState()
+ const [selectedFontColor, setSelectedFontColor] = useState()
+ const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
+ const [dimensionLineSettings, setDimensionLineSettings] = useRecoilState(dimensionLineSettingsState)
useEffect(() => {
if (!canvas) {
@@ -56,199 +69,352 @@ export function useCanvasSetting() {
})
break
}
- canvas.renderAll()
+ canvas?.renderAll()
}, [corridorDimension])
useEffect(() => {
- console.log('useCanvasSetting useEffect 실행1')
- fetchSettings()
- }, [objectNo])
+ console.log('useCanvasSetting useEffect 실행1', correntObjectNo)
+ }, [])
+ //흡착점 ON/OFF 변경 시
useEffect(() => {
- console.log('useCanvasSetting useEffect 실행2')
- //fetchSettings()
- //onClickOption()
- //fetchSettings()
+ console.log('useCanvasSetting useEffect 실행2', adsorptionPointMode.fontFlag, correntObjectNo)
+
+ if (adsorptionPointMode.fontFlag) {
+ onClickOption2()
+ frontSettings()
+ fetchSettings()
+ }
}, [adsorptionPointMode])
+ // 1 과 2 변경 시
useEffect(() => {
- console.log('useCanvasSetting useEffect 실행3')
- //fetchSettings()
- //onClickOption()
- //fetchSettings()
+ console.log('useCanvasSetting useEffect 실행3', settingModalFirstOptions.fontFlag, settingModalSecondOptions.fontFlag, correntObjectNo)
+ if (settingModalFirstOptions.fontFlag || settingModalSecondOptions.fontFlag) {
+ onClickOption2()
+ frontSettings()
+ fetchSettings()
+ }
}, [settingModalFirstOptions, settingModalSecondOptions])
+ // 글꼴 변경 시
+ useEffect(() => {
+ console.log('useCanvasSetting useEffect 실행4', globalFont.fontFlag, correntObjectNo)
+ if (globalFont.fontFlag) {
+ onClickOption2()
+ frontSettings()
+ fetchSettings()
+ }
+ }, [globalFont])
+
+ // 도명크기 변경 시
+ useEffect(() => {
+ console.log('useCanvasSetting useEffect 실행5', planSizeSettingMode.flag, correntObjectNo)
+
+ if (planSizeSettingMode.flag) {
+ onClickOption2()
+ frontSettings()
+ fetchSettings()
+ }
+ }, [planSizeSettingMode])
+
+ const getFonts = (itemValue) => {
+ if (!itemValue) return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
+ const data = [
+ { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
+ { id: 2, name: '@Yu Gothic', value: '@Yu Gothic' },
+ { id: 3, name: 'Yu Gothic', value: 'Yu Gothic' },
+ { id: 4, name: '@Yu Gothic UI', value: '@Yu Gothic UI' },
+ { id: 5, name: 'Yu Gothic UI', value: 'Yu Gothic UI' },
+ ].filter((font) => font.value === itemValue)
+ if (data.length !== 0) {
+ return data[0]
+ } else {
+ return { id: 1, name: 'MS PGothic', value: 'MS PGothic' }
+ }
+ }
+
+ const getFontSizes = (itemValue) => {
+ if (!itemValue) return { id: 16, name: 16, value: 16 }
+ const data = [
+ ...Array.from({ length: 4 }).map((_, index) => {
+ return { id: index + 8, name: index + 8, value: index + 8 }
+ }),
+ ...Array.from({ length: 9 }).map((_, index) => {
+ return { id: (index + 6) * 2, name: (index + 6) * 2, value: (index + 6) * 2 }
+ }),
+ { id: 36, name: 36, value: 36 },
+ { id: 48, name: 48, value: 48 },
+ { id: 72, name: 72, value: 72 },
+ ].filter((fontSize) => fontSize.value === itemValue)
+ if (data.length !== 0) {
+ return data[0]
+ } else {
+ return { id: 16, name: 16, value: 16 }
+ }
+ }
+
+ const getFontStyles = (itemValue) => {
+ if (!itemValue) return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
+ const data = [
+ { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' },
+ { id: 'italic', name: getMessage('font.style.italic'), value: 'italic' },
+ { id: 'bold', name: getMessage('font.style.bold'), value: 'bold' },
+ { id: 'boldAndItalic', name: getMessage('font.style.bold.italic'), value: 'boldAndItalic' },
+ ].filter((fontStyle) => fontStyle.value === itemValue)
+ if (data.length !== 0) {
+ return data[0]
+ } else {
+ return { id: 'normal', name: getMessage('font.style.normal'), value: 'normal' }
+ }
+ }
+
+ const getFontColors = (itemValue) => {
+ if (!itemValue) return { id: 'black', name: getMessage('color.black'), value: 'black' }
+ const data = [
+ { id: 'black', name: getMessage('color.black'), value: 'black' },
+ { id: 'red', name: getMessage('color.red'), value: 'red' },
+ { id: 'blue', name: getMessage('color.blue'), value: 'blue' },
+ { id: 'gray', name: getMessage('color.gray'), value: 'gray' },
+ { id: 'yellow', name: getMessage('color.yellow'), value: 'yellow' },
+ { id: 'green', name: getMessage('color.green'), value: 'green' },
+ { id: 'pink', name: getMessage('color.pink'), value: 'pink' },
+ { id: 'gold', name: getMessage('color.gold'), value: 'gold' },
+ { id: 'darkblue', name: getMessage('color.darkblue'), value: 'darkblue' },
+ ].filter((fontColor) => fontColor.value === itemValue)
+ if (data.length !== 0) {
+ return data[0]
+ } else {
+ return { id: 'black', name: getMessage('color.black'), value: 'black' }
+ }
+ }
+
const fetchSettings = async () => {
try {
- const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` })
+ const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${correntObjectNo}` })
console.log('res', res)
- const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
- const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
- const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
- const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
- const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
- const patternData = {
- adsorpPoint: res.adsorpPoint,
+ if (res) {
+ const optionData1 = settingModalFirstOptions.option1.map((item) => ({ ...item, selected: res[item.column] }))
+ const optionData2 = settingModalFirstOptions.option2.map((item) => ({ ...item, selected: res[item.column] }))
+ const optionData3 = settingModalSecondOptions.option3.map((item) => ({ ...item }))
+ const optionData4 = settingModalSecondOptions.option4.map((item) => ({ ...item, selected: res[item.column] }))
+ const optionData5 = settingModalFirstOptions.dimensionDisplay.map((item) => ({ ...item }))
+
+ //흡착점 ON/OFF
+ setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: res.adsorpPoint, fontFlag: false })
+
+ //치수선 설정
+ setDimensionLineSettings({ ...dimensionLineSettings, pixel: res.originPixel, color: res.originColor })
+
+ //도면크기 설정
+ setPlanSizeSettingMode({
+ ...planSizeSettingMode,
+ originHorizon: res.originHorizon,
+ originVertical: res.originVertical,
+ flag: false,
+ })
+
+ // 데이터 설정
+ setSettingModalFirstOptions({
+ ...settingModalFirstOptions,
+ option1: optionData1,
+ option2: optionData2,
+ dimensionDisplay: optionData5,
+ fontFlag: false,
+ })
+ setSettingModalSecondOptions({
+ ...settingModalSecondOptions,
+ option3: optionData3,
+ option4: optionData4,
+ fontFlag: false,
+ })
+
+ const fontPatternData = {
+ commonText: {
+ //문자 글꼴 조회 데이터
+ fontFamily: getFonts(res.wordFont),
+ fontWeight: getFontStyles(res.wordFontStyle),
+ fontSize: getFontSizes(res.wordFontSize),
+ fontColor: getFontColors(res.wordFontColor),
+ },
+ flowText: {
+ //흐름방향 글꼴 조회 데이터
+ fontFamily: getFonts(res.flowFont),
+ fontWeight: getFontStyles(res.flowFontStyle),
+ fontSize: getFontSizes(res.flowFontSize),
+ fontColor: getFontColors(res.flowFontColor),
+ },
+ dimensionLineText: {
+ //치수 글꼴 조회 데이터
+ fontFamily: getFonts(res.dimensioFont),
+ fontWeight: getFontStyles(res.dimensioFontStyle),
+ fontSize: getFontSizes(res.dimensioFontSize),
+ fontColor: getFontColors(res.dimensioFontColor),
+ },
+ circuitNumberText: {
+ //회로번호 글꼴 조회 데이터
+ fontFamily: getFonts(res.circuitNumFont),
+ fontWeight: getFontStyles(res.circuitNumFontStyle),
+ fontSize: getFontSizes(res.circuitNumFontSize),
+ fontColor: getFontColors(res.circuitNumFontColor),
+ },
+ lengthText: {
+ //치수선 글꼴 조회 데이터
+ fontFamily: getFonts(res.lengthFont),
+ fontWeight: getFontStyles(res.lengthFontStyle),
+ fontSize: getFontSizes(res.lengthFontSize),
+ fontColor: getFontColors(res.lengthFontColor),
+ },
+ //글꼴 설정 Flag
+ fontFlag: false,
+ }
+ console.log('fontPatternData', fontPatternData)
+
+ //조회된 글꼴 데이터 set
+ setGlobalFont(fontPatternData)
+ } else {
+ //조회된 글꼴 데이터가 없는 경우
+
+ //흡착점 ON/OFF
+ setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: false, fontFlag: false })
+
+ //치수선 설정
+ setDimensionLineSettings({ ...dimensionLineSettings })
+
+ //도면크기 설정
+ setPlanSizeSettingMode({
+ ...planSizeSettingMode,
+ flag: false,
+ })
+
+ // 데이터 설정
+ setSettingModalFirstOptions({
+ ...settingModalFirstOptions,
+ fontFlag: false,
+ })
+ setSettingModalSecondOptions({
+ ...settingModalSecondOptions,
+ fontFlag: false,
+ })
+
+ setGlobalFont({ ...globalFont, fontFlag: false })
}
-
- // 데이터 설정
- setSettingModalFirstOptions({
- option1: optionData1,
- option2: optionData2,
- dimensionDisplay: optionData5,
- })
- setSettingModalSecondOptions({
- option3: optionData3,
- option4: optionData4,
- })
-
- setAdsorptionPointMode(patternData.adsorpPoint)
-
- console.log('adsorptionPointMode', adsorptionPointMode)
+ frontSettings()
} catch (error) {
console.error('Data fetching error:', error)
}
}
// 옵션 클릭 후 저장
- const onClickOption = async (item) => {
- //치수 표시(단 건 선택)
- if (item.column === 'corridorDimension' || item.column === 'realDimension' || item.column === 'noneDimension') {
- console.log('치수 표시 ', item)
- const options = settingModalFirstOptions?.dimensionDisplay.map((option) => {
- option.selected = option.id === item.id
- return option
- })
+ const onClickOption2 = useCallback(async () => {
+ // 서버에 전송할 데이터
+ const dataToSend = {
+ firstOption1: option1.map((item) => ({
+ column: item.column,
+ selected: item.selected,
+ })),
+ firstOption2: option2.map((item) => ({
+ column: item.column,
+ selected: item.selected,
+ })),
+ firstOption3: dimensionDisplay.map((item) => ({
+ column: item.column,
+ selected: item.selected,
+ })),
+ secondOption2: option4.map((item) => ({
+ column: item.column,
+ selected: item.selected,
+ })),
+ }
+ // console.log('globalFont', globalFont)
+ const patternData = {
+ //견적서 번호
+ objectNo: correntObjectNo,
+ //디스플레이 설정(다중)
+ allocDisplay: dataToSend.firstOption1[0].selected,
+ outlineDisplay: dataToSend.firstOption1[1].selected,
+ gridDisplay: dataToSend.firstOption1[2].selected,
+ lineDisplay: dataToSend.firstOption1[3].selected,
+ wordDisplay: dataToSend.firstOption1[4].selected,
+ circuitNumDisplay: dataToSend.firstOption1[5].selected,
+ flowDisplay: dataToSend.firstOption1[6].selected,
+ trestleDisplay: dataToSend.firstOption1[7].selected,
+ imageDisplay: dataToSend.firstOption1[8].selected,
+ totalDisplay: dataToSend.firstOption1[9].selected,
+ //차수 표시(단 건)
+ corridorDimension: dataToSend.firstOption3[0].selected,
+ realDimension: dataToSend.firstOption3[1].selected,
+ noneDimension: dataToSend.firstOption3[2].selected,
+ //화면 표시(단 건)
+ onlyBorder: dataToSend.firstOption2[0].selected,
+ lineHatch: dataToSend.firstOption2[1].selected,
+ allPainted: dataToSend.firstOption2[2].selected,
+ //흡착범위 설정(단 건)
+ adsorpRangeSmall: dataToSend.secondOption2[0].selected,
+ adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
+ adsorpRangeMedium: dataToSend.secondOption2[2].selected,
+ adsorpRangeLarge: dataToSend.secondOption2[3].selected,
+ //흡착점 ON/OFF
+ adsorpPoint: adsorptionPointMode.adsorptionPoint,
+ //??: adsorptionRange, 사용여부 확인 필요
- //화면 표시(단 건 선택)
- } else if (item.column === 'onlyBorder' || item.column === 'lineHatch' || item.column === 'allPainted') {
- console.log('화면 표시 ', item)
- const options2 = settingModalFirstOptions?.option2.map((option2) => {
- option2.selected = option2.id === item.id
- return option2
- })
+ //글꼴 설정
+ //문자 글꼴
+ wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
+ wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
+ wordFontSize: globalFont.commonText.fontSize?.value ?? 16,
+ wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
- const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ //흐름방향 글꼴
+ flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
+ flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
+ flowFontSize: globalFont.flowText.fontSize?.value ?? 16,
+ flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
- polygons.forEach((polygon) => {
- setSurfaceShapePattern(polygon, item.column)
- })
+ //치수 글꼴
+ dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
+ dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
+ dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 16,
+ dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
- //흡착범위 설정(단 건 선택)
- } else if (
- item.column === 'adsorpRangeSmall' ||
- item.column === 'adsorpRangeSmallSemi' ||
- item.column === 'adsorpRangeMedium' ||
- item.column === 'adsorpRangeLarge'
- ) {
- console.log('화면 표시2 ', item, option4)
- // option4에서 한 개만 선택 가능하도록 처리
- const updatedOption4 = option4.map((option) =>
- option.id === item.id
- ? { ...option, selected: true }
- : {
- ...option,
- selected: false,
- },
- )
+ //회로번호 글꼴
+ circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
+ circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
+ circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 16,
+ circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
- setSettingModalSecondOptions({ option3, option4: updatedOption4 })
+ //치수선 글꼴
+ lengthFont: globalFont.lengthText.fontFamily?.value ?? 'MS PGothic',
+ lengthFontStyle: globalFont.lengthText.fontWeight?.value ?? 'normal',
+ lengthFontSize: globalFont.lengthText.fontSize?.value ?? 16,
+ lengthFontColor: globalFont.lengthText.fontColor?.value ?? 'black',
- //흡착점 ON / OFF
- } else if (item === 'adsorpPoint') {
- console.log('흡착점 ON / OFF ', item)
- const options2 = settingModalFirstOptions?.option2.map((option2) => {
- option2.selected = option2.id === item.id
- return option2
- })
+ //치수선 설정
+ originPixel: dimensionLineSettings.pixel,
+ originColor: dimensionLineSettings.color,
- const polygons = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
-
- polygons.forEach((polygon) => {
- setSurfaceShapePattern(polygon, item.column)
- })
-
- //디스플레이 설정(다 건 선택)
- } else {
- //console.log('디스플레이 설정1 ', item.column)
- console.log('디스플레이 설정 ', item)
- item.selected = !item.selected
+ //치수선 설정
+ originHorizon: planSizeSettingMode.originHorizon,
+ originVertical: planSizeSettingMode.originVertical,
}
- setSettingModalFirstOptions({ option1, option2, dimensionDisplay })
+ console.log('patternData ', patternData)
- try {
- // 서버에 전송할 데이터
- const dataToSend = {
- firstOption1: option1.map((item) => ({
- column: item.column,
- selected: item.selected,
- })),
- firstOption2: option2.map((item) => ({
- column: item.column,
- selected: item.selected,
- })),
- firstOption3: dimensionDisplay.map((item) => ({
- column: item.column,
- selected: item.selected,
- })),
- // secondOption1: secondOptions[0].option1.map((item) => ({
- // name: item.id,
- // name: item.name,
- // // 필요한 경우 데이터 항목 추가
- // })),
- secondOption2: option4.map((item) => ({
- column: item.column,
- selected: item.selected,
- })),
- }
-
- const patternData = {
- objectNo,
- //디스플레이 설정(다중)
- allocDisplay: dataToSend.firstOption1[0].selected,
- outlineDisplay: dataToSend.firstOption1[1].selected,
- gridDisplay: dataToSend.firstOption1[2].selected,
- lineDisplay: dataToSend.firstOption1[3].selected,
- wordDisplay: dataToSend.firstOption1[4].selected,
- circuitNumDisplay: dataToSend.firstOption1[5].selected,
- flowDisplay: dataToSend.firstOption1[6].selected,
- trestleDisplay: dataToSend.firstOption1[7].selected,
- imageDisplay: dataToSend.firstOption1[8].selected,
- totalDisplay: dataToSend.firstOption1[9].selected,
- //차수 표시(단 건)
- corridorDimension: dataToSend.firstOption3[0].selected,
- realDimension: dataToSend.firstOption3[1].selected,
- noneDimension: dataToSend.firstOption3[2].selected,
- //화면 표시(단 건)
- onlyBorder: dataToSend.firstOption2[0].selected,
- lineHatch: dataToSend.firstOption2[1].selected,
- allPainted: dataToSend.firstOption2[2].selected,
- //흡착범위 설정(단 건)
- adsorpRangeSmall: dataToSend.secondOption2[0].selected,
- adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected,
- adsorpRangeMedium: dataToSend.secondOption2[2].selected,
- adsorpRangeLarge: dataToSend.secondOption2[3].selected,
- //흡착점 ON/OFF
- adsorpPoint: adsorptionPointMode,
- }
-
- console.log('patternData ', patternData)
-
- // HTTP POST 요청 보내기
- await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => {
+ // HTTP POST 요청 보내기
+ await post({ url: `/api/canvas-management/canvas-settings`, data: patternData })
+ .then((res) => {
swalFire({ text: getMessage(res.returnMessage) })
// Canvas 디스플레이 설정 시 해당 옵션 적용
frontSettings()
})
- } catch (error) {
- swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
- }
+ .catch((error) => {
+ swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
+ })
- setAdsorptionRange(item.range)
- }
+ //setAdsorptionRange(item.range)
+ }, [settingModalFirstOptions, settingModalSecondOptions, adsorptionPointMode, globalFont, planSizeSettingMode])
// Canvas 디스플레이 설정 시 해당 옵션 적용
const frontSettings = async () => {
@@ -260,7 +426,7 @@ export function useCanvasSetting() {
// 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
// 'wordDisplay' 문자 표시
// 'circuitNumDisplay' 회로번호 표시
- // 'flowDisplay' 흐름방향 표시 'arrow'
+ // 'flowDisplay' 흐름방향 표시 'arrow', 'flowText'
// 'trestleDisplay' 가대 표시
// 'imageDisplay' 이미지 표시
// 'totalDisplay' 집계표 표시
@@ -277,13 +443,13 @@ export function useCanvasSetting() {
optionName = ['outerLine', POLYGON_TYPE.WALL]
break
case 'gridDisplay': //그리드 표시
- optionName = ['lindGrid', 'dotGrid']
+ optionName = ['lindGrid', 'dotGrid', 'tempGrid']
break
case 'lineDisplay': //지붕선 표시
optionName = ['roof', POLYGON_TYPE.ROOF]
break
case 'wordDisplay': //문자 표시
- optionName = ['6']
+ optionName = ['commonText']
break
case 'circuitNumDisplay': //회로번호 표시
optionName = ['7']
@@ -304,8 +470,8 @@ export function useCanvasSetting() {
// 표시 선택 상태(true/false)
optionSelected = option1[i].selected
- canvas
- .getObjects()
+ //canvas.getObjects() >> canvasObjects
+ canvasObjects
.filter((obj) => optionName.includes(obj.name))
//.filter((obj) => obj.name === optionName)
.forEach((obj) => {
@@ -313,6 +479,8 @@ export function useCanvasSetting() {
//obj.set({ visible: !obj.visible })
})
+ canvas?.renderAll()
+
// console.log(
// 'optionName',
// optionName,
@@ -322,14 +490,31 @@ export function useCanvasSetting() {
}
return {
+ canvas,
settingModalFirstOptions,
setSettingModalFirstOptions,
settingModalSecondOptions,
setSettingModalSecondOptions,
adsorptionPointMode,
setAdsorptionPointMode,
+ adsorptionRange,
+ setAdsorptionRange,
fetchSettings,
- onClickOption,
+ //onClickOption,
frontSettings,
+ globalFont,
+ setGlobalFont,
+ selectedFont,
+ setSelectedFont,
+ selectedFontWeight,
+ setSelectedFontWeight,
+ selectedFontSize,
+ setSelectedFontSize,
+ selectedFontColor,
+ setSelectedFontColor,
+ dimensionLineSettings,
+ setDimensionLineSettings,
+ planSizeSettingMode,
+ setPlanSizeSettingMode,
}
}
diff --git a/src/hooks/option/useFirstOption.js b/src/hooks/option/useFirstOption.js
deleted file mode 100644
index c296a942..00000000
--- a/src/hooks/option/useFirstOption.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import { useRecoilState, useRecoilValue } from 'recoil'
-import { canvasState } from '@/store/canvasAtom'
-import { useEffect } from 'react'
-import { settingModalFirstOptionsState } from '@/store/settingAtom'
-import { POLYGON_TYPE } from '@/common/common'
-
-export function useFirstOption() {
- const canvas = useRecoilValue(canvasState)
-
- const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
-
- useEffect(() => {
- const option1 = settingModalFirstOptions.option1
-
- // 'allocDisplay' 할당 표시
- // 'outlineDisplay' 외벽선 표시 'outerLine', POLYGON_TYPE.WALL
- // 'gridDisplay' 그리드 표시 'lindGrid', 'dotGrid'
- // 'lineDisplay' 지붕선 표시 'roof', POLYGON_TYPE.ROOF
- // 'wordDisplay' 문자 표시
- // 'circuitNumDisplay' 회로번호 표시
- // 'flowDisplay' 흐름방향 표시 'arrow'
- // 'trestleDisplay' 가대 표시
- // 'totalDisplay' 집계표 표시
-
- let optionName //옵션명
- let optionSelected //옵션상태
-
- for (let i = 0; i < option1.length; i++) {
- switch (option1[i].column) {
- case 'allocDisplay': //할당 표시
- optionName = ['1']
- break
- case 'outlineDisplay': //외벽선 표시
- optionName = ['outerLine', POLYGON_TYPE.WALL]
- break
- case 'gridDisplay': //그리드 표시
- optionName = ['lineGrid', 'dotGrid', 'adsorptionPoint', 'tempGrid']
- break
- case 'lineDisplay': //지붕선 표시
- optionName = ['roof', POLYGON_TYPE.ROOF]
- break
- case 'wordDisplay': //문자 표시
- optionName = ['6']
- break
- case 'circuitNumDisplay': //회로번호 표시
- optionName = ['7']
- break
- case 'flowDisplay': //흐름방향 표시
- optionName = ['arrow', 'flowText']
- break
- case 'trestleDisplay': //가대 표시
- optionName = ['8']
- break
- case 'totalDisplay': //집계표 표시
- optionName = ['9']
- break
- }
- // 표시 선택 상태(true/false)
- optionSelected = option1[i].selected
-
- canvas
- .getObjects()
- .filter((obj) => optionName.includes(obj.name))
- //.filter((obj) => obj.name === optionName)
- .forEach((obj) => {
- obj.set({ visible: optionSelected })
- //obj.set({ visible: !obj.visible })
- })
-
- canvas.renderAll()
-
- // console.log(
- // 'optionName',
- // optionName,
- // canvas.getObjects().filter((obj) => optionName.includes(obj.name)),
- // )
- }
- }, [settingModalFirstOptions])
-
- return { settingModalFirstOptions, setSettingModalFirstOptions }
-}
diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js
index 6e95e79c..55053b7e 100644
--- a/src/hooks/roofcover/useAuxiliaryDrawing.js
+++ b/src/hooks/roofcover/useAuxiliaryDrawing.js
@@ -15,20 +15,19 @@ import {
outerLineLength2State,
outerLineTypeState,
} from '@/store/outerLineAtom'
-import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util'
+import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal'
-import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
-import { QPolygon } from '@/components/fabric/QPolygon'
import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성
export function useAuxiliaryDrawing(id) {
const canvas = useRecoilValue(canvasState)
const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useContext(EventContext)
const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid()
@@ -94,7 +93,7 @@ export function useAuxiliaryDrawing(id) {
addCanvasMouseEventListener('mouse:move', mouseMove)
addCanvasMouseEventListener('mouse:down', mouseDown)
- addDocumentEventListener('contextmenu', document, cutAuxiliary)
+ // addDocumentEventListener('contextmenu', document, cutAuxiliary)
addDocumentEventListener('keydown', document, keydown[type])
return () => {
@@ -122,6 +121,21 @@ export function useAuxiliaryDrawing(id) {
setOuterLineDiagonalLength(0)
}
+ const move = (object, x, y) => {
+ const line = copy(object, x, y)
+ canvas.remove(object)
+ canvas.setActiveObject(line)
+ }
+
+ const copy = (object, x, y) => {
+ return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
+ stroke: 'red',
+ strokeWidth: 1,
+ selectable: true,
+ name: 'auxiliaryLine',
+ })
+ }
+
const keydown = {
outerLine: (e) => {
if (mousePointerArr.current.length === 0) {
@@ -130,7 +144,7 @@ export function useAuxiliaryDrawing(id) {
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement
if (activeElem !== length1Ref.current) {
- length1Ref.current.focus()
+ length1Ref?.current?.focus()
}
const key = e.key
@@ -180,7 +194,7 @@ export function useAuxiliaryDrawing(id) {
const activeElem = document.activeElement
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
- length1Ref.current.focus()
+ length1Ref?.current?.focus()
}
switch (key) {
@@ -455,9 +469,24 @@ export function useAuxiliaryDrawing(id) {
name: 'auxiliaryLine',
})
- lineHistory.current.push(line)
+ const historyLines = [...lineHistory.current]
+
+ const hasSameLine = historyLines.some((history) => {
+ return (
+ (isSamePoint(history.startPoint, line.startPoint) && isSamePoint(history.endPoint, line.endPoint)) ||
+ (isSamePoint(history.startPoint, line.endPoint) && isSamePoint(history.endPoint, line.startPoint))
+ )
+ })
+
mousePointerArr.current = []
clear()
+
+ if (hasSameLine) {
+ canvas.remove(line)
+ return
+ }
+
+ lineHistory.current.push(line)
}
const mouseDown = (e) => {
@@ -520,8 +549,24 @@ export function useAuxiliaryDrawing(id) {
otherAdsorptionPoints.push(intersectionPoint)
})
})
+ let innerLinePoints = []
+ canvas
+ .getObjects()
+ .filter((obj) => obj.innerLines)
+ .forEach((polygon) => {
+ polygon.innerLines.forEach((line) => {
+ innerLinePoints.push({ x: line.x1, y: line.y1 })
+ innerLinePoints.push({ x: line.x2, y: line.y2 })
+ })
+ })
- const adsorptionPoints = [...getAdsorptionPoints(), ...roofAdsorptionPoints.current, ...otherAdsorptionPoints, ...intersectionPoints.current]
+ const adsorptionPoints = [
+ ...getAdsorptionPoints(),
+ ...roofAdsorptionPoints.current,
+ ...otherAdsorptionPoints,
+ ...intersectionPoints.current,
+ ...innerLinePoints,
+ ]
let arrivalPoint = { x: pointer.x, y: pointer.y }
@@ -780,7 +825,6 @@ export function useAuxiliaryDrawing(id) {
//lineHistory.current에 있는 선들 중 startPoint와 endPoint가 겹치는 line은 제거
// 겹치는 선 하나는 canvas에서 제거한다.
-
const tempLines = [...lineHistory.current]
lineHistory.current = []
tempLines.forEach((line) => {
@@ -804,27 +848,30 @@ export function useAuxiliaryDrawing(id) {
const tempPolygonPoints = [...roofBase.points].map((obj) => {
return { x: Math.round(obj.x), y: Math.round(obj.y) }
})
- const roofInnerLines = innerLines.filter((line) => {
+ const roofInnerLines = [...roofBase.innerLines, ...innerLines].filter((line) => {
const inPolygon1 =
- tempPolygonPoints.some((point) => point.x === line.x1 && point.y === line.y1) ||
- roofBase.inPolygon({ x: line.x1, y: line.y1 }) ||
- roofBase.lines.some((line) => isPointOnLine(line, { x: line.x1, y: line.y1 }))
+ tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x1) && Math.round(point.y) === Math.round(line.y1)) ||
+ roofBase.inPolygon({ x: Math.round(line.x1), y: Math.round(line.y1) }) ||
+ roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x1), y: Math.round(line.y1) }))
const inPolygon2 =
- tempPolygonPoints.some((point) => point.x === line.x2 && point.y === line.y2) ||
- roofBase.inPolygon({ x: line.x2, y: line.y2 }) ||
- roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 }))
+ tempPolygonPoints.some((point) => Math.round(point.x) === Math.round(line.x2) && Math.round(point.y) === Math.round(line.y2)) ||
+ roofBase.inPolygon({ x: Math.round(line.x2), y: Math.round(line.y2) }) ||
+ roofBase.lines.some((line) => isPointOnLine(line, { x: Math.round(line.x2), y: Math.round(line.y2) }))
if (inPolygon1 && inPolygon2) {
- line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() }
+ line.attributes = {
+ ...line.attributes,
+ roofId: roofBase.id,
+ actualSize: line.attributes?.actualSize ?? 0,
+ planeSize: line.getLength(),
+ }
return true
}
})
- roofBase.innerLines = [...roofInnerLines]
-
+ roofBase.innerLines = lineHistory.current.length !== 0 ? [...roofInnerLines] : roofBase.innerLines
canvas.renderAll()
})
-
closePopup(id)
}
@@ -856,5 +903,8 @@ export function useAuxiliaryDrawing(id) {
handleRollback,
buttonAct,
setButtonAct,
+ move,
+ copy,
+ cutAuxiliary,
}
}
diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js
index 5619abfb..27e00a86 100644
--- a/src/hooks/roofcover/useEavesGableEdit.js
+++ b/src/hooks/roofcover/useEavesGableEdit.js
@@ -16,6 +16,7 @@ export function useEavesGableEdit(id) {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { addCanvasMouseEventListener, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { closePopup } = usePopup()
const TYPES = {
EAVES: 'eaves',
diff --git a/src/hooks/roofcover/useMovementSetting.js b/src/hooks/roofcover/useMovementSetting.js
index 34961ea0..509c53f3 100644
--- a/src/hooks/roofcover/useMovementSetting.js
+++ b/src/hooks/roofcover/useMovementSetting.js
@@ -6,7 +6,6 @@ import { useEffect, useRef, useState } from 'react'
import { useEvent } from '@/hooks/useEvent'
import { POLYGON_TYPE } from '@/common/common'
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
-import { QLine } from '@/components/fabric/QLine'
//동선이동 형 올림 내림
export function useMovementSetting(id) {
@@ -16,6 +15,7 @@ export function useMovementSetting(id) {
}
const canvas = useRecoilValue(canvasState)
const { initEvent, addCanvasMouseEventListener } = useEvent()
+ // const { initEvent, addCanvasMouseEventListener } = useContext(EventContext)
const { closePopup } = usePopup()
const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState)
diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js
index b2244da8..c668dd60 100644
--- a/src/hooks/roofcover/useOuterLineWall.js
+++ b/src/hooks/roofcover/useOuterLineWall.js
@@ -1,6 +1,6 @@
-import { useEffect, useRef } from 'react'
+import { useContext, useEffect, useRef } from 'react'
import { distanceBetweenPoints } from '@/util/canvas-util'
-import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
+import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import {
adsorptionPointAddModeState,
adsorptionPointModeState,
@@ -31,6 +31,7 @@ import { fabric } from 'fabric'
import { outlineDisplaySelector } from '@/store/settingAtom'
import { usePopup } from '@/hooks/usePopup'
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
+import { EventContext } from '@/app/floor-plan/EventProvider'
//외벽선 그리기
export function useOuterLineWall(id, propertiesId) {
@@ -43,6 +44,14 @@ export function useOuterLineWall(id, propertiesId) {
removeAllDocumentEventListeners,
removeMouseEvent,
} = useEvent()
+ // const {
+ // initEvent,
+ // // addCanvasMouseEventListener,
+ // // addDocumentEventListener,
+ // removeAllMouseEventListeners,
+ // removeAllDocumentEventListeners,
+ // removeMouseEvent,
+ // } = useContext(EventContext)
const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine()
const { tempGridMode } = useTempGrid()
@@ -65,6 +74,7 @@ export function useOuterLineWall(id, propertiesId) {
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
const [points, setPoints] = useRecoilState(outerLinePointsState)
+ const resetPoints = useResetRecoilState(outerLinePointsState)
const [type, setType] = useRecoilState(outerLineTypeState)
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
@@ -88,6 +98,13 @@ export function useOuterLineWall(id, propertiesId) {
clear()
return () => {
initEvent()
+
+ canvas
+ .getObjects()
+ .filter((obj) => obj.name === 'startPoint')
+ .forEach((obj) => {
+ canvas.remove(obj)
+ })
}
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js
index 3a1535a3..73a72e5a 100644
--- a/src/hooks/roofcover/usePropertiesSetting.js
+++ b/src/hooks/roofcover/usePropertiesSetting.js
@@ -125,6 +125,12 @@ export function usePropertiesSetting(id) {
}
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
+ const notSetAttributes = lines.filter((line) => !line.attributes?.type)
+ if (notSetAttributes.length > 0) {
+ alert('설정되지 않은 외벽선이 있습니다.')
+ return
+ }
+
lines.forEach((line) => {
line.set({
attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL },
diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js
index b4118dc1..ebb0184a 100644
--- a/src/hooks/roofcover/useRoofAllocationSetting.js
+++ b/src/hooks/roofcover/useRoofAllocationSetting.js
@@ -1,5 +1,5 @@
-import { useRecoilValue } from 'recoil'
-import { canvasState, currentObjectState } from '@/store/canvasAtom'
+import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
+import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal'
@@ -10,17 +10,22 @@ 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 useMenu from '@/hooks/common/useMenu'
+import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
+import { menuTypeState } from '@/store/menuAtom'
// 지붕면 할당
export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
- const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon()
+ const { drawDirectionArrow, addLengthText, splitPolygonWithLines, splitPolygonWithSeparate } = usePolygon()
const [popupId, setPopupId] = useState(uuidv4())
const { addPopup, closePopup, closeAll } = usePopup()
const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState)
const { swalFire } = useSwal()
+ const { setMenuNumber } = useCanvasMenu()
+ const setMenuType = useSetRecoilState(menuTypeState)
const roofMaterials = [
{
id: 'A',
@@ -107,7 +112,7 @@ export function useRoofAllocationSetting(id) {
}
})
})
- if (currentObject && currentObject.name && !currentObject.name.toLowerCase().includes('text')) {
+ if (currentObject && currentObject.name && ['auxiliaryLine', 'ridge', 'hip'].includes(currentObject.name)) {
currentObject.set({
strokeWidth: 4,
stroke: '#EA10AC',
@@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
setValues(values.filter((value) => value.id !== id))
}
+ const { handleMenu } = useMenu()
+ const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
+
// 선택한 지붕재로 할당
const handleSave = () => {
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
@@ -176,20 +184,23 @@ export function useRoofAllocationSetting(id) {
}
const apply = () => {
- const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL)
roofBases.forEach((roofBase) => {
try {
- splitPolygonWithLines(roofBase)
+ if (roofBase.separatePolygon.length > 0) {
+ splitPolygonWithSeparate(roofBase.separatePolygon)
+ } else {
+ splitPolygonWithLines(roofBase)
+ }
} catch (e) {
return
}
-
roofBase.innerLines.forEach((line) => {
canvas.remove(line)
})
- // canvas.remove(roofBase)
+ canvas.remove(roofBase)
})
wallLines.forEach((wallLine) => {
@@ -199,6 +210,10 @@ export function useRoofAllocationSetting(id) {
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
roofs.forEach((roof) => {
+ if (roof.isFixed) return
+ roof.set({
+ isFixed: true,
+ })
setSurfaceShapePattern(roof, roofDisplay.column)
drawDirectionArrow(roof)
})
@@ -209,6 +224,8 @@ export function useRoofAllocationSetting(id) {
})
setEditingLines([])
closeAll()
+ setMenuNumber(3)
+ setMenuType('surface')
}
const setLineSize = (id, size) => {
diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js
index e0af3205..a2c799f4 100644
--- a/src/hooks/roofcover/useRoofShapePassivitySetting.js
+++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js
@@ -26,6 +26,7 @@ export function useRoofShapePassivitySetting(id) {
const { showLine, hideLine, addPitchTextsByOuterLines } = useLine()
const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { drawRoofPolygon } = useMode()
const { addPolygonByLines } = usePolygon()
const currentObject = useRecoilValue(currentObjectState)
@@ -202,7 +203,11 @@ export function useRoofShapePassivitySetting(id) {
wall = addPolygonByLines(lines, { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
} else {
// 그냥 닫을 경우 처리
- wall = addPolygonByLines([...initLines.current], { name: POLYGON_TYPE.WALL, fill: 'transparent', stroke: 'black' })
+ wall = addPolygonByLines([...initLines.current], {
+ name: POLYGON_TYPE.WALL,
+ fill: 'transparent',
+ stroke: 'black',
+ })
lines.forEach((line, idx) => {
line.attributes = initLines.current[idx].attributes
})
diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js
index af55e557..c26c61ec 100644
--- a/src/hooks/roofcover/useRoofShapeSetting.js
+++ b/src/hooks/roofcover/useRoofShapeSetting.js
@@ -377,20 +377,20 @@ export function useRoofShapeSetting(id) {
}
// 기존 wallLine, roofBase 제거
- /*canvas
+ canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.WALL)
.forEach((line) => {
canvas.remove(line)
- })*/
+ })
- /*canvas
+ canvas
.getObjects()
- .filter((obj) => obj.name === POLYGON_TYPE.ROOF)
+ .filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.isFixed)
.forEach((obj) => {
canvas.remove(...obj.innerLines)
canvas.remove(obj)
- })*/
+ })
const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction })
polygon.lines = [...outerLines]
diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js
index ba6453ef..22af0f49 100644
--- a/src/hooks/roofcover/useWallLineOffsetSetting.js
+++ b/src/hooks/roofcover/useWallLineOffsetSetting.js
@@ -15,6 +15,7 @@ export function useWallLineOffsetSetting(id) {
const { closePopup } = usePopup()
const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const wallLineEditRef = useRef(null)
const length1Ref = useRef(null)
const length2Ref = useRef(null)
diff --git a/src/hooks/surface/usePlacementShapeDrawing.js b/src/hooks/surface/usePlacementShapeDrawing.js
index 24f641fe..95388ca4 100644
--- a/src/hooks/surface/usePlacementShapeDrawing.js
+++ b/src/hooks/surface/usePlacementShapeDrawing.js
@@ -33,12 +33,15 @@ import { POLYGON_TYPE } from '@/common/common'
import { usePopup } from '@/hooks/usePopup'
import { roofDisplaySelector } from '@/store/settingAtom'
+
// 면형상 배치
export function usePlacementShapeDrawing(id) {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
useEvent()
+ // const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
+ // useContext(EventContext)
const { getIntersectMousePoint } = useMouse()
const { addLine, removeLine } = useLine()
const { addPolygonByLines, drawDirectionArrow } = usePolygon()
@@ -429,56 +432,104 @@ export function usePlacementShapeDrawing(id) {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length1Value / 10,
+ y: prev[prev.length - 1].y + length2Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length1Value / 10,
+ y: prev[prev.length - 1].y + length2Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length1Value / 10,
+ y: prev[prev.length - 1].y - length2Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length1Value / 10,
+ y: prev[prev.length - 1].y - length2Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length2Value / 10,
+ y: prev[prev.length - 1].y + length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length2Value / 10,
+ y: prev[prev.length - 1].y - length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '←' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length2Value / 10,
+ y: prev[prev.length - 1].y + length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '←' && arrow2Value === '↑') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length2Value / 10,
+ y: prev[prev.length - 1].y - length1Value / 10,
+ },
+ ]
})
}
@@ -534,35 +585,65 @@ export function usePlacementShapeDrawing(id) {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length2Value / 10,
+ y: prev[prev.length - 1].y + length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↓' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length2Value / 10,
+ y: prev[prev.length - 1].y + length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↑' && arrow2Value === '→') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length2Value / 10,
+ y: prev[prev.length - 1].y - length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '↑' && arrow2Value === '←') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x - length2Value / 10,
+ y: prev[prev.length - 1].y - length1Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '→' && arrow2Value === '↓') {
setPoints((prev) => {
if (prev.length === 0) {
return []
}
- return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
+ return [
+ ...prev,
+ {
+ x: prev[prev.length - 1].x + length1Value / 10,
+ y: prev[prev.length - 1].y + length2Value / 10,
+ },
+ ]
})
} else if (arrow1Value === '→' && arrow2Value === '↑') {
setPoints((prev) => {
diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js
index 67ee1874..844829d3 100644
--- a/src/hooks/surface/useSurfaceShapeBatch.js
+++ b/src/hooks/surface/useSurfaceShapeBatch.js
@@ -2,7 +2,7 @@
import { useRecoilValue } from 'recoil'
import { canvasState, globalPitchState } from '@/store/canvasAtom'
-import { MENU, BATCH_TYPE, POLYGON_TYPE } from '@/common/common'
+import { MENU, POLYGON_TYPE } from '@/common/common'
import { getIntersectionPoint, setSurfaceShapePattern } from '@/util/canvas-util'
import { degreesToRadians } from '@turf/turf'
import { QPolygon } from '@/components/fabric/QPolygon'
@@ -27,6 +27,7 @@ export function useSurfaceShapeBatch() {
const slope = useRecoilValue(slopeSelector(globalPitch))
const { swalFire } = useSwal()
const { addCanvasMouseEventListener, initEvent } = useEvent()
+ // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext)
const { closePopup } = usePopup()
const applySurfaceShape = (surfaceRefs, selectedType, id) => {
@@ -102,6 +103,15 @@ export function useSurfaceShapeBatch() {
canvas?.add(obj)
+ canvas?.renderAll()
+ closePopup(id)
+ })
+
+ addCanvasMouseEventListener('mouse:down', (e) => {
+ isDrawing = false
+
+ canvas?.remove(obj)
+
//각도 추가
let originAngle = 0 //기본 남쪽
let direction = 'south'
@@ -119,21 +129,31 @@ export function useSurfaceShapeBatch() {
direction = 'north'
}
- obj.set({ direction: direction })
- obj.set({ originAngle: originAngle })
-
- canvas?.renderAll()
+ //회전, flip등이 먹은 기준으로 새로생성
+ const batchSurface = new QPolygon(obj.getCurrentPoints(), {
+ fill: 'transparent',
+ stroke: 'red',
+ strokeWidth: 1,
+ strokeDasharray: [10, 4],
+ fontSize: 12,
+ selectable: true,
+ lockMovementX: true, // X 축 이동 잠금
+ lockMovementY: true, // Y 축 이동 잠금
+ lockRotation: true, // 회전 잠금
+ lockScalingX: true, // X 축 크기 조정 잠금
+ lockScalingY: true, // Y 축 크기 조정 잠금
+ name: POLYGON_TYPE.ROOF,
+ originX: 'center',
+ originY: 'center',
+ pitch: globalPitch,
+ surfaceId: surfaceId,
+ direction: direction,
+ })
+ canvas?.add(batchSurface)
+ setSurfaceShapePattern(batchSurface, roofDisplay.column)
+ drawDirectionArrow(batchSurface)
closePopup(id)
- })
-
- addCanvasMouseEventListener('mouse:down', (e) => {
- isDrawing = false
- obj.set('name', POLYGON_TYPE.ROOF)
- obj.set('surfaceId', surfaceId)
initEvent()
- setSurfaceShapePattern(obj, roofDisplay.column)
- closePopup(id)
- drawDirectionArrow(obj)
})
}
}
@@ -307,8 +327,14 @@ export function useSurfaceShapeBatch() {
const angleInRadians = Math.asin(length2 / length3)
points = [
{ x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 },
- { x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) },
- { x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) },
+ {
+ x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians),
+ y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians),
+ },
+ {
+ x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians),
+ y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians),
+ },
{ x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 },
]
@@ -319,9 +345,18 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y - (length4 + length5) / 2 },
{ x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y + (length4 + length5) / 2 },
{ x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 },
- { x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 - length5 },
- { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 },
- { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 + length5 },
+ {
+ x: pointer.x - (length1 + length2 + length3) / 2 + length1,
+ y: pointer.y + (length4 + length5) / 2 - length5,
+ },
+ {
+ x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2,
+ y: pointer.y + (length4 + length5) / 2 - length5,
+ },
+ {
+ x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2,
+ y: pointer.y + (length4 + length5) / 2 - length5 + length5,
+ },
{
x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3,
y: pointer.y + (length4 + length5) / 2 - length5 + length5,
@@ -340,8 +375,14 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length3 },
{ x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 },
- { x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 + (length3 - length4) },
- { x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2, y: pointer.y + length4 / 2 - length3 + (length3 - length4) },
+ {
+ x: pointer.x - length1 / 2 + length1 - (length1 - length2),
+ y: pointer.y + length4 / 2 - length3 + (length3 - length4),
+ },
+ {
+ x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2,
+ y: pointer.y + length4 / 2 - length3 + (length3 - length4),
+ },
]
break
@@ -352,8 +393,14 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 },
- { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + (length4 - length5) },
- { x: pointer.x - length1 / 2 + length1 - length3 - length2, y: pointer.y + length4 / 2 - length4 + (length4 - length5) },
+ {
+ x: pointer.x - length1 / 2 + length1 - length3,
+ y: pointer.y + length4 / 2 - length4 + (length4 - length5),
+ },
+ {
+ x: pointer.x - length1 / 2 + length1 - length3 - length2,
+ y: pointer.y + length4 / 2 - length4 + (length4 - length5),
+ },
]
break
}
@@ -363,8 +410,14 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 },
{ x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 },
{ x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 },
- { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
- { x: pointer.x + length1 / 2 - length1 + length2 + length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
+ {
+ x: pointer.x + length1 / 2 - length1 + length2,
+ y: pointer.y + length4 / 2 - length5 - (length4 - length5),
+ },
+ {
+ x: pointer.x + length1 / 2 - length1 + length2 + length3,
+ y: pointer.y + length4 / 2 - length5 - (length4 - length5),
+ },
]
break
}
@@ -374,8 +427,14 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 },
{ x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 },
- { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
- { x: pointer.x - length1 / 2 + length1 - length2 - length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) },
+ {
+ x: pointer.x - length1 / 2 + length1 - length2,
+ y: pointer.y + length4 / 2 - length5 - (length4 - length5),
+ },
+ {
+ x: pointer.x - length1 / 2 + length1 - length2 - length3,
+ y: pointer.y + length4 / 2 - length5 - (length4 - length5),
+ },
]
break
}
@@ -386,7 +445,10 @@ export function useSurfaceShapeBatch() {
const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse)
points = [
- { x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle) },
+ {
+ x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle),
+ y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle),
+ },
{ x: pointer.x - length1 / 2, y: pointer.y + length3 / 2 },
{ x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 },
{
@@ -419,8 +481,8 @@ export function useSurfaceShapeBatch() {
{
fill: 'transparent',
stroke: 'black', //black
- strokeWidth: 2,
- selectable: true,
+ strokeWidth: 1,
+ selectable: false,
fontSize: 0,
},
)
@@ -429,6 +491,7 @@ export function useSurfaceShapeBatch() {
const scale = (length1 - length2) / coord.x
tmpPolygon.set({ scaleX: scale })
+ tmpPolygon.setViewLengthText(false)
pointsArray[0].x = 0
pointsArray[0].y = length3 //바닥면부터 시작하게
@@ -474,7 +537,10 @@ export function useSurfaceShapeBatch() {
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 },
{ x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 },
{ x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 },
- { x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, y: pointer.y + length4 / 2 - length4 + length5 },
+ {
+ x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2,
+ y: pointer.y + length4 / 2 - length4 + length5,
+ },
]
break
}
@@ -584,18 +650,6 @@ export function useSurfaceShapeBatch() {
text: '배치면 내용을 전부 삭제하시겠습니까?',
type: 'confirm',
confirmFn: () => {
- // canvas?.getObjects().forEach((obj) => {
- // if (
- // obj.name === POLYGON_TYPE.ROOF ||
- // obj.name === BATCH_TYPE.OPENING ||
- // obj.name === BATCH_TYPE.SHADOW ||
- // obj.name === BATCH_TYPE.TRIANGLE_DORMER ||
- // obj.name === BATCH_TYPE.PENTAGON_DORMER ||
- // obj.name === 'lengthText'
- // ) {
- // canvas?.remove(obj)
- // }
- // })
canvas.clear()
swalFire({ text: '삭제 완료 되었습니다.' })
},
@@ -661,34 +715,6 @@ export function useSurfaceShapeBatch() {
return groupObjectsArray
}
- function getAllRelatedObjects(id) {
- const ult = []
- const map = new Map()
-
- // Create a map of objects by their id
- canvas.getObjects().forEach((obj) => {
- map.set(obj.id, obj)
- })
-
- // Helper function to recursively find all related objects
- function findRelatedObjects(id) {
- const obj = map.get(id)
- if (obj) {
- result.push(obj)
- canvas.getObjects().forEach((o) => {
- if (o.parentId === id) {
- findRelatedObjects(o.id)
- }
- })
- }
- }
-
- // Start the search with the given parentId
- findRelatedObjects(id)
-
- return result
- }
-
const moveSurfaceShapeBatch = () => {
const roof = canvas.getActiveObject()
@@ -828,136 +854,75 @@ export function useSurfaceShapeBatch() {
canvas.renderAll()
}
- const surfaceShapeActualSize = () => {
- const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
- let notParallelLines = []
-
- const roofArray = []
-
- roofs.forEach((obj) => {
- const roof = {
- id: obj.id,
- surfaceId: obj.surfaceId,
- targetLines: [],
- baseLines: [],
- direction: obj.direction,
- }
- obj.lines.forEach((line) => {
- if (obj.direction === 'north' || obj.direction === 'south') {
- if (line.y1 !== line.y2) {
- roof.targetLines.push(line)
- } else {
- roof.baseLines.push(line)
- }
- } else {
- if (line.x1 !== line.x2) {
- roof.targetLines.push(line)
- } else {
- roof.baseLines.push(line)
- }
- }
- })
- roofArray.push(roof)
- })
-
- const slopeRadians = slope.angleValue * (Math.PI / 180)
- const tanSlope = Math.tan(slopeRadians)
-
- roofArray.forEach((roof) => {
- roof.targetLines.forEach((line) => {
- let calcLength = line.length
-
- if (roof.surfaceId === 1) {
- calcLength = roof.direction === 'north' || roof.direction === 'south' ? Math.abs(line.y1 - line.y2) : Math.abs(line.x1 - line.x2)
- }
-
- const h = calcLength * tanSlope
- const resultLength = Math.sqrt(Math.pow(calcLength, 2) + Math.pow(h, 2))
-
- line.set({
- attributes: {
- ...line.attributes,
- actualSize: parseInt((Math.floor(resultLength) * 10).toFixed(0)),
- planeSize: parseInt(line.length * 10),
- },
- })
- })
- roof.baseLines.forEach((line) => {
- line.set({
- attributes: {
- ...line.attributes,
- actualSize: parseInt((Math.floor(line.length) * 10).toFixed(0)),
- planeSize: parseInt(line.length * 10),
- },
- })
- })
- })
- }
-
- const changeSurfaceLinePropertyEvent = (roof) => {
+ const changeSurfaceLinePropertyEvent = () => {
let tmpLines = []
- roof.set({
- selectable: false,
- })
- canvas.discardActiveObject()
- roof.lines.forEach((obj, index) => {
- const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], {
- ...obj,
- stroke: 'rgb(3, 255, 0)',
- strokeWidth: 8,
- selectable: true,
- name: 'lineProperty',
- index: index,
+ const roof = canvas.getActiveObject()
+
+ if (roof) {
+ roof.set({
+ selectable: false,
})
- tmpLines.push(tmpLine)
- canvas.add(tmpLine)
- })
-
- addCanvasMouseEventListener('mouse:down', (e) => {
- const selectedLine = e.target
- if (selectedLine) {
- selectedLine.set({
- stroke: 'red',
- name: 'selectedLineProperty',
+ roof.lines.forEach((obj, index) => {
+ const tmpLine = new QLine([obj.x1, obj.y1, obj.x2, obj.y2], {
+ ...obj,
+ stroke: 'rgb(3, 255, 0)',
+ strokeWidth: 8,
+ selectable: true,
+ name: 'lineProperty',
+ lineIndex: index,
})
- tmpLines.forEach((line) => {
- if (line.index !== selectedLine.index) {
+
+ tmpLines.push(tmpLine)
+ canvas.add(tmpLine)
+ })
+
+ addCanvasMouseEventListener('mouse:down', (e) => {
+ const selectedLine = e.target
+ if (selectedLine && selectedLine.name !== 'roof') {
+ tmpLines.forEach((line) => {
line.set({
stroke: 'rgb(3, 255, 0)',
name: 'lineProperty',
})
- }
- })
- } else {
- tmpLines.forEach((line) => {
- line.set({
- stroke: 'rgb(3, 255, 0)',
- name: 'lineProperty',
})
- })
- }
- })
+ selectedLine.set({
+ stroke: 'red',
+ name: 'selectedLineProperty',
+ })
+ } else {
+ tmpLines.forEach((line) => {
+ line.set({
+ stroke: 'rgb(3, 255, 0)',
+ name: 'lineProperty',
+ })
+ })
+ }
+ })
- canvas.renderAll()
+ canvas.renderAll()
+ }
+ canvas.discardActiveObject()
}
- const changeSurfaceLineProperty = (property) => {
- console.log(property)
+ const changeSurfaceLineProperty = (property, roof) => {
if (!property) {
swalFire({ text: getMessage('modal.line.property.change'), icon: 'error' })
return
}
- const selectedLine = canvas.getActiveObjects()
- if (selectedLine && selectedLine[0].name === 'selectedLineProperty') {
+ const selectedLine = canvas.getActiveObjects()[0] //배열로 떨어짐 한개만 선택가능
+ if (selectedLine && selectedLine.name === 'selectedLineProperty') {
swalFire({
text: getMessage('modal.line.property.change.confirm'),
type: 'confirm',
confirmFn: () => {
- selectedLine.set({
+ const lineIndex = selectedLine.lineIndex
+ roof.lines[lineIndex].attributes = {
+ ...roof.lines[lineIndex].attributes,
type: property.value,
- })
+ }
+ canvas.renderAll()
},
})
} else {
@@ -965,22 +930,70 @@ export function useSurfaceShapeBatch() {
}
}
- const changeSurfaceFlowDirection = (roof, direction, orientation) => {
- roof.set({
- direction: direction,
+ const changeSurfaceLinePropertyReset = (roof) => {
+ const lines = canvas.getObjects().filter((obj) => obj.name === 'lineProperty' || obj.name === 'selectedLineProperty')
+
+ lines.forEach((line) => {
+ canvas.remove(line)
})
- drawDirectionArrow(roof)
+
+ if (roof) {
+ roof.set({
+ selectable: true,
+ })
+ }
canvas?.renderAll()
}
+ const updateFlippedPoints = (polygon) => {
+ if (!(polygon instanceof fabric.Polygon)) {
+ console.error('The object is not a Polygon.')
+ return
+ }
+
+ const { flipX, flipY, width, height, points, left, top, scaleX, scaleY } = polygon
+
+ // 현재 points의 사본 가져오기
+ const newPoints = points.map((point) => {
+ let x = point.x
+ let y = point.y
+
+ // flipX 적용
+ if (flipX) {
+ x = width - x
+ }
+
+ // flipY 적용
+ if (flipY) {
+ y = height - y
+ }
+
+ // 스케일 및 전역 좌표 고려
+ x = (x - width / 2) * scaleX + width / 2
+ y = (y - height / 2) * scaleY + height / 2
+
+ return { x, y }
+ })
+
+ // flipX, flipY를 초기화
+ polygon.flipX = false
+ polygon.flipY = false
+
+ // points 업데이트
+ polygon.set({ points: newPoints })
+ polygon.setCoords()
+
+ return polygon
+ }
+
return {
applySurfaceShape,
deleteAllSurfacesAndObjects,
moveSurfaceShapeBatch,
resizeSurfaceShapeBatch,
- surfaceShapeActualSize,
- changeSurfaceFlowDirection,
+
changeSurfaceLinePropertyEvent,
changeSurfaceLineProperty,
+ changeSurfaceLinePropertyReset,
}
}
diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js
index 6ffb03fc..46dbbb35 100644
--- a/src/hooks/useCanvas.js
+++ b/src/hooks/useCanvas.js
@@ -109,11 +109,24 @@ export function useCanvas(id) {
OBJECT_PROTOTYPE.forEach((type) => {
type.toObject = function (propertiesToInclude) {
let source = {}
+
for (let key in this) {
if (typeof this[key] !== 'function' && SAVE_KEY.includes(key)) {
source.key = this[key]
}
}
+
+ //QLine에 커스텀 어트리뷰트 넣기
+ if (this.type === 'QLine') {
+ if (this.attributes) {
+ this.attributes.type = this.attributes.type || 'default'
+ source.attributes = {
+ ...this.attributes,
+ type: this.attributes.type,
+ }
+ }
+ }
+
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), source)
}
})
diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js
index 42266c8a..0e8cd64b 100644
--- a/src/hooks/useCanvasEvent.js
+++ b/src/hooks/useCanvasEvent.js
@@ -225,7 +225,9 @@ export function useCanvasEvent() {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
- obj.set({ stroke: 'black' })
+ if (obj.name !== 'moduleSetupSurface') {
+ obj.set({ stroke: 'black' })
+ }
}
})
}
diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js
index 22e388db..f7f79f23 100644
--- a/src/hooks/useContextMenu.js
+++ b/src/hooks/useContextMenu.js
@@ -1,8 +1,7 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
-import { currentMenuState, currentObjectState } from '@/store/canvasAtom'
+import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { MENU } from '@/common/common'
-import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid'
@@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy'
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
import { gridColorState } from '@/store/gridAtom'
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom'
-import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy'
+import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit'
import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
@@ -24,7 +23,6 @@ import { useCommonUtils } from './common/useCommonUtils'
import { useMessage } from '@/hooks/useMessage'
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
-import ImageSizeSetting from '@/components/floor-plan/modal/image/ImageSizeSetting'
import PanelEdit from '@/components/floor-plan/modal/module/PanelEdit'
import DimensionLineSetting from '@/components/floor-plan/modal/dimensionLine/DimensionLineSetting'
import ColumnRemove from '@/components/floor-plan/modal/module/column/ColumnRemove'
@@ -34,8 +32,12 @@ import RowInsert from '@/components/floor-plan/modal/module/row/RowInsert'
import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumberEdit'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
+import { fontSelector, globalFontAtom } from '@/store/fontAtom'
+import { useLine } from '@/hooks/useLine'
+import { useSwal } from '@/hooks/useSwal'
export function useContextMenu() {
+ const canvas = useRecoilValue(canvasState)
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
@@ -51,7 +53,12 @@ export function useContextMenu() {
const [column, setColumn] = useState(null)
const { handleZoomClear } = useCanvasEvent()
const { moveObjectBatch } = useObjectBatch({})
- const { moveSurfaceShapeBatch, surfaceShapeActualSize } = useSurfaceShapeBatch()
+ const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
+ const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
+ const { addLine, removeLine } = useLine()
+ const commonTextFont = useRecoilValue(fontSelector('commonText'))
+ const { swalFire } = useSwal()
+
const currentMenuSetting = () => {
switch (currentMenu) {
case MENU.PLAN_DRAWING:
@@ -95,11 +102,6 @@ export function useContextMenu() {
case MENU.ROOF_COVERING.DEFAULT:
setContextMenu([
[
- {
- id: 'refresh',
- name: getMessage('refresh'),
- fn: () => handleZoomClear(),
- },
{
id: 'roofMaterialPlacement',
name: getMessage('contextmenu.roof.material.placement'),
@@ -121,11 +123,6 @@ export function useContextMenu() {
name: getMessage('contextmenu.wallline.remove'),
fn: () => deleteOuterLineObject(),
},
- {
- id: 'imageSizeEdit',
- name: getMessage('modal.image.size.setting'),
- component: ,
- },
],
[
{
@@ -137,30 +134,81 @@ export function useContextMenu() {
id: 'auxiliaryMove',
name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
shortcut: ['m', 'M'],
- component: ,
+ component: ,
},
{
id: 'auxiliaryCopy',
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
shortcut: ['c', 'C'],
- component: ,
+ component: ,
},
{
id: 'auxiliaryRemove',
shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
+ fn: () => {
+ const roof = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0]
+ const innerLines = roof.innerLines?.filter((line) => currentObject.id !== line.id)
+ roof.innerLines = [...innerLines]
+ canvas.remove(currentObject)
+ canvas.discardActiveObject()
+ },
},
{
id: 'auxiliaryVerticalBisector',
name: getMessage('contextmenu.auxiliary.vertical.bisector'),
- },
- {
- id: 'auxiliaryCut',
- name: getMessage('contextmenu.auxiliary.cut'),
+ fn: () => {
+ const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
+ const length = currentObject.length
+
+ let startX, startY, endX, endY
+ if (slope === 0) {
+ startX = endX = (currentObject.x1 + currentObject.x2) / 2
+ startY = currentObject.y2 - length / 2
+ endY = currentObject.y2 + length / 2
+ } else if (slope === Infinity) {
+ startX = currentObject.x1 - length / 2
+ startY = endY = (currentObject.y1 + currentObject.y2) / 2
+ endX = currentObject.x1 + length / 2
+ } else {
+ const bisectorSlope = -1 / slope
+ const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope)
+ const dy = bisectorSlope * dx
+
+ startX = (currentObject.x1 + currentObject.x2) / 2 + dx
+ startY = (currentObject.y1 + currentObject.y2) / 2 + dy
+ endX = (currentObject.x1 + currentObject.x2) / 2 - dx
+ endY = (currentObject.y1 + currentObject.y2) / 2 - dy
+ }
+
+ const line = addLine([startX, startY, endX, endY], {
+ stroke: 'red',
+ strokeWidth: 1,
+ selectable: true,
+ name: 'auxiliaryLine',
+ attributes: { ...currentObject.attributes },
+ })
+ canvas
+ .getObjects()
+ .filter((obj) => obj.id === currentObject.attributes.roofId)[0]
+ .innerLines.push(line)
+ },
},
{
id: 'auxiliaryRemoveAll',
name: getMessage('contextmenu.auxiliary.remove.all'),
+ fn: () => {
+ if (!currentObject) {
+ swalFire({ text: '지붕을 선택해주세요.' })
+ return
+ }
+ const innerLines = canvas.getObjects().filter((obj) => obj.id === currentObject.attributes.roofId)[0].innerLines
+ innerLines.forEach((line) => {
+ canvas.remove(line)
+ })
+ innerLines.length = 0
+ canvas.renderAll()
+ },
},
],
])
@@ -193,16 +241,12 @@ export function useContextMenu() {
shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`,
},
- {
- id: 'imageSizeEdit',
- name: getMessage('modal.image.size.setting'),
- component: ,
- },
],
[
{
id: 'roofMaterialEdit',
name: getMessage('contextmenu.roof.material.edit'),
+ component: ,
},
{
id: 'linePropertyEdit',
@@ -260,6 +304,7 @@ export function useContextMenu() {
useEffect(() => {
if (currentObject?.name) {
+ console.log('object', currentObject)
switch (currentObject.name) {
case 'triangleDormer':
case 'pentagonDormer':
@@ -304,11 +349,6 @@ export function useContextMenu() {
case 'roof':
setContextMenu([
[
- {
- id: 'surfaceShapeActualSize',
- name: '면형상 실측',
- fn: () => surfaceShapeActualSize(),
- },
{
id: 'sizeEdit',
name: '사이즈 변경',
@@ -429,7 +469,26 @@ export function useContextMenu() {
{
id: 'commonTextFontSetting',
name: getMessage('contextmenu.font.setting'),
- component: ,
+ component: (
+